]> sjero.net Git - wget/blobdiff - src/ftp.c
[svn] Remove the "rbuf" buffering layer. Provide peeking primitives instead.
[wget] / src / ftp.c
index c63ef827c4917a7c7f7defcd0f38ec0193840e9d..fdac6d49846d6d308f04eb6e109fca311db45786 100644 (file)
--- a/src/ftp.c
+++ b/src/ftp.c
@@ -16,7 +16,17 @@ 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
 
 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.  */
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+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.  */
 
 #include <config.h>
 
 
 #include <config.h>
 
@@ -37,18 +47,20 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "wget.h"
 #include "utils.h"
 #include "url.h"
 #include "wget.h"
 #include "utils.h"
 #include "url.h"
-#include "rbuf.h"
 #include "retr.h"
 #include "ftp.h"
 #include "connect.h"
 #include "host.h"
 #include "retr.h"
 #include "ftp.h"
 #include "connect.h"
 #include "host.h"
-#include "fnmatch.h"
 #include "netrc.h"
 #include "netrc.h"
+#include "convert.h"           /* for downloaded_file */
+#include "recur.h"             /* for INFINITE_RECURSION */
 
 #ifndef errno
 extern int errno;
 #endif
 
 
 #ifndef errno
 extern int errno;
 #endif
 
+extern LARGE_INT total_downloaded_bytes;
+
 /* File where the "ls -al" listing will be saved.  */
 #define LIST_FILENAME ".listing"
 
 /* File where the "ls -al" listing will be saved.  */
 #define LIST_FILENAME ".listing"
 
@@ -58,8 +70,8 @@ typedef struct
 {
   int st;                      /* connection status */
   int cmd;                     /* command code */
 {
   int st;                      /* connection status */
   int cmd;                     /* command code */
-  struct rbuf rbuf;            /* control connection buffer */
-  long dltime;                 /* time of the download */
+  int csock;                   /* control connection socket */
+  double dltime;               /* time of the download in msecs */
   enum stype rs;               /* remote system reported by ftp server */ 
   char *id;                    /* initial directory */
   char *target;                        /* target file name */
   enum stype rs;               /* remote system reported by ftp server */ 
   char *id;                    /* initial directory */
   char *target;                        /* target file name */
@@ -109,13 +121,119 @@ ftp_expected_bytes (const char *s)
   return res;
 }
 
   return res;
 }
 
+#ifdef ENABLE_IPV6
+/* 
+ * This function sets up a passive data connection with the FTP server.
+ * It is merely a wrapper around ftp_epsv, ftp_lpsv and ftp_pasv.
+ */
+static uerr_t
+ftp_do_pasv (int csock, ip_address *addr, int *port)
+{
+  uerr_t err;
+
+  /* We need to determine the address family and need to call
+     getpeername, so while we're at it, store the address to ADDR.
+     ftp_pasv and ftp_lpsv can simply override it.  */
+  if (!socket_ip_address (csock, addr, ENDPOINT_PEER))
+    abort ();
+
+  /* If our control connection is over IPv6, then we first try EPSV and then 
+   * LPSV if the former is not supported. If the control connection is over 
+   * IPv4, we simply issue the good old PASV request. */
+  switch (addr->type)
+    {
+    case IPV4_ADDRESS:
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> PASV ... ");
+      err = ftp_pasv (csock, addr, port);
+      break;
+    case IPV6_ADDRESS:
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> EPSV ... ");
+      err = ftp_epsv (csock, addr, port);
+
+      /* If EPSV is not supported try LPSV */
+      if (err == FTPNOPASV)
+        {
+          if (!opt.server_response)
+            logputs (LOG_VERBOSE, "==> LPSV ... ");
+          err = ftp_lpsv (csock, addr, port);
+        }
+      break;
+    default:
+      abort ();
+    }
+
+  return err;
+}
+
+/* 
+ * This function sets up an active data connection with the FTP server.
+ * It is merely a wrapper around ftp_eprt, ftp_lprt and ftp_port.
+ */
+static uerr_t
+ftp_do_port (int csock, int *local_sock)
+{
+  uerr_t err;
+  ip_address cip;
+
+  if (!socket_ip_address (csock, &cip, ENDPOINT_PEER))
+    abort ();
+
+  /* If our control connection is over IPv6, then we first try EPRT and then 
+   * LPRT if the former is not supported. If the control connection is over 
+   * IPv4, we simply issue the good old PORT request. */
+  switch (cip.type)
+    {
+    case IPV4_ADDRESS:
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> PORT ... ");
+      err = ftp_port (csock, local_sock);
+      break;
+    case IPV6_ADDRESS:
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> EPRT ... ");
+      err = ftp_eprt (csock, local_sock);
+
+      /* If EPRT is not supported try LPRT */
+      if (err == FTPPORTERR)
+        {
+          if (!opt.server_response)
+            logputs (LOG_VERBOSE, "==> LPRT ... ");
+          err = ftp_lprt (csock, local_sock);
+        }
+      break;
+    default:
+      abort ();
+    }
+  return err;
+}
+#else
+
+static uerr_t
+ftp_do_pasv (int csock, ip_address *addr, int *port)
+{
+  if (!opt.server_response)
+    logputs (LOG_VERBOSE, "==> PASV ... ");
+  return ftp_pasv (csock, addr, port);
+}
+
+static uerr_t
+ftp_do_port (int csock, int *local_sock)
+{
+  if (!opt.server_response)
+    logputs (LOG_VERBOSE, "==> PORT ... ");
+  return ftp_port (csock, local_sock);
+}
+#endif
+
 /* Retrieves a file with denoted parameters through opening an FTP
    connection to the server.  It always closes the data connection,
    and closes the control connection in case of error.  */
 static uerr_t
 getftp (struct url *u, long *len, long restval, ccon *con)
 {
 /* Retrieves a file with denoted parameters through opening an FTP
    connection to the server.  It always closes the data connection,
    and closes the control connection in case of error.  */
 static uerr_t
 getftp (struct url *u, long *len, long restval, ccon *con)
 {
-  int csock, dtsock, res;
+  int csock, dtsock, local_sock, res;
   uerr_t err;
   FILE *fp;
   char *user, *passwd, *respline;
   uerr_t err;
   FILE *fp;
   char *user, *passwd, *respline;
@@ -142,15 +260,14 @@ getftp (struct url *u, long *len, long restval, ccon *con)
   assert (user && passwd);
 
   dtsock = -1;
   assert (user && passwd);
 
   dtsock = -1;
+  local_sock = -1;
   con->dltime = 0;
 
   if (!(cmd & DO_LOGIN))
   con->dltime = 0;
 
   if (!(cmd & DO_LOGIN))
-    csock = RBUF_FD (&con->rbuf);
+    csock = con->csock;
   else                         /* cmd & DO_LOGIN */
     {
       char type_char;
   else                         /* cmd & DO_LOGIN */
     {
       char type_char;
-      struct address_list *al;
-
       char    *host = con->proxy ? con->proxy->host : u->host;
       int      port = con->proxy ? con->proxy->port : u->port;
       char *logname = user;
       char    *host = con->proxy ? con->proxy->host : u->host;
       int      port = con->proxy ? con->proxy->port : u->port;
       char *logname = user;
@@ -166,31 +283,18 @@ getftp (struct url *u, long *len, long restval, ccon *con)
 
       /* First: Establish the control connection.  */
 
 
       /* First: Establish the control connection.  */
 
-      al = lookup_host (host, 0);
-      if (!al)
+      csock = connect_to_host (host, port);
+      if (csock == E_HOST)
        return HOSTERR;
        return HOSTERR;
-      set_connection_host_name (host);
-      csock = connect_to_many (al, port, 0);
-      set_connection_host_name (NULL);
-      address_list_release (al);
-
-      if (csock < 0)
-       return errno == ECONNREFUSED ? CONREFUSED : CONERROR;
-
-      if (cmd & LEAVE_PENDING)
-       rbuf_initialize (&con->rbuf, csock);
-      else
-       rbuf_uninitialize (&con->rbuf);
-
-      /* Since this is a new connection, we may safely discard
-        anything left in the buffer.  */
-      rbuf_discard (&con->rbuf);
+      else if (csock < 0)
+       return (retryable_socket_connect_error (errno)
+               ? CONERROR : CONIMPOSSIBLE);
 
       /* Second: Login with proper USER/PASS sequence.  */
       logprintf (LOG_VERBOSE, _("Logging in as %s ... "), user);
       if (opt.server_response)
        logputs (LOG_ALWAYS, "\n");
 
       /* Second: Login with proper USER/PASS sequence.  */
       logprintf (LOG_VERBOSE, _("Logging in as %s ... "), user);
       if (opt.server_response)
        logputs (LOG_ALWAYS, "\n");
-      err = ftp_login (&con->rbuf, logname, passwd);
+      err = ftp_login (csock, logname, passwd);
 
       if (con->proxy)
        xfree (logname);
 
       if (con->proxy)
        xfree (logname);
@@ -202,37 +306,32 @@ getftp (struct url *u, long *len, long restval, ccon *con)
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
          break;
        case FTPSRVERR:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("Error in server greeting.\n"));
          return err;
          break;
        case FTPSRVERR:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("Error in server greeting.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
          break;
        case FTPLOGREFUSED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("The server refuses login.\n"));
          return err;
          break;
        case FTPLOGREFUSED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("The server refuses login.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return FTPLOGREFUSED;
          break;
        case FTPLOGINC:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("Login incorrect.\n"));
          return FTPLOGREFUSED;
          break;
        case FTPLOGINC:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("Login incorrect.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return FTPLOGINC;
          break;
        case FTPOK:
          return FTPLOGINC;
          break;
        case FTPOK:
@@ -247,7 +346,7 @@ Error in server response, closing control connection.\n"));
       /* Third: Get the system type */
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> SYST ... ");
       /* Third: Get the system type */
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> SYST ... ");
-      err = ftp_syst (&con->rbuf, &con->rs);
+      err = ftp_syst (csock, &con->rs);
       /* FTPRERR */
       switch (err)
        {
       /* FTPRERR */
       switch (err)
        {
@@ -255,8 +354,7 @@ Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
          break;
        case FTPSRVERR:
          return err;
          break;
        case FTPSRVERR:
@@ -278,7 +376,7 @@ Error in server response, closing control connection.\n"));
 
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> PWD ... ");
 
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> PWD ... ");
-      err = ftp_pwd(&con->rbuf, &con->id);
+      err = ftp_pwd(csock, &con->id);
       /* FTPRERR */
       switch (err)
        {
       /* FTPRERR */
       switch (err)
        {
@@ -286,13 +384,12 @@ Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
          break;
        case FTPSRVERR :
          /* PWD unsupported -- assume "/". */
          return err;
          break;
        case FTPSRVERR :
          /* PWD unsupported -- assume "/". */
-         FREE_MAYBE (con->id);
+         xfree_null (con->id);
          con->id = xstrdup ("/");
          break;
        case FTPOK:
          con->id = xstrdup ("/");
          break;
        case FTPOK:
@@ -332,7 +429,7 @@ Error in server response, closing control connection.\n"));
       type_char = ftp_process_type (u->params);
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> TYPE %c ... ", type_char);
       type_char = ftp_process_type (u->params);
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> TYPE %c ... ", type_char);
-      err = ftp_type (&con->rbuf, type_char);
+      err = ftp_type (csock, type_char);
       /* FTPRERR, WRITEFAILED, FTPUNKNOWNTYPE */
       switch (err)
        {
       /* FTPRERR, WRITEFAILED, FTPUNKNOWNTYPE */
       switch (err)
        {
@@ -340,16 +437,14 @@ Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
          break;
        case FTPUNKNOWNTYPE:
          return err;
          break;
        case FTPUNKNOWNTYPE:
@@ -357,8 +452,7 @@ Error in server response, closing control connection.\n"));
          logprintf (LOG_NOTQUIET,
                     _("Unknown type `%c', closing control connection.\n"),
                     type_char);
          logprintf (LOG_NOTQUIET,
                     _("Unknown type `%c', closing control connection.\n"),
                     type_char);
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
        case FTPOK:
          /* Everything is OK.  */
          return err;
        case FTPOK:
          /* Everything is OK.  */
@@ -387,11 +481,19 @@ Error in server response, closing control connection.\n"));
 
             A relative directory is one that does not begin with '/'
             and, on non-Unix OS'es, one that doesn't begin with
 
             A relative directory is one that does not begin with '/'
             and, on non-Unix OS'es, one that doesn't begin with
-            "<letter>:".  */
+            "[a-z]:".
+
+            This is not done for OS400, which doesn't use
+            "/"-delimited directories, nor does it support directory
+            hierarchies.  "CWD foo" followed by "CWD bar" leaves us
+            in "bar", not in "foo/bar", as would be customary
+            elsewhere.  */
 
          if (target[0] != '/'
              && !(con->rs != ST_UNIX
 
          if (target[0] != '/'
              && !(con->rs != ST_UNIX
-                  && ISALPHA (target[0]) && target[1] == ':'))
+                  && ISALPHA (target[0])
+                  && target[1] == ':')
+             && con->rs != ST_OS400)
            {
              int idlen = strlen (con->id);
              char *ntarget, *p;
            {
              int idlen = strlen (con->id);
              char *ntarget, *p;
@@ -439,7 +541,7 @@ Error in server response, closing control connection.\n"));
 
          if (!opt.server_response)
            logprintf (LOG_VERBOSE, "==> CWD %s ... ", target);
 
          if (!opt.server_response)
            logprintf (LOG_VERBOSE, "==> CWD %s ... ", target);
-         err = ftp_cwd (&con->rbuf, target);
+         err = ftp_cwd (csock, target);
          /* FTPRERR, WRITEFAILED, FTPNSFOD */
          switch (err)
            {
          /* FTPRERR, WRITEFAILED, FTPNSFOD */
          switch (err)
            {
@@ -447,24 +549,21 @@ Error in server response, closing control connection.\n"));
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-             CLOSE (csock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
              return err;
              break;
            case WRITEFAILED:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET,
                       _("Write failed, closing control connection.\n"));
              return err;
              break;
            case WRITEFAILED:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET,
                       _("Write failed, closing control connection.\n"));
-             CLOSE (csock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
              return err;
              break;
            case FTPNSFOD:
              logputs (LOG_VERBOSE, "\n");
              logprintf (LOG_NOTQUIET, _("No such directory `%s'.\n\n"),
                         u->dir);
              return err;
              break;
            case FTPNSFOD:
              logputs (LOG_VERBOSE, "\n");
              logprintf (LOG_NOTQUIET, _("No such directory `%s'.\n\n"),
                         u->dir);
-             CLOSE (csock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
              return err;
              break;
            case FTPOK:
              return err;
              break;
            case FTPOK:
@@ -489,7 +588,7 @@ Error in server response, closing control connection.\n"));
            logprintf (LOG_VERBOSE, "==> SIZE %s ... ", u->file);
        }
 
            logprintf (LOG_VERBOSE, "==> SIZE %s ... ", u->file);
        }
 
-      err = ftp_size(&con->rbuf, u->file, len);
+      err = ftp_size(csock, u->file, len);
       /* FTPRERR */
       switch (err)
        {
       /* FTPRERR */
       switch (err)
        {
@@ -498,8 +597,7 @@ Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
          return err;
          break;
        case FTPOK:
          return err;
          break;
        case FTPOK:
@@ -518,11 +616,9 @@ Error in server response, closing control connection.\n"));
     {
       if (opt.ftp_pasv > 0)
        {
     {
       if (opt.ftp_pasv > 0)
        {
-         ip_address     passive_addr;
-         unsigned short passive_port;
-         if (!opt.server_response)
-           logputs (LOG_VERBOSE, "==> PASV ... ");
-         err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
+         ip_address passive_addr;
+         int        passive_port;
+         err = ftp_do_pasv (csock, &passive_addr, &passive_port);
          /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
          switch (err)
            {
          /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
          switch (err)
            {
@@ -530,16 +626,14 @@ Error in server response, closing control connection.\n"));
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-             CLOSE (csock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
              return err;
              break;
            case WRITEFAILED:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET,
                       _("Write failed, closing control connection.\n"));
              return err;
              break;
            case WRITEFAILED:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET,
                       _("Write failed, closing control connection.\n"));
-             CLOSE (csock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
              return err;
              break;
            case FTPNOPASV:
              return err;
              break;
            case FTPNOPASV:
@@ -559,16 +653,19 @@ Error in server response, closing control connection.\n"));
            }   /* switch(err) */
          if (err==FTPOK)
            {
            }   /* switch(err) */
          if (err==FTPOK)
            {
-             dtsock = connect_to_one (&passive_addr, passive_port, 1);
+             DEBUGP (("trying to connect to %s port %d\n", 
+                     pretty_print_address (&passive_addr),
+                     passive_port));
+             dtsock = connect_to_ip (&passive_addr, passive_port, NULL);
              if (dtsock < 0)
                {
                  int save_errno = errno;
              if (dtsock < 0)
                {
                  int save_errno = errno;
-                 CLOSE (csock);
-                 rbuf_uninitialize (&con->rbuf);
-                 logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"),
+                 fd_close (csock);
+                 logprintf (LOG_VERBOSE, _("couldn't connect to %s port %hu: %s\n"),
                             pretty_print_address (&passive_addr), passive_port,
                             strerror (save_errno));
                             pretty_print_address (&passive_addr), passive_port,
                             strerror (save_errno));
-                 return save_errno == ECONNREFUSED ? CONREFUSED : CONERROR;
+                 return (retryable_socket_connect_error (save_errno)
+                         ? CONERROR : CONIMPOSSIBLE);
                }
 
              pasv_mode_open = 1;  /* Flag to avoid accept port */
                }
 
              pasv_mode_open = 1;  /* Flag to avoid accept port */
@@ -579,53 +676,50 @@ Error in server response, closing control connection.\n"));
 
       if (!pasv_mode_open)   /* Try to use a port command if PASV failed */
        {
 
       if (!pasv_mode_open)   /* Try to use a port command if PASV failed */
        {
-         if (!opt.server_response)
-           logputs (LOG_VERBOSE, "==> PORT ... ");
-         err = ftp_port (&con->rbuf);
-         /* FTPRERR, WRITEFAILED, bindport (CONSOCKERR, CONPORTERR, BINDERR,
-            LISTENERR), HOSTERR, FTPPORTERR */
+         err = ftp_do_port (csock, &local_sock);
+         /* FTPRERR, WRITEFAILED, bindport (FTPSYSERR), HOSTERR,
+            FTPPORTERR */
          switch (err)
            {
            case FTPRERR:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          switch (err)
            {
            case FTPRERR:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-             CLOSE (csock);
-             closeport (dtsock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
+             fd_close (dtsock);
+             fd_close (local_sock);
              return err;
              break;
            case WRITEFAILED:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET,
                       _("Write failed, closing control connection.\n"));
              return err;
              break;
            case WRITEFAILED:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET,
                       _("Write failed, closing control connection.\n"));
-             CLOSE (csock);
-             closeport (dtsock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
+             fd_close (dtsock);
+             fd_close (local_sock);
              return err;
              break;
            case CONSOCKERR:
              logputs (LOG_VERBOSE, "\n");
              logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno));
              return err;
              break;
            case CONSOCKERR:
              logputs (LOG_VERBOSE, "\n");
              logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno));
-             CLOSE (csock);
-             closeport (dtsock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
+             fd_close (dtsock);
+             fd_close (local_sock);
              return err;
              break;
              return err;
              break;
-           case CONPORTERR: case BINDERR: case LISTENERR:
-             /* What now?  These problems are local...  */
+           case FTPSYSERR:
              logputs (LOG_VERBOSE, "\n");
              logprintf (LOG_NOTQUIET, _("Bind error (%s).\n"),
                         strerror (errno));
              logputs (LOG_VERBOSE, "\n");
              logprintf (LOG_NOTQUIET, _("Bind error (%s).\n"),
                         strerror (errno));
-             closeport (dtsock);
+             fd_close (dtsock);
              return err;
              break;
            case FTPPORTERR:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("Invalid PORT.\n"));
              return err;
              break;
            case FTPPORTERR:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("Invalid PORT.\n"));
-             CLOSE (csock);
-             closeport (dtsock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
+             fd_close (dtsock);
+             fd_close (local_sock);
              return err;
              break;
            case FTPOK:
              return err;
              break;
            case FTPOK:
@@ -645,7 +739,7 @@ Error in server response, closing control connection.\n"));
     {
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> REST %ld ... ", restval);
     {
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> REST %ld ... ", restval);
-      err = ftp_rest (&con->rbuf, restval);
+      err = ftp_rest (csock, restval);
 
       /* FTPRERR, WRITEFAILED, FTPRESTFAIL */
       switch (err)
 
       /* FTPRERR, WRITEFAILED, FTPRESTFAIL */
       switch (err)
@@ -654,18 +748,18 @@ Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-         CLOSE (csock);
-         closeport (dtsock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
-         CLOSE (csock);
-         closeport (dtsock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return err;
          break;
        case FTPRESTFAIL:
          return err;
          break;
        case FTPRESTFAIL:
@@ -677,9 +771,9 @@ Error in server response, closing control connection.\n"));
              logprintf (LOG_NOTQUIET,
                         _("\nREST failed; will not truncate `%s'.\n"),
                         con->target);
              logprintf (LOG_NOTQUIET,
                         _("\nREST failed; will not truncate `%s'.\n"),
                         con->target);
-             CLOSE (csock);
-             closeport (dtsock);
-             rbuf_uninitialize (&con->rbuf);
+             fd_close (csock);
+             fd_close (dtsock);
+             fd_close (local_sock);
              return CONTNOTSUPPORTED;
            }
          logputs (LOG_VERBOSE, _("\nREST failed, starting from scratch.\n"));
              return CONTNOTSUPPORTED;
            }
          logputs (LOG_VERBOSE, _("\nREST failed, starting from scratch.\n"));
@@ -704,9 +798,9 @@ Error in server response, closing control connection.\n"));
         request.  */
       if (opt.spider)
        {
         request.  */
       if (opt.spider)
        {
-         CLOSE (csock);
-         closeport (dtsock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return RETRFINISHED;
        }
 
          return RETRFINISHED;
        }
 
@@ -719,7 +813,8 @@ Error in server response, closing control connection.\n"));
              logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
            }
        }
              logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
            }
        }
-      err = ftp_retr (&con->rbuf, u->file);
+
+      err = ftp_retr (csock, u->file);
       /* FTPRERR, WRITEFAILED, FTPNSFOD */
       switch (err)
        {
       /* FTPRERR, WRITEFAILED, FTPNSFOD */
       switch (err)
        {
@@ -727,24 +822,25 @@ Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-         CLOSE (csock);
-         closeport (dtsock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
-         CLOSE (csock);
-         closeport (dtsock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return err;
          break;
        case FTPNSFOD:
          logputs (LOG_VERBOSE, "\n");
          logprintf (LOG_NOTQUIET, _("No such file `%s'.\n\n"), u->file);
          return err;
          break;
        case FTPNSFOD:
          logputs (LOG_VERBOSE, "\n");
          logprintf (LOG_NOTQUIET, _("No such file `%s'.\n\n"), u->file);
-         closeport (dtsock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return err;
          break;
        case FTPOK:
          return err;
          break;
        case FTPOK:
@@ -767,7 +863,7 @@ Error in server response, closing control connection.\n"));
       /* As Maciej W. Rozycki (macro@ds2.pg.gda.pl) says, `LIST'
         without arguments is better than `LIST .'; confirmed by
         RFC959.  */
       /* As Maciej W. Rozycki (macro@ds2.pg.gda.pl) says, `LIST'
         without arguments is better than `LIST .'; confirmed by
         RFC959.  */
-      err = ftp_list (&con->rbuf, NULL);
+      err = ftp_list (csock, NULL);
       /* FTPRERR, WRITEFAILED */
       switch (err)
        {
       /* FTPRERR, WRITEFAILED */
       switch (err)
        {
@@ -775,25 +871,26 @@ Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
-         CLOSE (csock);
-         closeport (dtsock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
          return err;
          break;
        case WRITEFAILED:
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET,
                   _("Write failed, closing control connection.\n"));
-         CLOSE (csock);
-         closeport (dtsock);
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (csock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return err;
          break;
        case FTPNSFOD:
          logputs (LOG_VERBOSE, "\n");
          logprintf (LOG_NOTQUIET, _("No such file or directory `%s'.\n\n"),
                     ".");
          return err;
          break;
        case FTPNSFOD:
          logputs (LOG_VERBOSE, "\n");
          logprintf (LOG_NOTQUIET, _("No such file or directory `%s'.\n\n"),
                     ".");
-         closeport (dtsock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return err;
          break;
        case FTPOK:
          return err;
          break;
        case FTPOK:
@@ -808,6 +905,9 @@ Error in server response, closing control connection.\n"));
       expected_bytes = ftp_expected_bytes (ftp_last_respline);
     } /* cmd & DO_LIST */
 
       expected_bytes = ftp_expected_bytes (ftp_last_respline);
     } /* cmd & DO_LIST */
 
+  if (!(cmd & (DO_LIST | DO_RETR)) || (opt.spider && !(cmd & DO_LIST)))
+    return RETRFINISHED;
+
   /* Some FTP servers return the total length of file after REST
      command, others just return the remaining size. */
   if (*len && restval && expected_bytes
   /* Some FTP servers return the total length of file after REST
      command, others just return the remaining size. */
   if (*len && restval && expected_bytes
@@ -818,16 +918,13 @@ Error in server response, closing control connection.\n"));
     }
 
   /* If no transmission was required, then everything is OK.  */
     }
 
   /* If no transmission was required, then everything is OK.  */
-  if (!(cmd & (DO_LIST | DO_RETR)))
-    return RETRFINISHED;
-
   if (!pasv_mode_open)  /* we are not using pasive mode so we need
                              to accept */
     {
   if (!pasv_mode_open)  /* we are not using pasive mode so we need
                              to accept */
     {
-      /* Open the data transmission socket by calling acceptport().  */
-      err = acceptport (&dtsock);
-      /* Possible errors: ACCEPTERR.  */
-      if (err == ACCEPTERR)
+      /* Wait for the server to connect to the address we're waiting
+        at.  */
+      dtsock = accept_connection (local_sock);
+      if (dtsock < 0)
        {
          logprintf (LOG_NOTQUIET, "accept: %s\n", strerror (errno));
          return err;
        {
          logprintf (LOG_NOTQUIET, "accept: %s\n", strerror (errno));
          return err;
@@ -847,9 +944,9 @@ Error in server response, closing control connection.\n"));
       if (!fp)
        {
          logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno));
       if (!fp)
        {
          logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno));
-         CLOSE (csock);
-         rbuf_uninitialize (&con->rbuf);
-         closeport (dtsock);
+         fd_close (csock);
+         fd_close (dtsock);
+         fd_close (local_sock);
          return FOPENERR;
        }
     }
          return FOPENERR;
        }
     }
@@ -891,12 +988,13 @@ Error in server response, closing control connection.\n"));
     }
 
   /* Get the contents of the document.  */
     }
 
   /* Get the contents of the document.  */
-  res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf,
-                     0, &con->dltime);
+  res = fd_read_body (dtsock, fp, len, restval, expected_bytes, 0,
+                     &con->dltime);
   tms = time_str (NULL);
   tmrate = retr_rate (*len - restval, con->dltime, 0);
   /* Close data connection socket.  */
   tms = time_str (NULL);
   tmrate = retr_rate (*len - restval, con->dltime, 0);
   /* Close data connection socket.  */
-  closeport (dtsock);
+  fd_close (dtsock);
+  fd_close (local_sock);
   /* Close the local file.  */
   {
     /* Close or flush the file.  We have to be careful to check for
   /* Close the local file.  */
   {
     /* Close or flush the file.  We have to be careful to check for
@@ -910,13 +1008,13 @@ Error in server response, closing control connection.\n"));
     if (flush_res == EOF)
       res = -2;
   }
     if (flush_res == EOF)
       res = -2;
   }
+
   /* If get_contents couldn't write to fp, bail out.  */
   if (res == -2)
     {
       logprintf (LOG_NOTQUIET, _("%s: %s, closing control connection.\n"),
                 con->target, strerror (errno));
   /* If get_contents couldn't write to fp, bail out.  */
   if (res == -2)
     {
       logprintf (LOG_NOTQUIET, _("%s: %s, closing control connection.\n"),
                 con->target, strerror (errno));
-      CLOSE (csock);
-      rbuf_uninitialize (&con->rbuf);
+      fd_close (csock);
       return FWRITEERR;
     }
   else if (res == -1)
       return FWRITEERR;
     }
   else if (res == -1)
@@ -928,9 +1026,7 @@ Error in server response, closing control connection.\n"));
     }
 
   /* Get the server to tell us if everything is retrieved.  */
     }
 
   /* Get the server to tell us if everything is retrieved.  */
-  err = ftp_response (&con->rbuf, &respline);
-  /* ...and empty the buffer.  */
-  rbuf_discard (&con->rbuf);
+  err = ftp_response (csock, &respline);
   if (err != FTPOK)
     {
       xfree (respline);
   if (err != FTPOK)
     {
       xfree (respline);
@@ -943,8 +1039,7 @@ Error in server response, closing control connection.\n"));
         return FTPRETRINT, since there is a possibility that the
         whole file was retrieved nevertheless (but that is for
         ftp_loop_internal to decide).  */
         return FTPRETRINT, since there is a possibility that the
         whole file was retrieved nevertheless (but that is for
         ftp_loop_internal to decide).  */
-      CLOSE (csock);
-      rbuf_uninitialize (&con->rbuf);
+      fd_close (csock);
       return FTPRETRINT;
     } /* err != FTPOK */
   /* If retrieval failed for any reason, return FTPRETRINT, but do not
       return FTPRETRINT;
     } /* err != FTPOK */
   /* If retrieval failed for any reason, return FTPRETRINT, but do not
@@ -972,8 +1067,7 @@ Error in server response, closing control connection.\n"));
     {
       /* I should probably send 'QUIT' and check for a reply, but this
         is faster.  #### Is it OK, though?  */
     {
       /* I should probably send 'QUIT' and check for a reply, but this
         is faster.  #### Is it OK, though?  */
-      CLOSE (csock);
-      rbuf_uninitialize (&con->rbuf);
+      fd_close (csock);
     }
   /* If it was a listing, and opt.server_response is true,
      print it out.  */
     }
   /* If it was a listing, and opt.server_response is true,
      print it out.  */
@@ -1010,12 +1104,13 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
 {
   int count, orig_lp;
   long restval, len;
 {
   int count, orig_lp;
   long restval, len;
-  char *tms, *tmrate, *locf;
+  char *tms, *locf;
+  char *tmrate = NULL;
   uerr_t err;
   struct stat st;
 
   if (!con->target)
   uerr_t err;
   struct stat st;
 
   if (!con->target)
-    con->target = url_filename (u);
+    con->target = url_file_name (u);
 
   if (opt.noclobber && file_exists_p (con->target))
     {
 
   if (opt.noclobber && file_exists_p (con->target))
     {
@@ -1049,14 +1144,14 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
        {
          con->cmd = 0;
          con->cmd |= (DO_RETR | LEAVE_PENDING);
        {
          con->cmd = 0;
          con->cmd |= (DO_RETR | LEAVE_PENDING);
-         if (rbuf_initialized_p (&con->rbuf))
+         if (con->csock != -1)
            con->cmd &= ~ (DO_LOGIN | DO_CWD);
          else
            con->cmd |= (DO_LOGIN | DO_CWD);
        }
       else /* not on your own */
        {
            con->cmd &= ~ (DO_LOGIN | DO_CWD);
          else
            con->cmd |= (DO_LOGIN | DO_CWD);
        }
       else /* not on your own */
        {
-         if (rbuf_initialized_p (&con->rbuf))
+         if (con->csock != -1)
            con->cmd &= ~DO_LOGIN;
          else
            con->cmd |= DO_LOGIN;
            con->cmd &= ~DO_LOGIN;
          else
            con->cmd |= DO_LOGIN;
@@ -1105,21 +1200,20 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
        len = 0;
       err = getftp (u, &len, restval, con);
 
        len = 0;
       err = getftp (u, &len, restval, con);
 
-      if (!rbuf_initialized_p (&con->rbuf))
+      if (con->csock != -1)
        con->st &= ~DONE_CWD;
       else
        con->st |= DONE_CWD;
 
       switch (err)
        {
        con->st &= ~DONE_CWD;
       else
        con->st |= DONE_CWD;
 
       switch (err)
        {
-       case HOSTERR: case CONREFUSED: case FWRITEERR: case FOPENERR:
+       case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR:
        case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
          /* Fatal errors, give up.  */
          return err;
          break;
        case CONSOCKERR: case CONERROR: case FTPSRVERR: case FTPRERR:
        case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
          /* Fatal errors, give up.  */
          return err;
          break;
        case CONSOCKERR: case CONERROR: case FTPSRVERR: case FTPRERR:
-       case WRITEFAILED: case FTPUNKNOWNTYPE: case CONPORTERR:
-       case BINDERR: case LISTENERR: case ACCEPTERR:
+       case WRITEFAILED: case FTPUNKNOWNTYPE: case FTPSYSERR:
        case FTPPORTERR: case FTPLOGREFUSED: case FTPINVPASV:
          printwhat (count, opt.ntry);
          /* non-fatal errors */
        case FTPPORTERR: case FTPLOGREFUSED: case FTPINVPASV:
          printwhat (count, opt.ntry);
          /* non-fatal errors */
@@ -1143,19 +1237,21 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
        }
       /* Time?  */
       tms = time_str (NULL);
        }
       /* Time?  */
       tms = time_str (NULL);
-      tmrate = retr_rate (len - restval, con->dltime, 0);
+      if (!opt.spider)
+        tmrate = retr_rate (len - restval, con->dltime, 0);
 
       /* If we get out of the switch above without continue'ing, we've
         successfully downloaded a file.  Remember this fact. */
 
       /* If we get out of the switch above without continue'ing, we've
         successfully downloaded a file.  Remember this fact. */
-      downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
+      downloaded_file (FILE_DOWNLOADED_NORMALLY, locf);
 
       if (con->st & ON_YOUR_OWN)
        {
 
       if (con->st & ON_YOUR_OWN)
        {
-         CLOSE (RBUF_FD (&con->rbuf));
-         rbuf_uninitialize (&con->rbuf);
+         fd_close (con->csock);
+         con->csock = -1;
        }
        }
-      logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%ld]\n\n"),
-                tms, tmrate, locf, len);
+      if (!opt.spider)
+        logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%ld]\n\n"),
+                  tms, tmrate, locf, len);
       if (!opt.verbose && !opt.quiet)
        {
          /* Need to hide the password from the URL.  The `if' is here
       if (!opt.verbose && !opt.quiet)
        {
          /* Need to hide the password from the URL.  The `if' is here
@@ -1174,7 +1270,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
            /* --dont-remove-listing was specified, so do count this towards the
               number of bytes and files downloaded. */
            {
            /* --dont-remove-listing was specified, so do count this towards the
               number of bytes and files downloaded. */
            {
-             downloaded_increase (len);
+             total_downloaded_bytes += len;
              opt.numurls++;
            }
 
              opt.numurls++;
            }
 
@@ -1182,14 +1278,14 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
             by the more specific option --dont-remove-listing, and the code
             to do this deletion is in another function. */
        }
             by the more specific option --dont-remove-listing, and the code
             to do this deletion is in another function. */
        }
-      else
+      else if (!opt.spider)
        /* This is not a directory listing file. */
        {
          /* Unlike directory listing files, don't pretend normal files weren't
             downloaded if they're going to be deleted.  People seeding proxies,
             for instance, may want to know how many bytes and files they've
             downloaded through it. */
        /* This is not a directory listing file. */
        {
          /* Unlike directory listing files, don't pretend normal files weren't
             downloaded if they're going to be deleted.  People seeding proxies,
             for instance, may want to know how many bytes and files they've
             downloaded through it. */
-         downloaded_increase (len);
+         total_downloaded_bytes += len;
          opt.numurls++;
 
          if (opt.delete_after)
          opt.numurls++;
 
          if (opt.delete_after)
@@ -1201,7 +1297,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
                logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
            }
        }
                logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
            }
        }
-      
+
       /* Restore the original leave-pendingness.  */
       if (orig_lp)
        con->cmd |= LEAVE_PENDING;
       /* Restore the original leave-pendingness.  */
       if (orig_lp)
        con->cmd |= LEAVE_PENDING;
@@ -1210,10 +1306,10 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
       return RETROK;
     } while (!opt.ntry || (count < opt.ntry));
 
       return RETROK;
     } while (!opt.ntry || (count < opt.ntry));
 
-  if (rbuf_initialized_p (&con->rbuf) && (con->st & ON_YOUR_OWN))
+  if (con->csock != -1 && (con->st & ON_YOUR_OWN))
     {
     {
-      CLOSE (RBUF_FD (&con->rbuf));
-      rbuf_uninitialize (&con->rbuf);
+      fd_close (con->csock);
+      con->csock = -1;
     }
   return TRYLIMEXC;
 }
     }
   return TRYLIMEXC;
 }
@@ -1235,7 +1331,7 @@ ftp_get_listing (struct url *u, ccon *con, struct fileinfo **f)
   /* Find the listing file name.  We do it by taking the file name of
      the URL and replacing the last component with the listing file
      name.  */
   /* Find the listing file name.  We do it by taking the file name of
      the URL and replacing the last component with the listing file
      name.  */
-  uf = url_filename (u);
+  uf = url_file_name (u);
   lf = file_merge (uf, LIST_FILENAME);
   xfree (uf);
   DEBUGP ((_("Using `%s' as listing tmp file.\n"), lf));
   lf = file_merge (uf, LIST_FILENAME);
   xfree (uf);
   DEBUGP ((_("Using `%s' as listing tmp file.\n"), lf));
@@ -1304,7 +1400,7 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
     con->cmd &= ~DO_CWD;
   con->cmd |= (DO_RETR | LEAVE_PENDING);
 
     con->cmd &= ~DO_CWD;
   con->cmd |= (DO_RETR | LEAVE_PENDING);
 
-  if (!rbuf_initialized_p (&con->rbuf))
+  if (con->csock < 0)
     con->cmd |= DO_LOGIN;
   else
     con->cmd &= ~DO_LOGIN;
     con->cmd |= DO_LOGIN;
   else
     con->cmd &= ~DO_LOGIN;
@@ -1315,7 +1411,7 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
     {
       char *old_target, *ofile;
 
     {
       char *old_target, *ofile;
 
-      if (downloaded_exceeds_quota ())
+      if (opt.quota && total_downloaded_bytes > opt.quota)
        {
          --depth;
          return QUOTEXC;
        {
          --depth;
          return QUOTEXC;
@@ -1325,7 +1421,7 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
       ofile = xstrdup (u->file);
       url_set_file (u, f->name);
 
       ofile = xstrdup (u->file);
       url_set_file (u, f->name);
 
-      con->target = url_filename (u);
+      con->target = url_file_name (u);
       err = RETROK;
 
       dlthis = 1;
       err = RETROK;
 
       dlthis = 1;
@@ -1476,7 +1572,10 @@ Already have correct symlink %s -> %s\n\n"),
        logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), con->target);
 
       if (f->perms && f->type == FT_PLAINFILE && dlthis)
        logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), con->target);
 
       if (f->perms && f->type == FT_PLAINFILE && dlthis)
-       chmod (con->target, f->perms);
+        {
+         if (opt.preserve_perm)
+           chmod (con->target, f->perms);
+        }
       else
        DEBUGP (("Unrecognized permissions for %s.\n", con->target));
 
       else
        DEBUGP (("Unrecognized permissions for %s.\n", con->target));
 
@@ -1519,7 +1618,7 @@ ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con)
       int size;
       char *odir, *newdir;
 
       int size;
       char *odir, *newdir;
 
-      if (downloaded_exceeds_quota ())
+      if (opt.quota && total_downloaded_bytes > opt.quota)
        break;
       if (f->type != FT_DIRECTORY)
        continue;
        break;
       if (f->type != FT_DIRECTORY)
        continue;
@@ -1565,12 +1664,24 @@ Not descending to `%s' as it is excluded/not-included.\n"), newdir);
       /* Set the time-stamp?  */
     }
 
       /* Set the time-stamp?  */
     }
 
-  if (opt.quota && opt.downloaded > opt.quota)
+  if (opt.quota && total_downloaded_bytes > opt.quota)
     return QUOTEXC;
   else
     return RETROK;
 }
 
     return QUOTEXC;
   else
     return RETROK;
 }
 
+/* Return non-zero if S has a leading '/'  or contains '../' */
+static int
+has_insecure_name_p (const char *s)
+{
+  if (*s == '/')
+    return 1;
+
+  if (strstr(s, "../") != 0)
+    return 1;
+
+  return 0;
+}
 
 /* A near-top-level function to retrieve the files in a directory.
    The function calls ftp_get_listing, to get a linked list of files.
 
 /* A near-top-level function to retrieve the files in a directory.
    The function calls ftp_get_listing, to get a linked list of files.
@@ -1583,21 +1694,19 @@ Not descending to `%s' as it is excluded/not-included.\n"), newdir);
 static uerr_t
 ftp_retrieve_glob (struct url *u, ccon *con, int action)
 {
 static uerr_t
 ftp_retrieve_glob (struct url *u, ccon *con, int action)
 {
-  struct fileinfo *orig, *start;
+  struct fileinfo *f, *start;
   uerr_t res;
 
   con->cmd |= LEAVE_PENDING;
 
   uerr_t res;
 
   con->cmd |= LEAVE_PENDING;
 
-  res = ftp_get_listing (u, con, &orig);
+  res = ftp_get_listing (u, con, &start);
   if (res != RETROK)
     return res;
   if (res != RETROK)
     return res;
-  start = orig;
   /* First: weed out that do not conform the global rules given in
      opt.accepts and opt.rejects.  */
   if (opt.accepts || opt.rejects)
     {
   /* First: weed out that do not conform the global rules given in
      opt.accepts and opt.rejects.  */
   if (opt.accepts || opt.rejects)
     {
-      struct fileinfo *f = orig;
-
+      f = start;
       while (f)
        {
          if (f->type != FT_DIRECTORY && !acceptable (f->name))
       while (f)
        {
          if (f->type != FT_DIRECTORY && !acceptable (f->name))
@@ -1609,13 +1718,25 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
            f = f->next;
        }
     }
            f = f->next;
        }
     }
+  /* Remove all files with possible harmful names */
+  f = start;
+  while (f)
+    {
+      if (has_insecure_name_p (f->name))
+       {
+         logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
+         f = delelement (f, &start);
+       }
+      else
+       f = f->next;
+    }
   /* Now weed out the files that do not match our globbing pattern.
      If we are dealing with a globbing pattern, that is.  */
   if (*u->file && (action == GLOBALL || action == GETONE))
     {
       int matchres = 0;
   /* Now weed out the files that do not match our globbing pattern.
      If we are dealing with a globbing pattern, that is.  */
   if (*u->file && (action == GLOBALL || action == GETONE))
     {
       int matchres = 0;
-      struct fileinfo *f = start;
 
 
+      f = start;
       while (f)
        {
          matchres = fnmatch (u->file, f->name, 0);
       while (f)
        {
          matchres = fnmatch (u->file, f->name, 0);
@@ -1660,7 +1781,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
        }
     }
   freefileinfo (start);
        }
     }
   freefileinfo (start);
-  if (downloaded_exceeds_quota ())
+  if (opt.quota && total_downloaded_bytes > opt.quota)
     return QUOTEXC;
   else
     /* #### Should we return `res' here?  */
     return QUOTEXC;
   else
     /* #### Should we return `res' here?  */
@@ -1680,7 +1801,7 @@ ftp_loop (struct url *u, int *dt, struct url *proxy)
 
   memset (&con, 0, sizeof (con));
 
 
   memset (&con, 0, sizeof (con));
 
-  rbuf_uninitialize (&con.rbuf);
+  con.csock = -1;
   con.st = ON_YOUR_OWN;
   con.rs = ST_UNIX;
   con.id = NULL;
   con.st = ON_YOUR_OWN;
   con.rs = ST_UNIX;
   con.id = NULL;
@@ -1697,12 +1818,12 @@ ftp_loop (struct url *u, int *dt, struct url *proxy)
 
       if (res == RETROK)
        {
 
       if (res == RETROK)
        {
-         if (opt.htmlify)
+         if (opt.htmlify && !opt.spider)
            {
              char *filename = (opt.output_document
                                ? xstrdup (opt.output_document)
                                : (con.target ? xstrdup (con.target)
            {
              char *filename = (opt.output_document
                                ? xstrdup (opt.output_document)
                                : (con.target ? xstrdup (con.target)
-                                  : url_filename (u)));
+                                  : url_file_name (u)));
              res = ftp_index (filename, u, f);
              if (res == FTPOK && opt.verbose)
                {
              res = ftp_index (filename, u, f);
              if (res == FTPOK && opt.verbose)
                {
@@ -1747,11 +1868,11 @@ ftp_loop (struct url *u, int *dt, struct url *proxy)
   if (res == RETROK)
     *dt |= RETROKF;
   /* If a connection was left, quench it.  */
   if (res == RETROK)
     *dt |= RETROKF;
   /* If a connection was left, quench it.  */
-  if (rbuf_initialized_p (&con.rbuf))
-    CLOSE (RBUF_FD (&con.rbuf));
-  FREE_MAYBE (con.id);
+  if (con.csock != -1)
+    fd_close (con.csock);
+  xfree_null (con.id);
   con.id = NULL;
   con.id = NULL;
-  FREE_MAYBE (con.target);
+  xfree_null (con.target);
   con.target = NULL;
   return res;
 }
   con.target = NULL;
   return res;
 }
@@ -1766,7 +1887,7 @@ delelement (struct fileinfo *f, struct fileinfo **start)
   struct fileinfo *next = f->next;
 
   xfree (f->name);
   struct fileinfo *next = f->next;
 
   xfree (f->name);
-  FREE_MAYBE (f->linkto);
+  xfree_null (f->linkto);
   xfree (f);
 
   if (next)
   xfree (f);
 
   if (next)