aboutsummaryrefslogtreecommitdiffstats
path: root/pop2.c
diff options
context:
space:
mode:
Diffstat (limited to 'pop2.c')
-rw-r--r--pop2.c611
1 files changed, 611 insertions, 0 deletions
diff --git a/pop2.c b/pop2.c
new file mode 100644
index 00000000..3323fda1
--- /dev/null
+++ b/pop2.c
@@ -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);
+}