]> sjero.net Git - wget/blobdiff - src/ftp-basic.c
NEWS: cite --start-pos
[wget] / src / ftp-basic.c
index 512b2b312f59e3ea0a8ae802be7f294e9c67b40d..7a512c6ed607478ea39373c88196ba773aa07a29 100644 (file)
@@ -1,12 +1,14 @@
 /* Basic FTP routines.
-   Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+   2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
+   Inc.
 
 This file is part of GNU Wget.
 
 GNU Wget 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 of the License, 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 Wget is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,52 +16,33 @@ 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 Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+along with Wget.  If not, see <http://www.gnu.org/licenses/>.
 
-In addition, as a special exception, the Free Software Foundation
-gives permission to link the code of its release of Wget with the
-OpenSSL project's "OpenSSL" library (or with modified versions of it
-that use the same license as the "OpenSSL" library), and distribute
-the linked executables.  You must obey the GNU General Public License
-in all respects for all of the code used other than "OpenSSL".  If you
-modify this file, you may extend this exception to your version of the
-file, but you are not obligated to do so.  If you do not wish to do
-so, delete this exception statement from your version.  */
+Additional permission under GNU GPL version 3 section 7
 
-#include <config.h>
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.  */
 
+#include "wget.h"
+
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#include <sys/types.h>
-
-/* For inet_ntop. */
-#ifndef WINDOWS
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#endif
-
-#ifdef WINDOWS
-# include <winsock.h>
-#endif
-
-#include "wget.h"
+#include <string.h>
+#include <unistd.h>
 #include "utils.h"
-#include "rbuf.h"
 #include "connect.h"
 #include "host.h"
 #include "ftp.h"
+#include "retr.h"
 
 char ftp_last_respline[128];
 
@@ -67,47 +50,47 @@ char ftp_last_respline[128];
 /* Get the response of FTP server and allocate enough room to handle
    it.  <CR> and <LF> characters are stripped from the line, and the
    line is 0-terminated.  All the response lines but the last one are
-   skipped.  The last line is determined as described in RFC959.  */
+   skipped.  The last line is determined as described in RFC959.
+
+   If the line is successfully read, FTPOK is returned, and *ret_line
+   is assigned a freshly allocated line.  Otherwise, FTPRERR is
+   returned, and the value of *ret_line should be ignored.  */
+
 uerr_t
-ftp_response (struct rbuf *rbuf, char **line)
+ftp_response (int fd, char **ret_line)
 {
-  int i;
-  int bufsize = 40;
-
-  *line = (char *)xmalloc (bufsize);
-  do
+  while (1)
     {
-      for (i = 0; 1; i++)
-        {
-          int res;
-          if (i > bufsize - 1)
-            *line = (char *)xrealloc (*line, (bufsize <<= 1));
-          res = RBUF_READCHAR (rbuf, *line + i);
-          /* RES is number of bytes read.  */
-          if (res == 1)
-            {
-              if ((*line)[i] == '\n')
-                {
-                  (*line)[i] = '\0';
-                  /* Get rid of \r.  */
-                  if (i > 0 && (*line)[i - 1] == '\r')
-                    (*line)[i - 1] = '\0';
-                  break;
-                }
-            }
-          else
-            return FTPRERR;
-        }
+      char *p;
+      char *line = fd_read_line (fd);
+      if (!line)
+        return FTPRERR;
+
+      /* Strip trailing CRLF before printing the line, so that
+         quotting doesn't include bogus \012 and \015. */
+      p = strchr (line, '\0');
+      if (p > line && p[-1] == '\n')
+        *--p = '\0';
+      if (p > line && p[-1] == '\r')
+        *--p = '\0';
+
       if (opt.server_response)
-        logprintf (LOG_ALWAYS, "%s\n", *line);
+        logprintf (LOG_NOTQUIET, "%s\n",
+                   quotearg_style (escape_quoting_style, line));
       else
-        DEBUGP (("%s\n", *line));
+        DEBUGP (("%s\n", quotearg_style (escape_quoting_style, line)));
+
+      /* The last line of output is the one that begins with "ddd ". */
+      if (c_isdigit (line[0]) && c_isdigit (line[1]) && c_isdigit (line[2])
+          && line[3] == ' ')
+        {
+          strncpy (ftp_last_respline, line, sizeof (ftp_last_respline));
+          ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0';
+          *ret_line = line;
+          return FTPOK;
+        }
+      xfree (line);
     }
-  while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) &&
-           ISDIGIT ((*line)[2]) && (*line)[3] == ' '));
-  strncpy (ftp_last_respline, *line, sizeof (ftp_last_respline));
-  ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0';
-  return FTPOK;
 }
 
 /* Returns the malloc-ed FTP request, ending with <CR><LF>, printing
@@ -116,43 +99,58 @@ ftp_response (struct rbuf *rbuf, char **line)
 static char *
 ftp_request (const char *command, const char *value)
 {
-  char *res = (char *)xmalloc (strlen (command)
-                               + (value ? (1 + strlen (value)) : 0)
-                               + 2 + 1);
-  sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : "");
+  char *res;
+  if (value)
+    {
+      /* Check for newlines in VALUE (possibly injected by the %0A URL
+         escape) making the callers inadvertently send multiple FTP
+         commands at once.  Without this check an attacker could
+         intentionally redirect to ftp://server/fakedir%0Acommand.../
+         and execute arbitrary FTP command on a remote FTP server.  */
+      if (strpbrk (value, "\r\n"))
+        {
+          /* Copy VALUE to the stack and modify CR/LF to space. */
+          char *defanged, *p;
+          STRDUP_ALLOCA (defanged, value);
+          for (p = defanged; *p; p++)
+            if (*p == '\r' || *p == '\n')
+              *p = ' ';
+          DEBUGP (("\nDetected newlines in %s \"%s\"; changing to %s \"%s\"\n",
+                   command, quotearg_style (escape_quoting_style, value),
+                   command, quotearg_style (escape_quoting_style, defanged)));
+          /* Make VALUE point to the defanged copy of the string. */
+          value = defanged;
+        }
+      res = concat_strings (command, " ", value, "\r\n", (char *) 0);
+    }
+  else
+    res = concat_strings (command, "\r\n", (char *) 0);
   if (opt.server_response)
     {
       /* Hack: don't print out password.  */
       if (strncmp (res, "PASS", 4) != 0)
         logprintf (LOG_ALWAYS, "--> %s\n", res);
       else
-        logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
+        logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n\n");
     }
   else
     DEBUGP (("\n--> %s\n", res));
   return res;
 }
 
-#ifdef USE_OPIE
-const char *calculate_skey_response PARAMS ((int, const char *, const char *));
-#endif
-
 /* Sends the USER and PASS commands to the server, to control
    connection socket csock.  */
 uerr_t
-ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
+ftp_login (int csock, const char *acc, const char *pass)
 {
   uerr_t err;
   char *request, *respline;
   int nwritten;
 
   /* Get greeting.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   if (*respline != '2')
     {
       xfree (respline);
@@ -161,7 +159,7 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
   xfree (respline);
   /* Send USER username.  */
   request = ftp_request ("USER", acc);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -169,12 +167,9 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   /* An unprobable possibility of logging without a password.  */
   if (*respline == '2')
     {
@@ -187,48 +182,48 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
       xfree (respline);
       return FTPLOGREFUSED;
     }
-#ifdef USE_OPIE
+#ifdef ENABLE_OPIE
   {
     static const char *skey_head[] = {
       "331 s/key ",
       "331 opiekey "
     };
-    int i;
+    size_t i;
+    const char *seed = NULL;
 
-    for (i = 0; i < ARRAY_SIZE (skey_head); i++)
+    for (i = 0; i < countof (skey_head); i++)
       {
-        if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
-          break;
+        int l = strlen (skey_head[i]);
+        if (0 == strncasecmp (skey_head[i], respline, l))
+          {
+            seed = respline + l;
+            break;
+          }
       }
-    if (i < ARRAY_SIZE (skey_head))
+    if (seed)
       {
-        const char *cp;
         int skey_sequence = 0;
 
-        for (cp = respline + strlen (skey_head[i]);
-             '0' <= *cp && *cp <= '9';
-             cp++)
-          {
-            skey_sequence = skey_sequence * 10 + *cp - '0';
-          }
-        if (*cp == ' ')
-          cp++;
+        /* Extract the sequence from SEED.  */
+        for (; c_isdigit (*seed); seed++)
+          skey_sequence = 10 * skey_sequence + *seed - '0';
+        if (*seed == ' ')
+          ++seed;
         else
           {
-          bad:
             xfree (respline);
             return FTPLOGREFUSED;
           }
-        if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
-          goto bad;
-        pass = cp;
+        /* Replace the password with the SKEY response to the
+           challenge.  */
+        pass = skey_response (skey_sequence, seed, pass);
       }
   }
-#endif /* USE_OPIE */
+#endif /* ENABLE_OPIE */
   xfree (respline);
   /* Send PASS password.  */
   request = ftp_request ("PASS", pass);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -236,12 +231,9 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   if (*respline != '2')
     {
       xfree (respline);
@@ -252,222 +244,266 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
   return FTPOK;
 }
 
-#ifdef ENABLE_IPV6
+static void
+ip_address_to_port_repr (const ip_address *addr, int port, char *buf,
+                         size_t buflen)
+{
+  unsigned char *ptr;
+
+  assert (addr->family == AF_INET);
+  /* buf must contain the argument of PORT (of the form a,b,c,d,e,f). */
+  assert (buflen >= 6 * 4);
+
+  ptr = IP_INADDR_DATA (addr);
+  snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d", ptr[0], ptr[1],
+            ptr[2], ptr[3], (port & 0xff00) >> 8, port & 0xff);
+  buf[buflen - 1] = '\0';
+}
+
+/* Bind a port and send the appropriate PORT command to the FTP
+   server.  Use acceptport after RETR, to get the socket of data
+   connection.  */
 uerr_t
-ftp_eprt (struct rbuf *rbuf)
+ftp_port (int csock, int *local_sock)
 {
   uerr_t err;
-
   char *request, *respline;
-  ip_address in_addr;
-  unsigned short port;
+  ip_address addr;
+  int nwritten;
+  int port;
+  /* Must contain the argument of PORT (of the form a,b,c,d,e,f). */
+  char bytes[6 * 4 + 1];
+
+  /* Get the address of this side of the connection. */
+  if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
+    return FTPSYSERR;
 
-  char ipv6 [8 * (4 * 3 + 3) + 8];
-  char *bytes;
+  assert (addr.family == AF_INET);
 
   /* Setting port to 0 lets the system choose a free port.  */
   port = 0;
-  err = bindport (&port, ip_default_family);
-  if (err != BINDOK)   /* Bind the port.  */
-    return err;
 
-  /* Get the address of this side of the connection.  */
-  if (!conaddr (RBUF_FD (rbuf), &in_addr))
-    /* Huh?  This is not BINDERR! */
-    return BINDERR;
-  inet_ntop (AF_INET6, &in_addr, ipv6, sizeof (ipv6));
+  /* Bind the port.  */
+  *local_sock = bind_local (&addr, &port);
+  if (*local_sock < 0)
+    return FTPSYSERR;
+
+  /* Construct the argument of PORT (of the form a,b,c,d,e,f). */
+  ip_address_to_port_repr (&addr, port, bytes, sizeof (bytes));
 
-  /* Construct the argument of EPRT (of the form |2|IPv6.ascii|PORT.ascii|). */
-  bytes = alloca (3 + strlen (ipv6) + 1 + numdigit (port) + 1 + 1);
-  sprintf (bytes, "|2|%s|%u|", ipv6, port);
   /* Send PORT request.  */
-  request = ftp_request ("EPRT", bytes);
-  if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
+  request = ftp_request ("PORT", bytes);
+  nwritten = fd_write (csock, request, strlen (request), -1);
+  if (nwritten < 0)
     {
-      closeport (port);
       xfree (request);
+      fd_close (*local_sock);
       return WRITEFAILED;
     }
   xfree (request);
+
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
     {
-      closeport (port);
-      xfree (respline);
+      fd_close (*local_sock);
       return err;
     }
   if (*respline != '2')
     {
-      closeport (port);
       xfree (respline);
+      fd_close (*local_sock);
       return FTPPORTERR;
     }
   xfree (respline);
   return FTPOK;
 }
-#endif
+
+#ifdef ENABLE_IPV6
+static void
+ip_address_to_lprt_repr (const ip_address *addr, int port, char *buf,
+                         size_t buflen)
+{
+  unsigned char *ptr = IP_INADDR_DATA (addr);
+
+  /* buf must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+  assert (buflen >= 21 * 4);
+
+  /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+  switch (addr->family)
+    {
+    case AF_INET:
+      snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d", 4, 4,
+                ptr[0], ptr[1], ptr[2], ptr[3], 2,
+                (port & 0xff00) >> 8, port & 0xff);
+      break;
+    case AF_INET6:
+      snprintf (buf, buflen,
+                "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
+                6, 16,
+                ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7],
+                ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15],
+                2, (port & 0xff00) >> 8, port & 0xff);
+      break;
+    default:
+      abort ();
+    }
+}
 
 /* Bind a port and send the appropriate PORT command to the FTP
    server.  Use acceptport after RETR, to get the socket of data
    connection.  */
 uerr_t
-ftp_port (struct rbuf *rbuf)
+ftp_lprt (int csock, int *local_sock)
 {
   uerr_t err;
   char *request, *respline;
-  char bytes[6 * 4 +1];
+  ip_address addr;
+  int nwritten;
+  int port;
+  /* Must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+  char bytes[21 * 4 + 1];
 
-  ip_address in_addr;
-  ip4_address in_addr_4;
-  unsigned char *in_addr4_ptr = (unsigned char *)&in_addr_4;
+  /* Get the address of this side of the connection. */
+  if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
+    return FTPSYSERR;
+
+  assert (addr.family == AF_INET || addr.family == AF_INET6);
 
-  int nwritten;
-  unsigned short port;
-#ifdef ENABLE_IPV6
-  /*
-    Only try the Extented Version if we actually use IPv6
-  */
-  if (ip_default_family == AF_INET6)
-    {
-      err = ftp_eprt (rbuf);
-      if (err == FTPOK)
-       return err;
-    }
-#endif
   /* Setting port to 0 lets the system choose a free port.  */
   port = 0;
 
-  err = bindport (&port, AF_INET);
-  if (err != BINDOK)
-    return err;
+  /* Bind the port.  */
+  *local_sock = bind_local (&addr, &port);
+  if (*local_sock < 0)
+    return FTPSYSERR;
+
+  /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
+  ip_address_to_lprt_repr (&addr, port, bytes, sizeof (bytes));
 
-  /* Get the address of this side of the connection and convert it
-     (back) to IPv4.  */
-  if (!conaddr (RBUF_FD (rbuf), &in_addr))
-    /* Huh?  This is not BINDERR! */
-    return BINDERR;
-  if (!map_ip_to_ipv4 (&in_addr, &in_addr_4))
-    return BINDERR;
-
-  /* Construct the argument of PORT (of the form a,b,c,d,e,f).  Port
-     is unsigned short so (unsigned) (port & 0xff000) >> 8 is the same
-     like port >> 8
-   */
-  sprintf (bytes, "%d,%d,%d,%d,%d,%d",
-          in_addr4_ptr[0], in_addr4_ptr[1], in_addr4_ptr[2], in_addr4_ptr[3],
-          port >> 8, port & 0xff);
   /* Send PORT request.  */
-  request = ftp_request ("PORT", bytes);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  request = ftp_request ("LPRT", bytes);
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
+      fd_close (*local_sock);
       return WRITEFAILED;
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
     {
-      xfree (respline);
+      fd_close (*local_sock);
       return err;
     }
   if (*respline != '2')
     {
       xfree (respline);
+      fd_close (*local_sock);
       return FTPPORTERR;
     }
   xfree (respline);
   return FTPOK;
 }
 
-#ifdef ENABLE_IPV6
+static void
+ip_address_to_eprt_repr (const ip_address *addr, int port, char *buf,
+                         size_t buflen)
+{
+  int afnum;
+
+  /* buf must contain the argument of EPRT (of the form |af|addr|port|).
+   * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr
+   * 1 char for af (1-2) and 5 chars for port (0-65535) */
+  assert (buflen >= 4 + INET6_ADDRSTRLEN + 1 + 5);
+
+  /* Construct the argument of EPRT (of the form |af|addr|port|). */
+  afnum = (addr->family == AF_INET ? 1 : 2);
+  snprintf (buf, buflen, "|%d|%s|%d|", afnum, print_address (addr), port);
+  buf[buflen - 1] = '\0';
+}
+
+/* Bind a port and send the appropriate PORT command to the FTP
+   server.  Use acceptport after RETR, to get the socket of data
+   connection.  */
 uerr_t
-ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port, 
-         char *typ)
+ftp_eprt (int csock, int *local_sock)
 {
-  int err;
-  char *s, *respline;
-  char *request = ftp_request ("EPSV", typ);
-  if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
+  uerr_t err;
+  char *request, *respline;
+  ip_address addr;
+  int nwritten;
+  int port;
+  /* Must contain the argument of EPRT (of the form |af|addr|port|).
+   * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr
+   * 1 char for af (1-2) and 5 chars for port (0-65535) */
+  char bytes[4 + INET6_ADDRSTRLEN + 1 + 5 + 1];
+
+  /* Get the address of this side of the connection. */
+  if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
+    return FTPSYSERR;
+
+  /* Setting port to 0 lets the system choose a free port.  */
+  port = 0;
+
+  /* Bind the port.  */
+  *local_sock = bind_local (&addr, &port);
+  if (*local_sock < 0)
+    return FTPSYSERR;
+
+  /* Construct the argument of EPRT (of the form |af|addr|port|). */
+  ip_address_to_eprt_repr (&addr, port, bytes, sizeof (bytes));
+
+  /* Send PORT request.  */
+  request = ftp_request ("EPRT", bytes);
+  nwritten = fd_write (csock, request, strlen (request), -1);
+  if (nwritten < 0)
     {
       xfree (request);
+      fd_close (*local_sock);
       return WRITEFAILED;
     }
-  /* Get the server response.  */
-  err = ftp_response (rbuf, &respline);
+  xfree (request);
+  /* Get appropriate response.  */
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
     {
-      xfree (respline);
+      fd_close (*local_sock);
       return err;
     }
   if (*respline != '2')
     {
       xfree (respline);
-      return FTPNOPASV;
+      fd_close (*local_sock);
+      return FTPPORTERR;
     }
-  /* Parse the request.  */
-  s = respline;
-  /* respline::=229 Entering Extended Passive Mode (|||6446|) */
-  for (s += 4; *s && !ISDIGIT (*s); s++);
-  if (!*s)
-    return FTPINVPASV;
-  *port=0; 
-  for (; ISDIGIT (*s); s++) 
-    *port = (*s - '0') + 10 * (*port);
   xfree (respline);
-  /* Now we have the port but we need the IPv6 :-( */
-  {
-    wget_sockaddr remote;
-    int len = sizeof (remote);
-    struct sockaddr_in *ipv4_sock = ( struct sockaddr_in *)&remote;
-    getpeername (RBUF_FD (rbuf), (struct sockaddr*)&remote, &len);
-    switch(remote.sa.sa_family)
-      {
-        case AF_INET6:
-          memcpy (addr, &remote.sin6.sin6_addr, 16);
-         break;
-       case AF_INET:  
-          map_ipv4_to_ip ((ip4_address *)&ipv4_sock->sin_addr, addr);
-         break;
-       default:
-         abort();
-         return FTPINVPASV;
-         /* realy bad */
-      }
-  }
   return FTPOK;
 }
 #endif
 
-
 /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
    transfer.  Reads the response from server and parses it.  Reads the
    host and port addresses and returns them.  */
 uerr_t
-ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
+ftp_pasv (int csock, ip_address *addr, int *port)
 {
   char *request, *respline, *s;
   int nwritten, i;
   uerr_t err;
-  unsigned char addr4[4];
+  unsigned char tmp[6];
+
+  assert (addr != NULL);
+  assert (port != NULL);
+
+  xzero (*addr);
 
-#ifdef ENABLE_IPV6
-  if (ip_default_family == AF_INET6) 
-    {
-      err = ftp_epsv (rbuf, addr, port, "2");  /* try IPv6 with EPSV */
-      if (FTPOK == err) 
-        return FTPOK;
-      err = ftp_epsv (rbuf, addr, port, "1");  /* try IPv4 with EPSV */
-      if (FTPOK == err) 
-        return FTPOK;
-    }
-#endif  
   /* Form the request.  */
   request = ftp_request ("PASV", NULL);
   /* And send it.  */
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -475,28 +511,143 @@ ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
     }
   xfree (request);
   /* Get the server response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
+    return err;
+  if (*respline != '2')
     {
       xfree (respline);
-      return err;
+      return FTPNOPASV;
+    }
+  /* Parse the request.  */
+  s = respline;
+  for (s += 4; *s && !c_isdigit (*s); s++)
+    ;
+  if (!*s)
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+  for (i = 0; i < 6; i++)
+    {
+      tmp[i] = 0;
+      for (; c_isdigit (*s); s++)
+        tmp[i] = (*s - '0') + 10 * tmp[i];
+      if (*s == ',')
+        s++;
+      else if (i < 5)
+        {
+          /* When on the last number, anything can be a terminator.  */
+          xfree (respline);
+          return FTPINVPASV;
+        }
     }
+  xfree (respline);
+
+  addr->family = AF_INET;
+  memcpy (IP_INADDR_DATA (addr), tmp, 4);
+  *port = ((tmp[4] << 8) & 0xff00) + tmp[5];
+
+  return FTPOK;
+}
+
+#ifdef ENABLE_IPV6
+/* Similar to ftp_lprt, but uses `LPSV' to initiate the passive FTP
+   transfer.  Reads the response from server and parses it.  Reads the
+   host and port addresses and returns them.  */
+uerr_t
+ftp_lpsv (int csock, ip_address *addr, int *port)
+{
+  char *request, *respline, *s;
+  int nwritten, i, af, addrlen, portlen;
+  uerr_t err;
+  unsigned char tmp[16];
+  unsigned char tmpprt[2];
+
+  assert (addr != NULL);
+  assert (port != NULL);
+
+  xzero (*addr);
+
+  /* Form the request.  */
+  request = ftp_request ("LPSV", NULL);
+
+  /* And send it.  */
+  nwritten = fd_write (csock, request, strlen (request), -1);
+  if (nwritten < 0)
+    {
+      xfree (request);
+      return WRITEFAILED;
+    }
+  xfree (request);
+
+  /* Get the server response.  */
+  err = ftp_response (csock, &respline);
+  if (err != FTPOK)
+    return err;
   if (*respline != '2')
     {
       xfree (respline);
       return FTPNOPASV;
     }
-  /* Parse the request.  */
-  /* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).         */
+
+  /* Parse the response.  */
   s = respline;
-  for (s += 4; *s && !ISDIGIT (*s); s++);
+  for (s += 4; *s && !c_isdigit (*s); s++)
+    ;
   if (!*s)
-    return FTPINVPASV;
-  for (i = 0; i < 4; i++)
     {
-      addr4[i] = 0;
-      for (; ISDIGIT (*s); s++)
-        addr4[i] = (*s - '0') + 10 * addr4[i];
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  /* First, get the address family */
+  af = 0;
+  for (; c_isdigit (*s); s++)
+    af = (*s - '0') + 10 * af;
+
+  if (af != 4 && af != 6)
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  if (!*s || *s++ != ',')
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  /* Then, get the address length */
+  addrlen = 0;
+  for (; c_isdigit (*s); s++)
+    addrlen = (*s - '0') + 10 * addrlen;
+
+  if (!*s || *s++ != ',')
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  if (addrlen > 16)
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  if ((af == 4 && addrlen != 4)
+      || (af == 6 && addrlen != 16))
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  /* Now, we get the actual address */
+  for (i = 0; i < addrlen; i++)
+    {
+      tmp[i] = 0;
+      for (; c_isdigit (*s); s++)
+        tmp[i] = (*s - '0') + 10 * tmp[i];
       if (*s == ',')
         s++;
       else
@@ -506,33 +657,171 @@ ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
         }
     }
 
-  /* Eventually make an IPv4 in IPv6 adress if needed */
-  map_ipv4_to_ip ((ip4_address *)addr4, addr);
+  /* Now, get the port length */
+  portlen = 0;
+  for (; c_isdigit (*s); s++)
+    portlen = (*s - '0') + 10 * portlen;
 
-  *port=0;
-  for (; ISDIGIT (*s); s++)
-    *port = (*s - '0') + 10 * (*port);
-  if (*s == ',') 
-    s++;
+  if (!*s || *s++ != ',')
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  if (portlen > 2)
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  /* Finally, we get the port number */
+  tmpprt[0] = 0;
+  for (; c_isdigit (*s); s++)
+    tmpprt[0] = (*s - '0') + 10 * tmpprt[0];
+
+  if (!*s || *s++ != ',')
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  tmpprt[1] = 0;
+  for (; c_isdigit (*s); s++)
+    tmpprt[1] = (*s - '0') + 10 * tmpprt[1];
+
+  assert (s != NULL);
+
+  if (af == 4)
+    {
+      addr->family = AF_INET;
+      memcpy (IP_INADDR_DATA (addr), tmp, 4);
+      *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
+      DEBUGP (("lpsv addr is: %s\n", print_address(addr)));
+      DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
+      DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
+      DEBUGP (("*port is: %d\n", *port));
+    }
   else
+    {
+      assert (af == 6);
+      addr->family = AF_INET6;
+      memcpy (IP_INADDR_DATA (addr), tmp, 16);
+      *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
+      DEBUGP (("lpsv addr is: %s\n", print_address(addr)));
+      DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
+      DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
+      DEBUGP (("*port is: %d\n", *port));
+    }
+
+  xfree (respline);
+  return FTPOK;
+}
+
+/* Similar to ftp_eprt, but uses `EPSV' to initiate the passive FTP
+   transfer.  Reads the response from server and parses it.  Reads the
+   host and port addresses and returns them.  */
+uerr_t
+ftp_epsv (int csock, ip_address *ip, int *port)
+{
+  char *request, *respline, *start, delim, *s;
+  int nwritten, i;
+  uerr_t err;
+  int tport;
+
+  assert (ip != NULL);
+  assert (port != NULL);
+
+  /* IP already contains the IP address of the control connection's
+     peer, so we don't need to call socket_ip_address here.  */
+
+  /* Form the request.  */
+  /* EPSV 1 means that we ask for IPv4 and EPSV 2 means that we ask for IPv6. */
+  request = ftp_request ("EPSV", (ip->family == AF_INET ? "1" : "2"));
+
+  /* And send it.  */
+  nwritten = fd_write (csock, request, strlen (request), -1);
+  if (nwritten < 0)
+    {
+      xfree (request);
+      return WRITEFAILED;
+    }
+  xfree (request);
+
+  /* Get the server response.  */
+  err = ftp_response (csock, &respline);
+  if (err != FTPOK)
+    return err;
+  if (*respline != '2')
+    {
+      xfree (respline);
+      return FTPNOPASV;
+    }
+
+  assert (respline != NULL);
+
+  DEBUGP(("respline is %s\n", respline));
+
+  /* Skip the useless stuff and get what's inside the parentheses */
+  start = strchr (respline, '(');
+  if (start == NULL)
     {
       xfree (respline);
       return FTPINVPASV;
     }
 
-  {
-    unsigned short port2 = 0; 
-    for (; ISDIGIT (*s); s++) 
-      port2 = (*s - '0') + 10 * port2;
-    *port = (*port) * 256 + port2;
-  }
+  /* Skip the first two void fields */
+  s = start + 1;
+  delim = *s++;
+  if (delim < 33 || delim > 126)
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  for (i = 0; i < 2; i++)
+    {
+      if (*s++ != delim)
+        {
+          xfree (respline);
+        return FTPINVPASV;
+        }
+    }
+
+  /* Finally, get the port number */
+  tport = 0;
+  for (i = 1; c_isdigit (*s); s++)
+    {
+      if (i > 5)
+        {
+          xfree (respline);
+          return FTPINVPASV;
+        }
+      tport = (*s - '0') + 10 * tport;
+    }
+
+  /* Make sure that the response terminates correcty */
+  if (*s++ != delim)
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  if (*s != ')')
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  *port = tport;
+
   xfree (respline);
   return FTPOK;
 }
+#endif
 
 /* Sends the TYPE request to the server.  */
 uerr_t
-ftp_type (struct rbuf *rbuf, int type)
+ftp_type (int csock, int type)
 {
   char *request, *respline;
   int nwritten;
@@ -544,7 +833,7 @@ ftp_type (struct rbuf *rbuf, int type)
   stype[1] = 0;
   /* Send TYPE request.  */
   request = ftp_request ("TYPE", stype);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -552,12 +841,9 @@ ftp_type (struct rbuf *rbuf, int type)
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   if (*respline != '2')
     {
       xfree (respline);
@@ -571,7 +857,7 @@ ftp_type (struct rbuf *rbuf, int type)
 /* Changes the working directory by issuing a CWD command to the
    server.  */
 uerr_t
-ftp_cwd (struct rbuf *rbuf, const char *dir)
+ftp_cwd (int csock, const char *dir)
 {
   char *request, *respline;
   int nwritten;
@@ -579,7 +865,7 @@ ftp_cwd (struct rbuf *rbuf, const char *dir)
 
   /* Send CWD request.  */
   request = ftp_request ("CWD", dir);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -587,12 +873,9 @@ ftp_cwd (struct rbuf *rbuf, const char *dir)
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   if (*respline == '5')
     {
       xfree (respline);
@@ -610,16 +893,14 @@ ftp_cwd (struct rbuf *rbuf, const char *dir)
 
 /* Sends REST command to the FTP server.  */
 uerr_t
-ftp_rest (struct rbuf *rbuf, long offset)
+ftp_rest (int csock, wgint offset)
 {
   char *request, *respline;
   int nwritten;
   uerr_t err;
-  static char numbuf[24]; /* Buffer for the number */
 
-  number_to_string (numbuf, offset);
-  request = ftp_request ("REST", numbuf);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  request = ftp_request ("REST", number_to_static_string (offset));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -627,12 +908,9 @@ ftp_rest (struct rbuf *rbuf, long offset)
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   if (*respline != '3')
     {
       xfree (respline);
@@ -645,7 +923,7 @@ ftp_rest (struct rbuf *rbuf, long offset)
 
 /* Sends RETR command to the FTP server.  */
 uerr_t
-ftp_retr (struct rbuf *rbuf, const char *file)
+ftp_retr (int csock, const char *file)
 {
   char *request, *respline;
   int nwritten;
@@ -653,7 +931,7 @@ ftp_retr (struct rbuf *rbuf, const char *file)
 
   /* Send RETR request.  */
   request = ftp_request ("RETR", file);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -661,12 +939,9 @@ ftp_retr (struct rbuf *rbuf, const char *file)
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   if (*respline == '5')
     {
       xfree (respline);
@@ -685,46 +960,79 @@ ftp_retr (struct rbuf *rbuf, const char *file)
 /* Sends the LIST command to the server.  If FILE is NULL, send just
    `LIST' (no space).  */
 uerr_t
-ftp_list (struct rbuf *rbuf, const char *file)
+ftp_list (int csock, const char *file, bool avoid_list_a, bool avoid_list,
+          bool *list_a_used)
 {
   char *request, *respline;
   int nwritten;
   uerr_t err;
+  bool ok = false;
+  size_t i = 0;
 
-  /* Send LIST request.  */
-  request = ftp_request ("LIST", file);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
-  if (nwritten < 0)
-    {
-      xfree (request);
-      return WRITEFAILED;
-    }
-  xfree (request);
-  /* Get appropriate respone.  */
-  err = ftp_response (rbuf, &respline);
-  if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
-  if (*respline == '5')
-    {
-      xfree (respline);
-      return FTPNSFOD;
-    }
-  if (*respline != '1')
+  *list_a_used = false;
+
+  /* 2013-10-12 Andrea Urbani (matfanjol)
+     For more information about LIST and "LIST -a" please look at ftp.c,
+     function getftp, text "__LIST_A_EXPLANATION__".
+
+     If somebody changes the following commands, please, checks also the
+     later "i" variable.  */
+  const char *list_commands[] = { "LIST -a",
+                                  "LIST" };
+
+  if (avoid_list_a)
     {
-      xfree (respline);
-      return FTPRERR;
+      i = countof (list_commands)- 1;
+      DEBUGP (("(skipping \"LIST -a\")"));
     }
-  xfree (respline);
-  /* All OK.  */
-  return FTPOK;
+
+
+  do {
+    /* Send request.  */
+    request = ftp_request (list_commands[i], file);
+    nwritten = fd_write (csock, request, strlen (request), -1);
+    if (nwritten < 0)
+      {
+        xfree (request);
+        return WRITEFAILED;
+      }
+    xfree (request);
+    /* Get appropriate response.  */
+    err = ftp_response (csock, &respline);
+    if (err == FTPOK)
+      {
+        if (*respline == '5')
+          {
+            err = FTPNSFOD;
+          }
+        else if (*respline == '1')
+          {
+            err = FTPOK;
+            ok = true;
+            /* Which list command was used? */
+            *list_a_used = (i == 0);
+          }
+        else
+          {
+            err = FTPRERR;
+          }
+        xfree (respline);
+      }
+    ++i;
+    if ((avoid_list) && (i == 1))
+      {
+        /* I skip LIST */
+        ++i;
+        DEBUGP (("(skipping \"LIST\")"));
+      }
+  } while (i < countof (list_commands) && !ok);
+
+  return err;
 }
 
 /* Sends the SYST command to the server. */
 uerr_t
-ftp_syst (struct rbuf *rbuf, enum stype *server_type)
+ftp_syst (int csock, enum stype *server_type, enum ustype *unix_type)
 {
   char *request, *respline;
   int nwritten;
@@ -732,7 +1040,7 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type)
 
   /* Send SYST request.  */
   request = ftp_request ("SYST", NULL);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -741,12 +1049,9 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type)
   xfree (request);
 
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   if (*respline == '5')
     {
       xfree (respline);
@@ -755,16 +1060,30 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type)
 
   /* Skip the number (215, but 200 (!!!) in case of VMS) */
   strtok (respline, " ");
-  
+
   /* Which system type has been reported (we are interested just in the
      first word of the server response)?  */
   request = strtok (NULL, " ");
 
-  if (!strcasecmp (request, "VMS"))
+  *unix_type = UST_OTHER;
+
+  if (request == NULL)
+    *server_type = ST_OTHER;
+  else if (!strcasecmp (request, "VMS"))
     *server_type = ST_VMS;
   else if (!strcasecmp (request, "UNIX"))
-    *server_type = ST_UNIX;
-  else if (!strcasecmp (request, "WINDOWS_NT"))
+    {
+      *server_type = ST_UNIX;
+      /* 2013-10-17 Andrea Urbani (matfanjol)
+         I check more in depth the system type */
+      if (!strncasecmp (ftp_last_respline, "215 UNIX Type: L8", 17))
+        *unix_type = UST_TYPE_L8;
+      else if (!strncasecmp (ftp_last_respline,
+                             "215 UNIX MultiNet Unix Emulation V5.3(93)", 41))
+        *unix_type = UST_MULTINET;
+    }
+  else if (!strcasecmp (request, "WINDOWS_NT")
+           || !strcasecmp (request, "WINDOWS2000"))
     *server_type = ST_WINNT;
   else if (!strcasecmp (request, "MACOS"))
     *server_type = ST_MACOS;
@@ -780,7 +1099,7 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type)
 
 /* Sends the PWD command to the server. */
 uerr_t
-ftp_pwd (struct rbuf *rbuf, char **pwd)
+ftp_pwd (int csock, char **pwd)
 {
   char *request, *respline;
   int nwritten;
@@ -788,7 +1107,7 @@ ftp_pwd (struct rbuf *rbuf, char **pwd)
 
   /* Send PWD request.  */
   request = ftp_request ("PWD", NULL);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -796,14 +1115,12 @@ ftp_pwd (struct rbuf *rbuf, char **pwd)
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
-    {
-      xfree (respline);
-      return err;
-    }
+    return err;
   if (*respline == '5')
     {
+    err:
       xfree (respline);
       return FTPSRVERR;
     }
@@ -812,9 +1129,13 @@ ftp_pwd (struct rbuf *rbuf, char **pwd)
      and everything following it. */
   strtok (respline, "\"");
   request = strtok (NULL, "\"");
-  
+  if (!request)
+    /* Treat the malformed response as an error, which the caller has
+       to handle gracefully anyway.  */
+    goto err;
+
   /* Has the `pwd' been already allocated?  Free! */
-  FREE_MAYBE (*pwd);
+  xfree_null (*pwd);
 
   *pwd = xstrdup (request);
 
@@ -826,7 +1147,7 @@ ftp_pwd (struct rbuf *rbuf, char **pwd)
 /* Sends the SIZE command to the server, and returns the value in 'size'.
  * If an error occurs, size is set to zero. */
 uerr_t
-ftp_size (struct rbuf *rbuf, const char *file, long int *size)
+ftp_size (int csock, const char *file, wgint *size)
 {
   char *request, *respline;
   int nwritten;
@@ -834,7 +1155,7 @@ ftp_size (struct rbuf *rbuf, const char *file, long int *size)
 
   /* Send PWD request.  */
   request = ftp_request ("SIZE", file);
-  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  nwritten = fd_write (csock, request, strlen (request), -1);
   if (nwritten < 0)
     {
       xfree (request);
@@ -843,18 +1164,17 @@ ftp_size (struct rbuf *rbuf, const char *file, long int *size)
     }
   xfree (request);
   /* Get appropriate response.  */
-  err = ftp_response (rbuf, &respline);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
     {
-      xfree (respline);
       *size = 0;
       return err;
     }
   if (*respline == '5')
     {
-      /* 
+      /*
        * Probably means SIZE isn't supported on this server.
-       * Error is nonfatal since SIZE isn't in RFC 959 
+       * Error is nonfatal since SIZE isn't in RFC 959
        */
       xfree (respline);
       *size = 0;
@@ -862,10 +1182,10 @@ ftp_size (struct rbuf *rbuf, const char *file, long int *size)
     }
 
   errno = 0;
-  *size = strtol (respline + 4, NULL, 0);
-  if (errno) 
+  *size = str_to_wgint (respline + 4, NULL, 10);
+  if (errno)
     {
-      /* 
+      /*
        * Couldn't parse the response for some reason.  On the (few)
        * tests I've done, the response is 213 <SIZE> with nothing else -
        * maybe something a bit more resilient is necessary.  It's not a
@@ -889,7 +1209,7 @@ ftp_process_type (const char *params)
   if (params
       && 0 == strncasecmp (params, "type=", 5)
       && params[5] != '\0')
-    return TOUPPER (params[5]);
+    return c_toupper (params[5]);
   else
     return 'I';
 }