diff -Naur fetchmail-6.2.2/conf.c fetchmail-6.2.2.new/conf.c --- fetchmail-6.2.2/conf.c Thu Oct 31 17:26:19 2002 +++ fetchmail-6.2.2.new/conf.c Wed May 14 05:56:46 2003 @@ -246,7 +246,8 @@ indent('{'); using_kpop = - (ctl->server.protocol == P_POP3 && + (( ctl->server.protocol == P_POP3 + || ctl->server.protocol == P_POP3X) && #if !INET6_ENABLE ctl->server.port == KPOP_PORT && #else diff -Naur fetchmail-6.2.2/driver.c fetchmail-6.2.2.new/driver.c --- fetchmail-6.2.2/driver.c Fri Feb 28 11:53:57 2003 +++ fetchmail-6.2.2.new/driver.c Wed May 14 17:35:44 2003 @@ -555,7 +555,8 @@ fflush(stdout); } - if ((err = (ctl->server.base_protocol->trail)(mailserver_socket, ctl, num))) + if ((ctl->server.base_protocol->trail) + && (err = (ctl->server.base_protocol->trail)(mailserver_socket, ctl, num))) return(err); len = 0; if (!suppress_forward) diff -Naur fetchmail-6.2.2/env.c fetchmail-6.2.2.new/env.c --- fetchmail-6.2.2/env.c Mon Apr 1 10:22:36 2002 +++ fetchmail-6.2.2.new/env.c Wed May 14 05:57:00 2003 @@ -246,6 +246,7 @@ #endif /* POP2_ENABLE */ #ifdef POP3_ENABLE case P_POP3: return("POP3"); + case P_POP3X: return("POP3X"); case P_APOP: return("APOP"); case P_RPOP: return("RPOP"); #endif /* POP3_ENABLE */ diff -Naur fetchmail-6.2.2/fetchmail.c fetchmail-6.2.2.new/fetchmail.c --- fetchmail-6.2.2/fetchmail.c Fri Feb 28 23:16:15 2003 +++ fetchmail-6.2.2.new/fetchmail.c Wed May 14 06:00:51 2003 @@ -1296,6 +1296,8 @@ P_IMAP, #endif /* IMAP_ENABLE */ #ifdef POP3_ENABLE +/* POP3X can't be safely autoprobed... But if one uses the autoprobe feature, + maybe one doesn't need advanced fetaures. */ P_POP3, #endif /* POP3_ENABLE */ #ifdef POP2_ENABLE @@ -1355,6 +1357,14 @@ st = PS_PROTOCOL; #endif /* POP3_ENABLE */ break; + case P_POP3X: +#ifdef POP3_ENABLE + st = doPOP3X(ctl); +#else + report(stderr, GT_("POP3/POP3X support is not configured.\n")); + st = PS_PROTOCOL; +#endif /* POP3_ENABLE */ + break; case P_IMAP: #ifdef IMAP_ENABLE do { @@ -1478,7 +1488,8 @@ } } - if (ctl->server.protocol == P_POP3 + if (( ctl->server.protocol == P_POP3 + || ctl->server.protocol == P_POP3X) #if INET6_ENABLE && ctl->server.service && !strcmp(ctl->server.service, KPOP_PORT) #else /* INET6_ENABLE */ diff -Naur fetchmail-6.2.2/fetchmail.h fetchmail-6.2.2.new/fetchmail.h --- fetchmail-6.2.2/fetchmail.h Sat Mar 1 02:43:11 2003 +++ fetchmail-6.2.2.new/fetchmail.h Wed May 14 17:18:12 2003 @@ -17,6 +17,7 @@ #define P_IMAP 6 #define P_ETRN 7 #define P_ODMR 8 +#define P_POP3X 9 #if INET6_ENABLE #define SMTP_PORT "smtp" @@ -433,6 +434,7 @@ /* transact.c: transaction support */ void init_transact(const struct method *); +int end_of_header (const char *s); int readheaders(int sock, long fetchlen, long reallen, @@ -582,6 +584,7 @@ /* protocol driver and methods */ int doPOP2 (struct query *); int doPOP3 (struct query *); +int doPOP3X (struct query *); int doIMAP (struct query *); int doETRN (struct query *); int doODMR (struct query *); diff -Naur fetchmail-6.2.2/options.c fetchmail-6.2.2.new/options.c --- fetchmail-6.2.2/options.c Fri Oct 18 12:19:39 2002 +++ fetchmail-6.2.2.new/options.c Wed May 14 06:03:45 2003 @@ -351,12 +351,16 @@ #endif /* SDPS_ENABLE */ else if (strcasecmp(optarg,"pop3") == 0) ctl->server.protocol = P_POP3; + else if (strcasecmp(optarg,"pop3x") == 0) + ctl->server.protocol = P_POP3X; else if (strcasecmp(optarg,"apop") == 0) ctl->server.protocol = P_APOP; else if (strcasecmp(optarg,"rpop") == 0) ctl->server.protocol = P_RPOP; else if (strcasecmp(optarg,"kpop") == 0) { +/* NOTE: "kpop" could supposedly be extended as "kpopX", exactly + like "pop3", but I can't test it in my environment. Voluteers? */ ctl->server.protocol = P_POP3; #if INET6_ENABLE ctl->server.service = KPOP_PORT; diff -Naur fetchmail-6.2.2/pop3.c fetchmail-6.2.2.new/pop3.c --- fetchmail-6.2.2/pop3.c Fri Feb 28 12:44:57 2003 +++ fetchmail-6.2.2.new/pop3.c Thu Mar 18 21:29:12 2004 @@ -208,6 +208,7 @@ switch (ctl->server.protocol) { case P_POP3: + case P_POP3X: #ifdef RPA_ENABLE /* CompuServe POP3 Servers as of 990730 want AUTH first for RPA */ if (strstr(ctl->remotename, "@compuserve.com")) @@ -740,20 +741,51 @@ return(PS_SUCCESS); } -#endif /* UNUSED */ +#endif /* UNUSED */ /* ...but see below... */ -static int pop3_fetch(int sock, struct query *ctl, int number, int *lenp) +/* Ignoring header -- POP3 doesn't have a "BODY" command, so we have to + RETR the whole message and skip its header. It is worth if we want to + apply some anti-spam filer and the spamming messages have big bodies. */ +static int pop3_skip_headers(int sock) +{ + int ok; + char buf [POPBUFSIZE+1]; + + while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) + { + /* I don't know if it can actually happen -- it shouldn't. */ + if (DOTLINE(buf)) { + /* Malformed message: no header separator. */ + ok = PS_PROTOCOL; + break; + } + if (buf[0] == '\0' || end_of_header(buf)) + break; + } + return ok; +} + +/* Type of fetch algorythm. */ +enum pop3_fetchmode_t { + POP3_FETCHMODE_ALL, /* Traditional, one-step POP3 fetch. */ + POP3_FETCHMODE_HEADER, /* POP3X header fetch (TOP x 0) */ + POP3_FETCHMODE_BODY, /* POP3X body fetch (RETR/TOP + skip headers) */ +}; + +static int pop3_fetch(int sock, struct query *ctl, int number, int *lenp, enum pop3_fetchmode_t mode) /* request nth message */ { int ok; char buf[POPBUFSIZE+1]; + const char *cmd = NULL; #ifdef SDPS_ENABLE /* * See http://www.demon.net/services/mail/sdps-tech.html * for a description of what we're parsing here. */ - if (ctl->server.sdps) + if (ctl->server.sdps + && (mode == POP3_FETCHMODE_ALL || mode == POP3_FETCHMODE_HEADER)) { int linecount = 0; @@ -810,19 +842,101 @@ * POP3 server sends the entire message."). * * The line count passed (99999999) is the maximum value CompuServe will - * accept; it's much lower than the natural value 2147483646 (the maximum + * accept; it's much lower than the natural value 2147483647 (the maximum * twos-complement signed 32-bit integer minus 1) */ - if (ctl->keep || ctl->fetchall) - gen_send(sock, "RETR %d", number); - else - gen_send(sock, "TOP %d 99999999", number); + + switch (mode) { + case POP3_FETCHMODE_ALL: + case POP3_FETCHMODE_BODY: + if (ctl->keep || ctl->fetchall) + cmd = "RETR %d"; + else + cmd = "TOP %d 99999999"; + break; + case POP3_FETCHMODE_HEADER: + cmd = "TOP %d 0"; + break; + default: + /* ASSERT(0) */ + break; + } + gen_send(sock, cmd, number); + if ((ok = pop3_ok(sock, buf)) != 0) return(ok); + if (mode == POP3_FETCHMODE_BODY) { +/* Skipping headers... we can do it safely, because + POP3 is a "delimited" protocol: it relies on the + data termination string ("." alone); the message size + doesn't matter (see function "readbody" in "transact.c"). */ + if ((ok = pop3_skip_headers(sock)) != 0) + return(ok); + } + *lenp = -1; /* we got sizes from the LIST response */ return(PS_SUCCESS); } +static int last_op_was_fetch_body = 0; +static int pop3_fetch_all(int sock, struct query *ctl, int number, int *lenp) +{ + return pop3_fetch(sock,ctl,number,lenp,POP3_FETCHMODE_ALL); +} +static int pop3_fetch_headers(int sock, struct query *ctl, int number, int *lenp) +{ + last_op_was_fetch_body = 0; + return pop3_fetch(sock,ctl,number,lenp,POP3_FETCHMODE_HEADER); +} +static int pop3_fetch_body(int sock, struct query *ctl, int number, int *lenp) +{ + last_op_was_fetch_body = 1; + return pop3_fetch(sock,ctl,number,lenp,POP3_FETCHMODE_BODY); +} + +static int pop3_trail(int sock, struct query *ctl, int number) +{ + int ok; + char buf [POPBUFSIZE+1]; + +/* UGLY: The header has a trailing "." alone that the function + "readheaders" doesn't process (the function terminates as soon as an + empty line is received). But "readbody" does process the dot at the end + of a RETR or TOP stream. This is asymmetric. This function + is called after both "readheaders" and "readbody", but it must behave + differently: after a "readeders" (--> "pop3_fetch_headers") + it has to remove the dot, while after a "readbody" (--> "pop3_fetch_body") + it doesn't. I don't dare to change "readheaders", "readbody" or + "fetch_messages" because of their complexity. There are too many local + fixes for strange/buggy/nonstandard servers... The problem is + architectural (the protocol driver just starts the operations, but doesn't + see any result directly) and perhaps a deep review of the core fetch engine + could make it cleaner. In the meantime, I'll use a static variable + to register the receiving phase ;-). */ +/* NOTE: this DOES NOT WORK if the "TOP n 0" command doesn't produce + the header delimiter before the trailing dot. If the server behave this way, + the POP3X protocol can't work. */ + + if (last_op_was_fetch_body) + { + return 0; + } + + if ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) + { + while (buf[0] == '\0') { + if ((ok = gen_recv(sock, buf, sizeof(buf))) != 0) { + break; + } + } + /* last "TOP" commad left a "." alone... */ + if ((ok == 0) && !(DOTLINE(buf))) { + /* Malformed message: no header separator. */ + ok = PS_PROTOCOL; + } + } + return ok; +} static void mark_uid_seen(struct query *ctl, int number) /* Tell the UID code we've seen this. */ @@ -913,7 +1027,7 @@ pop3_getrange, /* query range of messages */ pop3_getsizes, /* we can get a list of sizes */ pop3_is_old, /* how do we tell a message is old? */ - pop3_fetch, /* request given message */ + pop3_fetch_all, /* request given message */ NULL, /* no way to fetch body alone */ NULL, /* no message trailer */ pop3_delete, /* how to delete a message */ @@ -922,17 +1036,53 @@ FALSE, /* no, we can't re-poll */ }; -int doPOP3 (struct query *ctl) -/* retrieve messages using POP3 */ +const static struct method pop3x = +{ + "POP3X", /* Post Office Protocol v3 with two-step fetch */ +#if INET6_ENABLE + "pop3", /* standard POP3 port */ + "pop3s", /* ssl POP3 port */ +#else /* INET6_ENABLE */ + 110, /* standard POP3 port */ + 995, /* ssl POP3 port */ +#endif /* INET6_ENABLE */ + FALSE, /* this is not a tagged protocol */ + TRUE, /* this uses a message delimiter */ + pop3_ok, /* parse command response */ + pop3_getauth, /* get authorization */ + pop3_getrange, /* query range of messages */ + pop3_getsizes, /* we can get a list of sizes */ + pop3_is_old, /* how do we tell a message is old? */ + pop3_fetch_headers, /* request given message's header */ + pop3_fetch_body, /* there is a (perhaps expensive) way to fetch body alone! */ + pop3_trail, /* no message trailer (but required by driver.c) */ + pop3_delete, /* how to delete a message */ + pop3_mark_seen, /* how to mark a message as seen */ + pop3_logout, /* log out, we're done */ + FALSE, /* no, we can't re-poll */ +}; + +static int doPOP_core(struct query *ctl, const struct method *m) { #ifndef MBOX if (ctl->mailboxes->id) { - fprintf(stderr,GT_("Option --remote is not supported with POP3\n")); + fprintf(stderr,GT_("Option --remote is not supported with %s\n"), + m -> name); return(PS_SYNTAX); } #endif /* MBOX */ peek_capable = !ctl->fetchall; - return(do_protocol(ctl, &pop3)); + return(do_protocol(ctl, m)); +} +int doPOP3 (struct query *ctl) +/* retrieve messages using POP3 */ +{ + return(doPOP_core(ctl, &pop3)); +} +int doPOP3X (struct query *ctl) +/* retrieve messages using POP3X */ +{ + return(doPOP_core(ctl, &pop3x)); } #endif /* POP3_ENABLE */ diff -Naur fetchmail-6.2.2/rcfile_l.l fetchmail-6.2.2.new/rcfile_l.l --- fetchmail-6.2.2/rcfile_l.l Fri Oct 18 12:19:40 2002 +++ fetchmail-6.2.2.new/rcfile_l.l Wed May 14 22:12:21 2003 @@ -191,6 +191,7 @@ (pop2)|(POP2) { yylval.proto = P_POP2; return PROTO; } (sdps)|(SDPS) { return SDPS; } (pop3)|(POP3) { yylval.proto = P_POP3; return PROTO; } +(pop3x)|(POP3X) { yylval.proto = P_POP3X; return PROTO; } (imap)|(IMAP) { yylval.proto = P_IMAP; return PROTO; } (apop)|(APOP) { yylval.proto = P_APOP; return PROTO; } (etrn)|(ETRN) { yylval.proto = P_ETRN; return PROTO; } diff -Naur fetchmail-6.2.2/transact.c fetchmail-6.2.2.new/transact.c --- fetchmail-6.2.2/transact.c Sat Mar 1 03:14:42 2003 +++ fetchmail-6.2.2.new/transact.c Wed Dec 3 18:56:33 2003 @@ -339,7 +339,7 @@ #define EMPTYLINE(s) ((s)[0] == '\r' && (s)[1] == '\n' && (s)[2] == '\0') -static int end_of_header (const char *s) +int end_of_header (const char *s) /* accept "\r*\n" as EOH in order to be bulletproof against broken survers */ { while (s[0] == '\r')