X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=pop3.c;h=3ea79fb2a739dc0eb6f8ead2c5ef95a933a647db;hb=38ef894b8f693965f6d3ec72e345f0308b456112;hp=ec8cbdf5e846854e3949e9bef3acbe1d70b52791;hpb=3180b5104a12515299d085cc2672a762eecd8184;p=~andy%2Ffetchmail diff --git a/pop3.c b/pop3.c index ec8cbdf5..3ea79fb2 100644 --- a/pop3.c +++ b/pop3.c @@ -16,26 +16,23 @@ #if defined(STDC_HEADERS) #include #endif - +#include + #include "fetchmail.h" #include "socket.h" #include "i18n.h" -#if OPIE_ENABLE +#ifdef OPIE_ENABLE #include #endif /* OPIE_ENABLE */ -#ifndef strstr /* glibc-2.1 declares this as a macro */ -extern char *strstr(); /* needed on sysV68 R3V7.1. */ -#endif /* strstr */ - static int last; #ifdef SDPS_ENABLE char *sdps_envfrom; char *sdps_envto; #endif /* SDPS_ENABLE */ -#if OPIE_ENABLE +#ifdef OPIE_ENABLE static char lastok[POPBUFSIZE+1]; #endif /* OPIE_ENABLE */ @@ -46,15 +43,15 @@ static char lastok[POPBUFSIZE+1]; #if defined(KERBEROS_V4) || defined(KERBEROS_V5) flag has_kerberos = FALSE; #endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */ - flag has_cram = FALSE; + static flag has_cram = FALSE; #ifdef OPIE_ENABLE flag has_otp = FALSE; #endif /* OPIE_ENABLE */ #ifdef SSL_ENABLE - flag has_ssl = FALSE; + static flag has_ssl = FALSE; #endif /* SSL_ENABLE */ -#if NTLM_ENABLE +#ifdef NTLM_ENABLE #include "ntlm.h" static tSmbNtlmAuthRequest request; @@ -148,7 +145,7 @@ static int pop3_ok (int sock, char *argbuf) else return(PS_PROTOCOL); - while (isalpha(*bufp)) + while (isalpha((unsigned char)*bufp)) bufp++; if (*bufp) @@ -156,7 +153,7 @@ static int pop3_ok (int sock, char *argbuf) if (strcmp(buf,"+OK") == 0) { -#if OPIE_ENABLE +#ifdef OPIE_ENABLE strcpy(lastok, bufp); #endif /* OPIE_ENABLE */ ok = 0; @@ -210,6 +207,9 @@ static int pop3_ok (int sock, char *argbuf) else ok = PS_PROTOCOL; +#if POPBUFSIZE > MSGBUFSIZE +#error "POPBUFSIZE must not be larger than MSGBUFSIZE" +#endif if (argbuf != NULL) strcpy(argbuf,bufp); } @@ -268,13 +268,23 @@ static int capa_probe(int sock) return(ok); } +static void set_peek_capable(struct query *ctl) +{ + /* we're peek-capable means that the use of TOP is enabled, + * see pop3_fetch for details - short story, we can use TOP if + * we have a means of reliably tracking which mail we need to + * refetch should the connection abort in the middle. + * fetchall forces RETR, as does keep without UIDL */ + peek_capable = !ctl->fetchall && (!ctl->keep || ctl->server.uidl); +} + static int pop3_getauth(int sock, struct query *ctl, char *greeting) /* apply for connection authorization */ { int ok; char *start,*end; char *msg; -#if OPIE_ENABLE +#ifdef OPIE_ENABLE char *challenge; #endif /* OPIE_ENABLE */ #ifdef SSL_ENABLE @@ -365,8 +375,7 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) * These authentication methods are blessed by RFC1734, * describing the POP3 AUTHentication command. */ - if ((ctl->use_ssl != FLAG_FALSE) || - (ctl->server.authenticate == A_ANY) || + if ((ctl->server.authenticate == A_ANY) || (ctl->server.authenticate == A_GSSAPI) || (ctl->server.authenticate == A_KERBEROS_V4) || (ctl->server.authenticate == A_OTP) || @@ -440,7 +449,7 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) * rather than doing SASL. */ if (has_kerberos && -#if INET6_ENABLE +#ifdef INET6_ENABLE ctl->server.service && (strcmp(ctl->server.service, KPOP_PORT)!=0) #else /* INET6_ENABLE */ ctl->server.port != KPOP_PORT @@ -486,11 +495,12 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) } /* ordinary validation, no one-time password or RPA */ - gen_transact(sock, "USER %s", ctl->remotename); + if ((ok = gen_transact(sock, "USER %s", ctl->remotename))) + break; -#if OPIE_ENABLE +#ifdef OPIE_ENABLE /* see RFC1938: A One-Time Password System */ - if (challenge = strstr(lastok, "otp-")) { + if ((challenge = strstr(lastok, "otp-"))) { char response[OPIE_RESPONSE_MAX+1]; int i; @@ -513,7 +523,7 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) } #endif /* OPIE_ENABLE */ - strcpy(shroud, ctl->password); + strlcpy(shroud, ctl->password, sizeof(shroud)); ok = gen_transact(sock, "PASS %s", ctl->password); shroud[0] = '\0'; #ifdef SSL_ENABLE @@ -551,11 +561,11 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) *++end = '\0'; /* copy timestamp and password into digestion buffer */ - xalloca(msg, char *, (end-start+1) + strlen(ctl->password) + 1); + msg = xmalloc((end-start+1) + strlen(ctl->password) + 1); strcpy(msg,start); strcat(msg,ctl->password); - strcpy(ctl->digest, MD5Digest((unsigned char *)msg)); + free(msg); ok = gen_transact(sock, "APOP %s %s", ctl->remotename, ctl->digest); break; @@ -579,6 +589,12 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) return(ok); } +/* Disable the sleep. Based on patch by Brian Candler 2004-04-19/2004-11-08, + * accepted by Matthias Andree. + * + * Rationale: the server must have locked the spool before returning +OK; + * this sleep just wastes time and hence, for modem and GSM CSD users, money. */ +#ifdef WANT_BOGUS /* * Empirical experience shows some server/OS combinations * may need a brief pause even after any lockfiles on the @@ -587,46 +603,85 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) * this is only ever an issue with extremely large mailboxes. */ sleep(3); /* to be _really_ safe, probably need sleep(5)! */ +#endif - /* we're peek-capable if use of TOP is enabled */ - peek_capable = !(ctl->fetchall || ctl->keep); + set_peek_capable(ctl); /* we're approved */ return(PS_SUCCESS); } -static int pop3_gettopid( int sock, int num , char *id) +/* cut off C string at first POSIX space */ +static void trim(char *s) { + s += strcspn(s, POSIX_space); + s[0] = '\0'; +} + +static int pop3_gettopid(int sock, int num , char *id, size_t idsize) { int ok; int got_it; char buf [POPBUFSIZE+1]; - sprintf( buf, "TOP %d 1", num ); + snprintf(buf, sizeof(buf), "TOP %d 1", num); if ((ok = gen_transact(sock, buf )) != 0) - return ok; + return ok; got_it = 0; while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) { if (DOTLINE(buf)) break; - if ( ! got_it && ! strncasecmp("Message-Id:", buf, 11 )) { + if (!got_it && 0 == strncasecmp("Message-Id:", buf, 11)) { + char *p = buf + 11; got_it = 1; - /* prevent stack overflows */ - buf[IDLEN+12] = 0; - sscanf( buf+12, "%s", id); + p += strspn(p, POSIX_space); + strlcpy(id, p, idsize); + trim(id); } } return 0; } -static int pop3_getuidl( int sock, int num , char *id) +/** Parse the UID response (leading +OK must have been + * stripped off) in buf, store the number in gotnum, and store the ID + * into the caller-provided buffer "id" of size "idsize". + * Returns PS_SUCCESS or PS_PROTOCOL for failure. */ +static int parseuid(const char *buf, unsigned long *gotnum, char *id, size_t idsize) +{ + const char *i; + char *j; + + /* skip leading blanks ourselves */ + i = buf; + i += strspn(i, POSIX_space); + errno = 0; + *gotnum = strtoul(i, &j, 10); + if (j == i || !*j || errno || NULL == strchr(POSIX_space, *j)) { + report(stderr, GT_("Cannot handle UIDL response from upstream server.\n")); + return PS_PROTOCOL; + } + j += strspn(j, POSIX_space); + strlcpy(id, j, idsize); + trim(id); + return PS_SUCCESS; +} + +/** request UIDL for single message \a num and stuff the result into the + * buffer \a id which can hold \a idsize bytes */ +static int pop3_getuidl(int sock, int num, char *id /** output */, size_t idsize) { int ok; char buf [POPBUFSIZE+1]; + unsigned long gotnum; + gen_send(sock, "UIDL %d", num); if ((ok = pop3_ok(sock, buf)) != 0) return(ok); - if (sscanf(buf, "%d %s", &num, id) != 2) - return(PS_PROTOCOL); + if ((ok = parseuid(buf, &gotnum, id, idsize))) + return ok; + if (gotnum != num) { + report(stderr, GT_("Server responded with UID for wrong message.\n")); + return PS_PROTOCOL; + } return(PS_SUCCESS); } @@ -643,7 +698,7 @@ static int pop3_fastuidl( int sock, struct query *ctl, unsigned int count, int struct idlist *new; try_nr = (first_nr + last_nr) / 2; - if( (ok = pop3_getuidl( sock, try_nr, id )) != 0 ) + if ((ok = pop3_getuidl(sock, try_nr, id, sizeof(id))) != 0) return ok; if ((new = str_in_list(&ctl->oldsaved, id, FALSE))) { @@ -705,10 +760,10 @@ static int pop3_slowuidl( int sock, struct query *ctl, int *countp, int *newp) int first_nr, list_len, try_id, try_nr, add_id; int num; char id [IDLEN+1]; - - if( (ok = pop3_gettopid( sock, 1, id )) != 0 ) + + if ((ok = pop3_gettopid(sock, 1, id, sizeof(id))) != 0) return ok; - + if( ( first_nr = str_nr_in_list(&ctl->oldsaved, id) ) == -1 ) { /* the first message is unknown -> all messages are new */ *newp = *countp; @@ -720,7 +775,7 @@ static int pop3_slowuidl( int sock, struct query *ctl, int *countp, int *newp) try_id = list_len - first_nr; /* -1 + 1 */ if( try_id > 1 ) { if( try_id <= *countp ) { - if( (ok = pop3_gettopid( sock, try_id, id )) != 0 ) + if ((ok = pop3_gettopid(sock, try_id, id, sizeof(id))) != 0) return ok; try_nr = str_nr_last_in_list(&ctl->oldsaved, id); @@ -744,7 +799,7 @@ static int pop3_slowuidl( int sock, struct query *ctl, int *countp, int *newp) } else try_id += add_id; - if( (ok = pop3_gettopid( sock, try_id, id )) != 0 ) + if ((ok = pop3_gettopid(sock, try_id, id, sizeof(id))) != 0) return ok; try_nr = str_nr_in_list(&ctl->oldsaved, id); } @@ -806,7 +861,7 @@ static int pop3_getrange(int sock, /* * Newer, RFC-1725-conformant POP servers may not have the LAST command. - * We work as hard as possible to hide this ugliness, but it makes + * We work as hard as possible to hide this ugliness, but it makes * counting new messages intrinsically quadratic in the worst case. */ last = 0; @@ -844,15 +899,15 @@ static int pop3_getrange(int sock, } *newp = (*countp - last); } - else - { + else + { if (dofastuidl) return(pop3_fastuidl( sock, ctl, *countp, newp)); /* grab the mailbox's UID list */ if ((ok = gen_transact(sock, "UIDL")) != 0) { /* don't worry, yet! do it the slow way */ - if((ok = pop3_slowuidl( sock, ctl, countp, newp))!=0) + if ((ok = pop3_slowuidl(sock, ctl, countp, newp))) { report(stderr, GT_("protocol error while fetching UIDLs\n")); return(PS_ERROR); @@ -860,27 +915,32 @@ static int pop3_getrange(int sock, } else { - int num; + unsigned long unum; *newp = 0; - while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) + while ((ok = gen_recv(sock, buf, sizeof(buf))) == PS_SUCCESS) { - if (DOTLINE(buf)) - break; - else if (sscanf(buf, "%d %s", &num, id) == 2) + if (DOTLINE(buf)) + break; + + if (parseuid(buf, &unum, id, sizeof(id)) == PS_SUCCESS) { - struct idlist *old, *new; + struct idlist *old, *new; new = save_str(&ctl->newsaved, id, UID_UNSEEN); - new->val.status.num = num; + new->val.status.num = unum; if ((old = str_in_list(&ctl->oldsaved, id, FALSE))) { flag mark = old->val.status.mark; if (mark == UID_DELETED || mark == UID_EXPUNGED) { + /* XXX FIXME: switch 3 occurrences from + * (int)unum or (unsigned int)unum to + * remove the cast and use %lu - not now + * though, time for new release */ if (outlevel >= O_VERBOSE) - report(stderr, GT_("id=%s (num=%d) was deleted, but is still present!\n"), id, num); + report(stderr, GT_("id=%s (num=%d) was deleted, but is still present!\n"), id, (int)unum); /* just mark it as seen now! */ old->val.status.mark = mark = UID_SEEN; } @@ -889,25 +949,26 @@ static int pop3_getrange(int sock, { (*newp)++; if (outlevel >= O_DEBUG) - report(stdout, GT_("%u is unseen\n"), num); + report(stdout, GT_("%u is unseen\n"), (unsigned int)unum); } } else { (*newp)++; if (outlevel >= O_DEBUG) - report(stdout, GT_("%u is unseen\n"), num); + report(stdout, GT_("%u is unseen\n"), (unsigned int)unum); /* add it to oldsaved also! In case, we do not * swap the lists (say, due to socket error), * the same mail will not be downloaded again. */ old = save_str(&ctl->oldsaved, id, UID_UNSEEN); - old->val.status.num = num; + old->val.status.num = unum; } - } - } - } - } + } else + return PS_ERROR; + } + } + } } return(PS_SUCCESS); @@ -990,7 +1051,7 @@ static int pop3_is_old(int sock, struct query *ctl, int num) } /* get the uidl first! */ - if (pop3_getuidl(sock, num, id) != PS_SUCCESS) + if (pop3_getuidl(sock, num, id, sizeof(id)) != PS_SUCCESS) return(TRUE); if ((new = str_in_list(&ctl->oldsaved, id, FALSE))) { @@ -1088,11 +1149,11 @@ static int pop3_fetch(int sock, struct query *ctl, int number, int *lenp) * In that case, marking the seen flag is the only way to prevent the * message from being re-fetched on subsequent runs. * - * Also use RETR if fetchall is on. This gives us a workaround - * for servers like usa.net's that bungle TOP. It's pretty - * harmless because fetchall guarantees that any message dropped - * by an interrupted RETR will be picked up on the next poll of the - * site. + * Also use RETR (that means no TOP, no peek) if fetchall is on. + * This gives us a workaround for servers like usa.net's that bungle + * TOP. It's pretty harmless because fetchall guarantees that any + * message dropped by an interrupted RETR will be picked up on the + * next poll of the site. * * We take advantage here of the fact that, according to all the * POP RFCs, "if the number of lines requested by the POP3 client @@ -1102,7 +1163,7 @@ static int pop3_fetch(int sock, struct query *ctl, int number, int *lenp) * The line count passed (99999999) is the maximum value CompuServe will * accept; it's much lower than the natural value 2147483646 (the maximum * twos-complement signed 32-bit integer minus 1) */ - if (ctl->keep || ctl->fetchall) + if (!peek_capable) gen_send(sock, "RETR %d", number); else gen_send(sock, "TOP %d 99999999", number); @@ -1182,7 +1243,7 @@ static int pop3_logout(int sock, struct query *ctl) static const struct method pop3 = { "POP3", /* Post Office Protocol v3 */ -#if INET6_ENABLE +#ifdef INET6_ENABLE "pop3", /* standard POP3 port */ "pop3s", /* ssl POP3 port */ #else /* INET6_ENABLE */ @@ -1215,7 +1276,8 @@ int doPOP3 (struct query *ctl) return(PS_SYNTAX); } #endif /* MBOX */ - peek_capable = !ctl->fetchall; + set_peek_capable(ctl); /* XXX FIXME: is this needed or do we always + call this from pop3_getauth anyways? */ return(do_protocol(ctl, &pop3)); } #endif /* POP3_ENABLE */