X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=pop3.c;h=cbbe7f75e80cea85f02c74b6ae3ad4862bee5b82;hb=420635f35f6261d709bbfeed50d9d6daab205366;hp=be576115c3b4d95c13e468167f2e28e2bafc42e8;hpb=6b113bbffd60a5428f04ab6ab34f1bca24d76e27;p=~andy%2Ffetchmail diff --git a/pop3.c b/pop3.c index be576115..cbbe7f75 100644 --- a/pop3.c +++ b/pop3.c @@ -1,6 +1,7 @@ /* * pop3.c -- POP3 protocol methods * + * Copyright 1998 by Eric S. Raymond. * For license terms, see the file COPYING in this directory. */ @@ -18,28 +19,29 @@ #include "fetchmail.h" #include "socket.h" +#include "i18n.h" -#if OPIE +#if OPIE_ENABLE #include -#endif /* OPIE */ +#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 phase; -#define PHASE_GETAUTH 0 -#define PHASE_GETRANGE 1 -#define PHASE_GETSIZES 2 -#define PHASE_FETCH 3 -#define PHASE_LOGOUT 4 static int last; +#ifdef SDPS_ENABLE +char *sdps_envfrom; +char *sdps_envto; +#endif /* SDPS_ENABLE */ -#if OPIE +#if OPIE_ENABLE static char lastok[POPBUFSIZE+1]; -#endif /* OPIE */ +#endif /* OPIE_ENABLE */ -int pop3_ok (int sock, char *argbuf) +#define DOTLINE(s) (s[0] == '.' && (s[1]=='\r'||s[1]=='\n'||s[1]=='\0')) + +static int pop3_ok (int sock, char *argbuf) /* parse command response */ { int ok; @@ -47,8 +49,7 @@ int pop3_ok (int sock, char *argbuf) char *bufp; if ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) - { - bufp = buf; + { bufp = buf; if (*bufp == '+' || *bufp == '-') bufp++; else @@ -62,31 +63,54 @@ int pop3_ok (int sock, char *argbuf) if (strcmp(buf,"+OK") == 0) { -#if OPIE +#if OPIE_ENABLE strcpy(lastok, bufp); -#endif /* OPIE */ +#endif /* OPIE_ENABLE */ ok = 0; } - else if (strcmp(buf,"-ERR") == 0) + else if (strncmp(buf,"-ERR", 4) == 0) { - if (phase > PHASE_GETAUTH) + if (stage > STAGE_GETAUTH) ok = PS_PROTOCOL; /* * We're checking for "lock busy", "unable to lock", - * "already locked" etc. here. This indicates that we - * have to wait for the server to clean up before we - * can poll again. + * "already locked", "wait a few minutes" etc. here. + * This indicates that we have to wait for the server to + * unwedge itself before we can poll again. * * PS_LOCKBUSY check empirically verified with two recent - * versions the Berkeley popper; QPOP (version 2.2) and + * versions of the Berkeley popper; QPOP (version 2.2) and * QUALCOMM Pop server derived from UCB (version 2.1.4-R3) + * These are caught by the case-indifferent "lock" check. + * The "wait" catches "mail storage services unavailable, + * wait a few minutes and try again" on the InterMail server. + * + * If these aren't picked up on correctly, fetchmail will + * think there is an authentication failure and wedge the + * connection in order to prevent futile polls. + * + * Gad, what a kluge. */ - else if (strstr(bufp,"lock")||strstr(bufp,"Lock")||strstr(bufp,"LOCK")) + else if (strstr(bufp,"lock") + || strstr(bufp,"Lock") + || strstr(bufp,"LOCK") + || strstr(bufp,"wait") + /* these are blessed by RFC 2449 */ + || strstr(bufp,"[IN-USE]")||strstr(bufp,"[LOGIN-DELAY]")) ok = PS_LOCKBUSY; + else if ((strstr(bufp,"Service") + || strstr(bufp,"service")) + && (strstr(bufp,"unavailable"))) + ok = PS_SERVBUSY; else ok = PS_AUTHFAIL; - if (*bufp) - error(0,0,bufp); + /* + * We always want to pass the user lock-busy messages, because + * they're red flags. Other stuff (like AUTH failures on non- + * RFC1734 servers) only if we're debugging. + */ + if (*bufp && (ok == PS_LOCKBUSY || outlevel >= O_MONITOR)) + report(stderr, "%s\n", bufp); } else ok = PS_PROTOCOL; @@ -98,81 +122,196 @@ int pop3_ok (int sock, char *argbuf) return(ok); } -int pop3_getauth(int sock, struct query *ctl, char *greeting) +static int pop3_getauth(int sock, struct query *ctl, char *greeting) /* apply for connection authorization */ { int ok; char *start,*end; char *msg; -#if OPIE +#if OPIE_ENABLE char *challenge; -#endif /* OPIE */ - - phase = PHASE_GETAUTH; +#endif /* OPIE_ENABLE */ +#if defined(GSSAPI) + flag has_gssapi = FALSE; +#endif /* defined(GSSAPI) */ +#if defined(KERBEROS_V4) || defined(KERBEROS_V5) + flag has_kerberos = FALSE; +#endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */ + flag has_cram = FALSE; +#ifdef OPIE_ENABLE + flag has_otp = FALSE; +#endif /* OPIE_ENABLE */ +#ifdef SSL_ENABLE + flag has_ssl = FALSE; +#endif /* SSL_ENABLE */ + +#ifdef SDPS_ENABLE + /* + * This needs to catch both demon.co.uk and demon.net. + * If we see either, and we're in multidrop mode, try to use + * the SDPS *ENV extension. + */ + if (!(ctl->server.sdps) && MULTIDROP(ctl) && strstr(greeting, "demon.")) + ctl->server.sdps = TRUE; +#endif /* SDPS_ENABLE */ switch (ctl->server.protocol) { case P_POP3: - ok = gen_transact(sock, "USER %s", ctl->remotename); - #ifdef RPA_ENABLE - /* - * CompuServe has changed its RPA behavior. Used to be they didn't - * accept PASS, but I'm told this changed in mid-November 1997. - */ - if (strstr(greeting, "csi.com")) - { - /* temporary fix to get back out of cleartext authentication */ - gen_transact(sock, "PASS %s", "dummypass"); - - /* AUTH command should return a list of available mechanisms */ - if (gen_transact(sock, "AUTH") == 0) - { - char buffer[10]; - flag has_rpa = FALSE; - - while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0) - { - if (buffer[0] == '.') - break; - if (strncasecmp(buffer, "rpa", 3) == 0) - has_rpa = TRUE; - } - if (has_rpa && !POP3_auth_rpa(ctl->remotename, - ctl->password, sock)) - return(PS_SUCCESS); - } - - return(PS_AUTHFAIL); - } + /* CompuServe POP3 Servers as of 990730 want AUTH first for RPA */ + if (strstr(ctl->remotename, "@compuserve.com")) + { + /* AUTH command should return a list of available mechanisms */ + if (gen_transact(sock, "AUTH") == 0) + { + char buffer[10]; + flag has_rpa = FALSE; + + while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0) + { + if (DOTLINE(buffer)) + break; + if (strncasecmp(buffer, "rpa", 3) == 0) + has_rpa = TRUE; + } + if (has_rpa && !POP3_auth_rpa(ctl->remotename, + ctl->password, sock)) + return(PS_SUCCESS); + } + + return(PS_AUTHFAIL); + } #endif /* RPA_ENABLE */ -#if OPIE - /* see RFC1938: A One-Time Password System */ - if (challenge = strstr(lastok, "otp-")) { - char response[OPIE_RESPONSE_MAX+1]; - int i; - - i = opiegenerator(challenge, !strcmp(ctl->password, "opie") ? "" : ctl->password, response); - if ((i == -2) && (cmd_daemon == -1)) { - char secret[OPIE_SECRET_MAX+1]; - fprintf(stderr, "Secret pass phrase: "); - if (opiereadpass(secret, sizeof(secret), 0)) - i = opiegenerator(challenge, secret, response); - memset(secret, 0, sizeof(secret)); - }; - - if (i) { - ok = PS_ERROR; - break; - }; + /* + * CAPA command may return a list including available + * authentication mechanisms. if it doesn't, no harm done, we + * just fall back to a plain login. Note that this code + * latches the server's authentication type, so that in daemon mode + * the CAPA check only needs to be done once at start of run. + * + * APOP was introduced in RFC 1450, and CAPA not until + * RFC2449. So the < check is an easy way to prevent CAPA from + * being sent to the more primitive POP3 servers dating from + * RFC 1081 and RFC 1225, which seem more likely to choke on + * it. This certainly catches IMAP-2000's POP3 gateway. + * + * These authentication methods are blessed by RFC1734, + * describing the POP3 AUTHentication command. + */ + if (ctl->server.authenticate == A_ANY + && strchr(greeting, '<') + && gen_transact(sock, "CAPA") == 0) + { + char buffer[64]; + + /* determine what authentication methods we have available */ + while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0) + { + if (DOTLINE(buffer)) + break; +#ifdef SSL_ENABLE + if (strstr(buffer, "STLS")) + has_ssl = TRUE; +#endif /* SSL_ENABLE */ +#if defined(GSSAPI) + if (strstr(buffer, "GSSAPI")) + has_gssapi = TRUE; +#endif /* defined(GSSAPI) */ +#if defined(KERBEROS_V4) + if (strstr(buffer, "KERBEROS_V4")) + has_kerberos = TRUE; +#endif /* defined(KERBEROS_V4) */ +#ifdef OPIE_ENABLE + if (strstr(buffer, "X-OTP")) + has_otp = TRUE; +#endif /* OPIE_ENABLE */ + if (strstr(buffer, "CRAM-MD5")) + has_cram = TRUE; + } + } + +#ifdef SSL_ENABLE + if (has_ssl && !ctl->use_ssl && +#if INET6_ENABLE + ctl->server.service && (strcmp(ctl->server.service, "pop3s")) +#else /* INET6_ENABLE */ + ctl->server.port != 995 +#endif /* INET6_ENABLE */ + ) + { + char *realhost; + + realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname; gen_transact(sock, "STLS"); + if (SSLOpen(sock,ctl->sslcert,ctl->sslkey,ctl->sslproto,ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1) + { + report(stderr, + GT_("SSL connection failed.\n")); + return(PS_AUTHFAIL); + } + } +#endif /* SSL_ENABLE */ + + /* + * OK, we have an authentication type now. + */ +#if defined(KERBEROS_V4) + /* + * Servers doing KPOP have to go through a dummy login sequence + * rather than doing SASL. + */ + if (has_kerberos && +#if INET6_ENABLE + ctl->server.service && (strcmp(ctl->server.service, KPOP_PORT)!=0) +#else /* INET6_ENABLE */ + ctl->server.port != KPOP_PORT +#endif /* INET6_ENABLE */ + && (ctl->server.authenticate == A_KERBEROS_V4 + || ctl->server.authenticate == A_KERBEROS_V5 + || ctl->server.authenticate == A_ANY)) + { + ok = do_rfc1731(sock, "AUTH", ctl->server.truename); + if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY) + break; + } +#endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */ + +#if defined(GSSAPI) + if (has_gssapi && + (ctl->server.authenticate == A_GSSAPI || + ctl->server.authenticate == A_ANY)) + { + ok = do_gssauth(sock,"AUTH",ctl->server.truename,ctl->remotename); + if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY) + break; + } +#endif /* defined(GSSAPI) */ - ok = gen_transact(sock, "PASS %s", response); - break; +#ifdef OPIE_ENABLE + if (has_otp && + (ctl->server.authenticate == A_OTP || + ctl->server.authenticate == A_ANY)) + { + ok = do_otp(sock, "AUTH", ctl); + if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY) + break; + } +#endif /* OPIE_ENABLE */ + + if (has_cram && + (ctl->server.authenticate == A_CRAM_MD5 || + ctl->server.authenticate == A_ANY)) + { + ok = do_cram_md5(sock, "AUTH", ctl, NULL); + if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY) + break; } -#endif /* OPIE */ /* ordinary validation, no one-time password or RPA */ + gen_transact(sock, "USER %s", ctl->remotename); + strcpy(shroud, ctl->password); ok = gen_transact(sock, "PASS %s", ctl->password); + shroud[0] = '\0'; break; case P_APOP: @@ -181,7 +320,8 @@ int pop3_getauth(int sock, struct query *ctl, char *greeting) for (start = greeting; *start != 0 && *start != '<'; start++) continue; if (*start == 0) { - error(0, -1, "Required APOP timestamp not found in greeting"); + report(stderr, + GT_("Required APOP timestamp not found in greeting\n")); return(PS_AUTHFAIL); } @@ -189,19 +329,19 @@ int pop3_getauth(int sock, struct query *ctl, char *greeting) for (end = start; *end != 0 && *end != '>'; end++) continue; if (*end == 0 || end == start + 1) { - error(0, -1, "Timestamp syntax error in greeting"); + report(stderr, + GT_("Timestamp syntax error in greeting\n")); return(PS_AUTHFAIL); } else *++end = '\0'; /* copy timestamp and password into digestion buffer */ - msg = (char *)xmalloc((end-start+1) + strlen(ctl->password) + 1); + xalloca(msg, char *, (end-start+1) + strlen(ctl->password) + 1); strcpy(msg,start); strcat(msg,ctl->password); - strcpy(ctl->digest, MD5Digest(msg)); - free(msg); + strcpy(ctl->digest, MD5Digest((unsigned char *)msg)); ok = gen_transact(sock, "APOP %s %s", ctl->remotename, ctl->digest); break; @@ -212,7 +352,7 @@ int pop3_getauth(int sock, struct query *ctl, char *greeting) break; default: - error(0, 0, "Undefined protocol request in POP3_auth"); + report(stderr, GT_("Undefined protocol request in POP3_auth\n")); ok = PS_ERROR; } @@ -220,7 +360,7 @@ int pop3_getauth(int sock, struct query *ctl, char *greeting) { /* maybe we detected a lock-busy condition? */ if (ok == PS_LOCKBUSY) - error(0, 0, "lock busy! Is another session active?"); + report(stderr, GT_("lock busy! Is another session active?\n")); return(ok); } @@ -234,27 +374,27 @@ int pop3_getauth(int sock, struct query *ctl, char *greeting) */ sleep(3); /* to be _really_ safe, probably need sleep(5)! */ - /* we're peek-capable because the TOP command exists */ - peek_capable = TRUE; + /* we're peek-capable if use of TOP is enabled */ + peek_capable = !(ctl->fetchall || ctl->keep); /* we're approved */ return(PS_SUCCESS); } -static int -pop3_gettopid( int sock, int num , char *id) +static int pop3_gettopid( int sock, int num , char *id) { int ok; int got_it; char buf [POPBUFSIZE+1]; sprintf( buf, "TOP %d 1", num ); - if( (ok = gen_transact(sock, buf ) ) != 0 ) + if ((ok = gen_transact(sock, buf )) != 0) return ok; got_it = 0; - while((ok = gen_recv(sock, buf, sizeof(buf))) == 0) { - if( buf[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 && ! strncasecmp("Message-Id:", buf, 11 )) { got_it = 1; /* prevent stack overflows */ buf[IDLEN+12] = 0; @@ -264,8 +404,7 @@ pop3_gettopid( int sock, int num , char *id) return 0; } -static int -pop3_slowuidl( int sock, struct query *ctl, int *countp, int *newp) +static int pop3_slowuidl( int sock, struct query *ctl, int *countp, int *newp) { /* This approach tries to get the message headers from the * remote hosts and compares the message-id to the already known @@ -328,8 +467,8 @@ pop3_slowuidl( int sock, struct query *ctl, int *countp, int *newp) try_id--; } } else { - error(0,0,"Messages inserted into list on server. " - "Cannot handle this."); + report(stderr, + GT_("Messages inserted into list on server. Cannot handle this.\n")); return -1; } } @@ -362,8 +501,6 @@ static int pop3_getrange(int sock, int ok; char buf [POPBUFSIZE+1]; - phase = PHASE_GETRANGE; - /* Ensure that the new list is properly empty */ ctl->newsaved = (struct idlist *)NULL; @@ -402,7 +539,7 @@ static int pop3_getrange(int sock, { if (sscanf(buf, "%d", &last) == 0) { - error(0, 0, "protocol error"); + report(stderr, GT_("protocol error\n")); return(PS_ERROR); } *newp = (*countp - last); @@ -415,7 +552,7 @@ static int pop3_getrange(int sock, /* don't worry, yet! do it the slow way */ if((ok = pop3_slowuidl( sock, ctl, countp, newp))!=0) { - error(0, 0, "protocol error while fetching UIDLs"); + report(stderr, GT_("protocol error while fetching UIDLs\n")); return(PS_ERROR); } } @@ -426,7 +563,7 @@ static int pop3_getrange(int sock, *newp = 0; while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) { - if (buf[0] == '.') + if (DOTLINE(buf)) break; else if (sscanf(buf, "%d %s", &num, id) == 2) { @@ -455,8 +592,6 @@ static int pop3_getsizes(int sock, int count, int *sizes) { int ok; - /* phase = PHASE_GETSIZES */ - if ((ok = gen_transact(sock, "LIST")) != 0) return(ok); else @@ -465,12 +600,17 @@ static int pop3_getsizes(int sock, int count, int *sizes) while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) { - int num, size; + unsigned int num, size; - if (buf[0] == '.') + if (DOTLINE(buf)) break; - else if (sscanf(buf, "%d %d", &num, &size) == 2) - sizes[num - 1] = size; + else if (sscanf(buf, "%u %u", &num, &size) == 2) { + if (num > 0 && num <= count) + sizes[num - 1] = size; + else + /* warn about possible attempt to induce buffer overrun */ + report(stderr, "Warning: ignoring bogus data for message sizes returned by server.\n"); + } } return(ok); @@ -500,8 +640,6 @@ static int pop_fetch_headers(int sock, struct query *ctl,int number,int *lenp) int ok; char buf[POPBUFSIZE+1]; - /* phase = PHASE_FETCH */ - gen_send(sock, "TOP %d 0", number); if ((ok = pop3_ok(sock, buf)) != 0) return(ok); @@ -518,32 +656,74 @@ static int pop3_fetch(int sock, struct query *ctl, int number, int *lenp) int ok; char buf[POPBUFSIZE+1]; - /* phase = PHASE_FETCH */ +#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) + { + int linecount = 0; + + sdps_envfrom = (char *)NULL; + sdps_envto = (char *)NULL; + gen_send(sock, "*ENV %d", number); + do { + if (gen_recv(sock, buf, sizeof(buf))) + { + break; + } + linecount++; + switch (linecount) { + case 4: + /* No need to wrap envelope from address */ + sdps_envfrom = xmalloc(strlen(buf)+1); + strcpy(sdps_envfrom,buf); + break; + case 5: + /* Wrap address with To: <> so nxtaddr() likes it */ + sdps_envto = xmalloc(strlen(buf)+7); + sprintf(sdps_envto,"To: <%s>",buf); + break; + } + } while + (!(buf[0] == '.' && (buf[1] == '\r' || buf[1] == '\n' || buf[1] == '\0'))); + } +#endif /* SDPS_ENABLE */ /* - * Though the POP RFCs don't document this fact, on every POP3 server - * I know of messages are marked "seen" only at the time the OK - * response to a RETR is issued. + * Though the POP RFCs don't document this fact, on almost every + * POP3 server I know of messages are marked "seen" only at the + * time the OK response to a RETR is issued. * * This means we can use TOP to fetch the message without setting its * seen flag. This is good! It means that if the protocol exchange * craps out during the message, it will still be marked `unseen' on - * the server. - * - * The line count passed is the maximum value of a twos-complement - * signed integer (we take advantage of the fact that, according - * to all the POP RFCs, "if the number of lines requested by the - * POP3 client is greater than than the number of lines in the - * body, then the POP3 server sends the entire message."). + * the server. (Exception: in early 1999 SpryNet's POP3 servers were + * reported to mark messages seen on a TOP fetch.) * * However...*don't* do this if we're using keep to suppress deletion! * In that case, marking the seen flag is the only way to prevent the * message from being re-fetched on subsequent runs. - */ - if (ctl->keep) + * + * 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. + * + * We take advantage here of the fact that, according to all the + * POP RFCs, "if the number of lines requested by the POP3 client + * is greater than than the number of lines in the body, then the + * 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 + * 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 2147483647", number); + gen_send(sock, "TOP %d 99999999", number); if ((ok = pop3_ok(sock, buf)) != 0) return(ok); @@ -564,27 +744,50 @@ static int pop3_logout(int sock, struct query *ctl) { int ok; - /* phase = PHASE_LOGOUT */ +#ifdef __UNUSED__ + /* + * We used to do this in case the server marks messages deleted when seen. + * (Yes, this has been reported, in the MercuryP/NLM server. + * It's even legal under RFC 1939 (section 8) as a site policy.) + * It interacted badly with UIDL, though. Thomas Zajic wrote: + * "Running 'fetchmail -F -v' and checking the logs, I found out + * that fetchmail did in fact flush my mailbox properly, but sent + * a RSET just before sending QUIT to log off. This caused the + * POP3 server to undo/forget about the previous DELEs, resetting + * my mailbox to its original (ie. unflushed) state. The + * ~/.fetchids file did get flushed though, so the next time + * fetchmail was run it saw all the old messages as new ones ..." + */ + if (ctl->keep) + gen_transact(sock, "RSET"); +#endif /* __UNUSED__ */ ok = gen_transact(sock, "QUIT"); if (!ok) expunge_uids(ctl); + if (ctl->lastid) + { + free(ctl->lastid); + ctl->lastid = NULL; + } + return(ok); } const static struct method pop3 = { "POP3", /* Post Office Protocol v3 */ -#if INET6 +#if INET6_ENABLE "pop3", /* standard POP3 port */ -#else /* INET6 */ + "pop3s", /* ssl POP3 port */ +#else /* INET6_ENABLE */ 110, /* standard POP3 port */ -#endif /* INET6 */ + 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 */ - NULL, /* no password canonicalization */ pop3_getauth, /* get authorization */ pop3_getrange, /* query range of messages */ pop3_getsizes, /* we can get a list of sizes */ @@ -602,11 +805,11 @@ int doPOP3 (struct query *ctl) { #ifndef MBOX if (ctl->mailboxes->id) { - fprintf(stderr,"Option --remote is not supported with POP3\n"); + fprintf(stderr,GT_("Option --remote is not supported with POP3\n")); return(PS_SYNTAX); } #endif /* MBOX */ - peek_capable = FALSE; + peek_capable = !ctl->fetchall; return(do_protocol(ctl, &pop3)); } #endif /* POP3_ENABLE */