diff options
author | Eric S. Raymond <esr@thyrsus.com> | 1996-06-24 19:00:51 +0000 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 1996-06-24 19:00:51 +0000 |
commit | cf227f48438b78f4cd72451c9b501b21b36fcf7c (patch) | |
tree | 18596fdaf30cad2d870ce8dac8ae2e1de4ad2192 | |
parent | 6124abb8368c51b6d568fd362078509c464a617c (diff) | |
download | fetchmail-cf227f48438b78f4cd72451c9b501b21b36fcf7c.tar.gz fetchmail-cf227f48438b78f4cd72451c9b501b21b36fcf7c.tar.bz2 fetchmail-cf227f48438b78f4cd72451c9b501b21b36fcf7c.zip |
Initial revision
svn path=/trunk/; revision=3
-rw-r--r-- | pop2.c | 611 | ||||
-rw-r--r-- | pop3.c | 781 |
2 files changed, 1392 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); +} @@ -0,0 +1,781 @@ +/* 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: pop3.c + project: popclient + programmer: Carl Harris, ceharris@mal.com + description: POP3 client code. + + $Log: pop3.c,v $ + Revision 1.1 1996/06/24 18:56:35 esr + Initial revision + + Revision 1.6 1995/08/14 18:36:42 ceharris + Patches to support POP3's LAST command. + Final revisions for beta3 release. + + Revision 1.5 1995/08/10 00:32:38 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:54 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:24 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 <ctype.h> +#include <errno.h> + +#include "socket.h" +#include "popclient.h" + +#define POP3_PORT 110 + +#ifdef HAVE_PROTOTYPES +/* prototypes for internal functions */ +int POP3_OK (char *buf, int socket); +int POP3_auth (struct optrec *options, int socket); +int POP3_sendQUIT (int socket); +int POP3_sendSTAT (int *msgcount, int socket); +int POP3_sendRETR (int msgnum, int socket); +int POP3_sendDELE (int msgnum, int socket); +int POP3_sendLAST (int *last, int socket); +int POP3_readmsg (int socket, int mboxfd, int topipe); +int POP3_BuildDigest (char *buf, struct optrec *options); +#endif + + +/********************************************************************* + function: doPOP3 + description: retrieve messages from the specified mail server + using Post Office Protocol 3. + + arguments: + servername name of 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: + globals: reads outlevel. + *********************************************************************/ + +int doPOP3 (servername,options) +char *servername; +struct optrec *options; +{ + int ok; + int mboxfd; + char buf [POPBUFSIZE]; + int socket; + int first,number,count; + + + /* open the folder if we're not using the system mailbox */ + if (options->foldertype != OF_SYSMBOX) + if ((mboxfd = openuserfolder(options)) < 0) + return(PS_IOERR); + + /* open the socket and get the greeting */ + if ((socket = Socket(servername,POP3_PORT)) < 0) { + perror("doPOP3: socket"); + return(PS_SOCKET); + } + + ok = POP3_OK(buf,socket); + if (ok != 0) { + if (ok != PS_SOCKET) + POP3_sendQUIT(socket); + close(socket); + return(ok); + } + + /* print the greeting */ + if (outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"%s\n",buf); + else + ; + +#if defined(HAVE_APOP_SUPPORT) + /* build MD5 digest from greeting timestamp + password */ + if (options->whichpop == P_APOP) + if (POP3_BuildDigest(buf,options) != 0) + return(PS_AUTHFAIL); + else + ; + else + ; /* not using APOP protocol this time */ +#endif + + /* try to get authorized */ + ok = POP3_auth(options,socket); + if (ok == PS_ERROR) + ok = PS_AUTHFAIL; + if (ok != 0) + goto cleanUp; + + /* find out how many messages are waiting */ + ok = POP3_sendSTAT(&count,socket); + if (ok != 0) { + goto cleanUp; + } + + /* Ask for number of last message retrieved */ + if (options->fetchall) + first = 1; + else { + ok = POP3_sendLAST(&first, socket); + if (ok != 0) + goto cleanUp; + + first++; + } + + /* show them how many messages we'll be downloading */ + if (outlevel > O_SILENT && outlevel < O_VERBOSE) + if (first > 1) + fprintf(stderr,"%d messages in folder, %d new messages.\n", + count, count - first + 1); + else + fprintf(stderr,"%d new messages in folder.\n", count); + else + ; + + if (count > 0) { + for (number = (options->flush || options->fetchall)? 1 : first; + number <= count; + number++) { + + /* open the mail pipe if we're using the system mailbox */ + if (options->foldertype == OF_SYSMBOX + && (options->fetchall || number >= first)) { + ok = (mboxfd = openmailpipe(options)) < 0 ? -1 : 0; + if (ok != 0) + goto cleanUp; + } + + if (options->flush && number < first && !options->fetchall) + ok = 0; /* no command to send here, will delete message below */ + else if (options->limit) + ok = POP3_sendTOP(number,options->limit,socket); + else + ok = POP3_sendRETR(number,socket); + if (ok != 0) + goto cleanUp; + + if (number >= first || options->fetchall) + ok = POP3_readmsg(socket,mboxfd,options->foldertype == OF_SYSMBOX); + else + ok = 0; + if (ok != 0) + goto cleanUp; + + if ((number < first && options->flush) || !options->keep) { + if (outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"flushing message %d\n", number); + else + ; + ok = POP3_sendDELE(number,socket); + if (ok != 0) + goto cleanUp; + } + else + ; /* message is kept */ + + /* close the mail pipe if we're using the system mailbox */ + if (options->foldertype == OF_SYSMBOX + && (options->fetchall || number >= first)) { + ok = closemailpipe(mboxfd); + if (ok != 0) + goto cleanUp; + } + } + + ok = POP3_sendQUIT(socket); + if (ok == 0) + ok = PS_SUCCESS; + close(socket); + return(ok); + } + else { + ok = POP3_sendQUIT(socket); + if (ok == 0) + ok = PS_NOMAIL; + close(socket); + return(ok); + } + +cleanUp: + if (ok != 0 && ok != PS_SOCKET) + POP3_sendQUIT(socket); + + if (options->foldertype != OF_SYSMBOX) + if (closeuserfolder(mboxfd) < 0 && ok == 0) + ok = PS_IOERR; + + if (ok == PS_IOERR || ok == PS_SOCKET) + perror("doPOP3: cleanUp"); + + return(ok); +} + + + +/********************************************************************* + function: POP3_OK + description: get the server's response to a command, and return + the extra arguments sent with the response. + arguments: + argbuf buffer to receive the argument string. + socket socket to which the server is connected. + + return value: zero if okay, else return code. + calls: SockGets + globals: reads outlevel. + *********************************************************************/ + +int POP3_OK (argbuf,socket) +char *argbuf; +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + char *bufp; + + if (SockGets(socket, buf, sizeof(buf)) == 0) { + if (outlevel == O_VERBOSE) + fprintf(stderr,"%s\n",buf); + + bufp = buf; + if (*bufp == '+' || *bufp == '-') + bufp++; + else + return(PS_PROTOCOL); + + while (isalpha(*bufp)) + bufp++; + *(bufp++) = '\0'; + + if (strcmp(buf,"+OK") == 0) + ok = 0; + else if (strcmp(buf,"-ERR") == 0) + ok = PS_ERROR; + else + ok = PS_PROTOCOL; + + if (argbuf != NULL) + strcpy(argbuf,bufp); + } + else + ok = PS_SOCKET; + + return(ok); +} + + + +/********************************************************************* + function: POP3_auth + description: send the USER and PASS commands to the server, and + get the server's response. + arguments: + options merged options record. + socket socket to which the server is connected. + + return value: zero if success, else status code. + calls: SockPrintf, POP3_OK. + globals: read outlevel. + *********************************************************************/ + +int POP3_auth (options,socket) +struct optrec *options; +int socket; +{ + char buf [POPBUFSIZE]; + + switch (options->whichpop) { + case P_POP3: + SockPrintf(socket,"USER %s\r\n",options->userid); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> USER %s\n",options->userid); + if (POP3_OK(buf,socket) != 0) + goto badAuth; + + SockPrintf(socket,"PASS %s\r\n",options->password); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> PASS password\n"); + if (POP3_OK(buf,socket) != 0) + goto badAuth; + + break; + +#if defined(HAVE_APOP_SUPPORT) + case P_APOP: + SockPrintf(socket,"APOP %s %s\r\n", + options->userid, options->digest); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> APOP %s %s\n",options->userid, options->digest); + if (POP3_OK(buf,socket) != 0) + goto badAuth; + break; +#endif /* HAVE_APOP_SUPPORT */ + +#if defined(HAVE_RPOP_SUPPORT) + case P_RPOP: + SockPrintf(socket, "RPOP %s\r\n", options->userid); + if (POP3_OK(buf,socket) != 0) + goto badAuth; + if (outlevel == O_VERBOSE) + fprintf(stderr,"> RPOP %s %s\n",options->userid); + break; +#endif /* HAVE_RPOP_SUPPORT */ + + default: + fprintf(stderr,"Undefined protocol request in POP3_auth\n"); + } + + /* we're approved */ + return(0); + + /*NOTREACHED*/ + +badAuth: + if (outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"%s\n",buf); + else + ; /* say nothing */ + + return(PS_ERROR); +} + + + + +/********************************************************************* + function: POP3_sendQUIT + 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, POP3_OK. + globals: reads outlevel. + *********************************************************************/ + +int POP3_sendQUIT (socket) +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + + SockPuts(socket,"QUIT"); + + if (outlevel == O_VERBOSE) + fprintf(stderr,"> QUIT\n"); + else + ; + + ok = POP3_OK(buf,socket); + if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"%s\n",buf); + + return(ok); +} + + + +/********************************************************************* + function: POP3_sendSTAT + description: send the STAT command to the POP3 server to find + out how many messages are waiting. + arguments: + count pointer to an integer to receive the message count. + socket socket to which the POP3 server is connected. + + return value: return code from POP3_OK. + calls: POP3_OK, SockPrintf + globals: reads outlevel. + *********************************************************************/ + +int POP3_sendSTAT (msgcount,socket) +int *msgcount; +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + int totalsize; + + SockPrintf(socket,"STAT\r\n"); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> STAT\n"); + + ok = POP3_OK(buf,socket); + if (ok == 0) + sscanf(buf,"%d %d",msgcount,&totalsize); + else if (outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"%s\n",buf); + + return(ok); +} + + + + +/********************************************************************* + function: POP3_sendRETR + description: send the RETR command to the POP3 server. + arguments: + msgnum message ID number + socket socket to which the POP3 server is connected. + + return value: return code from POP3_OK. + calls: POP3_OK, SockPrintf + globals: reads outlevel. + *********************************************************************/ + +int POP3_sendRETR (msgnum,socket) +int msgnum; +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + + SockPrintf(socket,"RETR %d\r\n",msgnum); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> RETR %d\n",msgnum); + + ok = POP3_OK(buf,socket); + if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"%s\n",buf); + + return(ok); +} + + +/********************************************************************* + function: POP3_sendTOP + description: send the TOP command to the POP3 server. + arguments: + msgnum message ID number + limit maximum number of message body lines to retrieve. + socket socket to which the POP3 server is connected. + + return value: return code from POP3_OK. + calls: POP3_OK, SockPrintf + globals: reads outlevel. + *********************************************************************/ + +int POP3_sendTOP (msgnum,limit,socket) +int msgnum; +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + + SockPrintf(socket,"TOP %d %d\r\n",msgnum,limit); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> TOP %d %d\n",msgnum,limit); + + ok = POP3_OK(buf,socket); + if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"option --limit failed; server says '%s'\n",buf); + + return(ok); +} + + + + +/********************************************************************* + function: POP3_sendDELE + description: send the DELE command to the POP3 server. + arguments: + msgnum message ID number + socket socket to which the POP3 server is connected. + + return value: return code from POP3_OK. + calls: POP3_OK, SockPrintF. + globals: reads outlevel. + *********************************************************************/ + +int POP3_sendDELE (msgnum,socket) +int msgnum; +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + + SockPrintf(socket,"DELE %d\r\n",msgnum); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> DELE %d\n",msgnum); + + ok = POP3_OK(buf,socket); + if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"%s\n",buf); + + return(ok); +} + + + +/********************************************************************* + function: POP3_readmsg + description: Read the message content as described in RFC 1225. + arguments: + 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 the system mailbox pipe. + + return value: zero if success else PS_* return code. + calls: SockGets. + globals: reads outlevel. + *********************************************************************/ + +int POP3_readmsg (socket,mboxfd,topipe) +int socket; +int mboxfd; +int topipe; +{ + char buf [MSGBUFSIZE]; + char *bufp; + char savec; + char fromBuf[MSGBUFSIZE]; + int needFrom; + int lines,sizeticker; + 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) + fputs(".\n",stderr); + else + ; + } + else + ; + + /* read the message content from the server */ + lines = 0; + sizeticker = MSGBUFSIZE; + while (1) { + if (SockGets(socket,buf,sizeof(buf)) < 0) + return(PS_SOCKET); + bufp = buf; + if (*bufp == '.') { + bufp++; + if (*bufp == 0) + break; /* end of message */ + } + strcat(bufp,"\n"); + + /* Check for Unix 'From' header, and a 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 && lines == 0) { + if (strlen(bufp) >= strlen("From ")) { + savec = *(bufp + 5); + *(bufp + 5) = 0; + needFrom = strcmp(bufp,"From ") != 0; + *(bufp + 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("POP3_readmsg: write"); + return(PS_IOERR); + } + } + } + + /* write this line to the file */ + if (write(mboxfd,bufp,strlen(bufp)) < 0) { + perror("POP3_readmsg: write"); + return(PS_IOERR); + } + + sizeticker -= strlen(bufp); + if (sizeticker <= 0) { + if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1) + fputc('.',stderr); + sizeticker = MSGBUFSIZE; + } + lines++; + } + + 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) < 0) { + perror("POP3_readmsg: write"); + return(PS_IOERR); + } + } + else { + /* The mail delivery agent may require a terminator. Write it if + it has been defined */ +#ifdef BINMAIL_TERM + if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) { + perror("POP3_readmsg: write"); + return(PS_IOERR); + } +#endif + } + + /* finish up display output */ + if (outlevel == O_VERBOSE) + fprintf(stderr,"(%d lines of message content)\n",lines); + else if (outlevel > O_SILENT && mboxfd != 1) + fputs(".\n",stderr); + else + ; + return(0); +} + + + + +/****************************************************************** + function: POP3_sendLAST + description: send the LAST command to the server, which should + return the number of the last message number retrieved + from the server. + arguments: + last integer buffer to receive last message# + + ret. value: non-zero on success, else zero. + globals: SockPrintf, POP3_OK. + calls: reads outlevel. + *****************************************************************/ + +int POP3_sendLAST (last, socket) +int *last; +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + + SockPrintf(socket,"LAST\r\n"); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> LAST\n"); + + ok = POP3_OK(buf,socket); + if (ok == 0 && sscanf(buf,"%d",last) == 0) + ok = PS_ERROR; + + if (ok != 0 && outlevel > O_SILENT) + fprintf(stderr,"Server says '%s' to LAST command.\n",buf); + + return(ok); +} + + +/****************************************************************** + function: POP3_BuildDigest + description: Construct the MD5 digest for the current session, + using the user-specified password, and the time + stamp in the POP3 greeting. + arguments: + buf greeting string + options merged options record. + + ret. value: zero on success, nonzero if no timestamp found in + greeting. + globals: none. + calls: MD5Digest. + *****************************************************************/ + +#if defined(HAVE_APOP_SUPPORT) +POP3_BuildDigest (buf,options) +char *buf; +struct optrec *options; +{ + char *start,*end; + char *msg; + + /* find start of timestamp */ + for (start = buf; *start != 0 && *start != '<'; start++) + ; + if (*start == 0) { + fprintf(stderr,"Required APOP timestamp not found in greeting\n"); + return(-1); + } + + /* find end of timestamp */ + for (end = start; *end != 0 && *end != '>'; end++) + ; + if (*end == 0 || (end - start - 1) == 1) { + fprintf(stderr,"Timestamp syntax error in greeting\n"); + return(-1); + } + + /* copy timestamp and password into digestion buffer */ + msg = (char *) malloc((end-start-1) + strlen(options->password) + 1); + *(++end) = 0; + strcpy(msg,start); + strcat(msg,options->password); + + strcpy(options->digest, MD5Digest(msg)); + free(msg); + return(0); +} +#endif /* HAVE_APOP_SUPPORT */ + |