X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=imap.c;h=65d225381b7c1b926c5d38d1e8312dbaf67c6ee2;hb=98cfcef26048bba06975e68a1aad05a8bac0d65d;hp=1364027f03a496796fbdf4c9fd32727c3e4c81c5;hpb=5a52b46d72abea688eda72c403634ec95046db40;p=~andy%2Ffetchmail diff --git a/imap.c b/imap.c index 1364027f..65d22538 100644 --- a/imap.c +++ b/imap.c @@ -10,18 +10,15 @@ #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 */ @@ -179,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]; @@ -188,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 */ @@ -269,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); } @@ -320,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; @@ -350,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 @@ -372,7 +369,9 @@ 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, const char *command, const char *name) @@ -408,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 @@ -438,9 +438,9 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) * 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: @@ -455,7 +455,8 @@ 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. */ - 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); @@ -463,9 +464,11 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) } 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); } @@ -530,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. @@ -607,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 @@ -754,45 +730,33 @@ 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 * 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 = (imap_version >= IMAP4) && ctl->keep; + flag skipdeleted = 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"))) { @@ -824,39 +788,25 @@ 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; @@ -1067,7 +1017,7 @@ 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; @@ -1155,7 +1105,7 @@ 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: @@ -1180,7 +1130,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); } @@ -1195,7 +1146,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); } @@ -1221,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 */ @@ -1311,26 +1257,26 @@ 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 */ 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++; @@ -1358,11 +1304,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)