]> sjero.net Git - wget/blobdiff - src/ftp.c
[svn] Imported Mauro's IPv6 changes.
[wget] / src / ftp.c
index 5c619be141fc4f4a2b12067dd900657541d8d5aa..7c0bee7b3e873fedcf809e42bd8e9758a7c9efce 100644 (file)
--- a/src/ftp.c
+++ b/src/ftp.c
@@ -52,13 +52,15 @@ so, delete this exception statement from your version.  */
 #include "ftp.h"
 #include "connect.h"
 #include "host.h"
-#include "fnmatch.h"
 #include "netrc.h"
+#include "convert.h"           /* for downloaded_file */
 
 #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"
 
@@ -119,6 +121,110 @@ ftp_expected_bytes (const char *s)
   return res;
 }
 
+#ifdef ENABLE_IPV6
+static int
+getfamily (int fd)
+{
+  struct sockaddr_storage ss;
+  struct sockaddr *sa = (struct sockaddr *)&ss;
+  socklen_t len = sizeof (ss);
+
+  assert (fd >= 0);
+
+  if (getpeername (fd, sa, &len) < 0)
+    /* Mauro Tortonesi: HOW DO WE HANDLE THIS ERROR? */
+    abort ();
+
+  return sa->sa_family;
+}
+
+/* 
+ * 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 (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
+{
+  uerr_t err;
+  int family;
+
+  family = getfamily (rbuf->fd);
+  assert (family == AF_INET || family == AF_INET6);
+
+  /* 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. */
+  if (family == AF_INET6)
+    {
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> EPSV ... ");
+      err = ftp_epsv (rbuf, addr, port);
+
+      /* If EPSV is not supported try LPSV */
+      if (err == FTPNOPASV)
+        {
+          if (!opt.server_response)
+            logputs (LOG_VERBOSE, "==> LPSV ... ");
+          err = ftp_lpsv (rbuf, addr, port);
+        }
+    }
+  else 
+    {
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> PASV ... ");
+      err = ftp_pasv (rbuf, addr, port);
+    }
+
+  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 (struct rbuf *rbuf)
+{
+  uerr_t err;
+  int family;
+
+  assert (rbuf != NULL);
+  assert (rbuf_initialized_p (rbuf));
+
+  family = getfamily (rbuf->fd);
+  assert (family == AF_INET || family == AF_INET6);
+
+  /* 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. */
+  if (family == AF_INET6)
+    {
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> EPRT ... ");
+      err = ftp_eprt (rbuf);
+
+      /* If EPRT is not supported try LPRT */
+      if (err == FTPPORTERR)
+        {
+          if (!opt.server_response)
+            logputs (LOG_VERBOSE, "==> LPRT ... ");
+          err = ftp_lprt (rbuf);
+        }
+    }
+  else 
+    {
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> PORT ... ");
+      err = ftp_port (rbuf);
+    }
+
+  return err;
+}
+#else
+#define ftp_do_pasv ftp_pasv
+#define ftp_do_port ftp_port
+#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.  */
@@ -540,7 +646,7 @@ Error in server response, closing control connection.\n"));
          unsigned short passive_port;
          if (!opt.server_response)
            logputs (LOG_VERBOSE, "==> PASV ... ");
-         err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
+         err = ftp_do_pasv (&con->rbuf, &passive_addr, &passive_port);
          /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
          switch (err)
            {
@@ -577,13 +683,16 @@ Error in server response, closing control connection.\n"));
            }   /* switch(err) */
          if (err==FTPOK)
            {
+             DEBUGP (("trying to connect to %s port %d\n", 
+                     pretty_print_address (&passive_addr),
+                     passive_port));
              dtsock = connect_to_one (&passive_addr, passive_port, 1);
              if (dtsock < 0)
                {
                  int save_errno = errno;
                  CLOSE (csock);
                  rbuf_uninitialize (&con->rbuf);
-                 logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"),
+                 logprintf (LOG_VERBOSE, _("couldn't connect to %s port %hu: %s\n"),
                             pretty_print_address (&passive_addr), passive_port,
                             strerror (save_errno));
                  return CONNECT_ERROR (save_errno);
@@ -599,7 +708,7 @@ Error in server response, closing control connection.\n"));
        {
          if (!opt.server_response)
            logputs (LOG_VERBOSE, "==> PORT ... ");
-         err = ftp_port (&con->rbuf);
+         err = ftp_do_port (&con->rbuf);
          /* FTPRERR, WRITEFAILED, bindport (CONSOCKERR, CONPORTERR, BINDERR,
             LISTENERR), HOSTERR, FTPPORTERR */
          switch (err)
@@ -737,6 +846,7 @@ Error in server response, closing control connection.\n"));
              logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
            }
        }
+
       err = ftp_retr (&con->rbuf, u->file);
       /* FTPRERR, WRITEFAILED, FTPNSFOD */
       switch (err)
@@ -826,6 +936,9 @@ Error in server response, closing control connection.\n"));
       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
@@ -836,9 +949,6 @@ Error in server response, closing control connection.\n"));
     }
 
   /* 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 */
     {
@@ -928,6 +1038,7 @@ Error in server response, closing control connection.\n"));
     if (flush_res == EOF)
       res = -2;
   }
+
   /* If get_contents couldn't write to fp, bail out.  */
   if (res == -2)
     {
@@ -1028,7 +1139,8 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
 {
   int count, orig_lp;
   long restval, len;
-  char *tms, *tmrate, *locf;
+  char *tms, *locf;
+  char *tmrate = NULL;
   uerr_t err;
   struct stat st;
 
@@ -1161,19 +1273,21 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
        }
       /* 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. */
-      downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
+      downloaded_file (FILE_DOWNLOADED_NORMALLY, locf);
 
       if (con->st & ON_YOUR_OWN)
        {
          CLOSE (RBUF_FD (&con->rbuf));
          rbuf_uninitialize (&con->rbuf);
        }
-      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
@@ -1192,7 +1306,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. */
            {
-             downloaded_increase (len);
+             total_downloaded_bytes += len;
              opt.numurls++;
            }
 
@@ -1200,14 +1314,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. */
        }
-      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. */
-         downloaded_increase (len);
+         total_downloaded_bytes += len;
          opt.numurls++;
 
          if (opt.delete_after)
@@ -1219,7 +1333,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
                logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
            }
        }
-      
+
       /* Restore the original leave-pendingness.  */
       if (orig_lp)
        con->cmd |= LEAVE_PENDING;
@@ -1333,7 +1447,7 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
     {
       char *old_target, *ofile;
 
-      if (downloaded_exceeds_quota ())
+      if (opt.quota && total_downloaded_bytes > opt.quota)
        {
          --depth;
          return QUOTEXC;
@@ -1537,7 +1651,7 @@ ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con)
       int size;
       char *odir, *newdir;
 
-      if (downloaded_exceeds_quota ())
+      if (opt.quota && total_downloaded_bytes > opt.quota)
        break;
       if (f->type != FT_DIRECTORY)
        continue;
@@ -1583,12 +1697,24 @@ Not descending to `%s' as it is excluded/not-included.\n"), newdir);
       /* 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 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.
@@ -1601,20 +1727,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)
 {
-  struct fileinfo *f, *orig, *start;
+  struct fileinfo *f, *start;
   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;
-  start = orig;
   /* First: weed out that do not conform the global rules given in
      opt.accepts and opt.rejects.  */
   if (opt.accepts || opt.rejects)
     {
-      f = orig;
+      f = start;
       while (f)
        {
          if (f->type != FT_DIRECTORY && !acceptable (f->name))
@@ -1627,10 +1752,10 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
        }
     }
   /* Remove all files with possible harmful names */
-  f = orig;
+  f = start;
   while (f)
     {
-      if (has_insecure_name_p(f->name))
+      if (has_insecure_name_p (f->name))
        {
          logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
          f = delelement (f, &start);
@@ -1689,7 +1814,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
        }
     }
   freefileinfo (start);
-  if (downloaded_exceeds_quota ())
+  if (opt.quota && total_downloaded_bytes > opt.quota)
     return QUOTEXC;
   else
     /* #### Should we return `res' here?  */
@@ -1726,7 +1851,7 @@ ftp_loop (struct url *u, int *dt, struct url *proxy)
 
       if (res == RETROK)
        {
-         if (opt.htmlify)
+         if (opt.htmlify && !opt.spider)
            {
              char *filename = (opt.output_document
                                ? xstrdup (opt.output_document)