]> sjero.net Git - wget/blobdiff - src/ftp.c
mass change: update copyright years.
[wget] / src / ftp.c
index 9d4344a8b9e57420d55f9a33932f7a91c9738716..2cc341bd2a09f70f561f6d5b26f0364ffabdc9dc 100644 (file)
--- a/src/ftp.c
+++ b/src/ftp.c
@@ -1,6 +1,7 @@
 /* File Transfer Protocol support.
-   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+   2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
+   Inc.
 
 This file is part of GNU Wget.
 
@@ -34,9 +35,7 @@ as that of the covered work.  */
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <unistd.h>
 #include <assert.h>
 #include <errno.h>
 #include <time.h>
@@ -69,7 +68,7 @@ typedef struct
   int cmd;                      /* command code */
   int csock;                    /* control connection socket */
   double dltime;                /* time of the download in msecs */
-  enum stype rs;                /* remote system reported by ftp server */ 
+  enum stype rs;                /* remote system reported by ftp server */
   char *id;                     /* initial directory */
   char *target;                 /* target file name */
   struct url *proxy;            /* FTWK-style proxy */
@@ -110,7 +109,7 @@ ftp_expected_bytes (const char *s)
 }
 
 #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.
  */
@@ -125,8 +124,8 @@ ftp_do_pasv (int csock, ip_address *addr, int *port)
   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 
+  /* 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->family)
     {
@@ -155,7 +154,7 @@ ftp_do_pasv (int csock, ip_address *addr, int *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.
  */
@@ -168,8 +167,8 @@ ftp_do_port (int csock, int *local_sock)
   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 
+  /* 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.family)
     {
@@ -241,7 +240,7 @@ static uerr_t ftp_get_listing (struct url *, ccon *, struct fileinfo **);
    and closes the control connection in case of error.  */
 static uerr_t
 getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread,
-        wgint restval, ccon *con)
+        wgint restval, ccon *con, int count)
 {
   int csock, dtsock, local_sock, res;
   uerr_t err = RETROK;          /* appease the compiler */
@@ -252,6 +251,7 @@ getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread,
   int cmd = con->cmd;
   bool pasv_mode_open = false;
   wgint expected_bytes = 0;
+  bool got_expected_bytes = false;
   bool rest_failed = false;
   int flags;
   wgint rd_size;
@@ -312,7 +312,7 @@ getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread,
         con->csock = -1;
 
       /* Second: Login with proper USER/PASS sequence.  */
-      logprintf (LOG_VERBOSE, _("Logging in as %s ... "), 
+      logprintf (LOG_VERBOSE, _("Logging in as %s ... "),
                  quotearg_style (escape_quoting_style, user));
       if (opt.server_response)
         logputs (LOG_ALWAYS, "\n");
@@ -429,7 +429,7 @@ Error in server response, closing control connection.\n"));
       */
 
       /* VMS will report something like "PUB$DEVICE:[INITIAL.FOLDER]".
-         Convert it to "/INITIAL/FOLDER" */ 
+         Convert it to "/INITIAL/FOLDER" */
       if (con->rs == ST_VMS)
         {
           char *path = strchr (con->id, '[');
@@ -502,10 +502,10 @@ Error in server response, closing control connection.\n"));
         logputs (LOG_VERBOSE, _("==> CWD not needed.\n"));
       else
         {
-          char *targ;
-         int cwd_count;
-         int cwd_end;
-         int cwd_start;
+          char *targ = NULL;
+          int cwd_count;
+          int cwd_end;
+          int cwd_start;
 
           char *target = u->dir;
 
@@ -529,7 +529,7 @@ Error in server response, closing control connection.\n"));
                Why is this wise even on UNIX?  It certainly fouls VMS.
                See below for a more reliable, more universal method.
             */
+
             /* 2008-04-22 MJC.
                I'm not crazy about it either. I'm informed it's useful
                for misconfigured servers that have some dirs in the path
@@ -603,7 +603,7 @@ Error in server response, closing control connection.\n"));
 #endif /* 0 */
 
           /* 2004-09-20 SMS.
-             A relative directory is relative to the initial directory. 
+             A relative directory is relative to the initial directory.
              Thus, what _is_ useful on VMS (and probably elsewhere) is
              to CWD to the initial directory (ideally, whatever the
              server reports, _exactly_, NOT badly UNIX-ixed), and then
@@ -690,7 +690,7 @@ Error in server response, closing control connection.\n"));
           if (!opt.server_response)
             logprintf (LOG_VERBOSE, "==> CWD (%d) %s ... ", cwd_count,
                        quotearg_style (escape_quoting_style, target));
-          err = ftp_cwd (csock, target);
+          err = ftp_cwd (csock, targ);
           /* FTPRERR, WRITEFAILED, FTPNSFOD */
           switch (err)
             {
@@ -738,7 +738,7 @@ Error in server response, closing control connection.\n"));
       if (opt.verbose)
         {
           if (!opt.server_response)
-            logprintf (LOG_VERBOSE, "==> SIZE %s ... ", 
+            logprintf (LOG_VERBOSE, "==> SIZE %s ... ",
                        quotearg_style (escape_quoting_style, u->file));
         }
 
@@ -755,6 +755,7 @@ Error in server response, closing control connection.\n"));
           con->csock = -1;
           return err;
         case FTPOK:
+          got_expected_bytes = true;
           /* Everything is OK.  */
           break;
         default:
@@ -765,6 +766,16 @@ Error in server response, closing control connection.\n"));
                      number_to_static_string (expected_bytes));
     }
 
+  if (cmd & DO_RETR && restval > 0 && restval == expected_bytes)
+    {
+      /* Server confirms that file has length restval. We should stop now.
+         Some servers (f.e. NcFTPd) return error when receive REST 0 */
+      logputs (LOG_VERBOSE, _("File has already been retrieved.\n"));
+      fd_close (csock);
+      con->csock = -1;
+      return RETRFINISHED;
+    }
+
   /* If anything is to be retrieved, PORT (or PASV) must be sent.  */
   if (cmd & (DO_LIST | DO_RETR))
     {
@@ -805,7 +816,7 @@ Error in server response, closing control connection.\n"));
             }   /* switch (err) */
           if (err==FTPOK)
             {
-              DEBUGP (("trying to connect to %s port %d\n", 
+              DEBUGP (("trying to connect to %s port %d\n",
                       print_address (&passive_addr), passive_port));
               dtsock = connect_to_ip (&passive_addr, passive_port, NULL);
               if (dtsock < 0)
@@ -935,14 +946,14 @@ Error in server response, closing control connection.\n"));
          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 
+         /* 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) 
+             while (f)
                {
                  if (!strcmp (f->name, u->file))
                    {
@@ -977,7 +988,7 @@ Error in server response, closing control connection.\n"));
             {
               if (restval)
                 logputs (LOG_VERBOSE, "\n");
-              logprintf (LOG_VERBOSE, "==> RETR %s ... ", 
+              logprintf (LOG_VERBOSE, "==> RETR %s ... ",
                          quotearg_style (escape_quoting_style, u->file));
             }
         }
@@ -1019,7 +1030,9 @@ Error in server response, closing control connection.\n"));
 
       if (!opt.server_response)
         logputs (LOG_VERBOSE, _("done.\n"));
-      expected_bytes = ftp_expected_bytes (ftp_last_respline);
+
+      if (! got_expected_bytes)
+        expected_bytes = ftp_expected_bytes (ftp_last_respline);
     } /* do retrieve */
 
   if (cmd & DO_LIST)
@@ -1065,7 +1078,9 @@ Error in server response, closing control connection.\n"));
         }
       if (!opt.server_response)
         logputs (LOG_VERBOSE, _("done.\n"));
-      expected_bytes = ftp_expected_bytes (ftp_last_respline);
+
+      if (! got_expected_bytes)
+        expected_bytes = ftp_expected_bytes (ftp_last_respline);
     } /* cmd & DO_LIST */
 
   if (!(cmd & (DO_LIST | DO_RETR)) || (opt.spider && !(cmd & DO_LIST)))
@@ -1095,7 +1110,7 @@ Error in server response, closing control connection.\n"));
     }
 
   /* Open the file -- if output_stream is set, use it instead.  */
-  
+
   /* 2005-04-17 SMS.
      Note that having the output_stream ("-O") file opened in main()
      (main.c) rather limits the ability in VMS to open the file
@@ -1116,7 +1131,7 @@ Error in server response, closing control connection.\n"));
           con->target = targ;
         }
 #endif /* def __VMS */
+
       mkalldirs (con->target);
       if (opt.backups)
         rotate_backups (con->target);
@@ -1135,7 +1150,7 @@ Error in server response, closing control connection.\n"));
 #else /* def __VMS */
 # define BIN_TYPE_FILE 1
 #endif /* def __VMS [else] */
+
       if (restval && !(con->cmd & DO_LIST))
         {
 #ifdef __VMS
@@ -1156,8 +1171,23 @@ Error in server response, closing control connection.\n"));
 #endif /* def __VMS [else] */
         }
       else if (opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct
-               || opt.output_document)
-        {
+               || opt.output_document || count > 0)
+        {        
+         if (opt.unlink && file_exists_p (con->target))
+           {
+             int res = unlink (con->target);
+             if (res < 0)
+               {
+                 logprintf (LOG_NOTQUIET, "%s: %s\n", con->target,
+                            strerror (errno));
+                 fd_close (csock);
+                 con->csock = -1;
+                 fd_close (dtsock);
+                 fd_close (local_sock);
+                 return UNLINKERR;
+               }
+           }
+
 #ifdef __VMS
           int open_id;
 
@@ -1331,7 +1361,7 @@ 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", 
+              logprintf (LOG_ALWAYS, "%s\n",
                          quotearg_style (escape_quoting_style, line));
               xfree (line);
             }
@@ -1348,7 +1378,7 @@ Error in server response, closing control connection.\n"));
    This loop either gets commands from con, or (if ON_YOUR_OWN is
    set), makes them up to retrieve the file given by the URL.  */
 static uerr_t
-ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
+ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con, char **local_file)
 {
   int count, orig_lp;
   wgint restval, len = 0, qtyread = 0;
@@ -1366,7 +1396,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
   else
     {
       /* URL-derived file.  Consider "-O file" name. */
-      con->target = url_file_name (u);
+      con->target = url_file_name (u, NULL);
       if (!opt.output_document)
         locf = con->target;
       else
@@ -1453,11 +1483,11 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
           xfree (hurl);
         }
       /* Send getftp the proper length, if fileinfo was provided.  */
-      if (f)
+      if (f && f->type != FT_SYMLINK)
         len = f->size;
       else
         len = 0;
-      err = getftp (u, len, &qtyread, restval, con);
+      err = getftp (u, len, &qtyread, restval, con, count);
 
       if (con->csock == -1)
         con->st &= ~DONE_CWD;
@@ -1468,6 +1498,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
         {
         case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR:
         case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
+        case UNLINKERR:
           /* Fatal errors, give up.  */
           return err;
         case CONSOCKERR: case CONERROR: case FTPSRVERR: case FTPRERR:
@@ -1480,7 +1511,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
             {
               /* Re-determine the file name. */
               xfree_null (con->target);
-              con->target = url_file_name (u);
+              con->target = url_file_name (u, NULL);
               locf = con->target;
             }
           continue;
@@ -1561,7 +1592,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
           total_downloaded_bytes += qtyread;
           numurls++;
 
-          if (opt.delete_after)
+          if (opt.delete_after && !input_file_url (opt.input_filename))
             {
               DEBUGP (("\
 Removing file due to --delete-after in ftp_loop_internal():\n"));
@@ -1576,6 +1607,10 @@ Removing file due to --delete-after in ftp_loop_internal():\n"));
         con->cmd |= LEAVE_PENDING;
       else
         con->cmd &= ~LEAVE_PENDING;
+
+      if (local_file)
+        *local_file = xstrdup (locf);
+
       return RETROK;
     } while (!opt.ntry || (count < opt.ntry));
 
@@ -1604,14 +1639,14 @@ 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_file_name (u);
+  uf = url_file_name (u, NULL);
   lf = file_merge (uf, LIST_FILENAME);
   xfree (uf);
   DEBUGP ((_("Using %s as listing tmp file.\n"), quote (lf)));
 
   con->target = xstrdup (lf);
   xfree (lf);
-  err = ftp_loop_internal (u, NULL, con);
+  err = ftp_loop_internal (u, NULL, con, NULL);
   lf = xstrdup (con->target);
   xfree (con->target);
   con->target = old_target;
@@ -1698,7 +1733,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_file_name (u);
+      con->target = url_file_name (u, NULL);
       err = RETROK;
 
       dlthis = true;
@@ -1806,7 +1841,7 @@ Already have correct symlink %s -> %s\n\n"),
           else                /* opt.retr_symlinks */
             {
               if (dlthis)
-                err = ftp_loop_internal (u, f, con);
+                err = ftp_loop_internal (u, f, con, NULL);
             } /* opt.retr_symlinks */
           break;
         case FT_DIRECTORY:
@@ -1817,7 +1852,7 @@ Already have correct symlink %s -> %s\n\n"),
         case FT_PLAINFILE:
           /* Call the retrieve loop.  */
           if (dlthis)
-            err = ftp_loop_internal (u, f, con);
+            err = ftp_loop_internal (u, f, con, NULL);
           break;
         case FT_UNKNOWN:
           logprintf (LOG_NOTQUIET, _("%s: unknown/unsupported file type.\n"),
@@ -1849,7 +1884,8 @@ Already have correct symlink %s -> %s\n\n"),
          original.  :( */
       if (actual_target != NULL)
         {
-          if (!(f->type == FT_SYMLINK && !opt.retr_symlinks)
+          if (opt.useservertimestamps
+              && !(f->type == FT_SYMLINK && !opt.retr_symlinks)
               && f->tstamp != -1
               && dlthis
               && file_exists_p (con->target))
@@ -2033,7 +2069,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
               if (matchres == -1)
                 {
                   logprintf (LOG_NOTQUIET, _("Error matching %s against %s: %s\n"),
-                             u->file, quotearg_style (escape_quoting_style, f->name), 
+                             u->file, quotearg_style (escape_quoting_style, f->name),
                              strerror (errno));
                   break;
                 }
@@ -2095,7 +2131,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
         {
           /* Let's try retrieving it anyway.  */
           con->st |= ON_YOUR_OWN;
-          res = ftp_loop_internal (u, NULL, con);
+          res = ftp_loop_internal (u, NULL, con, NULL);
           return res;
         }
 
@@ -2116,7 +2152,8 @@ 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, struct url *proxy, bool recursive, bool glob)
+ftp_loop (struct url *u, char **local_file, int *dt, struct url *proxy,
+          bool recursive, bool glob)
 {
   ccon con;                     /* FTP connection */
   uerr_t res;
@@ -2146,7 +2183,7 @@ ftp_loop (struct url *u, int *dt, struct url *proxy, bool recursive, bool glob)
               char *filename = (opt.output_document
                                 ? xstrdup (opt.output_document)
                                 : (con.target ? xstrdup (con.target)
-                                   : url_file_name (u)));
+                                   : url_file_name (u, NULL)));
               res = ftp_index (filename, u, f);
               if (res == FTPOK && opt.verbose)
                 {
@@ -2195,7 +2232,7 @@ ftp_loop (struct url *u, int *dt, struct url *proxy, bool recursive, bool glob)
                                    ispattern ? GLOB_GLOBALL : GLOB_GETONE);
         }
       else
-        res = ftp_loop_internal (u, NULL, &con);
+        res = ftp_loop_internal (u, NULL, &con, local_file);
     }
   if (res == FTPOK)
     res = RETROK;