diff options
Diffstat (limited to 'pop2.c')
-rw-r--r-- | pop2.c | 611 |
1 files changed, 611 insertions, 0 deletions
@@ -0,0 +1,611 @@ +/* Copyright 1993-95 by Carl Harris, Jr. + * All rights reserved + * + * Distribute freely, except: don't remove my name from the source or + * documentation (don't take credit for my work), mark your changes (don't + * get me blamed for your possible bugs), don't alter or remove this + * notice. May be sold if buildable source is provided to buyer. No + * warrantee of any kind, express or implied, is included with this + * software; use at your own risk, responsibility for damages (if any) to + * anyone resulting from the use of this software rests entirely with the + * user. + * + * Send bug reports, bug fixes, enhancements, requests, flames, etc., and + * I'll try to keep a version up to date. I can be reached as follows: + * Carl Harris <ceharris@mal.com> + */ + + +/*********************************************************************** + module: pop2.c + project: popclient + programmer: Carl Harris, ceharris@mal.com + description: POP2 client code. + + $Log: pop2.c,v $ + Revision 1.1 1996/06/24 19:00:51 esr + Initial revision + + Revision 1.6 1995/08/14 18:36:40 ceharris + Patches to support POP3's LAST command. + Final revisions for beta3 release. + + Revision 1.5 1995/08/10 00:32:36 ceharris + Preparation for 3.0b3 beta release: + - added code for --kill/--keep, --limit, --protocol, --flush + options; --pop2 and --pop3 options now obsoleted by --protocol. + - added support for APOP authentication, including --with-APOP + argument for configure. + - provisional and broken support for RPOP + - added buffering to SockGets and SockRead functions. + - fixed problem of command-line options not being correctly + carried into the merged options record. + + Revision 1.4 1995/08/09 01:32:53 ceharris + Version 3.0 beta 2 release. + Added + - .poprc functionality + - GNU long options + - multiple servers on the command line. + Fixed + - Passwords showing up in ps output. + + Revision 1.3 1995/08/08 01:01:22 ceharris + Added GNU-style long options processing. + Fixed password in 'ps' output problem. + Fixed various RCS tag blunders. + Integrated .poprc parser, lexer, etc into Makefile processing. + + ***********************************************************************/ + +#include <config.h> + +#include <stdio.h> +#if defined(STDC_HEADERS) +#include <string.h> +#endif +#if defined(HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include <sys/time.h> +#include <errno.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: + servername name of the server to which we'll connect. + options fully-specified options (i.e. parsed, defaults invoked, + etc). + + 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. + *********************************************************************/ + +int doPOP2 (servername,options) +char *servername; +struct optrec *options; +{ + int mboxfd; + int socket; + int number,msgsize,actsize; + int status = PS_UNDEFINED; + + /* check for unsupported options */ + if (options->limit) { + 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(servername,POP2_PORT)) < 0) { + perror("doPOP2: socket"); + return(PS_SOCKET); + } + + /* open/lock the folder if it is a user folder or stdout */ + if (options->foldertype != OF_SYSMBOX) + if ((mboxfd = openuserfolder(options)) < 0) + return(PS_IOERR); + + /* wait for the POP2 greeting */ + if (POP2_stateGREET(socket) != 0) { + POP2_quit(socket); + return(PS_PROTOCOL); + } + + /* log the user onto the server */ + POP2_sendHELO(options->userid,options->password,socket); + if ((number = POP2_stateNMBR(socket)) < 0) { + POP2_quit(socket); + return(PS_AUTHFAIL); + } + + /* set the remote folder if selected */ + if (*options->remotefolder != 0) { + POP2_sendFOLD(options->remotefolder,socket); + if ((number = POP2_stateNMBR(socket)) < 0) { + POP2_quit(socket); + return(PS_PROTOCOL); + } + } + + /* 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) { + + /* open the pipe */ + if (options->foldertype == OF_SYSMBOX) + if ((mboxfd = openmailpipe(options)) < 0) { + POP2_quit(socket); + return(PS_IOERR); + } + + POP2_sendcmd("RETR",socket); + actsize = POP2_stateXFER(msgsize,socket,mboxfd, + options->foldertype == OF_SYSMBOX); + 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); + return(PS_SOCKET); + } + + /* close the pipe */ + if (options->foldertype == OF_SYSMBOX) + if (closemailpipe(mboxfd) < 0) { + POP2_quit(socket); + return(PS_IOERR); + } + + msgsize = POP2_stateSIZE(socket); + } + POP2_quit(socket); + status = msgsize == 0 ? PS_SUCCESS : PS_PROTOCOL; + } + else { + POP2_quit(socket); + status = PS_NOMAIL; + } + + if (options->foldertype != OF_SYSMBOX) + 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 socket; +{ + 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. + + return value: none. + calls: SockPrintf. + globals: read outlevel. + *********************************************************************/ + +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 + ; +} + + +/********************************************************************* + 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; +{ + 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. + *********************************************************************/ + +int POP2_quit (socket) +int socket; +{ + SockPuts(socket,"QUIT"); + close(socket); + + if (outlevel == O_VERBOSE) + fprintf(stderr,"> QUIT\n"); + else + ; +} + + +/********************************************************************* + 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; +{ + 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); + } +} + + +/********************************************************************* + 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; +{ + 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); +} + + +/********************************************************************* + 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; +{ + 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; +{ + 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); +} |