X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=imap.c;h=a598519b59e1685c1d6eee7fb937cc609620837a;hb=87bcf29364c4640edb87cc2186b965d1a564d70c;hp=4a440865d6be1536616f285d0301d2b3978c615a;hpb=ca66045dfe48faa985f39c0208aded9f93d3da96;p=~andy%2Ffetchmail diff --git a/imap.c b/imap.c index 4a440865..a598519b 100644 --- a/imap.c +++ b/imap.c @@ -8,19 +8,17 @@ #include "config.h" #include #include +#include #include -#if defined(STDC_HEADERS) #include #include #include -#endif #include "fetchmail.h" #include "socket.h" -#include "i18n.h" +#include "gettext.h" /* imap_version values */ -#define IMAP2 -1 /* IMAP2 or IMAP2BIS, RFC1176 */ #define IMAP4 0 /* IMAP4 rev 0, RFC1730 */ #define IMAP4rev1 1 /* IMAP4 rev 1, RFC2060 */ @@ -74,7 +72,9 @@ static int imap_untagged_response(int sock, const char *buf) } else if (strstr(buf, " EXISTS")) { - count = atoi(buf+2); + char *t; unsigned long u; + errno = 0; + u = strtoul(buf+2, &t, 10); /* * Don't trust the message count passed by the server. * Without this check, it might be possible to do a @@ -82,11 +82,15 @@ static int imap_untagged_response(int sock, const char *buf) * count, and allocate a malloc area that would overlap * a portion of the stack. */ - if ((unsigned)count > INT_MAX/sizeof(int)) + if (errno /* strtoul failed */ + || t == buf+2 /* no valid data */ + || u > (unsigned long)(INT_MAX/sizeof(int)) /* too large */) { - report(stderr, GT_("bogus message count!")); + report(stderr, GT_("bogus message count in \"%s\"!"), buf); return(PS_PROTOCOL); } + count = u; /* safe as long as count <= INT_MAX - checked above */ + if ((recentcount = count - oldcount) < 0) recentcount = 0; @@ -117,14 +121,22 @@ static int imap_untagged_response(int sock, const char *buf) # if 0 else if (strstr(buf, " RECENT")) { + /* fixme: use strto[u]l and error checking */ recentcount = atoi(buf+2); } # endif else if (strstr(buf, " EXPUNGE")) { + unsigned long u; char *t; /* the response "* 10 EXPUNGE" means that the currently * tenth (i.e. only one) message has been deleted */ - if (atoi(buf+2) > 0) + errno = 0; + u = strtoul(buf+2, &t, 10); + if (errno /* conversion error */ || t == buf+2 /* no number found */) { + report(stderr, GT_("bogus EXPUNGE count in \"%s\"!"), buf); + return PS_PROTOCOL; + } + if (u > 0) { if (count > 0) count--; @@ -164,7 +176,7 @@ static int imap_untagged_response(int sock, const char *buf) return(PS_SUCCESS); } -static int imap_response(int sock, char *argbuf) +static int imap_response(int sock, char *argbuf, struct RecvSplit *rs) /* parse command response */ { char buf[MSGBUFSIZE+1]; @@ -173,7 +185,11 @@ static int imap_response(int sock, char *argbuf) int ok; char *cp; - if ((ok = gen_recv(sock, buf, sizeof(buf)))) + if (rs) + ok = gen_recv_split(sock, buf, sizeof(buf), rs); + else + ok = gen_recv(sock, buf, sizeof(buf)); + if (ok != PS_SUCCESS) return(ok); /* all tokens in responses are caseblind */ @@ -254,7 +270,7 @@ static int imap_ok(int sock, char *argbuf) { int ok; - while ((ok = imap_response(sock, argbuf)) == PS_UNTAGGED) + while ((ok = imap_response(sock, argbuf, NULL)) == PS_UNTAGGED) ; /* wait for the tagged response */ return(ok); } @@ -275,57 +291,13 @@ static int imap_ok(int sock, char *argbuf) static int do_imap_ntlm(int sock, struct query *ctl) { - tSmbNtlmAuthRequest request; - tSmbNtlmAuthChallenge challenge; - tSmbNtlmAuthResponse response; - - char msgbuf[2048]; - int result,len; + int result; gen_send(sock, "AUTHENTICATE NTLM"); - if ((result = gen_recv(sock, msgbuf, sizeof msgbuf))) + if ((result = ntlm_helper(sock, ctl, "IMAP"))) return result; - - if (msgbuf[0] != '+') - return PS_AUTHFAIL; - - buildSmbNtlmAuthRequest(&request,ctl->remotename,NULL); - - if (outlevel >= O_DEBUG) - dumpSmbNtlmAuthRequest(stdout, &request); - - memset(msgbuf,0,sizeof msgbuf); - to64frombits (msgbuf, &request, SmbLength(&request)); - - if (outlevel >= O_MONITOR) - report(stdout, "IMAP> %s\n", msgbuf); - - strcat(msgbuf,"\r\n"); - SockWrite (sock, msgbuf, strlen (msgbuf)); - - if ((gen_recv(sock, msgbuf, sizeof msgbuf))) - return result; - - len = from64tobits (&challenge, msgbuf, sizeof(challenge)); - - if (outlevel >= O_DEBUG) - dumpSmbNtlmAuthChallenge(stdout, &challenge); - - buildSmbNtlmAuthResponse(&challenge, &response,ctl->remotename,ctl->password); - - if (outlevel >= O_DEBUG) - dumpSmbNtlmAuthResponse(stdout, &response); - - memset(msgbuf,0,sizeof msgbuf); - to64frombits (msgbuf, &response, SmbLength(&response)); - - if (outlevel >= O_MONITOR) - report(stdout, "IMAP> %s\n", msgbuf); - - strcat(msgbuf,"\r\n"); - SockWrite (sock, msgbuf, strlen (msgbuf)); - + result = imap_ok (sock, NULL); if (result == PS_SUCCESS) return PS_SUCCESS; @@ -349,7 +321,7 @@ static void imap_canonicalize(char *result, char *raw, size_t maxlen) result[j] = '\0'; } -static void capa_probe(int sock, struct query *ctl) +static int capa_probe(int sock, struct query *ctl) /* set capability variables from a CAPA probe */ { int ok; @@ -379,12 +351,8 @@ static void capa_probe(int sock, struct query *ctl) report(stdout, GT_("Protocol identified as IMAP4 rev 0\n")); } } - else if (ok == PS_ERROR) - { - imap_version = IMAP2; - if (outlevel >= O_DEBUG) - report(stdout, GT_("Protocol identified as IMAP2 or IMAP2BIS\n")); - } + else + return ok; /* * Handle idling. We depend on coming through here on startup @@ -401,10 +369,12 @@ static void capa_probe(int sock, struct query *ctl) report(stdout, GT_("will idle after poll\n")); } - peek_capable = (imap_version >= IMAP4); + peek_capable = TRUE; + + return PS_SUCCESS; } -static int do_authcert (int sock, char *command, const char *name) +static int do_authcert (int sock, const char *command, const char *name) /* do authentication "external" (authentication provided by client cert) */ { char buf[256]; @@ -426,9 +396,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) /* apply for connection authorization */ { int ok = 0; -#ifdef SSL_ENABLE - int got_tls = 0; -#endif (void)greeting; /* @@ -440,7 +407,8 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) else expunge_period = 1; - capa_probe(sock, ctl); + if ((ok = capa_probe(sock, ctl))) + return ok; /* * If either (a) we saw a PREAUTH token in the greeting, or @@ -462,16 +430,17 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) if (ctl->sslcommonname) commonname = ctl->sslcommonname; - if (strstr(capabilities, "STARTTLS")) + if (strstr(capabilities, "STARTTLS") + || must_tls(ctl)) /* if TLS is mandatory, ignore capabilities */ { /* Use "tls1" rather than ctl->sslproto because tls1 is the only * protocol that will work with STARTTLS. Don't need to worry * whether TLS is mandatory or opportunistic unless SSLOpen() fails * (see below). */ if (gen_transact(sock, "STARTTLS") == PS_SUCCESS - && SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck, - ctl->sslcertpath, ctl->sslfingerprint, commonname, - ctl->server.pollname, &ctl->remotename) != -1) + && (set_timeout(mytimeout), SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck, + ctl->sslcertfile, ctl->sslcertpath, ctl->sslfingerprint, commonname, + ctl->server.pollname, &ctl->remotename)) != -1) { /* * RFC 2595 says this: @@ -486,22 +455,20 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) * Now that we're confident in our TLS connection we can * guarantee a secure capability re-probe. */ - got_tls = 1; - capa_probe(sock, ctl); + if ((ok = capa_probe(sock, ctl))) + return ok; if (outlevel >= O_VERBOSE) { report(stdout, GT_("%s: upgrade to TLS succeeded.\n"), commonname); } - } - } - - if (!got_tls) { - if (must_tls(ctl)) { + } else if (must_tls(ctl)) { /* Config required TLS but we couldn't guarantee it, so we must * stop. */ + set_timeout(0); report(stderr, GT_("%s: upgrade to TLS failed.\n"), commonname); return PS_SOCKET; } else { + set_timeout(0); if (outlevel >= O_VERBOSE) { report(stdout, GT_("%s: opportunistic upgrade to TLS failed, trying to continue\n"), commonname); } @@ -529,7 +496,7 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) * and IGNORE errors. */ { char *tmp = strstr(capabilities, " ID"); - if (tmp && !isalnum(tmp[3]) && strstr(ctl->server.via ? ctl->server.via : ctl->server.pollname, "yahoo.com")) { + if (tmp && !isalnum((unsigned char)tmp[3]) && strstr(ctl->server.via ? ctl->server.via : ctl->server.pollname, "yahoo.com")) { (void)gen_transact(sock, "ID (\"guid\" \"1\")"); } } @@ -551,15 +518,13 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) } #ifdef GSSAPI - if ((ctl->server.authenticate == A_ANY + if (((ctl->server.authenticate == A_ANY && check_gss_creds("imap", ctl->server.truename) == PS_SUCCESS) || ctl->server.authenticate == A_GSSAPI) && strstr(capabilities, "AUTH=GSSAPI")) { if ((ok = do_gssauth(sock, "AUTHENTICATE", "imap", ctl->server.truename, ctl->remotename))) { - /* SASL cancellation of authentication */ - gen_send(sock, "*"); if (ctl->server.authenticate != A_ANY) return ok; } else { @@ -568,24 +533,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) } #endif /* GSSAPI */ -#ifdef KERBEROS_V4 - if ((ctl->server.authenticate == A_ANY - || ctl->server.authenticate == A_KERBEROS_V4 - || ctl->server.authenticate == A_KERBEROS_V5) - && strstr(capabilities, "AUTH=KERBEROS_V4")) - { - if ((ok = do_rfc1731(sock, "AUTHENTICATE", ctl->server.truename))) - { - /* SASL cancellation of authentication */ - gen_send(sock, "*"); - if(ctl->server.authenticate != A_ANY) - return ok; - } - else - return ok; - } -#endif /* KERBEROS_V4 */ - /* * No such luck. OK, now try the variants that mask your password * in a challenge-response. @@ -596,8 +543,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) { if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL))) { - /* SASL cancellation of authentication */ - gen_send(sock, "*"); if(ctl->server.authenticate != A_ANY) return ok; } @@ -633,8 +578,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) && strstr (capabilities, "AUTH=NTLM")) { if ((ok = do_imap_ntlm(sock, ctl))) { - /* SASL cancellation of authentication */ - gen_send(sock, "*"); if(ctl->server.authenticate != A_ANY) return ok; } @@ -649,15 +592,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) } #endif /* NTLM_ENABLE */ -#ifdef __UNUSED__ /* The Cyrus IMAP4rev1 server chokes on this */ - /* this handles either AUTH=LOGIN or AUTH-LOGIN */ - if ((imap_version >= IMAP4rev1) && (!strstr(capabilities, "LOGIN"))) - { - report(stderr, - GT_("Required LOGIN capability not supported by server\n")); - } -#endif /* __UNUSED__ */ - /* * We're stuck with sending the password en clair. * The reason for this odd-looking logic is that some @@ -688,8 +622,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) free(remotename); if (ok) { - /* SASL cancellation of authentication */ - gen_send(sock, "*"); if(ctl->server.authenticate != A_ANY) return ok; } @@ -798,6 +730,113 @@ static int imap_idle(int sock) return(ok); } +static int imap_search(int sock, struct query *ctl, int count) +/* search for unseen messages */ +{ + int ok; + char buf[MSGBUFSIZE+1], *cp; + + /* Don't count deleted messages. Enabled only for IMAP4 servers or + * higher and only when keeping mails. This flag will have an + * effect only when user has marked some unread mails for deletion + * using another e-mail client. */ + flag skipdeleted = ctl->keep; + const char *undeleted; + + /* structure to keep the end portion of the incomplete response */ + struct RecvSplit rs; + + /* startcount is higher than count so that if there are no + * unseen messages, imap_getsizes() will not need to do + * anything! */ + startcount = count + 1; + + for (;;) + { + undeleted = (skipdeleted ? " UNDELETED" : ""); + gen_send(sock, "SEARCH UNSEEN%s", undeleted); + gen_recv_split_init("* SEARCH", &rs); + while ((ok = imap_response(sock, buf, &rs)) == PS_UNTAGGED) + { + if ((cp = strstr(buf, "* SEARCH"))) + { + char *ep; + + cp += 8; /* skip "* SEARCH" */ + while (*cp && unseen < count) + { + /* skip whitespace */ + while (*cp && isspace((unsigned char)*cp)) + cp++; + if (*cp) + { + unsigned long um; + + errno = 0; + um = strtoul(cp,&ep,10); + if (errno == 0 && ep > cp + && um <= INT_MAX && um <= (unsigned)count) + { + unseen_messages[unseen++] = um; + if (outlevel >= O_DEBUG) + report(stdout, GT_("%lu is unseen\n"), um); + if (startcount > um) + startcount = um; + } + cp = ep; + } + } + } + } + if (ok != PS_ERROR) /* success or non-protocol error */ + return(ok); + + /* there is a protocol error. try a different search command. */ + if (skipdeleted) + { + /* retry with "SEARCH UNSEEN" */ + skipdeleted = FALSE; + continue; + } + /* try with "FETCH 1:n FLAGS" */ + break; + } + + if (count == 1) + gen_send(sock, "FETCH %d FLAGS", count); + else + gen_send(sock, "FETCH %d:%d FLAGS", 1, count); + while ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED) + { + unsigned int num; + int consumed; + + /* expected response format: + * IMAP< * 1 FETCH (FLAGS (\Seen)) + * IMAP< * 2 FETCH (FLAGS (\Seen \Deleted)) + * IMAP< * 3 FETCH (FLAGS ()) + * IMAP< * 4 FETCH (FLAGS (\Recent)) + * IMAP< * 5 FETCH (UID 10 FLAGS (\Recent)) + */ + if (unseen < count + && sscanf(buf, "* %u %n", &num, &consumed) == 1 + && 0 == strncasecmp(buf+consumed, "FETCH", 5) + && isspace((unsigned char)buf[consumed+5]) + && num >= 1 && num <= (unsigned)count + && strstr(buf, "FLAGS ") + && !strstr(buf, "\\SEEN") + && !strstr(buf, "\\DELETED")) + { + unseen_messages[unseen++] = num; + if (outlevel >= O_DEBUG) + report(stdout, GT_("%u is unseen\n"), num); + if (startcount > num) + startcount = num; + } + } + return(ok); +} + static int imap_getrange(int sock, struct query *ctl, const char *folder, @@ -805,7 +844,6 @@ static int imap_getrange(int sock, /* get range of messages to be fetched */ { int ok; - char buf[MSGBUFSIZE+1], *cp; /* find out how many messages are waiting */ *bytes = -1; @@ -909,44 +947,7 @@ static int imap_getrange(int sock, memset(unseen_messages, 0, count * sizeof(unsigned int)); unseen = 0; - /* don't count deleted messages, in case user enabled keep last time */ - gen_send(sock, "SEARCH UNSEEN NOT DELETED"); - while ((ok = imap_response(sock, buf)) == PS_UNTAGGED) - { - if ((cp = strstr(buf, "* SEARCH"))) - { - char *ep; - - cp += 8; /* skip "* SEARCH" */ - /* startcount is higher than count so that if there are no - * unseen messages, imap_getsizes() will not need to do - * anything! */ - startcount = count + 1; - - while (*cp && unseen < count) - { - /* skip whitespace */ - while (*cp && isspace((unsigned char)*cp)) - cp++; - if (*cp) - { - unsigned long um; - - errno = 0; - um = strtoul(cp,&ep,10); - if (errno == 0 && um <= UINT_MAX && um <= (unsigned)count) - { - unseen_messages[unseen++] = um; - if (outlevel >= O_DEBUG) - report(stdout, GT_("%lu is unseen\n"), um); - if (startcount > um) - startcount = um; - } - cp = ep; - } - } - } - } + ok = imap_search(sock, ctl, count); if (ok != 0) { report(stderr, GT_("search for unseen messages failed\n")); @@ -1016,20 +1017,23 @@ static int imap_getpartialsizes(int sock, int first, int last, int *sizes) gen_send(sock, "FETCH %d:%d RFC822.SIZE", first, last); else /* no unseen messages! */ return(PS_SUCCESS); - while ((ok = imap_response(sock, buf)) == PS_UNTAGGED) + while ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED) { unsigned int size; int num; + int consumed; + char *ptr; - if (sscanf(buf, "* %d FETCH (RFC822.SIZE %u)", &num, &size) == 2 - /* some servers (like mail.internode.on.net bld-mail04) return UID information here - * + /* expected response formats: * IMAP> A0005 FETCH 1 RFC822.SIZE + * IMAP< * 1 FETCH (RFC822.SIZE 1187) * IMAP< * 1 FETCH (UID 16 RFC822.SIZE 1447) - * IMAP< A0005 OK FETCH completed - * */ - || sscanf(buf, "* %d FETCH (UID %*s RFC822.SIZE %u)", &num, &size) == 2) + if (sscanf(buf, "* %d %n", &num, &consumed) == 1 + && 0 == strncasecmp(buf + consumed, "FETCH", 5) + && isspace((unsigned char)buf[consumed + 5]) + && (ptr = strstr(buf, "RFC822.SIZE ")) + && sscanf(ptr, "RFC822.SIZE %u", &size) == 1) { if (num >= first && num <= last) sizes[num - first] = size; @@ -1072,6 +1076,7 @@ static int imap_is_old(int sock, struct query *ctl, int number) return(seen); } +#if 0 static char *skip_token(char *ptr) { while(isspace((unsigned char)*ptr)) ptr++; @@ -1079,12 +1084,15 @@ static char *skip_token(char *ptr) while(isspace((unsigned char)*ptr)) ptr++; return(ptr); } +#endif static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp) /* request headers of nth message */ { char buf [MSGBUFSIZE+1]; int num; + int ok; + char *ptr; (void)ctl; /* expunges change the fetch numbers */ @@ -1097,47 +1105,50 @@ static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp) gen_send(sock, "FETCH %d RFC822.HEADER", number); /* looking for FETCH response */ - for (;;) + if ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED) { - int ok; - char *ptr; - - if ((ok = gen_recv(sock, buf, sizeof(buf)))) - return(ok); - ptr = skip_token(buf); /* either "* " or "AXXXX " */ - if (sscanf(ptr, "%d FETCH (RFC822.HEADER {%d}", &num, lenp) == 2 - /* some servers (like mail.internode.on.net bld-mail04) return UID information here - * + int consumed; + /* expected response formats: * IMAP> A0006 FETCH 1 RFC822.HEADER + * IMAP< * 1 FETCH (RFC822.HEADER {1360} * IMAP< * 1 FETCH (UID 16 RFC822.HEADER {1360} - * ... - * IMAP< ) - * IMAP< A0006 OK FETCH completed - * + * IMAP< * 1 FETCH (UID 16 RFC822.SIZE 4029 RFC822.HEADER {1360} */ - || sscanf(ptr, "%d FETCH (UID %*s RFC822.HEADER {%d}", &num, lenp) == 2) - break; - /* try to recover from chronically fucked-up M$ Exchange servers */ - else if (!strncmp(ptr, "NO", 2)) + if (sscanf(buf, "* %d %n", &num, &consumed) == 1 + && 0 == strncasecmp(buf + consumed, "FETCH", 5) + && isspace((unsigned char)buf[5+consumed]) + && num == number + && (ptr = strstr(buf, "RFC822.HEADER")) + && sscanf(ptr, "RFC822.HEADER {%d}%n", lenp, &consumed) == 1 + && ptr[consumed-1] == '}') { - /* wait for a tagged response */ - if (strstr (buf, "* NO")) - imap_ok (sock, 0); - return(PS_TRANSIENT); + return(PS_SUCCESS); } - else if (!strncmp(ptr, "BAD", 3)) + + /* wait for a tagged response */ + imap_ok (sock, 0); + + /* try to recover for some responses */ + if (!strncmp(buf, "* NO", 4) || + !strncmp(buf, "* BAD", 5) || + strstr(buf, "FETCH ()")) { - /* wait for a tagged response */ - if (strstr (buf, "* BAD")) - imap_ok (sock, 0); - return(PS_TRANSIENT); + return(PS_TRANSIENT); } - } - if (num != number) + /* a response which does not match any of the above */ + if (outlevel > O_SILENT) + report(stderr, GT_("Incorrect FETCH response: %s.\n"), buf); return(PS_ERROR); - else - return(PS_SUCCESS); + } + else if (ok == PS_SUCCESS) + { + /* an unexpected tagged response */ + if (outlevel > O_SILENT) + report(stderr, GT_("Incorrect FETCH response: %s.\n"), buf); + return(PS_TRANSIENT); + } + return(ok); } static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp) @@ -1161,28 +1172,23 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp) * equivalent". However, we know of at least one server that * treats them differently in the presence of MIME attachments; * the latter form downloads the attachment, the former does not. - * The server is InterChange, and the fool who implemented this - * misfeature ought to be strung up by his thumbs. + * The server is InterChange. * * When I tried working around this by disabling use of the 4rev1 form, * I found that doing this breaks operation with M$ Exchange. * Annoyingly enough, Exchange's refusal to cope is technically legal - * under RFC2062. Trust Microsoft, the Great Enemy of interoperability - * standards, to find a way to make standards compliance irritating.... + * under RFC2062. */ switch (imap_version) { case IMAP4rev1: /* RFC 2060 */ + default: gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number); break; case IMAP4: /* RFC 1730 */ gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number); break; - - default: /* RFC 1176 */ - gen_send(sock, "FETCH %d RFC822.TEXT", number); - break; } /* looking for FETCH response */ @@ -1204,6 +1210,13 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp) return PS_SUCCESS; } + /* Understand the empty string. Seen on Yahoo. */ + /* XXX FIXME: we should be able to handle strings here. */ + if (strstr(buf+10, "\"\")")) { + *lenp = 0; + return PS_SUCCESS; + } + /* * Try to extract a length from the FETCH response. RFC2060 requires * it to be present, but at least one IMAP server (Novell GroupWise) @@ -1211,15 +1224,21 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp) * server called dbmail that returns huge garbage lengths. */ if ((cp = strchr(buf, '{'))) { + long l; char *t; errno = 0; - *lenp = (int)strtol(cp + 1, (char **)NULL, 10); - if (errno == ERANGE || *lenp < 0) - *lenp = -1; /* length is too big/small for us to handle */ - } - else + ++ cp; + l = strtol(cp, &t, 10); + if (errno || t == cp || (t && !strchr(t, '}')) /* parse error */ + || l < 0 || l > INT_MAX /* range check */) { + *lenp = -1; + } else { + *lenp = l; + } + } else { *lenp = -1; /* missing length part in FETCH reponse */ + } - return(PS_SUCCESS); + return PS_SUCCESS; } static int imap_trail(int sock, struct query *ctl, const char *tag) @@ -1244,20 +1263,13 @@ static int imap_delete(int sock, struct query *ctl, int number) number -= expunged; /* - * Use SILENT if possible as a minor throughput optimization. - * Note: this has been dropped from IMAP4rev1. - * * We set Seen because there are some IMAP servers (notably HP * OpenMail) that do message-receipt DSNs, but only when the seen * bit is set. This is the appropriate time -- we get here right * after the local SMTP response that says delivery was * successful. */ - if ((ok = gen_transact(sock, - imap_version == IMAP4 - ? "STORE %d +FLAGS.SILENT (\\Seen \\Deleted)" - : "STORE %d +FLAGS (\\Seen \\Deleted)", - number))) + if ((ok = gen_transact(sock, "STORE %d +FLAGS.SILENT (\\Seen \\Deleted)", number))) return(ok); else deletions++; @@ -1285,11 +1297,7 @@ static int imap_mark_seen(int sock, struct query *ctl, int number) /* expunges change the message numbers */ number -= expunged; - return(gen_transact(sock, - imap_version == IMAP4 - ? "STORE %d +FLAGS.SILENT (\\Seen)" - : "STORE %d +FLAGS (\\Seen)", - number)); + return(gen_transact(sock,"STORE %d +FLAGS.SILENT (\\Seen)", number)); } static int imap_end_mailbox_poll(int sock, struct query *ctl)