]> code.delx.au - gnu-emacs/blobdiff - lib-src/pop.c
(socket_connection): Move the code to resolve the POP
[gnu-emacs] / lib-src / pop.c
index d1b3992773872d33ac4dd0931ac0d9ed682a48a1..9ced477609538457e748fc32f513979ecc04e7ab 100644 (file)
@@ -1,5 +1,5 @@
 /* pop.c: client routines for talking to a POP3-protocol post-office server
-   Copyright (c) 1991, 1993, 1996, 1997 Free Software Foundation, Inc.
+   Copyright (c) 1991, 1993, 1996, 1997, 1999 Free Software Foundation, Inc.
    Written by Jonathan Kamens, jik@security.ov.com.
 
 This file is part of GNU Emacs.
@@ -21,21 +21,13 @@ Boston, MA 02111-1307, USA.  */
 
 #ifdef HAVE_CONFIG_H
 #define NO_SHORTNAMES  /* Tell config not to load remap.h */
-#include <../src/config.h>
+#include <config.h>
 #else
 #define MAIL_USE_POP
 #endif
 
 #ifdef MAIL_USE_POP
 
-#ifdef HAVE_CONFIG_H
-/* Cancel these substitutions made in config.h */
-#undef open
-#undef read
-#undef write
-#undef close
-#endif
-
 #include <sys/types.h>
 #ifdef WINDOWSNT
 #include "ntlib.h"
@@ -76,9 +68,6 @@ extern struct servent *hes_getservbyname (/* char *, char * */);
 #include <string.h>
 #define index strchr
 #endif
-#ifdef STDC_HEADERS
-#include <stdlib.h>
-#endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -130,25 +119,31 @@ extern int h_errno;
 #endif
 #endif
 
-static int socket_connection (/* char *, int */);
-static char *getline (/* popserver */);
-static int sendline (/* popserver, char * */);
-static int fullwrite (/* int, char *, int */);
-static int getok (/* popserver */);
+#ifndef _P
+# ifdef __STDC__
+#  define _P(a) a
+# else
+#  define _P(a) ()
+# endif /* __STDC__ */
+#endif /* ! __P */
+
+static int socket_connection _P((char *, int));
+static int pop_getline _P((popserver, char **));
+static int sendline _P((popserver, char *));
+static int fullwrite _P((int, char *, int));
+static int getok _P((popserver));
 #if 0
-static int gettermination (/* popserver */);
+static int gettermination _P((popserver));
 #endif
-static void pop_trash (/* popserver */);
-static char *find_crlf (/* char * */);
+static void pop_trash _P((popserver));
+static char *find_crlf _P((char *, int));
 
-#define ERROR_MAX 80           /* a pretty arbitrary size */
+#define ERROR_MAX 160          /* a pretty arbitrary size, but needs
+                                  to be bigger than the original
+                                  value of 80 */
 #define POP_PORT 110
 #define KPOP_PORT 1109
-#ifdef WINDOWSNT
 #define POP_SERVICE "pop3"     /* we don't want the POP2 port! */
-#else
-#define POP_SERVICE "pop"
-#endif
 #ifdef KERBEROS
 #define KPOP_SERVICE "kpop"
 #endif
@@ -301,7 +296,7 @@ pop_open (host, username, password, flags)
       free ((char *) server);
       return (0);
     }
-         
+
   server->file = sock;
   server->data = 0;
   server->buffer_index = 0;
@@ -372,8 +367,8 @@ pop_stat (server, count, size)
       strcpy (pop_error, "In multi-line query in pop_stat");
       return (-1);
     }
-     
-  if (sendline (server, "STAT") || (! (fromserver = getline (server))))
+
+  if (sendline (server, "STAT") || (pop_getline (server, &fromserver) < 0))
     return (-1);
 
   if (strncmp (fromserver, "+OK ", 4))
@@ -392,7 +387,7 @@ pop_stat (server, count, size)
     }
 
   *count = atoi (&fromserver[4]);
-     
+
   fromserver = index (&fromserver[4], ' ');
   if (! fromserver)
     {
@@ -469,7 +464,7 @@ pop_list (server, message, IDs, sizes)
          free ((char *) *sizes);
          return (-1);
        }
-      if (! (fromserver = getline (server)))
+      if (pop_getline (server, &fromserver) < 0)
        {
          free ((char *) *IDs);
          free ((char *) *sizes);
@@ -514,7 +509,7 @@ pop_list (server, message, IDs, sizes)
        }
       for (i = 0; i < how_many; i++)
        {
-         if (pop_multi_next (server, &fromserver))
+         if (pop_multi_next (server, &fromserver) <= 0)
            {
              free ((char *) *IDs);
              free ((char *) *sizes);
@@ -533,7 +528,7 @@ pop_list (server, message, IDs, sizes)
            }
          (*sizes)[i] = atoi (fromserver);
        }
-      if (pop_multi_next (server, &fromserver))
+      if (pop_multi_next (server, &fromserver) < 0)
        {
          free ((char *) *IDs);
          free ((char *) *sizes);
@@ -563,17 +558,21 @@ pop_list (server, message, IDs, sizes)
  *     markfrom
  *             If true, then mark the string "From " at the beginning
  *             of lines with '>'.
+ *     msg_buf Output parameter to which a buffer containing the
+ *             message is assigned.
  * 
- * Return value: A string pointing to the message, if successful, or
- *     null with pop_error set if not.
+ * Return value: The number of bytes in msg_buf, which may contain
+ *     embedded nulls, not including its final null, or -1 on error
+ *     with pop_error set.
  *
  * Side effects: May kill connection on error.
  */
-char *
-pop_retrieve (server, message, markfrom)
+int
+pop_retrieve (server, message, markfrom, msg_buf)
      popserver server;
      int message;
      int markfrom;
+     char **msg_buf;
 {
   int *IDs, *sizes, bufsize, fromcount = 0, cp = 0;
   char *ptr, *fromserver;
@@ -582,15 +581,15 @@ pop_retrieve (server, message, markfrom)
   if (server->in_multi)
     {
       strcpy (pop_error, "In multi-line query in pop_retrieve");
-      return (0);
+      return (-1);
     }
 
   if (pop_list (server, message, &IDs, &sizes))
-    return (0);
+    return (-1);
 
   if (pop_retrieve_first (server, message, &fromserver))
     {
-      return (0);
+      return (-1);
     }
 
   /*
@@ -608,17 +607,16 @@ pop_retrieve (server, message, markfrom)
     {
       strcpy (pop_error, "Out of memory in pop_retrieve");
       pop_retrieve_flush (server);
-      return (0);
+      return (-1);
     }
 
-  while (! (ret = pop_retrieve_next (server, &fromserver)))
+  while ((ret = pop_retrieve_next (server, &fromserver)) >= 0)
     {
-      int linesize;
-
       if (! fromserver)
        {
          ptr[cp] = '\0';
-         return (ptr);
+         *msg_buf = ptr;
+         return (cp);
        }
       if (markfrom && fromserver[0] == 'F' && fromserver[1] == 'r' &&
          fromserver[2] == 'o' && fromserver[3] == 'm' &&
@@ -632,23 +630,19 @@ pop_retrieve (server, message, markfrom)
                {
                  strcpy (pop_error, "Out of memory in pop_retrieve");
                  pop_retrieve_flush (server);
-                 return (0);
+                 return (-1);
                }
              fromcount = 0;
            }
          ptr[cp++] = '>';
        }
-      linesize = strlen (fromserver);
-      bcopy (fromserver, &ptr[cp], linesize);
-      cp += linesize;
+      bcopy (fromserver, &ptr[cp], ret);
+      cp += ret;
       ptr[cp++] = '\n';
     }
 
-  if (ret)
-    {
-      free (ptr);
-      return (0);
-    }
+  free (ptr);
+  return (-1);
 }     
 
 int
@@ -661,6 +655,14 @@ pop_retrieve_first (server, message, response)
   return (pop_multi_first (server, pop_error, response));
 }
 
+/*
+  Returns a negative number on error, 0 to indicate that the data has
+  all been read (i.e., the server has returned a "." termination
+  line), or a positive number indicating the number of bytes in the
+  returned buffer (which is null-terminated and may contain embedded
+  nulls, but the returned bytecount doesn't include the final null).
+  */
+
 int
 pop_retrieve_next (server, line)
      popserver server;
@@ -686,6 +688,14 @@ pop_top_first (server, message, lines, response)
   return (pop_multi_first (server, pop_error, response));
 }
 
+/*
+  Returns a negative number on error, 0 to indicate that the data has
+  all been read (i.e., the server has returned a "." termination
+  line), or a positive number indicating the number of bytes in the
+  returned buffer (which is null-terminated and may contain embedded
+  nulls, but the returned bytecount doesn't include the final null).
+  */
+
 int
 pop_top_next (server, line)
      popserver server;
@@ -714,7 +724,7 @@ pop_multi_first (server, command, response)
       return (-1);
     }
 
-  if (sendline (server, command) || (! (*response = getline (server))))
+  if (sendline (server, command) || (pop_getline (server, response) < 0))
     {
       return (-1);
     }
@@ -738,12 +748,22 @@ pop_multi_first (server, command, response)
     }
 }
 
+/*
+  Read the next line of data from SERVER and place a pointer to it
+  into LINE.  Return -1 on error, 0 if there are no more lines to read
+  (i.e., the server has returned a line containing only "."), or a
+  positive number indicating the number of bytes in the LINE buffer
+  (not including the final null).  The data in that buffer may contain
+  embedded nulls, but does not contain the final CRLF. When returning
+  0, LINE is set to null. */
+
 int
 pop_multi_next (server, line)
      popserver server;
      char **line;
 {
   char *fromserver;
+  int ret;
 
   if (! server->in_multi)
     {
@@ -751,8 +771,7 @@ pop_multi_next (server, line)
       return (-1);
     }
 
-  fromserver = getline (server);
-  if (! fromserver)
+  if ((ret = pop_getline (server, &fromserver)) < 0)
     {
       return (-1);
     }
@@ -768,13 +787,13 @@ pop_multi_next (server, line)
       else
        {
          *line = fromserver + 1;
-         return (0);
+         return (ret - 1);
        }
     }
   else
     {
       *line = fromserver;
-      return (0);
+      return (ret);
     }
 }
 
@@ -783,21 +802,20 @@ pop_multi_flush (server)
      popserver server;
 {
   char *line;
+  int ret;
 
   if (! server->in_multi)
     {
       return (0);
     }
 
-  while (! pop_multi_next (server, &line))
+  while ((ret = pop_multi_next (server, &line)))
     {
-      if (! line)
-       {
-         return (0);
-       }
+      if (ret < 0)
+       return (-1);
     }
 
-  return (-1);
+  return (0);
 }
 
 /* Function: pop_delete
@@ -878,7 +896,7 @@ pop_last (server)
      popserver server;
 {
   char *fromserver;
-     
+
   if (server->in_multi)
     {
       strcpy (pop_error, "In multi-line query in pop_last");
@@ -888,7 +906,7 @@ pop_last (server)
   if (sendline (server, "LAST"))
     return (-1);
 
-  if (! (fromserver = getline (server)))
+  if (pop_getline (server, &fromserver) < 0)
     return (-1);
 
   if (! strncmp (fromserver, "-ERR", 4))
@@ -991,7 +1009,7 @@ static int have_winsock = 0;
  * Arguments:
  *     host    The host to which to connect.
  *     flags   Option flags.
- *     
+ *
  * Return value: A file descriptor indicating the connection, or -1
  *     indicating failure, in which case an error has been copied
  *     into pop_error.
@@ -1036,17 +1054,6 @@ socket_connection (host, flags)
   }
 #endif
 
-  do
-    {
-      hostent = gethostbyname (host);
-      try_count++;
-      if ((! hostent) && ((h_errno != TRY_AGAIN) || (try_count == 5)))
-       {
-         strcpy (pop_error, "Could not determine POP server's address");
-         return (-1);
-       }
-    } while (! hostent);
-
   bzero ((char *) &addr, sizeof (addr));
   addr.sin_family = AF_INET;
 
@@ -1094,9 +1101,20 @@ socket_connection (host, flags)
       strncat (pop_error, strerror (errno),
               ERROR_MAX - sizeof (POP_SOCKET_ERROR));
       return (-1);
-         
+
     }
 
+  do
+    {
+      hostent = gethostbyname (host);
+      try_count++;
+      if ((! hostent) && ((h_errno != TRY_AGAIN) || (try_count == 5)))
+       {
+         strcpy (pop_error, "Could not determine POP server's address");
+         return (-1);
+       }
+    } while (! hostent);
+
   while (*hostent->h_addr_list)
     {
       bcopy (*hostent->h_addr_list, (char *) &addr.sin_addr,
@@ -1107,7 +1125,7 @@ socket_connection (host, flags)
     }
 
 #define CONNECT_ERROR "Could not connect to POP server: "
-     
+
   if (! *hostent->h_addr_list)
     {
       CLOSESOCKET (sock);
@@ -1115,7 +1133,7 @@ socket_connection (host, flags)
       strncat (pop_error, strerror (errno),
               ERROR_MAX - sizeof (CONNECT_ERROR));
       return (-1);
-         
+
     }
 
 #ifdef KERBEROS
@@ -1139,7 +1157,7 @@ socket_connection (host, flags)
 
       if ((rem = krb5_auth_con_init (kcontext, &auth_context)))
        goto krb5error;
-      
+
       if (rem = krb5_cc_default (kcontext, &ccdef))
        goto krb5error;
 
@@ -1197,7 +1215,7 @@ socket_connection (host, flags)
          CLOSESOCKET (sock);
          return (-1);
        }
-#else  /* ! KERBEROS5 */         
+#else  /* ! KERBEROS5 */
       ticket = (KTEXT) malloc (sizeof (KTEXT_ST));
       realhost = strdup (hostent->h_name);
       rem = krb_sendauth (0L, sock, ticket, "pop", realhost,
@@ -1224,7 +1242,7 @@ socket_connection (host, flags)
 } /* socket_connection */
 
 /*
- * Function: getline
+ * Function: pop_getline
  *
  * Purpose: Get a line of text from the connection and return a
  *     pointer to it.  The carriage return and linefeed at the end of
@@ -1234,16 +1252,22 @@ socket_connection (host, flags)
  * Arguments:
  *     server  The server from which to get the line of text.
  *
- * Returns: A non-null pointer if successful, or a null pointer on any
- *     error, with an error message copied into pop_error.
+ * Returns: The number of characters in the line, which is returned in
+ *     LINE, not including the final null.  A return value of 0
+ *     indicates a blank line.  A negative return value indicates an
+ *     error (in which case the contents of LINE are undefined.  In
+ *     case of error, an error message is copied into pop_error.
  *
- * Notes: The line returned is overwritten with each call to getline.
+ * Notes: The line returned is overwritten with each call to pop_getline.
  *
  * Side effects: Closes the connection on error.
+ *
+ * THE RETURNED LINE MAY CONTAIN EMBEDDED NULLS!
  */
-static char *
-getline (server)
+static int
+pop_getline (server, line)
      popserver server;
+     char **line;
 {
 #define GETLINE_ERROR "Error reading from server: "
 
@@ -1252,7 +1276,8 @@ getline (server)
 
   if (server->data)
     {
-      char *cp = find_crlf (server->buffer + server->buffer_index);
+      char *cp = find_crlf (server->buffer + server->buffer_index,
+                           server->data);
       if (cp)
        {
          int found;
@@ -1260,14 +1285,17 @@ getline (server)
 
          found = server->buffer_index;
          data_used = (cp + 2) - server->buffer - found;
-              
+
          *cp = '\0';           /* terminate the string to be returned */
          server->data -= data_used;
          server->buffer_index += data_used;
 
          if (pop_debug)
+           /* Embedded nulls will truncate this output prematurely,
+              but that's OK because it's just for debugging anyway. */
            fprintf (stderr, "<<< %s\n", server->buffer + found);
-         return (server->buffer + found);
+         *line = server->buffer + found;
+         return (data_used - 2);
        }
       else
        {
@@ -1300,9 +1328,9 @@ getline (server)
          server->buffer = (char *)realloc (server->buffer, server->buffer_size);
          if (! server->buffer)
            {
-             strcpy (pop_error, "Out of memory in getline");
+             strcpy (pop_error, "Out of memory in pop_getline");
              pop_trash (server);
-             return (0);
+             return (-1);
            }
        }
       ret = RECV (server->file, server->buffer + server->data,
@@ -1313,21 +1341,22 @@ getline (server)
          strncat (pop_error, strerror (errno),
                   ERROR_MAX - sizeof (GETLINE_ERROR));
          pop_trash (server);
-         return (0);
+         return (-1);
        }
       else if (ret == 0)
        {
-         strcpy (pop_error, "Unexpected EOF from server in getline");
+         strcpy (pop_error, "Unexpected EOF from server in pop_getline");
          pop_trash (server);
-         return (0);
+         return (-1);
        }
       else
        {
          char *cp;
          server->data += ret;
          server->buffer[server->data] = '\0';
-              
-         cp = find_crlf (server->buffer + search_offset);
+
+         cp = find_crlf (server->buffer + search_offset,
+                         server->data - search_offset);
          if (cp)
            {
              int data_used = (cp + 2) - server->buffer;
@@ -1337,7 +1366,8 @@ getline (server)
 
              if (pop_debug)
                fprintf (stderr, "<<< %s\n", server->buffer);
-             return (server->buffer);
+             *line = server->buffer;
+             return (data_used - 2);
            }
          /* As above, the "- 1" here is to account for the fact that
             we may have read a CR without its accompanying LF. */
@@ -1410,10 +1440,10 @@ fullwrite (fd, buf, nbytes)
      int nbytes;
 {
   char *cp;
-  int ret;
+  int ret = 0;
 
   cp = buf;
-  while ((ret = SEND (fd, cp, nbytes, 0)) > 0)
+  while (nbytes && ((ret = SEND (fd, cp, nbytes, 0)) > 0))
     {
       cp += ret;
       nbytes -= ret;
@@ -1431,7 +1461,7 @@ fullwrite (fd, buf, nbytes)
  *
  * Arguments:
  *     server  The server to read from.
- * 
+ *
  * Returns: 0 for success, else for failure and puts error in pop_error.
  *
  * Side effects: On failure, may make the connection unusable.
@@ -1442,7 +1472,7 @@ getok (server)
 {
   char *fromline;
 
-  if (! (fromline = getline (server)))
+  if (pop_getline (server, &fromline) < 0)
     {
       return (-1);
     }
@@ -1462,7 +1492,7 @@ getok (server)
       pop_trash (server);
       return (-1);
     }
-}        
+}
 
 #if 0
 /*
@@ -1481,8 +1511,7 @@ gettermination (server)
 {
   char *fromserver;
 
-  fromserver = getline (server);
-  if (! fromserver)
+  if (pop_getline (server, &fromserver) < 0)
     return (-1);
 
   if (strcmp (fromserver, "."))
@@ -1509,7 +1538,7 @@ gettermination (server)
  *     Changes made to the maildrop since the session was started (or
  *     since the last pop_reset) may be lost.
  */
-void 
+void
 pop_close (server)
      popserver server;
 {
@@ -1555,18 +1584,18 @@ pop_trash (server)
 #endif
 }
 
-/* Return a pointer to the first CRLF in IN_STRING,
-   or 0 if it does not contain one.  */
+/* Return a pointer to the first CRLF in IN_STRING, which can contain
+   embedded nulls and has LEN characters in it not including the final
+   null, or 0 if it does not contain one.  */
 
 static char *
-find_crlf (in_string)
+find_crlf (in_string, len)
      char *in_string;
+     int len;
 {
-  while (1)
+  while (len--)
     {
-      if (! *in_string)
-       return (0);
-      else if (*in_string == '\r')
+      if (*in_string == '\r')
        {
          if (*++in_string == '\n')
            return (in_string - 1);
@@ -1574,7 +1603,7 @@ find_crlf (in_string)
       else
        in_string++;
     }
-  /* NOTREACHED */
+  return (0);
 }
 
 #endif /* MAIL_USE_POP */