]> sjero.net Git - wget/blobdiff - src/ftp.c
[svn] Added support for cookies.
[wget] / src / ftp.c
index a3fc3d333eaa115429027d14c07ce29e5ddfcded..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"));
 
@@ -356,10 +355,10 @@ Error in server response, closing control connection.\n"));
          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:
                  {
@@ -368,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);
@@ -377,16 +376,14 @@ 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 ();
@@ -643,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;
@@ -804,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);
        }
     }
@@ -834,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.  */
@@ -948,7 +966,7 @@ Error in server response, closing control connection.\n"));
 static uerr_t
 ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con)
 {
-  int count, orig_lp;
+  int count, orig_lp, no_truncate;
   long restval, len;
   char *tms, *tmrate, *locf;
   uerr_t err;
@@ -979,6 +997,13 @@ 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
     {
@@ -1005,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);
@@ -1037,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;
@@ -1047,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;
@@ -1151,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;
@@ -1172,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))
@@ -1184,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 *,
@@ -1254,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.
@@ -1263,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 no 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)
        {
@@ -1365,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);
@@ -1427,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))
@@ -1467,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.  */
@@ -1555,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
@@ -1564,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)
            {
@@ -1620,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;
 }