X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=imap.c;h=cb87eda5378f258f20955c3524f2f84c6d29cd0e;hb=f6c7034fdbeb25d7d8eb0e62c0fe5de563eb7b55;hp=93f37235ffaf2ac7c7aa88520179dc4cbbe28d7e;hpb=d82bc9feeecb2ec42953bbe78c243fc9deba0de1;p=~andy%2Ffetchmail diff --git a/imap.c b/imap.c index 93f37235..cb87eda5 100644 --- a/imap.c +++ b/imap.c @@ -8,6 +8,7 @@ #include "config.h" #include #include +#include #include #if defined(STDC_HEADERS) #include @@ -178,7 +179,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]; @@ -187,7 +188,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 */ @@ -268,7 +273,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); } @@ -289,57 +294,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))) - 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))) + if ((result = ntlm_helper(sock, ctl, "IMAP"))) 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; @@ -363,7 +324,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; @@ -399,6 +360,8 @@ static void capa_probe(int sock, struct query *ctl) 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 @@ -416,6 +379,8 @@ static void capa_probe(int sock, struct query *ctl) } peek_capable = (imap_version >= IMAP4); + + return PS_SUCCESS; } static int do_authcert (int sock, const char *command, const char *name) @@ -440,9 +405,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; /* @@ -454,7 +416,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 @@ -476,16 +439,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, + && (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) + ctl->server.pollname, &ctl->remotename)) != -1) { /* * RFC 2595 says this: @@ -500,22 +464,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); } @@ -572,8 +534,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) 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 { @@ -610,8 +570,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; } @@ -647,8 +605,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; } @@ -702,8 +658,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; } @@ -812,13 +766,10 @@ static int imap_idle(int sock) return(ok); } -/* maximum number of numbers we can process in "SEARCH" response */ -# define IMAP_SEARCH_MAX 1000 - static int imap_search(int sock, struct query *ctl, int count) /* search for unseen messages */ { - int ok, first, last; + int ok; char buf[MSGBUFSIZE+1], *cp; /* Don't count deleted messages. Enabled only for IMAP4 servers or @@ -828,29 +779,20 @@ static int imap_search(int sock, struct query *ctl, int count) flag skipdeleted = (imap_version >= IMAP4) && ctl->keep; const char *undeleted; - /* Skip range search if there are less than or equal to - * IMAP_SEARCH_MAX mails. */ - flag skiprangesearch = (count <= IMAP_SEARCH_MAX); + /* 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 (first = 1, last = IMAP_SEARCH_MAX; first <= count; first += IMAP_SEARCH_MAX, last += IMAP_SEARCH_MAX) + for (;;) { - if (last > count) - last = count; - -restartsearch: undeleted = (skipdeleted ? " UNDELETED" : ""); - if (skiprangesearch) - gen_send(sock, "SEARCH UNSEEN%s", undeleted); - else if (last == first) - gen_send(sock, "SEARCH %d UNSEEN%s", last, undeleted); - else - gen_send(sock, "SEARCH %d:%d UNSEEN%s", first, last, undeleted); - while ((ok = imap_response(sock, buf)) == PS_UNTAGGED) + 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"))) { @@ -882,41 +824,28 @@ restartsearch: } } } - /* if there is a protocol error on the first loop, try a - * different search command */ - if (ok == PS_ERROR && first == 1) + if (ok != PS_ERROR) /* success or non-protocol error */ + return(ok); + + /* there is a protocol error. try a different search command. */ + if (skipdeleted) { - if (skipdeleted) - { - /* retry with "SEARCH 1:1000 UNSEEN" */ - skipdeleted = FALSE; - goto restartsearch; - } - if (!skiprangesearch) - { - /* retry with "SEARCH UNSEEN" */ - skiprangesearch = TRUE; - goto restartsearch; - } - /* try with "FETCH 1:n FLAGS" */ - goto fetchflags; + /* retry with "SEARCH UNSEEN" */ + skipdeleted = FALSE; + continue; } - if (ok != PS_SUCCESS) - return(ok); - /* loop back only when searching in range */ - if (skiprangesearch) - break; + /* try with "FETCH 1:n FLAGS" */ + break; } - return(PS_SUCCESS); -fetchflags: 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)) == PS_UNTAGGED) + while ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED) { unsigned int num; + int consumed; /* expected response format: * IMAP< * 1 FETCH (FLAGS (\Seen)) @@ -926,7 +855,9 @@ fetchflags: * IMAP< * 5 FETCH (UID 10 FLAGS (\Recent)) */ if (unseen < count - && sscanf(buf, "* %u FETCH ", &num) == 1 + && 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") @@ -1122,10 +1053,11 @@ 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; /* expected response formats: @@ -1133,7 +1065,9 @@ static int imap_getpartialsizes(int sock, int first, int last, int *sizes) * IMAP< * 1 FETCH (RFC822.SIZE 1187) * IMAP< * 1 FETCH (UID 16 RFC822.SIZE 1447) */ - if (sscanf(buf, "* %d FETCH ", &num) == 1 + 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) { @@ -1207,18 +1141,22 @@ 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 */ - if ((ok = imap_response(sock, buf)) == PS_UNTAGGED) + if ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED) { + 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< * 1 FETCH (UID 16 RFC822.SIZE 4029 RFC822.HEADER {1360} */ - if (sscanf(buf, "* %d FETCH ", &num) == 1 + 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}", lenp) == 1) + && (ptr = strstr(buf, "RFC822.HEADER")) + && sscanf(ptr, "RFC822.HEADER {%d}%n", lenp, &consumed) == 1 + && ptr[consumed-1] == '}') { return(PS_SUCCESS); } @@ -1228,7 +1166,8 @@ static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp) /* try to recover for some responses */ if (!strncmp(buf, "* NO", 4) || - !strncmp(buf, "* BAD", 5)) + !strncmp(buf, "* BAD", 5) || + strstr(buf, "FETCH ()")) { return(PS_TRANSIENT); } @@ -1243,7 +1182,7 @@ static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp) /* an unexpected tagged response */ if (outlevel > O_SILENT) report(stderr, GT_("Incorrect FETCH response: %s.\n"), buf); - return(PS_ERROR); + return(PS_TRANSIENT); } return(ok); } @@ -1312,6 +1251,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) @@ -1352,6 +1298,13 @@ static int imap_delete(int sock, struct query *ctl, int number) /* set delete flag for given message */ { int ok; + /* Select which flags to set on message deletion: */ + const char delflags_seen[] = "\\Seen \\Deleted"; + static const char *delflags; + /* Which environment variable to look for: */ + + /* DEFAULT since many fetchmail versions <= 6.3.X */ + delflags = delflags_seen; (void)ctl; /* expunges change the fetch numbers */ @@ -1361,17 +1314,18 @@ static int imap_delete(int sock, struct query *ctl, int number) * 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 + * We set \Seen because there are some IMAP servers (notably HP + * OpenMail and MS Exchange) do message-receipt DSNs, + * but only when the seen bit gets 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))) + ? "STORE %d +FLAGS.SILENT (%s)" + : "STORE %d +FLAGS (%s)", + number, delflags))) return(ok); else deletions++;