]> sjero.net Git - wget/blobdiff - src/ftp.c
[svn] Added support for cookies.
[wget] / src / ftp.c
index fd25d3b5a787437c19b03e7ce69f2f8543e7430f..e9b686097cd28c3c980c7f6c9d60cfa4de60fee8 100644 (file)
--- a/src/ftp.c
+++ b/src/ftp.c
@@ -26,13 +26,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #else
 # include <strings.h>
 #endif
-#include <ctype.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #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"
@@ -49,7 +51,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 extern int errno;
 #endif
 #ifndef h_errno
+# ifndef __CYGWIN__
 extern int h_errno;
+# endif
 #endif
 
 /* File where the "ls -al" listing will be saved.  */
@@ -57,12 +61,6 @@ extern int h_errno;
 
 extern char ftp_last_respline[];
 
-/* #### Global variables??  These two should be members of struct
-   ccon!  */
-
-static enum stype host_type=ST_UNIX;
-static char *pwd;
-
 /* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in
    the string S, and return the number converted to long, if found, 0
    otherwise.  */
@@ -109,7 +107,7 @@ ftp_expected_bytes (const char *s)
    connection to the server.  It always closes the data connection,
    and closes the control connection in case of error.  */
 static uerr_t
-getftp (const struct urlinfo *u, long *len, long restval, ccon *con)
+getftp (struct urlinfo *u, long *len, long restval, ccon *con)
 {
   int csock, dtsock, res;
   uerr_t err;
@@ -249,7 +247,7 @@ Error in server response, closing control connection.\n"));
       /* Third: Get the system type */
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> SYST ... ");
-      err = ftp_syst (&con->rbuf, &host_type);
+      err = ftp_syst (&con->rbuf, &con->rs);
       /* FTPRERR */
       switch (err)
        {
@@ -280,11 +278,12 @@ Error in server response, closing control connection.\n"));
 
       if (!opt.server_response)
        logprintf (LOG_VERBOSE, "==> PWD ... ");
-      err = ftp_pwd(&con->rbuf, &pwd);
+      err = ftp_pwd(&con->rbuf, &con->id);
       /* FTPRERR */
       switch (err)
-      {
-       case FTPRERR || FTPSRVERR :
+       {
+       case FTPRERR:
+       case FTPSRVERR :
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("\
 Error in server response, closing control connection.\n"));
@@ -298,7 +297,7 @@ Error in server response, closing control connection.\n"));
        default:
          abort ();
          break;
-      }
+       }
       if (!opt.server_response)
        logputs (LOG_VERBOSE, _("done.\n"));
 
@@ -353,12 +352,13 @@ Error in server response, closing control connection.\n"));
          /* Change working directory. If the FTP host runs VMS and
              the path specified is absolute, we will have to convert
              it to VMS style as VMS does not like leading slashes */
+         DEBUGP (("changing working directory\n"));
          if (*(u->dir) == '/')
            {
-             int pwd_len = strlen (pwd);
+             int pwd_len = strlen (con->id);
              char *result = (char *)alloca (strlen (u->dir) + pwd_len + 10);
              *result = '\0';
-             switch (host_type)
+             switch (con->rs)
                {
                case ST_VMS:
                  {
@@ -367,7 +367,7 @@ Error in server response, closing control connection.\n"));
                    for (tmpp = tmp_dir; *tmpp; tmpp++)
                      if (*tmpp=='/')
                        *tmpp = '.';
-                   strcpy (result, pwd);
+                   strcpy (result, con->id);
                    /* pwd ends with ']', we have to get rid of it */
                    result[pwd_len - 1]= '\0';
                    strcat (result, tmp_dir);
@@ -375,16 +375,15 @@ Error in server response, closing control connection.\n"));
                  }
                  break;
                case ST_UNIX:
+               case ST_WINNT:
+               case ST_MACOS:
                  /* pwd_len == 1 means pwd = "/", but u->dir begins with '/'
                     already */
                  if (pwd_len > 1)
-                   strcpy (result, pwd);
+                   strcpy (result, con->id);
                  strcat (result, u->dir);
-                 /* These look like debugging messages to me.  */
-#if 0
-                 logprintf (LOG_VERBOSE, "\npwd=\"%s\"", pwd);
-                 logprintf (LOG_VERBOSE, "\nu->dir=\"%s\"", u->dir);
-#endif
+                 DEBUGP(("\npwd=\"%s\"", con->id));
+                 DEBUGP(("\nu->dir=\"%s\"", u->dir));
                  break;
                default:
                  abort ();
@@ -641,6 +640,19 @@ Error in server response, closing control connection.\n"));
          return err;
          break;
        case FTPRESTFAIL:
+         /* If `-c' is specified and the file already existed when
+            Wget was started, it would be a bad idea for us to start
+            downloading it from scratch, effectively truncating it.  */
+         if (opt.always_rest && (cmd & NO_TRUNCATE))
+           {
+             logprintf (LOG_NOTQUIET,
+                        _("\nREST failed; will not truncate `%s'.\n"),
+                        u->local);
+             CLOSE (csock);
+             closeport (dtsock);
+             rbuf_uninitialize (&con->rbuf);
+             return CONTNOTSUPPORTED;
+           }
          logputs (LOG_VERBOSE, _("\nREST failed, starting from scratch.\n"));
          restval = 0L;
          break;
@@ -802,12 +814,20 @@ Error in server response, closing control connection.\n"));
     }
   else
     {
+      extern int global_download_count;
       fp = opt.dfp;
-      if (!restval)
+
+      /* 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)
        {
          /* This will silently fail for streams that don't correspond
             to regular files, but that's OK.  */
          rewind (fp);
+         /* ftruncate is needed because opt.dfp is opened in append
+            mode if opt.always_rest is set.  */
+         ftruncate (fileno (fp), 0);
          clearerr (fp);
        }
     }
@@ -832,7 +852,7 @@ Error in server response, closing control connection.\n"));
   res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf, 0);
   con->dltime = elapsed_time ();
   tms = time_str (NULL);
-  tmrate = rate (*len - restval, con->dltime);
+  tmrate = rate (*len - restval, con->dltime, 0);
   /* Close data connection socket.  */
   closeport (dtsock);
   /* Close the local file.  */
@@ -946,9 +966,7 @@ Error in server response, closing control connection.\n"));
 static uerr_t
 ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con)
 {
-  static int first_retrieval = 1;
-
-  int count, orig_lp;
+  int count, orig_lp, no_truncate;
   long restval, len;
   char *tms, *tmrate, *locf;
   uerr_t err;
@@ -979,28 +997,19 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con)
 
   orig_lp = con->cmd & LEAVE_PENDING ? 1 : 0;
 
+  /* In `-c' is used, check whether the file we're writing to exists
+     before we've done anything.  If so, we'll refuse to truncate it
+     if the server doesn't support continued downloads.  */
+  no_truncate = 0;
+  if (opt.always_rest)
+    no_truncate = file_exists_p (locf);
+
   /* THE loop.  */
   do
     {
       /* Increment the pass counter.  */
       ++count;
-      /* Wait before the retrieval (unless this is the very first
-        retrieval).
-        Check if we are retrying or not, wait accordingly - HEH */
-      if (!first_retrieval && (opt.wait || (count && opt.waitretry)))
-       {
-         if (count)
-           {
-             if (count<opt.waitretry)
-               sleep(count);
-             else
-               sleep(opt.waitretry);
-           }
-         else
-           sleep (opt.wait);
-       }
-      if (first_retrieval)
-       first_retrieval = 0;
+      sleep_between_retrievals (count);
       if (con->st & ON_YOUR_OWN)
        {
          con->cmd = 0;
@@ -1021,12 +1030,14 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con)
          else
            con->cmd |= DO_CWD;
        }
+      if (no_truncate)
+       con->cmd |= NO_TRUNCATE;
       /* Assume no restarting.  */
       restval = 0L;
       if ((count > 1 || opt.always_rest)
          && !(con->cmd & DO_LIST)
-         && file_exists_p (u->local))
-       if (stat (u->local, &st) == 0)
+         && file_exists_p (locf))
+       if (stat (locf, &st) == 0 && S_ISREG (st.st_mode))
          restval = st.st_size;
       /* Get the current time string.  */
       tms = time_str (NULL);
@@ -1053,7 +1064,7 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con)
       err = getftp (u, &len, restval, con);
       /* Time?  */
       tms = time_str (NULL);
-      tmrate = rate (len - restval, con->dltime);
+      tmrate = rate (len - restval, con->dltime, 0);
 
       if (!rbuf_initialized_p (&con->rbuf))
        con->st &= ~DONE_CWD;
@@ -1063,7 +1074,7 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con)
       switch (err)
        {
        case HOSTERR: case CONREFUSED: case FWRITEERR: case FOPENERR:
-       case FTPNSFOD: case FTPLOGINC: case FTPNOPASV:
+       case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
          /* Fatal errors, give up.  */
          return err;
          break;
@@ -1167,10 +1178,9 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con)
 
 /* Return the directory listing in a reusable format.  The directory
    is specifed in u->dir.  */
-static struct fileinfo *
-ftp_get_listing (struct urlinfo *u, ccon *con)
+uerr_t
+ftp_get_listing (struct urlinfo *u, ccon *con, struct fileinfo **f)
 {
-  struct fileinfo *f;
   uerr_t err;
   char *olocal = u->local;
   char *list_filename, *ofile;
@@ -1188,9 +1198,9 @@ ftp_get_listing (struct urlinfo *u, ccon *con)
   err = ftp_loop_internal (u, NULL, con);
   u->local = olocal;
   if (err == RETROK)
-    f = ftp_parse_ls (list_filename, host_type);
+    *f = ftp_parse_ls (list_filename, con->rs);
   else
-    f = NULL;
+    *f = NULL;
   if (opt.remove_listing)
     {
       if (unlink (list_filename))
@@ -1200,7 +1210,7 @@ ftp_get_listing (struct urlinfo *u, ccon *con)
     }
   xfree (list_filename);
   con->cmd &= ~DO_LIST;
-  return f;
+  return err;
 }
 
 static uerr_t ftp_retrieve_dirs PARAMS ((struct urlinfo *, struct fileinfo *,
@@ -1270,7 +1280,7 @@ ftp_retrieve_list (struct urlinfo *u, struct fileinfo *f, ccon *con)
 
       dlthis = 1;
       if (opt.timestamping && f->type == FT_PLAINFILE)
-       {
+        {
          struct stat st;
          /* If conversion of HTML files retrieved via FTP is ever implemented,
             we'll need to stat() <file>.orig here when -K has been specified.
@@ -1279,30 +1289,38 @@ ftp_retrieve_list (struct urlinfo *u, struct fileinfo *f, ccon *con)
             .orig suffix. */
          if (!stat (u->local, &st))
            {
+              int eq_size;
+              int cor_val;
              /* Else, get it from the file.  */
              local_size = st.st_size;
              tml = st.st_mtime;
-             if (local_size == f->size && tml >= f->tstamp)
+              /* Compare file sizes only for servers that tell us correct
+                 values. Assumme sizes being equal for servers that lie
+                 about file size.  */
+              cor_val = (con->rs == ST_UNIX || con->rs == ST_WINNT);
+              eq_size = cor_val ? (local_size == f->size) : 1 ;
+             if (f->tstamp <= tml && eq_size)
                {
-                 logprintf (LOG_VERBOSE, _("\
-Server file not newer than local file `%s' -- not retrieving.\n\n"), u->local);
+                 /* 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"), u->local);
                  dlthis = 0;
                }
-             else if (local_size != f->size)
-               {
-                 if (host_type == ST_VMS)
-                   {
-                     logprintf (LOG_VERBOSE, _("\
-Cannot compare sizes, remote system is VMS.\n"));
-                     dlthis = 0;
-                   }
-                 else
-                   {
-                     logprintf (LOG_VERBOSE, _("\
-The sizes do not match (local %ld) -- retrieving.\n"), local_size);
-                   }
-               }
-           }
+             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"),
+                             u->local);
+                }
+              else
+                {
+                  /* Sizes do not match */
+                  logprintf (LOG_VERBOSE, _("\
+The sizes do not match (local %ld) -- retrieving.\n\n"), local_size);
+                }
+            }
        }       /* opt.timestamping && f->type == FT_PLAINFILE */
       switch (f->type)
        {
@@ -1381,13 +1399,23 @@ Already have correct symlink %s -> %s\n\n"),
       /* Set the time-stamp information to the local file.  Symlinks
         are not to be stamped because it sets the stamp on the
         original.  :( */
-      if (!opt.dfp
-         && !(f->type == FT_SYMLINK && !opt.retr_symlinks)
+      if (!(f->type == FT_SYMLINK && !opt.retr_symlinks)
          && f->tstamp != -1
           && dlthis
          && file_exists_p (u->local))
        {
-         touch (u->local, f->tstamp);
+         /* #### This code repeats in http.c and ftp.c.  Move it to a
+             function!  */
+         const char *fl = NULL;
+         if (opt.output_document)
+           {
+             if (opt.od_known_regular)
+               fl = opt.output_document;
+           }
+         else
+           fl = u->local;
+         if (fl)
+           touch (fl, f->tstamp);
        }
       else if (f->tstamp == -1)
        logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), u->local);
@@ -1443,9 +1471,6 @@ ftp_retrieve_dirs (struct urlinfo *u, struct fileinfo *f, ccon *con)
       if (len > current_length)
        current_container = (char *)alloca (len);
       u->dir = current_container;
-      /* When retrieving recursively, all directories must be
-        absolute.  This restriction will (hopefully!) be lifted in
-        the future.  */
       sprintf (u->dir, "/%s%s%s", odir + (*odir == '/'),
              (!*odir || (*odir == '/' && !* (odir + 1))) ? "" : "/", f->name);
       if (!accdir (u->dir, ALLABS))
@@ -1483,7 +1508,9 @@ ftp_retrieve_glob (struct urlinfo *u, ccon *con, int action)
 
   con->cmd |= LEAVE_PENDING;
 
-  orig = ftp_get_listing (u, con);
+  res = ftp_get_listing (u, con, &orig);
+  if (res != RETROK)
+    return res;
   start = orig;
   /* First: weed out that do not conform the global rules given in
      opt.accepts and opt.rejects.  */
@@ -1571,8 +1598,12 @@ ftp_loop (struct urlinfo *u, int *dt)
 
   *dt = 0;
 
+  memset (&con, 0, sizeof (con));
+
   rbuf_uninitialize (&con.rbuf);
   con.st = ON_YOUR_OWN;
+  con.rs = ST_UNIX;
+  con.id = NULL;
   res = RETROK;                        /* in case it's not used */
 
   /* If the file name is empty, the user probably wants a directory
@@ -1580,9 +1611,10 @@ ftp_loop (struct urlinfo *u, int *dt)
      opt.htmlify is 0, of course.  :-) */
   if (!*u->file && !opt.recursive)
     {
-      struct fileinfo *f = ftp_get_listing (u, &con);
+      struct fileinfo *f;
+      res = ftp_get_listing (u, &con, &f);
 
-      if (f)
+      if (res == RETROK)
        {
          if (opt.htmlify)
            {
@@ -1636,8 +1668,8 @@ ftp_loop (struct urlinfo *u, int *dt)
   /* If a connection was left, quench it.  */
   if (rbuf_initialized_p (&con.rbuf))
     CLOSE (RBUF_FD (&con.rbuf));
-  FREE_MAYBE (pwd);
-  pwd = NULL;
+  FREE_MAYBE (con.id);
+  con.id = NULL;
   return res;
 }