]> code.delx.au - gnu-emacs/blobdiff - lib-src/pop.c
Implement getaddrinfo fallback for MS-Windows
[gnu-emacs] / lib-src / pop.c
index a269144c9156d2a8d009ef3a4d8ec010f9294678..21d721546b7e49e3886b039109d5d69924bf39b0 100644 (file)
@@ -1,6 +1,6 @@
 /* pop.c: client routines for talking to a POP3-protocol post-office server
 
-Copyright (C) 1991, 1993, 1996-1997, 1999, 2001-2013 Free Software
+Copyright (C) 1991, 1993, 1996-1997, 1999, 2001-2016 Free Software
 Foundation, Inc.
 
 Author: Jonathan Kamens <jik@security.ov.com>
@@ -28,7 +28,17 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <sys/types.h>
 #ifdef WINDOWSNT
 #include "ntlib.h"
-#include <winsock.h>
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501    /* for getaddrinfo stuff */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#undef getaddrinfo
+#define getaddrinfo  sys_getaddrinfo
+#undef freeaddrinfo
+#define freeaddrinfo sys_freeaddrinfo
+int sys_getaddrinfo (const char * node, const char * service,
+                    const struct addrinfo * hints, struct addrinfo ** res);
+void sys_freeaddrinfo (struct addrinfo * ai);
 #undef SOCKET_ERROR
 #define RECV(s,buf,len,flags) recv (s,buf,len,flags)
 #define SEND(s,buf,len,flags) send (s,buf,len,flags)
@@ -42,10 +52,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #endif
 #include <pop.h>
 
-#ifdef sun
-#include <malloc.h>
-#endif /* sun */
-
 #ifdef HESIOD
 #include <hesiod.h>
 /*
@@ -124,7 +130,7 @@ static char *find_crlf (char *, int);
 #endif
 
 char pop_error[ERROR_MAX];
-int pop_debug = 0;
+bool pop_debug = false;
 
 /*
  * Function: pop_open (char *host, char *username, char *password,
@@ -269,8 +275,8 @@ pop_open (char *host, char *username, char *password, int flags)
   server->data = 0;
   server->buffer_index = 0;
   server->buffer_size = GETLINE_MIN;
-  server->in_multi = 0;
-  server->trash_started = 0;
+  server->in_multi = false;
+  server->trash_started = false;
 
   if (getok (server))
     return (0);
@@ -686,7 +692,7 @@ pop_multi_first (popserver server, const char *command, char **response)
   else if (0 == strncmp (*response, "+OK", 3))
     {
       for (*response += 3; **response == ' '; (*response)++) /* empty */;
-      server->in_multi = 1;
+      server->in_multi = true;
       return (0);
     }
   else
@@ -728,7 +734,7 @@ pop_multi_next (popserver server, char **line)
       if (! fromserver[1])
        {
          *line = 0;
-         server->in_multi = 0;
+         server->in_multi = false;
          return (0);
        }
       else
@@ -1188,7 +1194,7 @@ socket_connection (char *host, int flags)
            {
              int errlen = err_ret->text.length;
              snprintf (pop_error + pop_error_len, ERROR_MAX - pop_error_len,
-                       " [server says '.*%s']", errlen, err_ret->text.data);
+                       " [server says '%.*s']", errlen, err_ret->text.data);
            }
 #elif defined HAVE_KRB5_ERROR_E_TEXT
          if (err_ret && err_ret->e_text && **err_ret->e_text)
@@ -1397,8 +1403,7 @@ sendline (popserver server, const char *line)
      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");
+  strcpy (stpcpy (buf, line), "\r\n");
   ret = fullwrite (server->file, buf, strlen (buf));
 
   if (ret < 0)
@@ -1546,7 +1551,7 @@ pop_trash (popserver server)
       /* avoid recursion; sendline can call pop_trash */
       if (server->trash_started)
        return;
-      server->trash_started = 1;
+      server->trash_started = true;
 
       sendline (server, "RSET");
       sendline (server, "QUIT");
@@ -1586,4 +1591,143 @@ find_crlf (char *in_string, int len)
   return (0);
 }
 
+#ifdef WINDOWSNT
+/* The following 2 functions are only available since XP, so we load
+   them dynamically and provide fallbacks.  */
+
+int (WINAPI *pfn_getaddrinfo) (const char *, const char *,
+                              const struct addrinfo *, struct addrinfo **);
+void (WINAPI *pfn_freeaddrinfo) (struct addrinfo *);
+
+static int
+load_ws2 (void)
+{
+  static int ws2_loaded = 0;
+
+  if (!ws2_loaded)
+    {
+      HANDLE ws2_lib = LoadLibrary ("Ws2_32.dll");
+
+      if (ws2_lib != NULL)
+       {
+         ws2_loaded = 1;
+         pfn_getaddrinfo = (void *) GetProcAddress (ws2_lib, "getaddrinfo");
+         pfn_freeaddrinfo = (void *) GetProcAddress (ws2_lib, "freeaddrinfo");
+         /* Paranoia: these two functions should go together, so if
+            one is absent, we cannot use the other.  */
+         if (pfn_getaddrinfo == NULL)
+           pfn_freeaddrinfo = NULL;
+         else if (pfn_freeaddrinfo == NULL)
+           pfn_getaddrinfo = NULL;
+       }
+    }
+  if (!ws2_loaded)
+    {
+      errno = ENETDOWN;
+      return -1;
+    }
+  return 0;
+}
+
+
+int
+sys_getaddrinfo (const char *node, const char *service,
+                const struct addrinfo *hints, struct addrinfo **res)
+{
+  int rc;
+
+  if (load_ws2 () != 0)
+    {
+      errno = ENETDOWN;
+      return WSANO_RECOVERY;
+    }
+
+  if (pfn_getaddrinfo)
+    rc = pfn_getaddrinfo (node, service, hints, res);
+  else
+    {
+      int port = 0;
+      struct hostent *host_info;
+      struct gai_storage {
+       struct addrinfo addrinfo;
+       struct sockaddr_in sockaddr_in;
+      } *gai_storage;
+
+      /* We don't support any flags besides AI_CANONNAME.  */
+      if (hints && (hints->ai_flags & ~(AI_CANONNAME)) != 0)
+       return WSAEINVAL;
+      /* NODE cannot be NULL, since pop.c has fallbacks for that.  */
+      if (!node)
+       return WSAHOST_NOT_FOUND;
+
+      if (service)
+       {
+         const char *protocol =
+           (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
+         struct servent *srv = getservbyname (service, protocol);
+
+         if (srv)
+           port = srv->s_port;
+         else
+           return WSAHOST_NOT_FOUND;
+       }
+
+      gai_storage = calloc (1, sizeof *gai_storage);
+      gai_storage->sockaddr_in.sin_port = port;
+      host_info = gethostbyname (node);
+      if (host_info)
+       {
+         memcpy (&gai_storage->sockaddr_in.sin_addr,
+                 host_info->h_addr, host_info->h_length);
+         gai_storage->sockaddr_in.sin_family = host_info->h_addrtype;
+       }
+      else
+       {
+         free (gai_storage);
+         return WSAHOST_NOT_FOUND;
+       }
+
+      gai_storage->addrinfo.ai_addr =
+       (struct sockaddr *)&gai_storage->sockaddr_in;
+      gai_storage->addrinfo.ai_addrlen = sizeof (gai_storage->sockaddr_in);
+      if (hints && (hints->ai_flags & AI_CANONNAME) != 0)
+       {
+         gai_storage->addrinfo.ai_canonname = strdup (host_info->h_name);
+         if (!gai_storage->addrinfo.ai_canonname)
+           {
+             free (gai_storage);
+             return WSA_NOT_ENOUGH_MEMORY;
+           }
+       }
+      gai_storage->addrinfo.ai_protocol = (hints) ? hints->ai_protocol : 0;
+      gai_storage->addrinfo.ai_socktype = (hints) ? hints->ai_socktype : 0;
+      gai_storage->addrinfo.ai_family = gai_storage->sockaddr_in.sin_family;
+      gai_storage->addrinfo.ai_next = NULL;
+
+      *res = &gai_storage->addrinfo;
+      rc = 0;
+    }
+
+  return rc;
+}
+
+void
+sys_freeaddrinfo (struct addrinfo *ai)
+{
+  if (load_ws2 () != 0)
+    {
+      errno = ENETDOWN;
+      return;
+    }
+
+  if (pfn_freeaddrinfo)
+    pfn_freeaddrinfo (ai);
+  else
+    {
+      if (ai->ai_canonname)
+       free (ai->ai_canonname);
+      free (ai);
+    }
+}
+#endif /* WINDOWSNT */
 #endif /* MAIL_USE_POP */