]> sjero.net Git - wget/blobdiff - src/ftp.c
Duplicate string to circumvent eventual memory corruption.
[wget] / src / ftp.c
index 2f5ecd157d88fd449bbab1d3eff848541562a8a0..d58d67dfe87ff107ab59ed0b9c65c76f34356bb3 100644 (file)
--- a/src/ftp.c
+++ b/src/ftp.c
@@ -1,6 +1,6 @@
 /* File Transfer Protocol support.
    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
 This file is part of GNU Wget.
 
@@ -17,17 +17,18 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
 
-In addition, as a special exception, the Free Software Foundation
-gives permission to link the code of its release of Wget with the
-OpenSSL project's "OpenSSL" library (or with modified versions of it
-that use the same license as the "OpenSSL" library), and distribute
-the linked executables.  You must obey the GNU General Public License
-in all respects for all of the code used other than "OpenSSL".  If you
-modify this file, you may extend this exception to your version of the
-file, but you are not obligated to do so.  If you do not wish to do
-so, delete this exception statement from your version.  */
+Additional permission under GNU GPL version 3 section 7
 
-#include <config.h>
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.  */
+
+#include "wget.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -39,7 +40,6 @@ so, delete this exception statement from your version.  */
 #include <errno.h>
 #include <time.h>
 
-#include "wget.h"
 #include "utils.h"
 #include "url.h"
 #include "retr.h"
@@ -51,7 +51,11 @@ so, delete this exception statement from your version.  */
 #include "recur.h"              /* for INFINITE_RECURSION */
 
 /* File where the "ls -al" listing will be saved.  */
+#ifdef MSDOS
+#define LIST_FILENAME "_listing"
+#else
 #define LIST_FILENAME ".listing"
+#endif
 
 typedef struct
 {
@@ -65,6 +69,7 @@ typedef struct
   struct url *proxy;            /* FTWK-style proxy */
 } ccon;
 
+extern int numurls;
 
 /* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in
    the string S, and return the number converted to wgint, if found, 0
@@ -84,11 +89,11 @@ ftp_expected_bytes (const char *s)
       res = str_to_wgint (s, (char **) &s, 10);
       if (!*s)
         return 0;
-      while (*s && ISSPACE (*s))
+      while (*s && c_isspace (*s))
         ++s;
       if (!*s)
         return 0;
-      if (TOLOWER (*s) != 'b')
+      if (c_tolower (*s) != 'b')
         continue;
       if (strncasecmp (s, "byte", 4))
         continue;
@@ -212,7 +217,7 @@ print_length (wgint size, wgint start, bool authoritative)
     logprintf (LOG_VERBOSE, " (%s)", human_readable (size));
   if (start > 0)
     {
-      if (start >= 1024)
+      if (size - start >= 1024)
         logprintf (LOG_VERBOSE, _(", %s (%s) remaining"),
                    number_to_static_string (size - start),
                    human_readable (size - start));
@@ -223,6 +228,8 @@ print_length (wgint size, wgint start, bool authoritative)
   logputs (LOG_VERBOSE, !authoritative ? _(" (unauthoritative)\n") : "\n");
 }
 
+static uerr_t ftp_get_listing (struct url *, ccon *, struct fileinfo **);
+
 /* 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.  */
@@ -296,7 +303,8 @@ getftp (struct url *u, wgint *len, wgint restval, ccon *con)
         con->csock = -1;
 
       /* Second: Login with proper USER/PASS sequence.  */
-      logprintf (LOG_VERBOSE, _("Logging in as %s ... "), escnonprint (user));
+      logprintf (LOG_VERBOSE, _("Logging in as %s ... "), 
+                 quotearg_style (escape_quoting_style, user));
       if (opt.server_response)
         logputs (LOG_ALWAYS, "\n");
       err = ftp_login (csock, logname, passwd);
@@ -492,7 +500,7 @@ Error in server response, closing control connection.\n"));
 
           if (target[0] != '/'
               && !(con->rs != ST_UNIX
-                   && ISALPHA (target[0])
+                   && c_isalpha (target[0])
                    && target[1] == ':')
               && con->rs != ST_OS400)
             {
@@ -541,7 +549,8 @@ Error in server response, closing control connection.\n"));
             }
 
           if (!opt.server_response)
-            logprintf (LOG_VERBOSE, "==> CWD %s ... ", escnonprint (target));
+            logprintf (LOG_VERBOSE, "==> CWD %s ... ", 
+                       quotearg_style (escape_quoting_style, target));
           err = ftp_cwd (csock, target);
           /* FTPRERR, WRITEFAILED, FTPNSFOD */
           switch (err)
@@ -562,8 +571,8 @@ Error in server response, closing control connection.\n"));
               return err;
             case FTPNSFOD:
               logputs (LOG_VERBOSE, "\n");
-              logprintf (LOG_NOTQUIET, _("No such directory `%s'.\n\n"),
-                         escnonprint (u->dir));
+              logprintf (LOG_NOTQUIET, _("No such directory %s.\n\n"),
+                         quote (u->dir));
               fd_close (csock);
               con->csock = -1;
               return err;
@@ -584,7 +593,8 @@ Error in server response, closing control connection.\n"));
       if (opt.verbose)
         {
           if (!opt.server_response)
-            logprintf (LOG_VERBOSE, "==> SIZE %s ... ", escnonprint (u->file));
+            logprintf (LOG_VERBOSE, "==> SIZE %s ... ", 
+                       quotearg_style (escape_quoting_style, u->file));
         }
 
       err = ftp_size (csock, u->file, len);
@@ -772,12 +782,43 @@ 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 we're in spider mode, don't really retrieve anything except
+        the directory listing and verify whether the given "file" exists.  */
       if (opt.spider)
         {
+         bool exists = false;
+         uerr_t res;
+         struct fileinfo *f;
+         res = ftp_get_listing (u, con, &f);
+         /* Set the DO_RETR command flag again, because it gets unset when 
+            calling ftp_get_listing() and would otherwise cause an assertion 
+            failure earlier on when this function gets repeatedly called 
+            (e.g., when recursing).  */
+         con->cmd |= DO_RETR;
+         if (res == RETROK)
+           {
+             while (f) 
+               {
+                 if (!strcmp (f->name, u->file))
+                   {
+                     exists = true;
+                     break;
+                   }
+                 f = f->next;
+               }
+              if (exists)
+                {
+                  logputs (LOG_VERBOSE, "\n");
+                  logprintf (LOG_NOTQUIET, _("File %s exists.\n"),
+                             quote (u->file));
+                }
+             else
+                {
+                 logputs (LOG_VERBOSE, "\n");
+                 logprintf (LOG_NOTQUIET, _("No such file %s.\n"),
+                            quote (u->file));
+               }
+           }
           fd_close (csock);
           con->csock = -1;
           fd_close (dtsock);
@@ -791,7 +832,8 @@ Error in server response, closing control connection.\n"));
             {
               if (restval)
                 logputs (LOG_VERBOSE, "\n");
-              logprintf (LOG_VERBOSE, "==> RETR %s ... ", escnonprint (u->file));
+              logprintf (LOG_VERBOSE, "==> RETR %s ... ", 
+                         quotearg_style (escape_quoting_style, u->file));
             }
         }
 
@@ -819,8 +861,8 @@ Error in server response, closing control connection.\n"));
           return err;
         case FTPNSFOD:
           logputs (LOG_VERBOSE, "\n");
-          logprintf (LOG_NOTQUIET, _("No such file `%s'.\n\n"),
-                     escnonprint (u->file));
+          logprintf (LOG_NOTQUIET, _("No such file %s.\n\n"),
+                     quote (u->file));
           fd_close (dtsock);
           fd_close (local_sock);
           return err;
@@ -866,8 +908,8 @@ Error in server response, closing control connection.\n"));
           return err;
         case FTPNSFOD:
           logputs (LOG_VERBOSE, "\n");
-          logprintf (LOG_NOTQUIET, _("No such file or directory `%s'.\n\n"),
-                     ".");
+          logprintf (LOG_NOTQUIET, _("No such file or directory %s.\n\n"),
+                     quote ("."));
           fd_close (dtsock);
           fd_close (local_sock);
           return err;
@@ -914,7 +956,7 @@ Error in server response, closing control connection.\n"));
       if (opt.backups)
         rotate_backups (con->target);
 
-      if (restval)
+      if (restval && !(con->cmd & DO_LIST))
         fp = fopen (con->target, "ab");
       else if (opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct
                || opt.output_document)
@@ -968,7 +1010,7 @@ Error in server response, closing control connection.\n"));
                       expected_bytes ? expected_bytes - restval : 0,
                       restval, &rd_size, len, &con->dltime, flags);
 
-  tms = time_str (time (NULL));
+  tms = datetime_str (time (NULL));
   tmrate = retr_rate (rd_size, con->dltime);
   total_download_time += con->dltime;
 
@@ -1059,7 +1101,8 @@ Error in server response, closing control connection.\n"));
               char *p = strchr (line, '\0');
               while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
                 *--p = '\0';
-              logprintf (LOG_ALWAYS, "%s\n", escnonprint (line));
+              logprintf (LOG_ALWAYS, "%s\n", 
+                         quotearg_style (escape_quoting_style, line));
               xfree (line);
             }
           fclose (fp);
@@ -1087,10 +1130,12 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
   if (!con->target)
     con->target = url_file_name (u);
 
-  if (opt.noclobber && file_exists_p (con->target))
+  /* If the output_document was given, then this check was already done and
+     the file didn't exist. Hence the !opt.output_document */
+  if (opt.noclobber && !opt.output_document && file_exists_p (con->target))
     {
       logprintf (LOG_VERBOSE,
-                 _("File `%s' already there; not retrieving.\n"), con->target);
+                 _("File %s already there; not retrieving.\n"), quote (con->target));
       /* If the file is there, we suppose it's retrieved OK.  */
       return RETROK;
     }
@@ -1137,7 +1182,9 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
         }
 
       /* Decide whether or not to restart.  */
-      if (opt.always_rest
+      if (con->cmd & DO_LIST)
+        restval = 0;
+      else if (opt.always_rest
           && stat (locf, &st) == 0
           && S_ISREG (st.st_mode))
         /* When -c is used, continue from on-disk size.  (Can't use
@@ -1150,7 +1197,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
         restval = 0;
 
       /* Get the current time string.  */
-      tms = time_str (time (NULL));
+      tms = datetime_str (time (NULL));
       /* Print fetch message, if opt.verbose.  */
       if (opt.verbose)
         {
@@ -1159,8 +1206,8 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
           strcpy (tmp, "        ");
           if (count > 1)
             sprintf (tmp, _("(try:%2d)"), count);
-          logprintf (LOG_VERBOSE, "--%s--  %s\n  %s => `%s'\n",
-                     tms, hurl, tmp, locf);
+          logprintf (LOG_VERBOSE, "--%s--  %s\n  %s => %s\n",
+                     tms, hurl, tmp, quote (locf));
 #ifdef WINDOWS
           ws_changetitle (hurl);
 #endif
@@ -1214,7 +1261,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
           /* Not as great.  */
           abort ();
         }
-      tms = time_str (time (NULL));
+      tms = datetime_str (time (NULL));
       if (!opt.spider)
         tmrate = retr_rate (len - restval, con->dltime);
 
@@ -1228,8 +1275,17 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
           con->csock = -1;
         }
       if (!opt.spider)
-        logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%s]\n\n"),
-                   tms, tmrate, locf, number_to_static_string (len));
+        {
+          bool write_to_stdout = (opt.output_document && HYPHENP (opt.output_document));
+
+          logprintf (LOG_VERBOSE,
+                     write_to_stdout
+                     ? _("%s (%s) - written to stdout %s[%s]\n\n")
+                     : _("%s (%s) - %s saved [%s]\n\n"),
+                     tms, tmrate,
+                     write_to_stdout ? "" : quote (locf),
+                     number_to_static_string (len));
+        }
       if (!opt.verbose && !opt.quiet)
         {
           /* Need to hide the password from the URL.  The `if' is here
@@ -1249,7 +1305,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
                number of bytes and files downloaded. */
             {
               total_downloaded_bytes += len;
-              opt.numurls++;
+              numurls++;
             }
 
           /* Deletion of listing files is not controlled by --delete-after, but
@@ -1264,7 +1320,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
              for instance, may want to know how many bytes and files they've
              downloaded through it. */
           total_downloaded_bytes += len;
-          opt.numurls++;
+          numurls++;
 
           if (opt.delete_after)
             {
@@ -1312,23 +1368,26 @@ ftp_get_listing (struct url *u, ccon *con, struct fileinfo **f)
   uf = url_file_name (u);
   lf = file_merge (uf, LIST_FILENAME);
   xfree (uf);
-  DEBUGP ((_("Using `%s' as listing tmp file.\n"), lf));
+  DEBUGP ((_("Using %s as listing tmp file.\n"), quote (lf)));
 
-  con->target = lf;
+  con->target = xstrdup (lf);
   err = ftp_loop_internal (u, NULL, con);
+  xfree (con->target);
   con->target = old_target;
 
   if (err == RETROK)
-    *f = ftp_parse_ls (lf, con->rs);
-  else
-    *f = NULL;
-  if (opt.remove_listing)
     {
-      if (unlink (lf))
-        logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
-      else
-        logprintf (LOG_VERBOSE, _("Removed `%s'.\n"), lf);
+      *f = ftp_parse_ls (lf, con->rs);
+      if (opt.remove_listing)
+        {
+          if (unlink (lf))
+            logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
+          else
+            logprintf (LOG_VERBOSE, _("Removed %s.\n"), quote (lf));
+        }
     }
+  else
+    *f = NULL;
   xfree (lf);
   con->cmd &= ~DO_LIST;
   return err;
@@ -1431,15 +1490,15 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
                   /* Remote file is older, file sizes can be compared and
                      are both equal. */
                   logprintf (LOG_VERBOSE, _("\
-Remote file no newer than local file `%s' -- not retrieving.\n"), con->target);
+Remote file no newer than local file %s -- not retrieving.\n"), quote (con->target));
                   dlthis = false;
                 }
               else if (eq_size)
                 {
                   /* Remote file is newer or sizes cannot be matched */
                   logprintf (LOG_VERBOSE, _("\
-Remote file is newer than local file `%s' -- retrieving.\n\n"),
-                             con->target);
+Remote file is newer than local file %s -- retrieving.\n\n"),
+                             quote (con->target));
                 }
               else
                 {
@@ -1481,14 +1540,15 @@ The sizes do not match (local %s) -- retrieving.\n\n"),
                             {
                               logprintf (LOG_VERBOSE, _("\
 Already have correct symlink %s -> %s\n\n"),
-                                         con->target, escnonprint (f->linkto));
+                                         quote (con->target),
+                                         quote (f->linkto));
                               dlthis = false;
                               break;
                             }
                         }
                     }
                   logprintf (LOG_VERBOSE, _("Creating symlink %s -> %s\n"),
-                             con->target, escnonprint (f->linkto));
+                             quote (con->target), quote (f->linkto));
                   /* Unlink before creating symlink!  */
                   unlink (con->target);
                   if (symlink (f->linkto, con->target) == -1)
@@ -1497,8 +1557,8 @@ Already have correct symlink %s -> %s\n\n"),
                 } /* have f->linkto */
 #else  /* not HAVE_SYMLINK */
               logprintf (LOG_NOTQUIET,
-                         _("Symlinks not supported, skipping symlink `%s'.\n"),
-                         con->target);
+                         _("Symlinks not supported, skipping symlink %s.\n"),
+                         quote (con->target));
 #endif /* not HAVE_SYMLINK */
             }
           else                /* opt.retr_symlinks */
@@ -1509,8 +1569,8 @@ Already have correct symlink %s -> %s\n\n"),
           break;
         case FT_DIRECTORY:
           if (!opt.recursive)
-            logprintf (LOG_NOTQUIET, _("Skipping directory `%s'.\n"),
-                       escnonprint (f->name));
+            logprintf (LOG_NOTQUIET, _("Skipping directory %s.\n"),
+                       quote (f->name));
           break;
         case FT_PLAINFILE:
           /* Call the retrieve loop.  */
@@ -1519,7 +1579,7 @@ Already have correct symlink %s -> %s\n\n"),
           break;
         case FT_UNKNOWN:
           logprintf (LOG_NOTQUIET, _("%s: unknown/unsupported file type.\n"),
-                     escnonprint (f->name));
+                     quote (f->name));
           break;
         }       /* switch */
 
@@ -1531,16 +1591,8 @@ Already have correct symlink %s -> %s\n\n"),
           && dlthis
           && file_exists_p (con->target))
         {
-          /* #### This code repeats in http.c and ftp.c.  Move it to a
-             function!  */
           const char *fl = NULL;
-          if (opt.output_document)
-            {
-              if (output_stream_regular)
-                fl = opt.output_document;
-            }
-          else
-            fl = con->target;
+          set_local_file (&fl, con->target);
           if (fl)
             touch (fl, f->tstamp);
         }
@@ -1624,8 +1676,8 @@ ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con)
       if (!accdir (newdir))
         {
           logprintf (LOG_VERBOSE, _("\
-Not descending to `%s' as it is excluded/not-included.\n"),
-                     escnonprint (newdir));
+Not descending to %s as it is excluded/not-included.\n"),
+                     quote (newdir));
           continue;
         }
 
@@ -1689,8 +1741,8 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
         {
           if (f->type != FT_DIRECTORY && !acceptable (f->name))
             {
-              logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"),
-                         escnonprint (f->name));
+              logprintf (LOG_VERBOSE, _("Rejecting %s.\n"),
+                         quote (f->name));
               f = delelement (f, &start);
             }
           else
@@ -1703,8 +1755,8 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
     {
       if (has_insecure_name_p (f->name))
         {
-          logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"),
-                     escnonprint (f->name));
+          logprintf (LOG_VERBOSE, _("Rejecting %s.\n"),
+                     quote (f->name));
           f = delelement (f, &start);
         }
       else
@@ -1712,31 +1764,48 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
     }
   /* 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 == GLOB_GLOBALL || action == GLOB_GETONE))
+  if (*u->file)
     {
-      int (*matcher) (const char *, const char *, int)
-        = opt.ignore_case ? fnmatch_nocase : fnmatch;
-      int matchres = 0;
-
-      f = start;
-      while (f)
+      if (action == GLOB_GLOBALL)
         {
-          matchres = matcher (u->file, f->name, 0);
+          int (*matcher) (const char *, const char *, int)
+            = opt.ignore_case ? fnmatch_nocase : fnmatch;
+          int matchres = 0;
+
+          f = start;
+          while (f)
+            {
+              matchres = matcher (u->file, f->name, 0);
+              if (matchres == -1)
+                {
+                  logprintf (LOG_NOTQUIET, _("Error matching %s against %s: %s\n"),
+                             u->file, quotearg_style (escape_quoting_style, f->name), 
+                             strerror (errno));
+                  break;
+                }
+              if (matchres == FNM_NOMATCH)
+                f = delelement (f, &start); /* delete the element from the list */
+              else
+                f = f->next;        /* leave the element in the list */
+            }
           if (matchres == -1)
             {
-              logprintf (LOG_NOTQUIET, "%s: %s\n", con->target,
-                         strerror (errno));
-              break;
+              freefileinfo (start);
+              return RETRBADPATTERN;
             }
-          if (matchres == FNM_NOMATCH)
-            f = delelement (f, &start); /* delete the element from the list */
-          else
-            f = f->next;        /* leave the element in the list */
         }
-      if (matchres == -1)
+      else if (action == GLOB_GETONE)
         {
-          freefileinfo (start);
-          return RETRBADPATTERN;
+          int (*cmp) (const char *, const char *)
+            = opt.ignore_case ? strcasecmp : strcmp;
+          f = start;
+          while (f)
+            {
+              if (0 != cmp(u->file, f->name))
+                f = delelement (f, &start);
+              else
+                f = f->next;
+            }
         }
     }
   if (start)
@@ -1744,15 +1813,15 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
       /* Just get everything.  */
       ftp_retrieve_list (u, start, con);
     }
-  else if (!start)
+  else
     {
       if (action == GLOB_GLOBALL)
         {
           /* No luck.  */
           /* #### This message SUCKS.  We should see what was the
              reason that nothing was retrieved.  */
-          logprintf (LOG_VERBOSE, _("No matches on pattern `%s'.\n"),
-                     escnonprint (u->file));
+          logprintf (LOG_VERBOSE, _("No matches on pattern %s.\n"),
+                     quote (u->file));
         }
       else /* GLOB_GETONE or GLOB_GETALL */
         {
@@ -1817,13 +1886,13 @@ ftp_loop (struct url *u, int *dt, struct url *proxy, bool recursive, bool glob)
                       else
                         sz = -1;
                       logprintf (LOG_NOTQUIET,
-                                 _("Wrote HTML-ized index to `%s' [%s].\n"),
-                                 filename, number_to_static_string (sz));
+                                 _("Wrote HTML-ized index to %s [%s].\n"),
+                                 quote (filename), number_to_static_string (sz));
                     }
                   else
                     logprintf (LOG_NOTQUIET,
-                               _("Wrote HTML-ized index to `%s'.\n"),
-                               filename);
+                               _("Wrote HTML-ized index to %s.\n"),
+                               quote (filename));
                 }
               xfree (filename);
             }