]> sjero.net Git - wget/blobdiff - src/ftp-basic.c
[svn] Commit IPv6 support by Thomas Lussnig.
[wget] / src / ftp-basic.c
index 8d433a69fe0d79fdfc5bfdc22a3920ce2f836fa0..b3b14a7aa2c9f5d574d85f3c8d6473b58f513bdd 100644 (file)
@@ -1,37 +1,42 @@
 /* Basic FTP routines.
    Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
 
-This file is part of Wget.
+This file is part of GNU Wget.
 
-This program is free software; you can redistribute it and/or modify
+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.
 
-This program is distributed in the hope that it will be useful,
+GNU Wget is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 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 this program; if not, write to the Free Software
+along with Wget; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 #include <config.h>
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <errno.h>
+
 #ifdef HAVE_STRING_H
 # include <string.h>
 #else
 # include <strings.h>
 #endif
-#include <ctype.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #include <sys/types.h>
 
+/* For inet_ntop. */
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
 #ifdef WINDOWS
 # include <winsock.h>
 #endif
@@ -41,13 +46,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "rbuf.h"
 #include "connect.h"
 #include "host.h"
-
-#ifndef errno
-extern int errno;
-#endif
-#ifndef h_errno
-extern int h_errno;
-#endif
+#include "ftp.h"
 
 char ftp_last_respline[128];
 
@@ -66,33 +65,33 @@ ftp_response (struct rbuf *rbuf, char **line)
   do
     {
       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;
-       }
+        {
+          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;
+        }
       if (opt.server_response)
-       logprintf (LOG_ALWAYS, "%s\n", *line);
+        logprintf (LOG_ALWAYS, "%s\n", *line);
       else
-       DEBUGP (("%s\n", *line));
+        DEBUGP (("%s\n", *line));
     }
   while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) &&
-          ISDIGIT ((*line)[2]) && (*line)[3] == ' '));
+           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;
@@ -105,16 +104,16 @@ static char *
 ftp_request (const char *command, const char *value)
 {
   char *res = (char *)xmalloc (strlen (command)
-                              + (value ? (1 + strlen (value)) : 0)
-                              + 2 + 1);
+                               + (value ? (1 + strlen (value)) : 0)
+                               + 2 + 1);
   sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : "");
   if (opt.server_response)
     {
       /* Hack: don't print out password.  */
       if (strncmp (res, "PASS", 4) != 0)
-       logprintf (LOG_ALWAYS, "--> %s\n", res);
+        logprintf (LOG_ALWAYS, "--> %s\n", res);
       else
-       logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
+        logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
     }
   else
     DEBUGP (("\n--> %s\n", res));
@@ -138,41 +137,41 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline != '2')
     {
-      free (respline);
+      xfree (respline);
       return FTPSRVERR;
     }
-  free (respline);
+  xfree (respline);
   /* Send USER username.  */
   request = ftp_request ("USER", acc);
   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get appropriate response.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   /* An unprobable possibility of logging without a password.  */
   if (*respline == '2')
     {
-      free (respline);
+      xfree (respline);
       return FTPOK;
     }
   /* Else, only response 3 is appropriate.  */
   if (*respline != '3')
     {
-      free (respline);
+      xfree (respline);
       return FTPLOGREFUSED;
     }
 #ifdef USE_OPIE
@@ -185,61 +184,117 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
 
     for (i = 0; i < ARRAY_SIZE (skey_head); i++)
       {
-       if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
-         break;
+        if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
+          break;
       }
     if (i < ARRAY_SIZE (skey_head))
       {
-       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++;
-       else
-         {
-         bad:
-           free (respline);
-           return FTPLOGREFUSED;
-         }
-       if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
-         goto bad;
-       pass = cp;
+        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++;
+        else
+          {
+          bad:
+            xfree (respline);
+            return FTPLOGREFUSED;
+          }
+        if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
+          goto bad;
+        pass = cp;
       }
   }
 #endif /* USE_OPIE */
-  free (respline);
+  xfree (respline);
   /* Send PASS password.  */
   request = ftp_request ("PASS", pass);
   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get appropriate response.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline != '2')
     {
-      free (respline);
+      xfree (respline);
       return FTPLOGINC;
     }
-  free (respline);
+  xfree (respline);
   /* All OK.  */
   return FTPOK;
 }
 
+#ifdef INET6
+uerr_t
+ftp_eprt (struct rbuf *rbuf)
+{
+  uerr_t err;
+
+  char *request, *respline;
+  ip_address in_addr;
+  unsigned short port;
+
+  char ipv6 [8 * (4 * 3 + 3) + 8];
+  char *bytes;
+
+  /* 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));
+
+  /* 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)))
+    {
+      closeport (port);
+      xfree (request);
+      return WRITEFAILED;
+    }
+  xfree (request);
+  /* Get appropriate response.  */
+  err = ftp_response (rbuf, &respline);
+  if (err != FTPOK)
+    {
+      closeport (port);
+      xfree (respline);
+      return err;
+    }
+  if (*respline != '2')
+    {
+      closeport (port);
+      xfree (respline);
+      return FTPPORTERR;
+    }
+  xfree (respline);
+  return FTPOK;
+}
+#endif
+
 /* Bind a port and send the appropriate PORT command to the FTP
    server.  Use acceptport after RETR, to get the socket of data
    connection.  */
@@ -247,102 +302,218 @@ uerr_t
 ftp_port (struct rbuf *rbuf)
 {
   uerr_t err;
-  char *request, *respline, *bytes;
-  unsigned char *in_addr;
+  char *request, *respline;
+  char bytes[6 * 4 +1];
+
+  ip_address in_addr;
+  ip4_address in_addr_4;
+  unsigned char *in_addr4_ptr = (unsigned char *)&in_addr_4;
+
   int nwritten;
   unsigned short port;
-
+#ifdef INET6
+  /*
+    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;
-  /* Bind the port.  */
-  err = bindport (&port);
+
+  err = bindport (&port, AF_INET);
   if (err != BINDOK)
     return err;
-  /* Get the address of this side of the connection.  */
-  if (!(in_addr = conaddr (RBUF_FD (rbuf))))
-    return HOSTERR;
-  /* Construct the argument of PORT (of the form a,b,c,d,e,f).  */
-  bytes = (char *)alloca (6 * 4 + 1);
-  sprintf (bytes, "%d,%d,%d,%d,%d,%d", in_addr[0], in_addr[1],
-         in_addr[2], in_addr[3], (unsigned) (port & 0xff00) >> 8,
-         port & 0xff);
+
+  /* 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));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get appropriate response.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline != '2')
     {
-      free (respline);
+      xfree (respline);
       return FTPPORTERR;
     }
-  free (respline);
+  xfree (respline);
   return FTPOK;
 }
 
+#ifdef INET6
+uerr_t
+ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port, 
+         char *typ)
+{
+  int err;
+  char *s, *respline;
+  char *request = ftp_request ("EPSV", typ);
+  if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
+    {
+      xfree (request);
+      return WRITEFAILED;
+    }
+  /* Get the server response.  */
+  err = ftp_response (rbuf, &respline);
+  if (err != FTPOK)
+    {
+      xfree (respline);
+      return err;
+    }
+  if (*respline != '2')
+    {
+      xfree (respline);
+      return FTPNOPASV;
+    }
+  /* 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, unsigned char *addr)
+ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
 {
   char *request, *respline, *s;
   int nwritten, i;
   uerr_t err;
+  unsigned char addr4[4];
 
+#ifdef INET6
+  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));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get the server response.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline != '2')
     {
-      free (respline);
+      xfree (respline);
       return FTPNOPASV;
     }
   /* Parse the request.  */
+  /* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).         */
   s = respline;
   for (s += 4; *s && !ISDIGIT (*s); s++);
   if (!*s)
     return FTPINVPASV;
-  for (i = 0; i < 6; i++)
+  for (i = 0; i < 4; i++)
     {
-      addr[i] = 0;
+      addr4[i] = 0;
       for (; ISDIGIT (*s); s++)
-       addr[i] = (*s - '0') + 10 * addr[i];
+        addr4[i] = (*s - '0') + 10 * addr4[i];
       if (*s == ',')
-       s++;
-      else if (i < 5)
-       {
-         /* When on the last number, anything can be a terminator.  */
-         free (respline);
-         return FTPINVPASV;
-       }
+        s++;
+      else
+        {
+          xfree (respline);
+          return FTPINVPASV;
+        }
     }
-  free (respline);
+
+  /* Eventually make an IPv4 in IPv6 adress if needed */
+  map_ipv4_to_ip ((ip4_address *)addr4, addr);
+
+  *port=0;
+  for (; ISDIGIT (*s); s++)
+    *port = (*s - '0') + 10 * (*port);
+  if (*s == ',') 
+    s++;
+  else
+    {
+      xfree (respline);
+      return FTPINVPASV;
+    }
+
+  {
+    unsigned short port2 = 0; 
+    for (; ISDIGIT (*s); s++) 
+      port2 = (*s - '0') + 10 * port2;
+    *port = (*port) * 256 + port2;
+  }
+  xfree (respline);
   return FTPOK;
 }
 
@@ -363,23 +534,23 @@ ftp_type (struct rbuf *rbuf, int type)
   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get appropriate response.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline != '2')
     {
-      free (respline);
+      xfree (respline);
       return FTPUNKNOWNTYPE;
     }
-  free (respline);
+  xfree (respline);
   /* All OK.  */
   return FTPOK;
 }
@@ -398,28 +569,28 @@ ftp_cwd (struct rbuf *rbuf, const char *dir)
   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get appropriate response.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline == '5')
     {
-      free (respline);
+      xfree (respline);
       return FTPNSFOD;
     }
   if (*respline != '2')
     {
-      free (respline);
+      xfree (respline);
       return FTPRERR;
     }
-  free (respline);
+  xfree (respline);
   /* All OK.  */
   return FTPOK;
 }
@@ -431,30 +602,30 @@ ftp_rest (struct rbuf *rbuf, long offset)
   char *request, *respline;
   int nwritten;
   uerr_t err;
-  static char numbuf[20]; /* Buffer for the number */
+  static char numbuf[24]; /* Buffer for the number */
 
-  long_to_string (numbuf, offset);
+  number_to_string (numbuf, offset);
   request = ftp_request ("REST", numbuf);
   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get appropriate response.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline != '3')
     {
-      free (respline);
+      xfree (respline);
       return FTPRESTFAIL;
     }
-  free (respline);
+  xfree (respline);
   /* All OK.  */
   return FTPOK;
 }
@@ -472,28 +643,28 @@ ftp_retr (struct rbuf *rbuf, const char *file)
   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get appropriate response.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline == '5')
     {
-      free (respline);
+      xfree (respline);
       return FTPNSFOD;
     }
   if (*respline != '1')
     {
-      free (respline);
+      xfree (respline);
       return FTPRERR;
     }
-  free (respline);
+  xfree (respline);
   /* All OK.  */
   return FTPOK;
 }
@@ -512,28 +683,198 @@ ftp_list (struct rbuf *rbuf, const char *file)
   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
   if (nwritten < 0)
     {
-      free (request);
+      xfree (request);
       return WRITEFAILED;
     }
-  free (request);
+  xfree (request);
   /* Get appropriate respone.  */
   err = ftp_response (rbuf, &respline);
   if (err != FTPOK)
     {
-      free (respline);
+      xfree (respline);
       return err;
     }
   if (*respline == '5')
     {
-      free (respline);
+      xfree (respline);
       return FTPNSFOD;
     }
   if (*respline != '1')
     {
-      free (respline);
+      xfree (respline);
       return FTPRERR;
     }
-  free (respline);
+  xfree (respline);
   /* All OK.  */
   return FTPOK;
 }
+
+/* Sends the SYST command to the server. */
+uerr_t
+ftp_syst (struct rbuf *rbuf, enum stype *server_type)
+{
+  char *request, *respline;
+  int nwritten;
+  uerr_t err;
+
+  /* Send SYST request.  */
+  request = ftp_request ("SYST", NULL);
+  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  if (nwritten < 0)
+    {
+      xfree (request);
+      return WRITEFAILED;
+    }
+  xfree (request);
+
+  /* Get appropriate response.  */
+  err = ftp_response (rbuf, &respline);
+  if (err != FTPOK)
+    {
+      xfree (respline);
+      return err;
+    }
+  if (*respline == '5')
+    {
+      xfree (respline);
+      return FTPSRVERR;
+    }
+
+  /* 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"))
+    *server_type = ST_VMS;
+  else if (!strcasecmp (request, "UNIX"))
+    *server_type = ST_UNIX;
+  else if (!strcasecmp (request, "WINDOWS_NT"))
+    *server_type = ST_WINNT;
+  else if (!strcasecmp (request, "MACOS"))
+    *server_type = ST_MACOS;
+  else
+    *server_type = ST_OTHER;
+
+  xfree (respline);
+  /* All OK.  */
+  return FTPOK;
+}
+
+/* Sends the PWD command to the server. */
+uerr_t
+ftp_pwd (struct rbuf *rbuf, char **pwd)
+{
+  char *request, *respline;
+  int nwritten;
+  uerr_t err;
+
+  /* Send PWD request.  */
+  request = ftp_request ("PWD", NULL);
+  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  if (nwritten < 0)
+    {
+      xfree (request);
+      return WRITEFAILED;
+    }
+  xfree (request);
+  /* Get appropriate response.  */
+  err = ftp_response (rbuf, &respline);
+  if (err != FTPOK)
+    {
+      xfree (respline);
+      return err;
+    }
+  if (*respline == '5')
+    {
+      xfree (respline);
+      return FTPSRVERR;
+    }
+
+  /* Skip the number (257), leading citation mark, trailing citation mark
+     and everything following it. */
+  strtok (respline, "\"");
+  request = strtok (NULL, "\"");
+  
+  /* Has the `pwd' been already allocated?  Free! */
+  FREE_MAYBE (*pwd);
+
+  *pwd = xstrdup (request);
+
+  xfree (respline);
+  /* All OK.  */
+  return FTPOK;
+}
+
+/* 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)
+{
+  char *request, *respline;
+  int nwritten;
+  uerr_t err;
+
+  /* Send PWD request.  */
+  request = ftp_request ("SIZE", file);
+  nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+  if (nwritten < 0)
+    {
+      xfree (request);
+      *size = 0;
+      return WRITEFAILED;
+    }
+  xfree (request);
+  /* Get appropriate response.  */
+  err = ftp_response (rbuf, &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 
+       */
+      xfree (respline);
+      *size = 0;
+      return FTPOK;
+    }
+
+  errno = 0;
+  *size = strtol (respline + 4, NULL, 0);
+  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
+       * fatal error, however.
+       */
+      xfree (respline);
+      *size = 0;
+      return FTPOK;
+    }
+
+  xfree (respline);
+  /* All OK.  */
+  return FTPOK;
+}
+
+/* If URL's params are of the form "type=X", return character X.
+   Otherwise, return 'I' (the default type).  */
+char
+ftp_process_type (const char *params)
+{
+  if (params
+      && 0 == strncasecmp (params, "type=", 5)
+      && params[5] != '\0')
+    return TOUPPER (params[5]);
+  else
+    return 'I';
+}