]> code.delx.au - gnu-emacs/blobdiff - lib-src/pop.c
* emacsclient.c (window_system): Delete redundant variable.
[gnu-emacs] / lib-src / pop.c
index b51e82c82bfa8d010d3c623367c03762f45a087d..ffc7879b86391b8af717d453a5e32a701c05ff66 100644 (file)
@@ -1,13 +1,15 @@
 /* pop.c: client routines for talking to a POP3-protocol post-office server
-   Copyright (c) 1991, 1993, 1996, 1997, 1999 Free Software Foundation, Inc.
-   Written by Jonathan Kamens, jik@security.ov.com.
+   Copyright (C) 1991, 1993, 1996, 1997, 1999, 2001, 2002, 2003, 2004,
+                 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
+
+Author: 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
+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)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,12 +17,10 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 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
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
+
 
 #ifdef HAVE_CONFIG_H
-#define NO_SHORTNAMES  /* Tell config not to load remap.h */
 #include <config.h>
 #else
 #define MAIL_USE_POP
@@ -76,17 +76,6 @@ extern struct servent *hes_getservbyname (/* char *, char * */);
 # ifdef HAVE_KRB5_H
 #  include <krb5.h>
 # endif
-# ifdef HAVE_DES_H
-#  include <des.h>
-# else
-#  ifdef HAVE_KERBEROSIV_DES_H
-#   include <kerberosIV/des.h>
-#  else
-#   ifdef HAVE_KERBEROS_DES_H
-#    include <kerberos/des.h>
-#   endif
-#  endif
-# endif
 # ifdef HAVE_KRB_H
 #  include <krb.h>
 # else
@@ -145,7 +134,7 @@ static char *find_crlf __P((char *, int));
 #define KPOP_PORT 1109
 #define POP_SERVICE "pop3"     /* we don't want the POP2 port! */
 #ifdef KERBEROS
-#define KPOP_SERVICE "kpop"
+#define KPOP_SERVICE "kpop"    /* never used: look for 20060515 to see why */
 #endif
 
 char pop_error[ERROR_MAX];
@@ -274,10 +263,11 @@ pop_open (host, username, password, flags)
          return (0);
        }
     }
-  if (password)
+  if (password)                        /* always true, detected 20060515 */
     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)
@@ -361,6 +351,7 @@ pop_stat (server, count, size)
      int *size;
 {
   char *fromserver;
+  char *end_ptr;
 
   if (server->in_multi)
     {
@@ -386,18 +377,26 @@ pop_stat (server, count, size)
       return (-1);
     }
 
-  *count = atoi (&fromserver[4]);
-
-  fromserver = index (&fromserver[4], ' ');
-  if (! fromserver)
+  errno = 0;
+  *count = strtol (&fromserver[4], &end_ptr, 10);
+  /* Check validity of string-to-integer conversion. */
+  if (fromserver + 4 == end_ptr || *end_ptr != ' ' || errno)
     {
-      strcpy (pop_error,
-             "Badly formatted response from server in pop_stat");
+      strcpy (pop_error, "Unexpected response from POP server in pop_stat");
       pop_trash (server);
       return (-1);
     }
 
-  *size = atoi (fromserver + 1);
+  fromserver = end_ptr;
+
+  errno = 0;
+  *size = strtol (fromserver + 1, &end_ptr, 10);
+  if (fromserver + 1 == end_ptr || errno)
+    {
+      strcpy (pop_error, "Unexpected response from POP server in pop_stat");
+      pop_trash (server);
+      return (-1);
+    }
 
   return (0);
 }
@@ -922,7 +921,17 @@ pop_last (server)
     }
   else
     {
-      return (atoi (&fromserver[4]));
+      char *end_ptr;
+      int count;
+      errno = 0;
+      count = strtol (&fromserver[4], &end_ptr, 10);
+      if (fromserver + 4 == end_ptr || errno)
+       {
+         strcpy (pop_error, "Unexpected response from server in pop_last");
+         pop_trash (server);
+         return (-1);
+       }
+      return count;
     }
 }
 
@@ -989,8 +998,7 @@ pop_quit (server)
       close (server->file);
     }
 
-  if (server->buffer)
-    free (server->buffer);
+  free (server->buffer);
   free ((char *) server);
 
   return (ret);
@@ -1019,12 +1027,19 @@ socket_connection (host, flags)
      char *host;
      int flags;
 {
+#ifdef HAVE_GETADDRINFO
+  struct addrinfo *res, *it;
+  struct addrinfo hints;
+  int ret;
+#else /* !HAVE_GETADDRINFO */
   struct hostent *hostent;
+#endif
   struct servent *servent;
   struct sockaddr_in addr;
   char found_port = 0;
   char *service;
   int sock;
+  char *realhost;
 #ifdef KERBEROS
 #ifdef KERBEROS5
   krb5_error_code rem;
@@ -1040,11 +1055,11 @@ socket_connection (host, flags)
   CREDENTIALS cred;
   Key_schedule schedule;
   int rem;
-  char *realhost;
 #endif /* KERBEROS5 */
 #endif /* KERBEROS */
 
   int try_count = 0;
+  int connect_ok;
 
 #ifdef WINDOWSNT
   {
@@ -1057,6 +1072,7 @@ socket_connection (host, flags)
   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
@@ -1083,6 +1099,7 @@ socket_connection (host, flags)
        }
       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);
@@ -1104,6 +1121,46 @@ socket_connection (host, flags)
 
     }
 
+#ifdef HAVE_GETADDRINFO
+  memset (&hints, 0, sizeof(hints));
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_CANONNAME;
+  hints.ai_family = AF_INET;
+  do
+    {
+      ret = getaddrinfo (host, service, &hints, &res);
+      try_count++;
+      if (ret != 0 && (ret != EAI_AGAIN || try_count == 5))
+       {
+         strcpy (pop_error, "Could not determine POP server's address");
+         return (-1);
+       }
+    } while (ret != 0);
+
+  if (ret == 0)
+    {
+      it = res;
+      while (it)
+        {
+          if (it->ai_addrlen == sizeof (addr))
+            {
+              struct sockaddr_in *in_a = (struct sockaddr_in *) it->ai_addr;
+              bcopy (&in_a->sin_addr, (char *) &addr.sin_addr,
+                     sizeof (addr.sin_addr));
+              if (! connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
+                break;
+            }
+          it = it->ai_next;
+        }
+      connect_ok = it != NULL;
+      if (connect_ok)
+        {
+          realhost = alloca (strlen (it->ai_canonname) + 1);
+          strcpy (realhost, it->ai_canonname);
+        }
+      freeaddrinfo (res);
+    }
+#else /* !HAVE_GETADDRINFO */
   do
     {
       hostent = gethostbyname (host);
@@ -1123,10 +1180,18 @@ socket_connection (host, flags)
        break;
       hostent->h_addr_list++;
     }
+  connect_ok = *hostent->h_addr_list != NULL;
+  if (! connect_ok)
+    {
+      realhost = alloca (strlen (hostent->h_name) + 1);
+      strcpy (realhost, hostent->h_name);
+    }
+
+#endif /* !HAVE_GETADDRINFO */
 
 #define CONNECT_ERROR "Could not connect to POP server: "
 
-  if (! *hostent->h_addr_list)
+  if (! connect_ok)
     {
       CLOSESOCKET (sock);
       strcpy (pop_error, CONNECT_ERROR);
@@ -1137,6 +1202,7 @@ socket_connection (host, flags)
     }
 
 #ifdef KERBEROS
+
 #define KRB_ERROR "Kerberos error connecting to POP server: "
   if (! (flags & POP_NO_KERBEROS))
     {
@@ -1164,7 +1230,7 @@ socket_connection (host, flags)
       if (rem = krb5_cc_get_principal (kcontext, ccdef, &client))
        goto krb5error;
 
-      for (cp = hostent->h_name; *cp; cp++)
+      for (cp = realhost; *cp; cp++)
        {
          if (isupper (*cp))
            {
@@ -1172,7 +1238,7 @@ socket_connection (host, flags)
            }
        }
 
-      if (rem = krb5_sname_to_principal (kcontext, hostent->h_name,
+      if (rem = krb5_sname_to_principal (kcontext, realhost,
                                         POP_SERVICE, FALSE, &server))
        goto krb5error;
 
@@ -1188,11 +1254,12 @@ socket_connection (host, flags)
       krb5_free_principal (kcontext, server);
       if (rem)
        {
+         strcpy (pop_error, KRB_ERROR);
+         strncat (pop_error, error_message (rem),
+                  ERROR_MAX - sizeof (KRB_ERROR));
+#if defined HAVE_KRB5_ERROR_TEXT
          if (err_ret && err_ret->text.length)
            {
-             strcpy (pop_error, KRB_ERROR);
-             strncat (pop_error, error_message (rem),
-                      ERROR_MAX - sizeof (KRB_ERROR));
              strncat (pop_error, " [server says '",
                       ERROR_MAX - strlen (pop_error) - 1);
              strncat (pop_error, err_ret->text.data,
@@ -1201,12 +1268,17 @@ socket_connection (host, flags)
              strncat (pop_error, "']",
                       ERROR_MAX - strlen (pop_error) - 1);
            }
-         else
+#elif defined HAVE_KRB5_ERROR_E_TEXT
+         if (err_ret && err_ret->e_text && strlen(*err_ret->e_text))
            {
-             strcpy (pop_error, KRB_ERROR);
-             strncat (pop_error, error_message (rem),
-                      ERROR_MAX - sizeof (KRB_ERROR));
+             strncat (pop_error, " [server says '",
+                      ERROR_MAX - strlen (pop_error) - 1);
+             strncat (pop_error, *err_ret->e_text,
+                      ERROR_MAX - strlen (pop_error) - 1);
+             strncat (pop_error, "']",
+                      ERROR_MAX - strlen (pop_error) - 1);
            }
+#endif
          if (err_ret)
            krb5_free_error (kcontext, err_ret);
          krb5_auth_con_free (kcontext, auth_context);
@@ -1217,7 +1289,6 @@ socket_connection (host, flags)
        }
 #else  /* ! KERBEROS5 */
       ticket = (KTEXT) malloc (sizeof (KTEXT_ST));
-      realhost = strdup (hostent->h_name);
       rem = krb_sendauth (0L, sock, ticket, "pop", realhost,
                          (char *) krb_realmofhost (realhost),
                          (unsigned long) 0, &msg_data, &cred, schedule,
@@ -1225,7 +1296,6 @@ socket_connection (host, flags)
                          (struct sockaddr_in *) 0,
                          "KPOPV0.1");
       free ((char *) ticket);
-      free (realhost);
       if (rem != KSUCCESS)
        {
          strcpy (pop_error, KRB_ERROR);
@@ -1403,12 +1473,24 @@ sendline (server, line)
 {
 #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)
     {
@@ -1552,7 +1634,7 @@ pop_close (server)
  * Function: pop_trash
  *
  * Purpose: Like pop_close or pop_quit, but doesn't deallocate the
- *     memory associated with the server.  It is legal to call
+ *     memory associated with the server.  It is valid to call
  *     pop_close or pop_quit after this function has been called.
  */
 static void
@@ -1607,3 +1689,6 @@ find_crlf (in_string, len)
 }
 
 #endif /* MAIL_USE_POP */
+
+/* arch-tag: ceb37041-b7ad-49a8-a63d-286618b8367d
+   (do not change this comment) */