]> Pileus Git - ~andy/fetchmail/blobdiff - pop2.c
Credit John Beck's fixes.
[~andy/fetchmail] / pop2.c
diff --git a/pop2.c b/pop2.c
index 28056b1376f6ba421364ce5f0fc02e16837f819d..03d58a18f92334be44b0c57bd5774e1cf6893d18 100644 (file)
--- a/pop2.c
+++ b/pop2.c
-/* 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  <config.h>
+#include  "config.h"
 
+#ifdef POP2_ENABLE
 #include  <stdio.h>
 #if defined(STDC_HEADERS)
-#include  <string.h>
+#include <stdlib.h>
 #endif
-#if defined(HAVE_UNISTD_H)
-#include  <unistd.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
 #endif
-
-#include  <sys/time.h>
-#include  <errno.h>
-
+#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 */