X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=pop2.c;h=03d58a18f92334be44b0c57bd5774e1cf6893d18;hb=d31db10231e9ed89f64fdf6e0fb7cae182aa377e;hp=28056b1376f6ba421364ce5f0fc02e16837f819d;hpb=b9725878c38cc4ae8c3af81d55dab77a6b83a02b;p=~andy%2Ffetchmail diff --git a/pop2.c b/pop2.c index 28056b13..03d58a18 100644 --- a/pop2.c +++ b/pop2.c @@ -1,569 +1,185 @@ -/* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond - * All rights reserved. +/* + * pop2.c -- POP2 protocol methods + * + * Copyright 1997 by Eric S. Raymond * For license terms, see the file COPYING in this directory. */ -/*********************************************************************** - module: pop2.c - project: popclient - programmer: Carl Harris, ceharris@mal.com - Hacks and bug fixes by esr. - description: POP2 client code. - - ***********************************************************************/ - -#include +#include "config.h" +#ifdef POP2_ENABLE #include #if defined(STDC_HEADERS) -#include +#include #endif -#if defined(HAVE_UNISTD_H) -#include +#ifdef HAVE_STRING_H +#include #endif - -#include -#include - +#include "fetchmail.h" #include "socket.h" -#include "popclient.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: - options fully-specified options (i.e. parsed, defaults invoked, - etc). +#include "i18n.h" - return value: exit code from the set of PS_.* constants defined in - popclient.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. - *********************************************************************/ +static int pound_arg, equal_arg; -int doPOP2 (options) -struct hostrec *options; +static int pop2_ok (int sock, char *argbuf) +/* parse POP2 command response */ { - 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 in POP2\n"); - return(PS_SYNTAX); - } - else if (options->flush) { - fprintf(stderr,"Option --flush is not supported in POP2\n"); - return(PS_SYNTAX); - } - else if (options->fetchall) { - fprintf(stderr,"Option --all is not supported in POP2\n"); - return(PS_SYNTAX); - } - else - ; - - /* open the socket to the POP server */ - if ((socket = Socket(options->servername,POP2_PORT)) < 0) { - perror("doPOP2: socket"); - return(PS_SOCKET); - } - - /* open/lock the folder if it is a user folder or stdout */ - if (options->output == TO_FOLDER) - if ((mboxfd = openuserfolder(options)) < 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(options->remotename,options->password,socket); - if ((number = POP2_stateNMBR(socket)) < 0) { - POP2_quit(socket); - status = PS_AUTHFAIL; - goto closeUp; - } - - /* set the remote folder if selected */ - if (*options->remotefolder != 0) { - POP2_sendFOLD(options->remotefolder,socket); - if ((number = POP2_stateNMBR(socket)) < 0) { - POP2_quit(socket); - status = PS_PROTOCOL; - goto closeUp; + int ok; + char buf [POPBUFSIZE+1]; + + pound_arg = equal_arg = -1; + + if ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) + { + 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); } - } - - /* tell 'em how many messages are waiting */ - if (outlevel > O_SILENT && outlevel < O_VERBOSE) - fprintf(stderr,"%d messages in folder %s\n",number,options->remotefolder); - else - ; - - /* fall into a retrieve/acknowledge loop */ - if (number > 0) { - POP2_sendcmd("READ",socket); - msgsize = POP2_stateSIZE(socket); - while (msgsize > 0) { + return(ok); +} - /* open the pipe */ - if (options->output == TO_MDA) - if ((mboxfd = openmailpipe(options)) < 0) { - POP2_quit(socket); - return(PS_IOERR); - } +static int pop2_getauth(int sock, struct query *ctl, char *buf) +/* apply for connection authorization */ +{ + int status; - POP2_sendcmd("RETR",socket); - actsize = POP2_stateXFER(msgsize,socket,mboxfd, - options->output == TO_MDA); - if (actsize == msgsize) - if (options->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; - } + (void)buf; - /* close the pipe */ - if (options->output == TO_MDA) - if (closemailpipe(mboxfd) < 0) { - POP2_quit(socket); - status = PS_IOERR; - goto closeUp; - } - - msgsize = POP2_stateSIZE(socket); + if (ctl->sslproto && !strcasecmp(ctl->sslproto, "tls1") && !ctl->use_ssl) + { + report(stderr, GT_("POP2 does not support STLS. Giving up.\n")); + return PS_SOCKET; } - POP2_quit(socket); - status = msgsize == 0 ? PS_SUCCESS : PS_PROTOCOL; - } - else { - POP2_quit(socket); - status = PS_NOMAIL; - } -closeUp: - if (options->output == TO_FOLDER) - closeuserfolder(mboxfd); + if (ctl->server.authenticate != A_ANY && ctl->server.authenticate != A_PASSWORD) + { + report(stderr, GT_("POP2 only supports password authentication. Giving up.\n")); + return PS_AUTHFAIL; + } - return(status); + strlcpy(shroud, ctl->password, sizeof(shroud)); + status = gen_transact(sock, + "HELO %s %s", + ctl->remotename, ctl->password); + memset(shroud, 0x55, sizeof(shroud)); + shroud[0] = '\0'; + 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 socket; +static int pop2_getrange(int sock, struct query *ctl, const char *folder, + int *countp, int *newp, int *bytes) +/* get range of messages to be fetched */ { - SockPuts(socket,cmd); - - if (outlevel == O_VERBOSE) - fprintf(stderr,"> %s\n",cmd); - else - ; -} - - -/********************************************************************* - 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. + (void)ctl; - return value: none. - calls: SockPrintf. - globals: read outlevel. - *********************************************************************/ + /* maybe the user wanted a non-default folder */ + if (folder) + { + int ok = gen_transact(sock, "FOLD %s", folder); -int POP2_sendHELO (userid,password,socket) -char *userid, *password; -int socket; -{ - SockPrintf(socket,"HELO %s %s\r\n",userid,password); - - - if (outlevel == O_VERBOSE) - fprintf(stderr,"> HELO %s password\n",userid); - else - ; + if (ok != 0) + return(ok); + if (pound_arg == -1) + return(PS_ERROR); + } + else + /* + * We should have picked up a count of messages in the user's + * default inbox from the pop2_getauth() response. + * + * Note: this logic only works because there is no way to select + * both the unnamed folder and named folders within a single + * fetchmail run. If that assumption ever becomes invalid, the + * pop2_getauth code will have to stash the pound response away + * explicitly in case it gets stepped on. + */ + if (pound_arg == -1) + return(PS_ERROR); + + *countp = pound_arg; + *bytes = *newp = -1; + + return(0); } - -/********************************************************************* - 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; -int socket; +static int pop2_fetch(int sock, struct query *ctl, int number, int *lenp) +/* request nth message */ { - 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. + int ok; - arguments: - socket socket to which the server is connected. + (void)ctl; + *lenp = 0; + ok = gen_transact(sock, "READ %d", number); + if (ok) + return(0); + *lenp = equal_arg; - return value: none. - calls: SockPuts. - globals: reads outlevel. - *********************************************************************/ + gen_send(sock, "RETR"); -int POP2_quit (socket) -int socket; -{ - SockPuts(socket,"QUIT"); - close(socket); - - if (outlevel == O_VERBOSE) - fprintf(stderr,"> QUIT\n"); - else - ; + return(ok); } - -/********************************************************************* - 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) -int socket; +static int pop2_trail(int sock, struct query *ctl, const char *tag) +/* send acknowledgement for message data */ { - char buf [POPBUFSIZE]; - - /* 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,"%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); - } + (void)ctl; + (void)tag; + return(gen_transact(sock, ctl->keep ? "ACKS" : "ACKD")); } - -/********************************************************************* - function: POP2_stateNMBR - description: process the NMBR state as described in RFC 937. - arguments: - socket ...to which the server is connected. - - 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; +static int pop2_logout(int sock, struct query *ctl) +/* send logout command */ { - int number; - char buf [POPBUFSIZE]; - - /* 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); + (void)ctl; + return(gen_transact(sock, "QUIT")); } - -/********************************************************************* - 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) -int socket; +static const struct method pop2 = { - int msgsize; - char buf [POPBUFSIZE]; - - /* 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); -} - - -/********************************************************************* - 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; + "POP2", /* Post Office Protocol v2 */ + "pop2", /* standard POP2 port */ + "pop2", /* ssl POP2 port - not */ + FALSE, /* this is not a tagged protocol */ + FALSE, /* does not use message delimiter */ + pop2_ok, /* parse command response */ + pop2_getauth, /* get authorization */ + pop2_getrange, /* query range of messages */ + NULL, /* no way to get sizes */ + NULL, /* no way to get sizes of subsets */ + NULL, /* messages are always new */ + pop2_fetch, /* request given message */ + NULL, /* no way to fetch body alone */ + pop2_trail, /* eat message trailer */ + NULL, /* no POP2 delete method */ + NULL, /* how to mark a message as seen */ + NULL, /* how to end mailbox processing */ + pop2_logout, /* log out, we're done */ + FALSE /* no, we can't re-poll */ +}; + +int doPOP2 (struct query *ctl) +/* retrieve messages using POP2 */ { - int i,buflen,actsize; - char buf [MSGBUFSIZE]; - char frombuf [MSGBUFSIZE]; - char savec; - int msgTop; - int needFrom; - - time_t now; - - /* 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 */ - } - 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 { - /* 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); - } -#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); + peek_capable = FALSE; + return(do_protocol(ctl, &pop2)); } +#endif /* POP2_ENABLE */ + +/* pop2.c ends here */