]> sjero.net Git - wget/blobdiff - src/ftp.c
[svn] Update progress code to use higher timer resolution.
[wget] / src / ftp.c
index 1c4385427562e6258e451816d0d21760597fb752..966b90b56faa3227a3e54dbdf106ffedfcbba9cd 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
-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>
 
@@ -33,9 +43,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <sys/types.h>
 #include <assert.h>
 #include <errno.h>
-#ifndef WINDOWS
-# include <netdb.h>            /* for h_errno */
-#endif
 
 #include "wget.h"
 #include "utils.h"
@@ -51,11 +58,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifndef errno
 extern int errno;
 #endif
-#ifndef h_errno
-# ifndef __CYGWIN__
-extern int h_errno;
-# endif
-#endif
 
 /* File where the "ls -al" listing will be saved.  */
 #define LIST_FILENAME ".listing"
@@ -67,10 +69,11 @@ typedef struct
   int st;                      /* connection status */
   int cmd;                     /* command code */
   struct rbuf rbuf;            /* control connection buffer */
-  long dltime;                 /* time of the download */
+  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 */
+  struct url *proxy;           /* FTWK-style proxy */
 } ccon;
 
 
@@ -127,10 +130,8 @@ getftp (struct url *u, long *len, long restval, ccon *con)
   FILE *fp;
   char *user, *passwd, *respline;
   char *tms, *tmrate;
-  struct wget_timer *timer;
-  unsigned char pasv_addr[6];
   int cmd = con->cmd;
-  int passive_mode_open = 0;
+  int pasv_mode_open = 0;
   long expected_bytes = 0L;
 
   assert (con != NULL);
@@ -160,20 +161,31 @@ getftp (struct url *u, long *len, long restval, ccon *con)
       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;
+
+      if (con->proxy)
+       {
+         /* If proxy is in use, log in as username@target-site. */
+         logname = xmalloc (strlen (user) + 1 + strlen (u->host) + 1);
+         sprintf (logname, "%s@%s", user, u->host);
+       }
+
       /* Login to the server: */
 
       /* First: Establish the control connection.  */
 
-      al = lookup_host (u->host, 0);
+      al = lookup_host (host, 0);
       if (!al)
        return HOSTERR;
-      set_connection_host_name (u->host);
-      csock = connect_to_many (al, u->port, 0);
+      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;
+       return CONNECT_ERROR (errno);
 
       if (cmd & LEAVE_PENDING)
        rbuf_initialize (&con->rbuf, csock);
@@ -188,7 +200,11 @@ getftp (struct url *u, long *len, long restval, ccon *con)
       logprintf (LOG_VERBOSE, _("Logging in as %s ... "), user);
       if (opt.server_response)
        logputs (LOG_ALWAYS, "\n");
-      err = ftp_login (&con->rbuf, user, passwd);
+      err = ftp_login (&con->rbuf, logname, passwd);
+
+      if (con->proxy)
+       xfree (logname);
+
       /* FTPRERR, FTPSRVERR, WRITEFAILED, FTPLOGREFUSED, FTPLOGINC */
       switch (err)
        {
@@ -265,7 +281,7 @@ Error in server response, closing control connection.\n"));
          abort ();
          break;
        }
-      if (!opt.server_response)
+      if (!opt.server_response && err != FTPSRVERR)
        logputs (LOG_VERBOSE, _("done.    "));
 
       /* Fourth: Find the initial ftp directory */
@@ -277,7 +293,6 @@ Error in server response, closing control connection.\n"));
       switch (err)
        {
        case FTPRERR:
-       case FTPSRVERR :
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
@@ -285,6 +300,11 @@ Error in server response, closing control connection.\n"));
          rbuf_uninitialize (&con->rbuf);
          return err;
          break;
+       case FTPSRVERR :
+         /* PWD unsupported -- assume "/". */
+         FREE_MAYBE (con->id);
+         con->id = xstrdup ("/");
+         break;
        case FTPOK:
          /* Everything is OK.  */
          break;
@@ -373,17 +393,31 @@ Error in server response, closing control connection.\n"));
 
          /* Change working directory.  To change to a non-absolute
             Unix directory, we need to prepend initial directory
-            (con->id) to it.  Absolute directories "just work".  */
+            (con->id) to it.  Absolute directories "just work".
 
-         if (*target != '/')
+            A relative directory is one that does not begin with '/'
+            and, on non-Unix OS'es, one that doesn't begin with
+            "<letter>:".  */
+
+         if (target[0] != '/'
+             && !(con->rs != ST_UNIX
+                  && ISALPHA (target[0]) && target[1] == ':'))
            {
              int idlen = strlen (con->id);
-             char *ntarget = (char *)alloca (idlen + 1 + strlen (u->dir) + 1);
-             /* idlen == 1 means con->id = "/" */
-             sprintf (ntarget, "%s%s%s", con->id, idlen == 1 ? "" : "/",
-                      target);
+             char *ntarget, *p;
+
+             /* Strip trailing slash(es) from con->id. */
+             while (idlen > 0 && con->id[idlen - 1] == '/')
+               --idlen;
+             p = ntarget = (char *)alloca (idlen + 1 + strlen (u->dir) + 1);
+             memcpy (p, con->id, idlen);
+             p += idlen;
+             *p++ = '/';
+             strcpy (p, target);
+
               DEBUGP (("Prepended initial PWD to relative path:\n"));
-              DEBUGP (("  old: '%s'\n  new: '%s'\n", target, ntarget));
+              DEBUGP (("   pwd: '%s'\n   old: '%s'\n  new: '%s'\n",
+                      con->id, target, ntarget));
              target = ntarget;
            }
 
@@ -494,12 +528,11 @@ Error in server response, closing control connection.\n"));
     {
       if (opt.ftp_pasv > 0)
        {
-         char thost[256];
-         unsigned short tport;
-
+         ip_address     passive_addr;
+         unsigned short passive_port;
          if (!opt.server_response)
            logputs (LOG_VERBOSE, "==> PASV ... ");
-         err = ftp_pasv (&con->rbuf, pasv_addr);
+         err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
          /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
          switch (err)
            {
@@ -533,40 +566,28 @@ Error in server response, closing control connection.\n"));
            default:
              abort ();
              break;
-           }
+           }   /* switch(err) */
          if (err==FTPOK)
            {
-             struct address_list *al;
-
-             sprintf (thost, "%d.%d.%d.%d",
-                      pasv_addr[0], pasv_addr[1], pasv_addr[2], pasv_addr[3]);
-             tport = (pasv_addr[4] << 8) + pasv_addr[5];
-
-             al = lookup_host (thost, 0);
-             if (!al)
-               {
-                 CLOSE (csock);
-                 rbuf_uninitialize (&con->rbuf);
-                 return HOSTERR;
-               }
-             dtsock = connect_to_many (al, tport, 0);
-             address_list_release (al);
-
+             dtsock = connect_to_one (&passive_addr, passive_port, 1);
              if (dtsock < 0)
                {
                  int save_errno = errno;
                  CLOSE (csock);
                  rbuf_uninitialize (&con->rbuf);
-                 return save_errno == ECONNREFUSED ? CONREFUSED : CONERROR;
+                 logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"),
+                            pretty_print_address (&passive_addr), passive_port,
+                            strerror (save_errno));
+                 return CONNECT_ERROR (save_errno);
                }
 
-             passive_mode_open= 1;  /* Flag to avoid accept port */
+             pasv_mode_open = 1;  /* Flag to avoid accept port */
              if (!opt.server_response)
                logputs (LOG_VERBOSE, _("done.    "));
            } /* err==FTP_OK */
        }
 
-      if (!passive_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 ... ");
@@ -609,15 +630,6 @@ Error in server response, closing control connection.\n"));
              closeport (dtsock);
              return err;
              break;
-           case HOSTERR:
-             logputs (LOG_VERBOSE, "\n");
-             logprintf (LOG_NOTQUIET, "%s: %s\n", u->host,
-                        herrmsg (h_errno));
-             CLOSE (csock);
-             closeport (dtsock);
-             rbuf_uninitialize (&con->rbuf);
-             return HOSTERR;
-             break;
            case FTPPORTERR:
              logputs (LOG_VERBOSE, "\n");
              logputs (LOG_NOTQUIET, _("Invalid PORT.\n"));
@@ -696,6 +708,18 @@ Error in server response, closing control connection.\n"));
 
   if (cmd & DO_RETR)
     {
+      /* If we're in spider mode, don't really retrieve anything.  The
+        fact that we got to this point should be proof enough that
+        the file exists, vaguely akin to HTTP's concept of a "HEAD"
+        request.  */
+      if (opt.spider)
+       {
+         CLOSE (csock);
+         closeport (dtsock);
+         rbuf_uninitialize (&con->rbuf);
+         return RETRFINISHED;
+       }
+
       if (opt.verbose)
        {
          if (!opt.server_response)
@@ -807,7 +831,7 @@ Error in server response, closing control connection.\n"));
   if (!(cmd & (DO_LIST | DO_RETR)))
     return RETRFINISHED;
 
-  if (!passive_mode_open)  /* we are not using pasive mode so we need
+  if (!pasv_mode_open)  /* we are not using pasive mode so we need
                              to accept */
     {
       /* Open the data transmission socket by calling acceptport().  */
@@ -847,7 +871,7 @@ Error in server response, closing control connection.\n"));
       /* Rewind the output document if the download starts over and if
         this is the first download.  See gethttp() for a longer
         explanation.  */
-      if (!restval && global_download_count == 0)
+      if (!restval && global_download_count == 0 && opt.dfp != stdout)
        {
          /* This will silently fail for streams that don't correspond
             to regular files, but that's OK.  */
@@ -875,11 +899,10 @@ Error in server response, closing control connection.\n"));
                   legible (expected_bytes - restval));
       logputs (LOG_VERBOSE, _(" (unauthoritative)\n"));
     }
-  timer = wtimer_new ();
+
   /* Get the contents of the document.  */
-  res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf, 0);
-  con->dltime = wtimer_elapsed (timer);
-  wtimer_delete (timer);
+  res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf,
+                     0, &con->dltime);
   tms = time_str (NULL);
   tmrate = retr_rate (*len - restval, con->dltime, 0);
   /* Close data connection socket.  */
@@ -1002,7 +1025,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
   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))
     {
@@ -1222,7 +1245,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.  */
-  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));
@@ -1312,7 +1335,7 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
       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;
@@ -1331,6 +1354,11 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
              /* Else, get it from the file.  */
              local_size = st.st_size;
              tml = st.st_mtime;
+#ifdef WINDOWS
+             /* Modification time granularity is 2 seconds for Windows, so
+                increase local time by 1 second for later comparison. */
+             tml++;
+#endif
               /* Compare file sizes only for servers that tell us correct
                  values. Assumme sizes being equal for servers that lie
                  about file size.  */
@@ -1565,7 +1593,7 @@ 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 *orig, *start;
+  struct fileinfo *f, *orig, *start;
   uerr_t res;
 
   con->cmd |= LEAVE_PENDING;
@@ -1578,8 +1606,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
      opt.accepts and opt.rejects.  */
   if (opt.accepts || opt.rejects)
     {
-      struct fileinfo *f = orig;
-
+      f = orig;
       while (f)
        {
          if (f->type != FT_DIRECTORY && !acceptable (f->name))
@@ -1591,13 +1618,25 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
            f = f->next;
        }
     }
+  /* Remove all files with possible harmful names */
+  f = orig;
+  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;
-      struct fileinfo *f = start;
 
+      f = start;
       while (f)
        {
          matchres = fnmatch (u->file, f->name, 0);
@@ -1653,7 +1692,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
    of URL.  Inherently, its capabilities are limited on what can be
    encoded into a URL.  */
 uerr_t
-ftp_loop (struct url *u, int *dt)
+ftp_loop (struct url *u, int *dt, struct url *proxy)
 {
   ccon con;                    /* FTP connection */
   uerr_t res;
@@ -1666,6 +1705,7 @@ ftp_loop (struct url *u, int *dt)
   con.st = ON_YOUR_OWN;
   con.rs = ST_UNIX;
   con.id = NULL;
+  con.proxy = proxy;
   res = RETROK;                        /* in case it's not used */
 
   /* If the file name is empty, the user probably wants a directory
@@ -1683,7 +1723,7 @@ ftp_loop (struct url *u, int *dt)
              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)
                {
@@ -1717,8 +1757,8 @@ ftp_loop (struct url *u, int *dt)
          /* ftp_retrieve_glob is a catch-all function that gets called
             if we need globbing, time-stamping or recursion.  Its
             third argument is just what we really need.  */
-         ftp_retrieve_glob (u, &con,
-                            (opt.ftp_glob && wild) ? GLOBALL : GETONE);
+         res = ftp_retrieve_glob (u, &con,
+                                  (opt.ftp_glob && wild) ? GLOBALL : GETONE);
        }
       else
        res = ftp_loop_internal (u, NULL, &con);