diff options
-rw-r--r-- | NEWS | 14 | ||||
-rw-r--r-- | driver.c | 23 | ||||
-rw-r--r-- | fetchmail.man | 5 | ||||
-rw-r--r-- | pop2.c | 620 |
4 files changed, 132 insertions, 530 deletions
@@ -2,9 +2,7 @@ To-do list: -Option to enable EMACS-like user folder versioning on each run. - -The IMAP support is naive. Chris Newman, one of the IMAP maintainers, +1. The IMAP support is naive. Chris Newman, one of the IMAP maintainers, criticized it as follows: ------------------------------- CUT HERE ----------------------------------- On Wed, 18 Sep 1996, Eric S. Raymond wrote: @@ -42,17 +40,19 @@ The key thing to remember is that in IMAP the server holds the authoratative list of messages and the client just holds a cache. This is a very different model from POP. ------------------------------- CUT HERE ----------------------------------- +If we ever decide that concurrent runs need to work safely, this will have +to be fixed. -IMAP extensions for secure challenge-response. +2. Support IMAP4 extensions for secure challenge-response. -Recode POP2 to use the same driver/method strategy as POP3/IMAP, so -all three protocols will be able to forward messages through the generic -driver to SMTP. (This requires that we find a POP2 server to test with.) +3. Option to enable EMACS-like user folder versioning on each run. fetchmail-1.0 (Mon Sep 23 19:54:01 EDT 1996): * Name change (it ain't just for POP3 any more). +* SMTP forwarding and header-rewrite features work with POP2 now. + * Stricter RFC822 conformance, so SMTP to qmail works. Thanks to Cameron MacPherson <unsound@oz.net> for these changes. @@ -114,10 +114,6 @@ struct method *proto; goto closeUp; } - /* print the greeting */ - if (outlevel == O_VERBOSE) - fprintf(stderr,"%s greeting: %s\n", protocol->name, buf); - /* try to get authorized to fetch mail */ ok = (protocol->getauth)(socket, queryctl, buf); if (ok == PS_ERROR) @@ -180,14 +176,17 @@ struct method *proto; } /* maybe we delete this message now? */ - if ((number < first && queryctl->flush) || !queryctl->keep) { - if (outlevel > O_SILENT && outlevel < O_VERBOSE) - fprintf(stderr,"flushing message %d\n", number); - else - ; - ok = gen_transact(socket, protocol->delete_cmd, number); - if (ok != 0) - goto cleanUp; + if (protocol->delete_cmd) + { + if ((number < first && queryctl->flush) || !queryctl->keep) { + if (outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"flushing message %d\n", number); + else + ; + ok = gen_transact(socket, protocol->delete_cmd, number); + if (ok != 0) + goto cleanUp; + } } /* close the mail pipe, we'll reopen before next message */ diff --git a/fetchmail.man b/fetchmail.man index 339e0bf6..70a85220 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -579,11 +579,6 @@ Running more than one concurrent instance of .I fetchmail on the same mailbox may cause messages to be lost or remain unfetched. .PP -When using POP2, the --smtphost option doesn't work, and mail headers -are not rewritten to enable replies as described under --norewrite. -This isn't a protocol problem, it's because the developer couldn't -find a POP2 server to test the necessary code reorganization with. -.PP The --remotefolder option doesn't work with POP3, the protocol won't support it. .PP @@ -28,551 +28,159 @@ #include "socket.h" #include "fetchmail.h" - -/* TCP port number for POP2 as defined by RFC 937 */ -#define POP2_PORT 109 - #if HAVE_PROTOTYPES -/* prototypes for internal functions */ -int POP2_sendcmd (char *cmd, int socket); -int POP2_sendHELO (char *userid, char *password, int socket); -int POP2_sendFOLD (char *folder, int socket); -int POP2_quit (int socket); -int POP2_stateGREET (int socket); -int POP2_stateNMBR (int socket); -int POP2_stateSIZE (int socket); -int POP2_stateXFER (int msgsize, int socket, int mboxfd, int topipe); #endif - /********************************************************************* - function: doPOP2 - description: retrieve messages from the specified mail server - using Post Office Protocol 2. - arguments: - queryctl fully-specified options (i.e. parsed, defaults invoked, - etc). + Method declarations for POP2 - return value: exit code from the set of PS_.* constants defined in - fetchmail.h - calls: POP2_stateGREET, POP2_stateNMBR, POP2_stateSIZE, - POP2_stateXFER, POP2_sendcmd, POP2_sendHELO, - POP2_sendFOLD, POP2_quit, Socket, openuserfolder, - closeuserfolder, openmailpipe, closemailpipe. - globals: reads outlevel. *********************************************************************/ -int doPOP2 (queryctl) -struct hostrec *queryctl; -{ - int mboxfd; - int socket; - int number,msgsize,actsize; - int status = PS_UNDEFINED; - - /* check for unsupported options */ - if (linelimit) { - fprintf(stderr,"Option --limit is not supported with POP2\n"); - return(PS_SYNTAX); - } - else if (queryctl->flush) { - fprintf(stderr,"Option --flush is not supported with POP2\n"); - return(PS_SYNTAX); - } - else if (queryctl->fetchall) { - fprintf(stderr,"Option --all is not supported with POP2\n"); - return(PS_SYNTAX); - } - else if (queryctl->smtphost[0]) { - fprintf(stderr,"Option --smtphost is not supported with POP2\n"); - return(PS_SYNTAX); - } - else - ; - - /* open the socket to the POP server */ - if ((socket = Socket(queryctl->servername, - queryctl->port ? queryctl->port : POP2_PORT)) < 0) - { - perror("doPOP2: socket"); - return(PS_SOCKET); - } - - /* open/lock the folder if it is a user folder or stdout */ - if (queryctl->output == TO_FOLDER) - if ((mboxfd = openuserfolder(queryctl)) < 0) - return(PS_IOERR); - - /* wait for the POP2 greeting */ - if (POP2_stateGREET(socket) != 0) { - POP2_quit(socket); - status = PS_PROTOCOL; - goto closeUp; - } - - /* log the user onto the server */ - POP2_sendHELO(queryctl->remotename,queryctl->password,socket); - if ((number = POP2_stateNMBR(socket)) < 0) { - POP2_quit(socket); - status = PS_AUTHFAIL; - goto closeUp; - } - - /* set the remote folder if selected */ - if (*queryctl->remotefolder != 0) { - POP2_sendFOLD(queryctl->remotefolder,socket); - if ((number = POP2_stateNMBR(socket)) < 0) { - POP2_quit(socket); - status = PS_PROTOCOL; - goto closeUp; - } - } - - /* tell 'em how many messages are waiting */ - if (outlevel > O_SILENT && outlevel < O_VERBOSE) - if (number == 0) - fprintf(stderr,"No mail from %s\n",number,queryctl->servername); - else - fprintf(stderr,"%d message%s from %s\n", - number, number > 1 ? "s" : "", queryctl->servername); - +static int pound_arg, equal_arg; - /* fall into a retrieve/acknowledge loop */ - if (number > 0) { - - POP2_sendcmd("READ",socket); - msgsize = POP2_stateSIZE(socket); - while (msgsize > 0) { - - /* open the pipe */ - if (queryctl->output == TO_MDA) - if ((mboxfd = openmailpipe(queryctl)) < 0) { - POP2_quit(socket); - return(PS_IOERR); - } - - POP2_sendcmd("RETR",socket); - actsize = POP2_stateXFER(msgsize,socket,mboxfd, - queryctl->output == TO_MDA); - if (actsize == msgsize) - if (queryctl->keep) - POP2_sendcmd("ACKS",socket); - else - POP2_sendcmd("ACKD",socket); - else if (actsize >= 0) - POP2_sendcmd("NACK",socket); - else { - POP2_quit(socket); - status = PS_SOCKET; - goto closeUp; - } - - /* close the pipe */ - if (queryctl->output == TO_MDA) - if (closemailpipe(mboxfd) < 0) { - POP2_quit(socket); - status = PS_IOERR; - goto closeUp; - } - - msgsize = POP2_stateSIZE(socket); - } - POP2_quit(socket); - status = msgsize == 0 ? PS_SUCCESS : PS_PROTOCOL; - } - else { - POP2_quit(socket); - status = PS_NOMAIL; - } - -closeUp: - if (queryctl->output == TO_FOLDER) - closeuserfolder(mboxfd); - - return(status); -} - - - -/********************************************************************* - function: POP2_sendcmd - description: send a command string (with no arguments) a server. - arguments: - cmd command string to send. - socket socket to which the server is connected. - - return value: none. - calls: SockPuts. - globals: reads outlevel. - *********************************************************************/ - -int POP2_sendcmd (cmd,socket) -char *cmd; +int pop2_ok (argbuf,socket) +/* parse POP2 command response */ +char *argbuf; int socket; { - SockPuts(socket,cmd); + int ok; + char buf [POPBUFSIZE+1]; + + pound_arg = equal_arg = -1; + if (SockGets(socket, buf, sizeof(buf)) >= 0) { + if (outlevel == O_VERBOSE) + fprintf(stderr,"%s\n",buf); + + if (buf[0] == '+') + ok = 0; + else if (buf[0] == '#') + { + pound_arg = atoi(buf+1); + ok = 0; + } + else if (buf[0] == '=') + { + equal_arg = atoi(buf+1); + ok = 0; + } + else if (buf[0] == '-') + ok = PS_ERROR; + else + ok = PS_PROTOCOL; + + if (argbuf != NULL) + strcpy(argbuf,buf); + } + else + ok = PS_SOCKET; - if (outlevel == O_VERBOSE) - fprintf(stderr,"> %s\n",cmd); - else - ; + return(ok); } - -/********************************************************************* - function: POP2_sendHELO - description: send the HELO command to the server. - arguments: - userid user's mailserver id. - password user's mailserver password. - socket socket to which the server is connected. - - return value: none. - calls: SockPrintf. - globals: read outlevel. - *********************************************************************/ - -int POP2_sendHELO (userid,password,socket) -char *userid, *password; +int pop2_getauth(socket, queryctl, buf) +/* apply for connection authorization */ int socket; +struct hostrec *queryctl; +char *buf; { - SockPrintf(socket,"HELO %s %s\r\n",userid,password); - - - if (outlevel == O_VERBOSE) - fprintf(stderr,"> HELO %s password\n",userid); - else - ; + return(gen_transact(socket, + "HELO %s %s", + queryctl->remotename, queryctl->password)); } - -/********************************************************************* - function: POP2_sendFOLD - description: send the FOLD command to the server. - arguments: - folder name of the folder to open on the server. - socket socket to which the server is connected. - - return value: none. - calls: SockPrintf. - globals: reads outlevel. - *********************************************************************/ - -int POP2_sendFOLD (folder,socket) -char *folder; +static pop2_getrange(socket, queryctl, countp, firstp) +/* get range of messages to be fetched */ int socket; +struct hostrec *queryctl; +int *countp; +int *firstp; { - SockPrintf(socket,"FOLD %s\r\n",folder); - - if (outlevel == O_VERBOSE) - fprintf(stderr,"> FOLD %s\n",folder); - else - ; -} - - -/********************************************************************* - function: POP2_quit - description: send the QUIT command to the server and close - the socket. - - arguments: - socket socket to which the server is connected. - - return value: none. - calls: SockPuts. - globals: reads outlevel. - *********************************************************************/ + /* + * We should have picked up a count of messages in the user's + * default inbox from the pop2_getauth() response. + */ + if (pound_arg == -1) + return(PS_ERROR); + + /* maybe the user wanted a non-default folder */ + if (queryctl->remotefolder[0]) + { + int ok = gen_transact(socket, "FOLD %s", queryctl->remotefolder); + + if (ok != 0) + return(ok); + if (pound_arg == -1) + return(PS_ERROR); + } -int POP2_quit (socket) -int socket; -{ - SockPuts(socket,"QUIT"); - close(socket); + *firstp = 1; + *countp = pound_arg; - if (outlevel == O_VERBOSE) - fprintf(stderr,"> QUIT\n"); - else - ; + return(0); } - -/********************************************************************* - function: POP2_stateGREET - description: process the GREET state as described in RFC 937. - arguments: - socket ...to which server is connected. - - return value: zero if server's handling of the GREET state was - correct, else non-zero (may indicate difficulty - at the socket). - calls: SockGets. - globals: reads outlevel. - *********************************************************************/ - -int POP2_stateGREET (socket) +static int pop2_fetch(socket, number, limit, lenp) +/* request nth message */ int socket; +int number; +int limit; +int *lenp; { - char buf [POPBUFSIZE+1]; - - /* read the greeting from the server */ - if (SockGets(socket, buf, sizeof(buf)) >= 0) { - - /* echo the server's greeting to the user */ - if (outlevel > O_SILENT) - fprintf(stderr,"POP2 greeting: %s\n",buf); - else - ; - /* is the greeting in the correct format? */ - if (*buf == '+') - return(0); - else - return(-1); - } - else { - /* an error at the socket */ - if (outlevel > O_SILENT) - perror("error reading socket\n"); - else - ; - return(-1); - } -} + int ok; + *lenp = 0; + ok = gen_transact(socket, "READ %d", number); + if (ok) + return(0); + *lenp = equal_arg; -/********************************************************************* - function: POP2_stateNMBR - description: process the NMBR state as described in RFC 937. - arguments: - socket ...to which the server is connected. + gen_send(socket, "RETR"); - return value: zero if the expected NMBR state action occured, else - non-zero. Following HELO, a non-zero return value - usually here means the user authorization at the server - failed. - calls: SockGets. - globals: reads outlevel. - *********************************************************************/ - -int POP2_stateNMBR (socket) -int socket; -{ - int number; - char buf [POPBUFSIZE+1]; - - /* read the NMBR (#ccc) message from the server */ - if (SockGets(socket, buf, sizeof(buf)) >= 0) { - - /* is the message in the proper format? */ - if (*buf == '#') { - number = atoi(buf + 1); - if (outlevel == O_VERBOSE) - fprintf(stderr,"%s\n",buf); - else - ; - } - else { - number = -1; - if (outlevel > O_SILENT) - fprintf(stderr,"%s\n",buf); - else - ; - } - } - else { - /* socket problem */ - number = -1; - if (outlevel == O_VERBOSE) - perror("socket read error\n"); - else - ; - } - return(number); + return(ok); } - -/********************************************************************* - function: POP2_stateSIZE - description: process the SIZE state as described in RFC 937. - arguments: - socket ...to which the server is connected. - - return value: zero if the expected SIZE state action occured, else - non-zero (usually indicates a protocol violation). - calls: SockGets. - globals: reads outlevel. - *********************************************************************/ - -int POP2_stateSIZE (socket) +static pop2_trail(socket, queryctl, number) +/* send acknowledgement for message data */ int socket; +struct hostrec *queryctl; +int number; { - int msgsize; - char buf [POPBUFSIZE+1]; - - /* read the SIZE message (=ccc) from the server */ - if (SockGets(socket, buf, sizeof(buf)) >= 0) - /* is the message in the correct format? */ - if (*buf == '=') { - msgsize = atoi(buf + 1); - if (outlevel == O_VERBOSE) - fprintf(stderr,"%s\n",buf); - else - ; - } - else { - msgsize = -1; - if (outlevel > O_SILENT) - fprintf(stderr,"%s\n",buf); - else - ; - } - else { - /* socket problem */ - msgsize = -1; - if (outlevel == O_VERBOSE) - perror("socket read error\n"); - else - ; - } - - return(msgsize); + return(gen_transact(socket, queryctl->keep ? "ACKS" : "ACKD")); } - -/********************************************************************* - function: POP2_stateXFER - description: process the XFER state as described in RFC 937. - arguments: - msgsize content length of the message as reported in the - SIZE state. - socket ... to which the server is connected. - mboxfd open file descriptor to which the retrieved message will - be written. - topipe true if we're writing to a the /bin/mail pipe. - - return value: - >= 0 actual length of the message received. - < 0 socket I/O problem. - - calls: SockRead. - globals: reads outlevel. - *********************************************************************/ - -int POP2_stateXFER (msgsize,socket,mboxfd,topipe) -int msgsize; -int socket; -int mboxfd; -int topipe; +static struct method pop2 = { - int i,buflen,actsize; - char buf [MSGBUFSIZE+1]; - char frombuf [MSGBUFSIZE+1]; - char savec; - int msgTop; - int needFrom; - - time_t now; + "POP2", /* Post Office Protocol v2 */ + 109, /* standard POP2 port */ + 0, /* this is not a tagged protocol */ + 0, /* does not use message delimiter */ + pop2_ok, /* parse command response */ + pop2_getauth, /* get authorization */ + pop2_getrange, /* query range of messages */ + pop2_fetch, /* request given message */ + pop2_trail, /* eat message trailer */ + NULL, /* no POP2 delete command */ + NULL, /* no POP2 expunge command */ + "QUIT", /* the POP2 exit command */ +}; - /* This keeps the retrieved message count for display purposes */ - static int msgnum = 0; - - /* set up for status message if outlevel allows it */ - if (outlevel > O_SILENT && outlevel < O_VERBOSE) { - fprintf(stderr,"reading message %d",++msgnum); - /* won't do the '...' if retrieved messages are being sent to stdout */ - if (mboxfd == 1) /* we're writing to stdout */ - fputs(".\n",stderr); - else - ; - } - else - ; - - - /* read the specified message content length from the server */ - actsize = 0; - msgTop = !0; - while (msgsize > 0) { - buflen = msgsize <= MSGBUFSIZE ? msgsize : MSGBUFSIZE; - /* read a bufferful */ - if (SockRead(socket, buf, buflen) == 0) { - - /* Check for Unix 'From' header, and add bogus one if it's not - present -- only if not using an MDA. - XXX -- should probably parse real From: header and use its - address field instead of bogus 'POPmail' string. - */ - if (!topipe && msgTop) { - msgTop = 0; - if (strlen(buf) >= strlen("From ")) { - savec = *(buf + 5); - *(buf + 5) = 0; - needFrom = strcmp(buf,"From ") != 0; - *(buf + 5) = savec; - } - else - needFrom = 1; - if (needFrom) { - now = time(NULL); - sprintf(frombuf,"From POPmail %s",ctime(&now)); - if (write(mboxfd,frombuf,strlen(frombuf)) < 0) { - perror("POP2_stateXFER: write"); - return(-1); - } - } - } - - /* write to folder, stripping CR chars in the process */ - for (i = 0; i < buflen; i++) - if (*(buf + i) != '\r') - if (write(mboxfd,buf + i,1) < 0) { - perror("POP2_stateXFER: write"); - return(-1); - } - else - ; /* it was written */ - else - ; /* ignore CR character */ +int doPOP2 (queryctl) +struct hostrec *queryctl; +{ + /* check for unsupported options */ + if (linelimit) { + fprintf(stderr,"Option --limit is not supported with POP2\n"); + return(PS_SYNTAX); } - else - return(-1); /* socket problem */ - - /* write another . for every bufferful received */ - if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1) - fputc('.',stderr); - else - ; - msgsize -= buflen; - actsize += buflen; - } - - if (!topipe) { - /* The server may not write the extra newline required by the Unix - mail folder format, so we write one here just in case */ - if (write(mboxfd,"\n",1) < 1) { - perror("POP2_stateXFER: write"); - return(-1); + else if (queryctl->flush) { + fprintf(stderr,"Option --flush is not supported with POP2\n"); + return(PS_SYNTAX); } - } - else { - /* the mailer might require some sort of termination string, send - it if it is defined */ -#ifdef BINMAIL_TERM - if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) { - perror("POP2_stateXFER: write"); - return(-1); + else if (queryctl->fetchall) { + fprintf(stderr,"Option --all is not supported with POP2\n"); + return(PS_SYNTAX); } -#endif - } - - /* finish up display output */ - if (outlevel == O_VERBOSE) - fprintf(stderr,"(%d characters of message content)\n",actsize); - else if (outlevel > O_SILENT && mboxfd != 0) - fputc('\n',stderr); - else - ; - return(actsize); + return(do_protocol(queryctl, &pop2)); } |