]> code.delx.au - gnu-emacs/blobdiff - lib-src/pop.c
[USE_ATSUI] (Fmac_atsu_font_face_attributes):
[gnu-emacs] / lib-src / pop.c
index 69c330b21c60d8fc8fb764f42754fcb7e21dbe3f..9fcbe4b370cdc3c2744e24005c11785860ec7d05 100644 (file)
@@ -1,12 +1,13 @@
 /* pop.c: client routines for talking to a POP3-protocol post-office server
 /* pop.c: client routines for talking to a POP3-protocol post-office server
-   Copyright (c) 1991, 1993, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1991, 1993, 1996, 1997, 1999, 2001, 2002, 2003, 2004,
+                 2005, 2006, 2007  Free Software Foundation, Inc.
    Written by Jonathan Kamens, jik@security.ov.com.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
    Written by Jonathan Kamens, jik@security.ov.com.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -16,26 +17,18 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 #ifdef HAVE_CONFIG_H
 #define NO_SHORTNAMES  /* Tell config not to load remap.h */
 
 #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
 
 #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"
 #include <sys/types.h>
 #ifdef WINDOWSNT
 #include "ntlib.h"
@@ -72,37 +65,42 @@ extern struct servent *hes_getservbyname (/* char *, char * */);
 #include <netdb.h>
 #include <errno.h>
 #include <stdio.h>
 #include <netdb.h>
 #include <errno.h>
 #include <stdio.h>
+#ifdef STDC_HEADERS
+#include <string.h>
+#define index strchr
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 
 #ifdef KERBEROS
 
 #ifdef KERBEROS
-#ifndef KRB5
-#ifdef SOLARIS2
-#include <des.h>
-#include <krb.h>
-#else /* SOLARIS2 */
-#include <kerberos/des.h>
-#include <kerberos/krb.h>
-#endif /* SOLARIS2 */
-#else /* KRB5 */
-#include <krb5/krb5.h>
-#include <krb5/ext-proto.h>
-#include <ctype.h>
-#endif /* KRB5 */
+# ifdef HAVE_KRB5_H
+#  include <krb5.h>
+# endif
+# ifdef HAVE_KRB_H
+#  include <krb.h>
+# else
+#  ifdef HAVE_KERBEROSIV_KRB_H
+#   include <kerberosIV/krb.h>
+#  else
+#   ifdef HAVE_KERBEROS_KRB_H
+#    include <kerberos/krb.h>
+#   endif
+#  endif
+# endif
+# ifdef HAVE_COM_ERR_H
+#  include <com_err.h>
+# endif
 #endif /* KERBEROS */
 
 #endif /* KERBEROS */
 
-extern char *getenv (/* char * */);
-extern char *getlogin (/* void */);
-extern char *getpass (/* char * */);
-extern char *strerror (/* int */);
-extern char *index ();
-
 #ifdef KERBEROS
 #ifdef KERBEROS
-#ifndef KRB5
+#ifndef KERBEROS5
 extern int krb_sendauth (/* long, int, KTEXT, char *, char *, char *,
                            u_long, MSG_DAT *, CREDENTIALS *, Key_schedule,
                            struct sockaddr_in *, struct sockaddr_in *,
                            char * */);
 extern char *krb_realmofhost (/* char * */);
 extern int krb_sendauth (/* long, int, KTEXT, char *, char *, char *,
                            u_long, MSG_DAT *, CREDENTIALS *, Key_schedule,
                            struct sockaddr_in *, struct sockaddr_in *,
                            char * */);
 extern char *krb_realmofhost (/* char * */);
-#endif /* ! KRB5 */
+#endif /* ! KERBEROS5 */
 #endif /* KERBEROS */
 
 #ifndef WINDOWSNT
 #endif /* KERBEROS */
 
 #ifndef WINDOWSNT
@@ -111,31 +109,33 @@ extern int h_errno;
 #endif
 #endif
 
 #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
 #if 0
-static int gettermination (/* popserver */);
+static int gettermination __P((popserver));
 #endif
 #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
 #define POP_PORT 110
 #define KPOP_PORT 1109
-#ifdef WINDOWSNT
 #define POP_SERVICE "pop3"     /* we don't want the POP2 port! */
 #define POP_SERVICE "pop3"     /* we don't want the POP2 port! */
-#else
-#define POP_SERVICE "pop"
-#endif
 #ifdef KERBEROS
 #ifdef KERBEROS
-#ifdef KRB5
-#define KPOP_SERVICE "k5pop";
-#else
-#define KPOP_SERVICE "kpop"
-#endif
+#define KPOP_SERVICE "kpop"    /* never used: look for 20060515 to see why */
 #endif
 
 char pop_error[ERROR_MAX];
 #endif
 
 char pop_error[ERROR_MAX];
@@ -251,7 +251,7 @@ pop_open (host, username, password, flags)
 #else
 #define DONT_NEED_PASSWORD 0
 #endif
 #else
 #define DONT_NEED_PASSWORD 0
 #endif
+
   if ((! password) && (! DONT_NEED_PASSWORD))
     {
       if (! (flags & POP_NO_GETPASS))
   if ((! password) && (! DONT_NEED_PASSWORD))
     {
       if (! (flags & POP_NO_GETPASS))
@@ -264,10 +264,11 @@ pop_open (host, username, password, flags)
          return (0);
        }
     }
          return (0);
        }
     }
-  if (password)
+  if (password)                        /* always true, detected 20060515 */
     flags |= POP_NO_KERBEROS;
   else
     flags |= POP_NO_KERBEROS;
   else
-    password = username;
+    password = username;       /* dead code, detected 20060515 */
+  /** "kpop" service is  never used: look for 20060515 to see why **/
 
   sock = socket_connection (host, flags);
   if (sock == -1)
 
   sock = socket_connection (host, flags);
   if (sock == -1)
@@ -286,7 +287,7 @@ pop_open (host, username, password, flags)
       free ((char *) server);
       return (0);
     }
       free ((char *) server);
       return (0);
     }
-         
+
   server->file = sock;
   server->data = 0;
   server->buffer_index = 0;
   server->file = sock;
   server->data = 0;
   server->buffer_index = 0;
@@ -357,8 +358,8 @@ pop_stat (server, count, size)
       strcpy (pop_error, "In multi-line query in pop_stat");
       return (-1);
     }
       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))
     return (-1);
 
   if (strncmp (fromserver, "+OK ", 4))
@@ -377,7 +378,7 @@ pop_stat (server, count, size)
     }
 
   *count = atoi (&fromserver[4]);
     }
 
   *count = atoi (&fromserver[4]);
-     
+
   fromserver = index (&fromserver[4], ' ');
   if (! fromserver)
     {
   fromserver = index (&fromserver[4], ' ');
   if (! fromserver)
     {
@@ -454,7 +455,7 @@ pop_list (server, message, IDs, sizes)
          free ((char *) *sizes);
          return (-1);
        }
          free ((char *) *sizes);
          return (-1);
        }
-      if (! (fromserver = getline (server)))
+      if (pop_getline (server, &fromserver) < 0)
        {
          free ((char *) *IDs);
          free ((char *) *sizes);
        {
          free ((char *) *IDs);
          free ((char *) *sizes);
@@ -499,7 +500,7 @@ pop_list (server, message, IDs, sizes)
        }
       for (i = 0; i < how_many; i++)
        {
        }
       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);
            {
              free ((char *) *IDs);
              free ((char *) *sizes);
@@ -518,7 +519,7 @@ pop_list (server, message, IDs, sizes)
            }
          (*sizes)[i] = atoi (fromserver);
        }
            }
          (*sizes)[i] = atoi (fromserver);
        }
-      if (pop_multi_next (server, &fromserver))
+      if (pop_multi_next (server, &fromserver) < 0)
        {
          free ((char *) *IDs);
          free ((char *) *sizes);
        {
          free ((char *) *IDs);
          free ((char *) *sizes);
@@ -548,17 +549,21 @@ pop_list (server, message, IDs, sizes)
  *     markfrom
  *             If true, then mark the string "From " at the beginning
  *             of lines with '>'.
  *     markfrom
  *             If true, then mark the string "From " at the beginning
  *             of lines with '>'.
- * 
- * Return value: A string pointing to the message, if successful, or
- *     null with pop_error set if not.
+ *     msg_buf Output parameter to which a buffer containing the
+ *             message is assigned.
+ *
+ * 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.
  */
  *
  * 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;
      popserver server;
      int message;
      int markfrom;
+     char **msg_buf;
 {
   int *IDs, *sizes, bufsize, fromcount = 0, cp = 0;
   char *ptr, *fromserver;
 {
   int *IDs, *sizes, bufsize, fromcount = 0, cp = 0;
   char *ptr, *fromserver;
@@ -567,15 +572,15 @@ pop_retrieve (server, message, markfrom)
   if (server->in_multi)
     {
       strcpy (pop_error, "In multi-line query in pop_retrieve");
   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))
     }
 
   if (pop_list (server, message, &IDs, &sizes))
-    return (0);
+    return (-1);
 
   if (pop_retrieve_first (server, message, &fromserver))
     {
 
   if (pop_retrieve_first (server, message, &fromserver))
     {
-      return (0);
+      return (-1);
     }
 
   /*
     }
 
   /*
@@ -593,17 +598,16 @@ pop_retrieve (server, message, markfrom)
     {
       strcpy (pop_error, "Out of memory in pop_retrieve");
       pop_retrieve_flush (server);
     {
       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';
       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' &&
        }
       if (markfrom && fromserver[0] == 'F' && fromserver[1] == 'r' &&
          fromserver[2] == 'o' && fromserver[3] == 'm' &&
@@ -617,24 +621,20 @@ pop_retrieve (server, message, markfrom)
                {
                  strcpy (pop_error, "Out of memory in pop_retrieve");
                  pop_retrieve_flush (server);
                {
                  strcpy (pop_error, "Out of memory in pop_retrieve");
                  pop_retrieve_flush (server);
-                 return (0);
+                 return (-1);
                }
              fromcount = 0;
            }
          ptr[cp++] = '>';
        }
                }
              fromcount = 0;
            }
          ptr[cp++] = '>';
        }
-      linesize = strlen (fromserver);
-      bcopy (fromserver, &ptr[cp], linesize);
-      cp += linesize;
+      bcopy (fromserver, &ptr[cp], ret);
+      cp += ret;
       ptr[cp++] = '\n';
     }
 
       ptr[cp++] = '\n';
     }
 
-  if (ret)
-    {
-      free (ptr);
-      return (0);
-    }
-}     
+  free (ptr);
+  return (-1);
+}
 
 int
 pop_retrieve_first (server, message, response)
 
 int
 pop_retrieve_first (server, message, response)
@@ -646,6 +646,14 @@ pop_retrieve_first (server, message, response)
   return (pop_multi_first (server, pop_error, 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;
 int
 pop_retrieve_next (server, line)
      popserver server;
@@ -671,6 +679,14 @@ pop_top_first (server, message, lines, response)
   return (pop_multi_first (server, pop_error, 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;
 int
 pop_top_next (server, line)
      popserver server;
@@ -699,7 +715,7 @@ pop_multi_first (server, command, response)
       return (-1);
     }
 
       return (-1);
     }
 
-  if (sendline (server, command) || (! (*response = getline (server))))
+  if (sendline (server, command) || (pop_getline (server, response) < 0))
     {
       return (-1);
     }
     {
       return (-1);
     }
@@ -723,12 +739,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
 pop_multi_next (server, line)
      popserver server;
      char **line;
 {
   char *fromserver;
+  int ret;
 
   if (! server->in_multi)
     {
 
   if (! server->in_multi)
     {
@@ -736,8 +762,7 @@ pop_multi_next (server, line)
       return (-1);
     }
 
       return (-1);
     }
 
-  fromserver = getline (server);
-  if (! fromserver)
+  if ((ret = pop_getline (server, &fromserver)) < 0)
     {
       return (-1);
     }
     {
       return (-1);
     }
@@ -753,13 +778,13 @@ pop_multi_next (server, line)
       else
        {
          *line = fromserver + 1;
       else
        {
          *line = fromserver + 1;
-         return (0);
+         return (ret - 1);
        }
     }
   else
     {
       *line = fromserver;
        }
     }
   else
     {
       *line = fromserver;
-      return (0);
+      return (ret);
     }
 }
 
     }
 }
 
@@ -768,21 +793,20 @@ pop_multi_flush (server)
      popserver server;
 {
   char *line;
      popserver server;
 {
   char *line;
+  int ret;
 
   if (! server->in_multi)
     {
       return (0);
     }
 
 
   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
 }
 
 /* Function: pop_delete
@@ -863,7 +887,7 @@ pop_last (server)
      popserver server;
 {
   char *fromserver;
      popserver server;
 {
   char *fromserver;
-     
+
   if (server->in_multi)
     {
       strcpy (pop_error, "In multi-line query in pop_last");
   if (server->in_multi)
     {
       strcpy (pop_error, "In multi-line query in pop_last");
@@ -873,7 +897,7 @@ pop_last (server)
   if (sendline (server, "LAST"))
     return (-1);
 
   if (sendline (server, "LAST"))
     return (-1);
 
-  if (! (fromserver = getline (server)))
+  if (pop_getline (server, &fromserver) < 0)
     return (-1);
 
   if (! strncmp (fromserver, "-ERR", 4))
     return (-1);
 
   if (! strncmp (fromserver, "-ERR", 4))
@@ -976,7 +1000,7 @@ static int have_winsock = 0;
  * Arguments:
  *     host    The host to which to connect.
  *     flags   Option flags.
  * 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.
  * Return value: A file descriptor indicating the connection, or -1
  *     indicating failure, in which case an error has been copied
  *     into pop_error.
@@ -993,8 +1017,10 @@ socket_connection (host, flags)
   char *service;
   int sock;
 #ifdef KERBEROS
   char *service;
   int sock;
 #ifdef KERBEROS
-#ifdef KRB5
+#ifdef KERBEROS5
   krb5_error_code rem;
   krb5_error_code rem;
+  krb5_context kcontext = 0;
+  krb5_auth_context auth_context = 0;
   krb5_ccache ccdef;
   krb5_principal client, server;
   krb5_error *err_ret;
   krb5_ccache ccdef;
   krb5_principal client, server;
   krb5_error *err_ret;
@@ -1005,7 +1031,8 @@ socket_connection (host, flags)
   CREDENTIALS cred;
   Key_schedule schedule;
   int rem;
   CREDENTIALS cred;
   Key_schedule schedule;
   int rem;
-#endif /* KRB5 */
+  char *realhost;
+#endif /* KERBEROS5 */
 #endif /* KERBEROS */
 
   int try_count = 0;
 #endif /* KERBEROS */
 
   int try_count = 0;
@@ -1018,20 +1045,10 @@ socket_connection (host, flags)
   }
 #endif
 
   }
 #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;
 
   bzero ((char *) &addr, sizeof (addr));
   addr.sin_family = AF_INET;
 
+  /** "kpop" service is  never used: look for 20060515 to see why **/
 #ifdef KERBEROS
   service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE;
 #else
 #ifdef KERBEROS
   service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE;
 #else
@@ -1058,6 +1075,7 @@ socket_connection (host, flags)
        }
       else
        {
        }
       else
        {
+  /** "kpop" service is  never used: look for 20060515 to see why **/
 #ifdef KERBEROS
          addr.sin_port = htons ((flags & POP_NO_KERBEROS) ?
                                POP_PORT : KPOP_PORT);
 #ifdef KERBEROS
          addr.sin_port = htons ((flags & POP_NO_KERBEROS) ?
                                POP_PORT : KPOP_PORT);
@@ -1067,18 +1085,29 @@ socket_connection (host, flags)
        }
     }
 
        }
     }
 
-#define SOCKET_ERROR "Could not create socket for POP connection: "
+#define POP_SOCKET_ERROR "Could not create socket for POP connection: "
 
   sock = socket (PF_INET, SOCK_STREAM, 0);
   if (sock < 0)
     {
 
   sock = socket (PF_INET, SOCK_STREAM, 0);
   if (sock < 0)
     {
-      strcpy (pop_error, SOCKET_ERROR);
+      strcpy (pop_error, POP_SOCKET_ERROR);
       strncat (pop_error, strerror (errno),
       strncat (pop_error, strerror (errno),
-              ERROR_MAX - sizeof (SOCKET_ERROR));
+              ERROR_MAX - sizeof (POP_SOCKET_ERROR));
       return (-1);
       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,
   while (*hostent->h_addr_list)
     {
       bcopy (*hostent->h_addr_list, (char *) &addr.sin_addr,
@@ -1089,7 +1118,7 @@ socket_connection (host, flags)
     }
 
 #define CONNECT_ERROR "Could not connect to POP server: "
     }
 
 #define CONNECT_ERROR "Could not connect to POP server: "
-     
+
   if (! *hostent->h_addr_list)
     {
       CLOSESOCKET (sock);
   if (! *hostent->h_addr_list)
     {
       CLOSESOCKET (sock);
@@ -1097,19 +1126,21 @@ socket_connection (host, flags)
       strncat (pop_error, strerror (errno),
               ERROR_MAX - sizeof (CONNECT_ERROR));
       return (-1);
       strncat (pop_error, strerror (errno),
               ERROR_MAX - sizeof (CONNECT_ERROR));
       return (-1);
-         
+
     }
 
 #ifdef KERBEROS
 #define KRB_ERROR "Kerberos error connecting to POP server: "
   if (! (flags & POP_NO_KERBEROS))
     {
     }
 
 #ifdef KERBEROS
 #define KRB_ERROR "Kerberos error connecting to POP server: "
   if (! (flags & POP_NO_KERBEROS))
     {
-#ifdef KRB5
-      krb5_init_ets ();
-
-      if (rem = krb5_cc_default (&ccdef))
+#ifdef KERBEROS5
+      if ((rem = krb5_init_context (&kcontext)))
        {
        krb5error:
        {
        krb5error:
+         if (auth_context)
+           krb5_auth_con_free (kcontext, auth_context);
+         if (kcontext)
+           krb5_free_context (kcontext);
          strcpy (pop_error, KRB_ERROR);
          strncat (pop_error, error_message (rem),
                   ERROR_MAX - sizeof(KRB_ERROR));
          strcpy (pop_error, KRB_ERROR);
          strncat (pop_error, error_message (rem),
                   ERROR_MAX - sizeof(KRB_ERROR));
@@ -1117,10 +1148,14 @@ socket_connection (host, flags)
          return (-1);
        }
 
          return (-1);
        }
 
-      if (rem = krb5_cc_get_principal (ccdef, &client))
-       {
-         goto krb5error;
-       }
+      if ((rem = krb5_auth_con_init (kcontext, &auth_context)))
+       goto krb5error;
+
+      if (rem = krb5_cc_default (kcontext, &ccdef))
+       goto krb5error;
+
+      if (rem = krb5_cc_get_principal (kcontext, ccdef, &client))
+       goto krb5error;
 
       for (cp = hostent->h_name; *cp; cp++)
        {
 
       for (cp = hostent->h_name; *cp; cp++)
        {
@@ -1130,22 +1165,20 @@ socket_connection (host, flags)
            }
        }
 
            }
        }
 
-      if (rem = krb5_sname_to_principal (hostent->h_name, POP_SERVICE,
-                                        FALSE, &server))
-       {
-         goto krb5error;
-       }
+      if (rem = krb5_sname_to_principal (kcontext, hostent->h_name,
+                                        POP_SERVICE, FALSE, &server))
+       goto krb5error;
 
 
-      rem = krb5_sendauth ((krb5_pointer) &sock, "KPOPV1.0", client, server,
+      rem = krb5_sendauth (kcontext, &auth_context,
+                          (krb5_pointer) &sock, "KPOPV1.0", client, server,
                          AP_OPTS_MUTUAL_REQUIRED,
                          0,    /* no checksum */
                          0,    /* no creds, use ccache instead */
                          ccdef,
                          AP_OPTS_MUTUAL_REQUIRED,
                          0,    /* no checksum */
                          0,    /* no creds, use ccache instead */
                          ccdef,
-                         0,    /* don't need seq # */
-                         0,    /* don't need subsession key */
                          &err_ret,
                          &err_ret,
+                         0,    /* don't need subsession key */
                          0);   /* don't need reply */
                          0);   /* don't need reply */
-      krb5_free_principal (server);
+      krb5_free_principal (kcontext, server);
       if (rem)
        {
          if (err_ret && err_ret->text.length)
       if (rem)
        {
          if (err_ret && err_ret->text.length)
@@ -1168,20 +1201,24 @@ socket_connection (host, flags)
                       ERROR_MAX - sizeof (KRB_ERROR));
            }
          if (err_ret)
                       ERROR_MAX - sizeof (KRB_ERROR));
            }
          if (err_ret)
-           krb5_free_error (err_ret);
+           krb5_free_error (kcontext, err_ret);
+         krb5_auth_con_free (kcontext, auth_context);
+         krb5_free_context (kcontext);
 
          CLOSESOCKET (sock);
          return (-1);
        }
 
          CLOSESOCKET (sock);
          return (-1);
        }
-#else  /* ! KRB5 */      
+#else  /* ! KERBEROS5 */
       ticket = (KTEXT) malloc (sizeof (KTEXT_ST));
       ticket = (KTEXT) malloc (sizeof (KTEXT_ST));
-      rem = krb_sendauth (0L, sock, ticket, "pop", hostent->h_name,
-                         (char *) krb_realmofhost (hostent->h_name),
+      realhost = strdup (hostent->h_name);
+      rem = krb_sendauth (0L, sock, ticket, "pop", realhost,
+                         (char *) krb_realmofhost (realhost),
                          (unsigned long) 0, &msg_data, &cred, schedule,
                          (struct sockaddr_in *) 0,
                          (struct sockaddr_in *) 0,
                          "KPOPV0.1");
       free ((char *) ticket);
                          (unsigned long) 0, &msg_data, &cred, schedule,
                          (struct sockaddr_in *) 0,
                          (struct sockaddr_in *) 0,
                          "KPOPV0.1");
       free ((char *) ticket);
+      free (realhost);
       if (rem != KSUCCESS)
        {
          strcpy (pop_error, KRB_ERROR);
       if (rem != KSUCCESS)
        {
          strcpy (pop_error, KRB_ERROR);
@@ -1190,7 +1227,7 @@ socket_connection (host, flags)
          CLOSESOCKET (sock);
          return (-1);
        }
          CLOSESOCKET (sock);
          return (-1);
        }
-#endif /* KRB5 */
+#endif /* KERBEROS5 */
     }
 #endif /* KERBEROS */
 
     }
 #endif /* KERBEROS */
 
@@ -1198,7 +1235,7 @@ socket_connection (host, flags)
 } /* socket_connection */
 
 /*
 } /* 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
  *
  * Purpose: Get a line of text from the connection and return a
  *     pointer to it.  The carriage return and linefeed at the end of
@@ -1208,16 +1245,22 @@ socket_connection (host, flags)
  * Arguments:
  *     server  The server from which to get the line of text.
  *
  * 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.
  *
  * 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;
      popserver server;
+     char **line;
 {
 #define GETLINE_ERROR "Error reading from server: "
 
 {
 #define GETLINE_ERROR "Error reading from server: "
 
@@ -1226,7 +1269,8 @@ getline (server)
 
   if (server->data)
     {
 
   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;
       if (cp)
        {
          int found;
@@ -1234,14 +1278,17 @@ getline (server)
 
          found = server->buffer_index;
          data_used = (cp + 2) - server->buffer - found;
 
          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)
          *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);
            fprintf (stderr, "<<< %s\n", server->buffer + found);
-         return (server->buffer + found);
+         *line = server->buffer + found;
+         return (data_used - 2);
        }
       else
        {
        }
       else
        {
@@ -1274,9 +1321,9 @@ getline (server)
          server->buffer = (char *)realloc (server->buffer, server->buffer_size);
          if (! server->buffer)
            {
          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);
              pop_trash (server);
-             return (0);
+             return (-1);
            }
        }
       ret = RECV (server->file, server->buffer + server->data,
            }
        }
       ret = RECV (server->file, server->buffer + server->data,
@@ -1287,21 +1334,22 @@ getline (server)
          strncat (pop_error, strerror (errno),
                   ERROR_MAX - sizeof (GETLINE_ERROR));
          pop_trash (server);
          strncat (pop_error, strerror (errno),
                   ERROR_MAX - sizeof (GETLINE_ERROR));
          pop_trash (server);
-         return (0);
+         return (-1);
        }
       else if (ret == 0)
        {
        }
       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);
          pop_trash (server);
-         return (0);
+         return (-1);
        }
       else
        {
          char *cp;
          server->data += ret;
          server->buffer[server->data] = '\0';
        }
       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;
          if (cp)
            {
              int data_used = (cp + 2) - server->buffer;
@@ -1311,9 +1359,12 @@ getline (server)
 
              if (pop_debug)
                fprintf (stderr, "<<< %s\n", server->buffer);
 
              if (pop_debug)
                fprintf (stderr, "<<< %s\n", server->buffer);
-             return (server->buffer);
+             *line = server->buffer;
+             return (data_used - 2);
            }
            }
-         search_offset += ret;
+         /* As above, the "- 1" here is to account for the fact that
+            we may have read a CR without its accompanying LF. */
+         search_offset += ret - 1;
        }
     }
 
        }
     }
 
@@ -1345,12 +1396,24 @@ sendline (server, line)
 {
 #define SENDLINE_ERROR "Error writing to POP server: "
   int ret;
 {
 #define SENDLINE_ERROR "Error writing to POP server: "
   int ret;
-
-  ret = fullwrite (server->file, line, strlen (line));
-  if (ret >= 0)
-    {                          /* 0 indicates that a blank line was written */
-      ret = fullwrite (server->file, "\r\n", 2);
-    }
+  char *buf;
+
+  /* Combine the string and the CR-LF into one buffer.  Otherwise, two
+     reasonable network stack optimizations, Nagle's algorithm and
+     delayed acks, combine to delay us a fraction of a second on every
+     message we send.  (Movemail writes line without \r\n, client
+     kernel sends packet, server kernel delays the ack to see if it
+     can combine it with data, movemail writes \r\n, client kernel
+     waits because it has unacked data already in its outgoing queue,
+     client kernel eventually times out and sends.)
+
+     This can be something like 0.2s per command, which can add up
+     over a few dozen messages, and is a big chunk of the time we
+     spend fetching mail from a server close by.  */
+  buf = alloca (strlen (line) + 3);
+  strcpy (buf, line);
+  strcat (buf, "\r\n");
+  ret = fullwrite (server->file, buf, strlen (buf));
 
   if (ret < 0)
     {
 
   if (ret < 0)
     {
@@ -1382,10 +1445,10 @@ fullwrite (fd, buf, nbytes)
      int nbytes;
 {
   char *cp;
      int nbytes;
 {
   char *cp;
-  int ret;
+  int ret = 0;
 
   cp = buf;
 
   cp = buf;
-  while ((ret = SEND (fd, cp, nbytes, 0)) > 0)
+  while (nbytes && ((ret = SEND (fd, cp, nbytes, 0)) > 0))
     {
       cp += ret;
       nbytes -= ret;
     {
       cp += ret;
       nbytes -= ret;
@@ -1403,7 +1466,7 @@ fullwrite (fd, buf, nbytes)
  *
  * Arguments:
  *     server  The server to read from.
  *
  * 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.
  * Returns: 0 for success, else for failure and puts error in pop_error.
  *
  * Side effects: On failure, may make the connection unusable.
@@ -1414,7 +1477,7 @@ getok (server)
 {
   char *fromline;
 
 {
   char *fromline;
 
-  if (! (fromline = getline (server)))
+  if (pop_getline (server, &fromline) < 0)
     {
       return (-1);
     }
     {
       return (-1);
     }
@@ -1434,7 +1497,7 @@ getok (server)
       pop_trash (server);
       return (-1);
     }
       pop_trash (server);
       return (-1);
     }
-}        
+}
 
 #if 0
 /*
 
 #if 0
 /*
@@ -1453,8 +1516,7 @@ gettermination (server)
 {
   char *fromserver;
 
 {
   char *fromserver;
 
-  fromserver = getline (server);
-  if (! fromserver)
+  if (pop_getline (server, &fromserver) < 0)
     return (-1);
 
   if (strcmp (fromserver, "."))
     return (-1);
 
   if (strcmp (fromserver, "."))
@@ -1481,7 +1543,7 @@ gettermination (server)
  *     Changes made to the maildrop since the session was started (or
  *     since the last pop_reset) may be lost.
  */
  *     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;
 {
 pop_close (server)
      popserver server;
 {
@@ -1527,18 +1589,18 @@ pop_trash (server)
 #endif
 }
 
 #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 *
 
 static char *
-find_crlf (in_string)
+find_crlf (in_string, len)
      char *in_string;
      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);
        {
          if (*++in_string == '\n')
            return (in_string - 1);
@@ -1546,7 +1608,10 @@ find_crlf (in_string)
       else
        in_string++;
     }
       else
        in_string++;
     }
-  /* NOTREACHED */
+  return (0);
 }
 
 #endif /* MAIL_USE_POP */
 }
 
 #endif /* MAIL_USE_POP */
+
+/* arch-tag: ceb37041-b7ad-49a8-a63d-286618b8367d
+   (do not change this comment) */