aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS14
-rw-r--r--driver.c23
-rw-r--r--fetchmail.man5
-rw-r--r--pop2.c620
4 files changed, 132 insertions, 530 deletions
diff --git a/NEWS b/NEWS
index 104e9c8b..f70db50e 100644
--- a/NEWS
+++ b/NEWS
@@ -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.
diff --git a/driver.c b/driver.c
index 6bfd8bff..a37ab807 100644
--- a/driver.c
+++ b/driver.c
@@ -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
diff --git a/pop2.c b/pop2.c
index 2d6d4fe2..16fbb73d 100644
--- a/pop2.c
+++ b/pop2.c
@@ -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));
}