X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Fftp.c;h=966b90b56faa3227a3e54dbdf106ffedfcbba9cd;hp=bf50ab326b9a1fb5c5d5409323b7ed7b4a0305e0;hb=9228f0bf53d3b42459daeb28372196a007de3014;hpb=963863113f53c23446d50cf6224096718b9367bb diff --git a/src/ftp.c b/src/ftp.c index bf50ab32..966b90b5 100644 --- a/src/ftp.c +++ b/src/ftp.c @@ -1,21 +1,32 @@ /* File Transfer Protocol support. - Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001 + Free Software Foundation, Inc. -This file is part of Wget. +This file is part of GNU Wget. -This program is free software; you can redistribute it and/or modify +GNU Wget is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. -This program is distributed in the hope that it will be useful, +GNU Wget is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +along with Wget; if not, write to the Free Software +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 @@ -32,9 +43,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include -#ifndef WINDOWS -# include /* for h_errno */ -#endif #include "wget.h" #include "utils.h" @@ -50,17 +58,25 @@ 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" extern char ftp_last_respline[]; +typedef struct +{ + int st; /* connection status */ + int cmd; /* command code */ + struct rbuf rbuf; /* control connection buffer */ + 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; + + /* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in the string S, and return the number converted to long, if found, 0 otherwise. */ @@ -107,20 +123,20 @@ 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 (struct urlinfo *u, long *len, long restval, ccon *con) +getftp (struct url *u, long *len, long restval, ccon *con) { int csock, dtsock, res; uerr_t err; FILE *fp; char *user, *passwd, *respline; char *tms, *tmrate; - 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); - assert (u->local != NULL); + assert (con->target != NULL); + /* Debug-check of the sanity of the request by making sure that LIST and RETR are never both requested (since we can handle only one at a time. */ @@ -132,8 +148,6 @@ getftp (struct urlinfo *u, long *len, long restval, ccon *con) passwd = u->passwd; search_netrc (u->host, (const char **)&user, (const char **)&passwd, 1); user = user ? user : opt.ftp_acc; - if (!opt.ftp_pass) - opt.ftp_pass = ftp_getaddress (); passwd = passwd ? passwd : opt.ftp_pass; assert (user && passwd); @@ -144,57 +158,53 @@ getftp (struct urlinfo *u, long *len, long restval, ccon *con) csock = RBUF_FD (&con->rbuf); else /* cmd & DO_LOGIN */ { + 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. */ - logprintf (LOG_VERBOSE, _("Connecting to %s:%hu... "), u->host, u->port); - err = make_connection (&csock, u->host, u->port); + + al = lookup_host (host, 0); + if (!al) + return HOSTERR; + 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 CONNECT_ERROR (errno); + if (cmd & LEAVE_PENDING) rbuf_initialize (&con->rbuf, csock); else rbuf_uninitialize (&con->rbuf); - switch (err) - { - /* Do not close the socket in first several cases, since it - wasn't created at all. */ - case HOSTERR: - logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_NOTQUIET, "%s: %s\n", u->host, herrmsg (h_errno)); - return HOSTERR; - break; - case CONSOCKERR: - logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno)); - return CONSOCKERR; - break; - case CONREFUSED: - logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_NOTQUIET, _("Connection to %s:%hu refused.\n"), - u->host, u->port); - CLOSE (csock); - rbuf_uninitialize (&con->rbuf); - return CONREFUSED; - case CONERROR: - logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_NOTQUIET, "connect: %s\n", strerror (errno)); - CLOSE (csock); - rbuf_uninitialize (&con->rbuf); - return CONERROR; - break; - default: - DO_NOTHING; - /* #### Hmm? */ - } + /* Since this is a new connection, we may safely discard anything left in the buffer. */ rbuf_discard (&con->rbuf); /* Second: Login with proper USER/PASS sequence. */ - logputs (LOG_VERBOSE, _("connected!\n")); 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) { @@ -271,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 */ @@ -283,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")); @@ -291,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; @@ -325,9 +339,10 @@ Error in server response, closing control connection.\n")); logputs (LOG_VERBOSE, _("done.\n")); /* Fifth: Set the FTP type. */ + type_char = ftp_process_type (u->params); if (!opt.server_response) - logprintf (LOG_VERBOSE, "==> TYPE %c ... ", TOUPPER (u->ftp_type)); - err = ftp_type (&con->rbuf, TOUPPER (u->ftp_type)); + logprintf (LOG_VERBOSE, "==> TYPE %c ... ", type_char); + err = ftp_type (&con->rbuf, type_char); /* FTPRERR, WRITEFAILED, FTPUNKNOWNTYPE */ switch (err) { @@ -351,7 +366,7 @@ Error in server response, closing control connection.\n")); logputs (LOG_VERBOSE, "\n"); logprintf (LOG_NOTQUIET, _("Unknown type `%c', closing control connection.\n"), - TOUPPER (u->ftp_type)); + type_char); CLOSE (csock); rbuf_uninitialize (&con->rbuf); return err; @@ -378,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 + ":". */ + + 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; } @@ -462,17 +491,48 @@ Error in server response, closing control connection.\n")); else /* do not CWD */ logputs (LOG_VERBOSE, _("==> CWD not required.\n")); + if ((cmd & DO_RETR) && restval && *len == 0) + { + if (opt.verbose) + { + if (!opt.server_response) + logprintf (LOG_VERBOSE, "==> SIZE %s ... ", u->file); + } + + err = ftp_size(&con->rbuf, u->file, len); + /* FTPRERR */ + switch (err) + { + case FTPRERR: + case FTPSRVERR : + logputs (LOG_VERBOSE, "\n"); + logputs (LOG_NOTQUIET, _("\ +Error in server response, closing control connection.\n")); + CLOSE (csock); + rbuf_uninitialize (&con->rbuf); + return err; + break; + case FTPOK: + /* Everything is OK. */ + break; + default: + abort (); + break; + } + if (!opt.server_response) + logputs (LOG_VERBOSE, _("done.\n")); + } + /* If anything is to be retrieved, PORT (or PASV) must be sent. */ if (cmd & (DO_LIST | DO_RETR)) { 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) { @@ -506,62 +566,28 @@ Error in server response, closing control connection.\n")); default: abort (); break; - } + } /* switch(err) */ if (err==FTPOK) { - 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]; - DEBUGP ((_("Will try connecting to %s:%hu.\n"), thost, tport)); - err = make_connection (&dtsock, thost, tport); - switch (err) + dtsock = connect_to_one (&passive_addr, passive_port, 1); + if (dtsock < 0) { - /* Do not close the socket in first several cases, - since it wasn't created at all. */ - case HOSTERR: - logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_NOTQUIET, "%s: %s\n", thost, - herrmsg (h_errno)); - CLOSE (csock); - rbuf_uninitialize (&con->rbuf); - return HOSTERR; - break; - case CONSOCKERR: - logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno)); - CLOSE (csock); - rbuf_uninitialize (&con->rbuf); - return CONSOCKERR; - break; - case CONREFUSED: - logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_NOTQUIET, - _("Connection to %s:%hu refused.\n"), - thost, tport); - CLOSE (csock); - rbuf_uninitialize (&con->rbuf); - closeport (dtsock); - return CONREFUSED; - case CONERROR: - logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_NOTQUIET, "connect: %s\n", - strerror (errno)); + int save_errno = errno; CLOSE (csock); rbuf_uninitialize (&con->rbuf); - closeport (dtsock); - return CONERROR; - break; - default: - /* #### What?! */ - DO_NOTHING; + 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 ... "); @@ -604,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")); @@ -669,7 +686,7 @@ Error in server response, closing control connection.\n")); { logprintf (LOG_NOTQUIET, _("\nREST failed; will not truncate `%s'.\n"), - u->local); + con->target); CLOSE (csock); closeport (dtsock); rbuf_uninitialize (&con->rbuf); @@ -691,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) @@ -802,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(). */ @@ -818,16 +847,16 @@ Error in server response, closing control connection.\n")); /* Open the file -- if opt.dfp is set, use it instead. */ if (!opt.dfp || con->cmd & DO_LIST) { - mkalldirs (u->local); + mkalldirs (con->target); if (opt.backups) - rotate_backups (u->local); + rotate_backups (con->target); /* #### Is this correct? */ - chmod (u->local, 0600); + chmod (con->target, 0600); - fp = fopen (u->local, restval ? "ab" : "wb"); + fp = fopen (con->target, restval ? "ab" : "wb"); if (!fp) { - logprintf (LOG_NOTQUIET, "%s: %s\n", u->local, strerror (errno)); + logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno)); CLOSE (csock); rbuf_uninitialize (&con->rbuf); closeport (dtsock); @@ -842,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. */ @@ -860,6 +889,7 @@ Error in server response, closing control connection.\n")); if (restval) logprintf (LOG_VERBOSE, _(" [%s to go]"), legible (*len - restval)); logputs (LOG_VERBOSE, "\n"); + expected_bytes = *len; /* for get_contents/show_progress */ } else if (expected_bytes) { @@ -869,12 +899,12 @@ Error in server response, closing control connection.\n")); legible (expected_bytes - restval)); logputs (LOG_VERBOSE, _(" (unauthoritative)\n")); } - reset_timer (); + /* Get the contents of the document. */ - res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf, 0); - con->dltime = elapsed_time (); + res = get_contents (dtsock, fp, len, restval, expected_bytes, &con->rbuf, + 0, &con->dltime); tms = time_str (NULL); - tmrate = rate (*len - restval, con->dltime, 0); + tmrate = retr_rate (*len - restval, con->dltime, 0); /* Close data connection socket. */ closeport (dtsock); /* Close the local file. */ @@ -894,7 +924,7 @@ Error in server response, closing control connection.\n")); if (res == -2) { logprintf (LOG_NOTQUIET, _("%s: %s, closing control connection.\n"), - u->local, strerror (errno)); + con->target, strerror (errno)); CLOSE (csock); rbuf_uninitialize (&con->rbuf); return FWRITEERR; @@ -959,10 +989,10 @@ Error in server response, closing control connection.\n")); print it out. */ if (opt.server_response && (con->cmd & DO_LIST)) { - mkalldirs (u->local); - fp = fopen (u->local, "r"); + mkalldirs (con->target); + fp = fopen (con->target, "r"); if (!fp) - logprintf (LOG_ALWAYS, "%s: %s\n", u->local, strerror (errno)); + logprintf (LOG_ALWAYS, "%s: %s\n", con->target, strerror (errno)); else { char *line; @@ -986,29 +1016,29 @@ 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 urlinfo *u, struct fileinfo *f, ccon *con) +ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con) { - int count, orig_lp, no_truncate; + int count, orig_lp; long restval, len; char *tms, *tmrate, *locf; uerr_t err; struct stat st; - if (!u->local) - u->local = url_filename (u); + if (!con->target) + con->target = url_file_name (u); - if (opt.noclobber && file_exists_p (u->local)) + if (opt.noclobber && file_exists_p (con->target)) { logprintf (LOG_VERBOSE, - _("File `%s' already there, not retrieving.\n"), u->local); + _("File `%s' already there, not retrieving.\n"), con->target); /* If the file is there, we suppose it's retrieved OK. */ return RETROK; } /* Remove it if it's a link. */ - remove_link (u->local); + remove_link (con->target); if (!opt.output_document) - locf = u->local; + locf = con->target; else locf = opt.output_document; @@ -1019,13 +1049,6 @@ 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 { @@ -1052,8 +1075,7 @@ 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) @@ -1061,12 +1083,20 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con) && file_exists_p (locf)) if (stat (locf, &st) == 0 && S_ISREG (st.st_mode)) restval = st.st_size; + + /* In `-c' is used, check whether the file we're writing to + exists and is of non-zero length. If so, we'll refuse to + truncate it if the server doesn't support continued + downloads. */ + if (opt.always_rest && restval > 0) + con->cmd |= NO_TRUNCATE; + /* Get the current time string. */ tms = time_str (NULL); /* Print fetch message, if opt.verbose. */ if (opt.verbose) { - char *hurl = str_url (u->proxy ? u->proxy : u, 1); + char *hurl = url_string (u, 1); char tmp[15]; strcpy (tmp, " "); if (count > 1) @@ -1084,9 +1114,6 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con) else len = 0; err = getftp (u, &len, restval, con); - /* Time? */ - tms = time_str (NULL); - tmrate = rate (len - restval, con->dltime, 0); if (!rbuf_initialized_p (&con->rbuf)) con->st &= ~DONE_CWD; @@ -1124,6 +1151,9 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con) /* Not as great. */ abort (); } + /* Time? */ + tms = time_str (NULL); + tmrate = retr_rate (len - restval, con->dltime, 0); /* If we get out of the switch above without continue'ing, we've successfully downloaded a file. Remember this fact. */ @@ -1141,7 +1171,7 @@ ftp_loop_internal (struct urlinfo *u, struct fileinfo *f, ccon *con) /* Need to hide the password from the URL. The `if' is here so that we don't do the needless allocation every time. */ - char *hurl = str_url (u->proxy ? u->proxy : u, 1); + char *hurl = url_string (u, 1); logprintf (LOG_NONVERBOSE, "%s URL: %s [%ld] -> \"%s\" [%d]\n", tms, hurl, len, locf, count); xfree (hurl); @@ -1201,43 +1231,48 @@ 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. */ uerr_t -ftp_get_listing (struct urlinfo *u, ccon *con, struct fileinfo **f) +ftp_get_listing (struct url *u, ccon *con, struct fileinfo **f) { uerr_t err; - char *olocal = u->local; - char *list_filename, *ofile; + char *uf; /* url file name */ + char *lf; /* list file name */ + char *old_target = con->target; con->st &= ~ON_YOUR_OWN; con->cmd |= (DO_LIST | LEAVE_PENDING); con->cmd &= ~DO_RETR; - /* Get the listing filename. */ - ofile = u->file; - u->file = LIST_FILENAME; - list_filename = url_filename (u); - u->file = ofile; - u->local = list_filename; - DEBUGP ((_("Using `%s' as listing tmp file.\n"), list_filename)); + + /* 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); + lf = file_merge (uf, LIST_FILENAME); + xfree (uf); + DEBUGP ((_("Using `%s' as listing tmp file.\n"), lf)); + + con->target = lf; err = ftp_loop_internal (u, NULL, con); - u->local = olocal; + con->target = old_target; + if (err == RETROK) - *f = ftp_parse_ls (list_filename, con->rs); + *f = ftp_parse_ls (lf, con->rs); else *f = NULL; if (opt.remove_listing) { - if (unlink (list_filename)) + if (unlink (lf)) logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno)); else - logprintf (LOG_VERBOSE, _("Removed `%s'.\n"), list_filename); + logprintf (LOG_VERBOSE, _("Removed `%s'.\n"), lf); } - xfree (list_filename); + xfree (lf); con->cmd &= ~DO_LIST; return err; } -static uerr_t ftp_retrieve_dirs PARAMS ((struct urlinfo *, struct fileinfo *, +static uerr_t ftp_retrieve_dirs PARAMS ((struct url *, struct fileinfo *, ccon *)); -static uerr_t ftp_retrieve_glob PARAMS ((struct urlinfo *, ccon *, int)); +static uerr_t ftp_retrieve_glob PARAMS ((struct url *, ccon *, int)); static struct fileinfo *delelement PARAMS ((struct fileinfo *, struct fileinfo **)); static void freefileinfo PARAMS ((struct fileinfo *f)); @@ -1250,11 +1285,10 @@ static void freefileinfo PARAMS ((struct fileinfo *f)); If opt.recursive is set, after all files have been retrieved, ftp_retrieve_dirs will be called to retrieve the directories. */ static uerr_t -ftp_retrieve_list (struct urlinfo *u, struct fileinfo *f, ccon *con) +ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con) { static int depth = 0; uerr_t err; - char *olocal, *ofile; struct fileinfo *orig; long local_size; time_t tml; @@ -1289,15 +1323,19 @@ ftp_retrieve_list (struct urlinfo *u, struct fileinfo *f, ccon *con) while (f) { + char *old_target, *ofile; + if (downloaded_exceeds_quota ()) { --depth; return QUOTEXC; } - olocal = u->local; - ofile = u->file; - u->file = f->name; - u->local = url_filename (u); + old_target = con->target; + + ofile = xstrdup (u->file); + url_set_file (u, f->name); + + con->target = url_file_name (u); err = RETROK; dlthis = 1; @@ -1309,13 +1347,18 @@ ftp_retrieve_list (struct urlinfo *u, struct fileinfo *f, ccon *con) I'm not implementing it now since files on an FTP server are much more likely than files on an HTTP server to legitimately have a .orig suffix. */ - if (!stat (u->local, &st)) + if (!stat (con->target, &st)) { int eq_size; int cor_val; /* 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. */ @@ -1326,7 +1369,7 @@ ftp_retrieve_list (struct urlinfo *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"), u->local); +Remote file no newer than local file `%s' -- not retrieving.\n"), con->target); dlthis = 0; } else if (eq_size) @@ -1334,7 +1377,7 @@ Remote file no newer than local file `%s' -- not retrieving.\n"), u->local); /* 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); + con->target); } else { @@ -1362,30 +1405,30 @@ The sizes do not match (local %ld) -- retrieving.\n\n"), local_size); struct stat st; /* Check whether we already have the correct symbolic link. */ - int rc = lstat (u->local, &st); + int rc = lstat (con->target, &st); if (rc == 0) { size_t len = strlen (f->linkto) + 1; if (S_ISLNK (st.st_mode)) { char *link_target = (char *)alloca (len); - size_t n = readlink (u->local, link_target, len); + size_t n = readlink (con->target, link_target, len); if ((n == len - 1) && (memcmp (link_target, f->linkto, n) == 0)) { logprintf (LOG_VERBOSE, _("\ Already have correct symlink %s -> %s\n\n"), - u->local, f->linkto); + con->target, f->linkto); dlthis = 0; break; } } } logprintf (LOG_VERBOSE, _("Creating symlink %s -> %s\n"), - u->local, f->linkto); + con->target, f->linkto); /* Unlink before creating symlink! */ - unlink (u->local); - if (symlink (f->linkto, u->local) == -1) + unlink (con->target); + if (symlink (f->linkto, con->target) == -1) logprintf (LOG_NOTQUIET, "symlink: %s\n", strerror (errno)); logputs (LOG_VERBOSE, "\n"); @@ -1393,7 +1436,7 @@ Already have correct symlink %s -> %s\n\n"), #else /* not HAVE_SYMLINK */ logprintf (LOG_NOTQUIET, _("Symlinks not supported, skipping symlink `%s'.\n"), - u->local); + con->target); #endif /* not HAVE_SYMLINK */ } else /* opt.retr_symlinks */ @@ -1424,7 +1467,7 @@ Already have correct symlink %s -> %s\n\n"), if (!(f->type == FT_SYMLINK && !opt.retr_symlinks) && f->tstamp != -1 && dlthis - && file_exists_p (u->local)) + && file_exists_p (con->target)) { /* #### This code repeats in http.c and ftp.c. Move it to a function! */ @@ -1435,27 +1478,31 @@ Already have correct symlink %s -> %s\n\n"), fl = opt.output_document; } else - fl = u->local; + fl = con->target; if (fl) touch (fl, f->tstamp); } else if (f->tstamp == -1) - logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), u->local); + logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), con->target); if (f->perms && f->type == FT_PLAINFILE && dlthis) - chmod (u->local, f->perms); + chmod (con->target, f->perms); else - DEBUGP (("Unrecognized permissions for %s.\n", u->local)); + DEBUGP (("Unrecognized permissions for %s.\n", con->target)); + + xfree (con->target); + con->target = old_target; + + url_set_file (u, ofile); + xfree (ofile); - xfree (u->local); - u->local = olocal; - u->file = ofile; /* Break on fatals. */ if (err == QUOTEXC || err == HOSTERR || err == FWRITEERR) break; con->cmd &= ~ (DO_CWD | DO_LOGIN); f = f->next; - } /* while */ + } + /* We do not want to call ftp_retrieve_dirs here */ if (opt.recursive && !(opt.reclevel != INFINITE_RECURSION && depth >= opt.reclevel)) @@ -1472,51 +1519,62 @@ Already have correct symlink %s -> %s\n\n"), ftp_retrieve_glob on each directory entry. The function knows about excluded directories. */ static uerr_t -ftp_retrieve_dirs (struct urlinfo *u, struct fileinfo *f, ccon *con) +ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con) { - char *odir; - char *current_container = NULL; - int current_length = 0; + char *container = NULL; + int container_size = 0; for (; f; f = f->next) { - int len; + int size; + char *odir, *newdir; if (downloaded_exceeds_quota ()) break; if (f->type != FT_DIRECTORY) continue; - odir = u->dir; - len = strlen (u->dir) + 1 + strlen (f->name) + 1; + /* Allocate u->dir off stack, but reallocate only if a larger - string is needed. */ - if (len > current_length) - current_container = (char *)alloca (len); - u->dir = current_container; + string is needed. It's a pity there's no "realloca" for an + item on the bottom of the stack. */ + size = strlen (u->dir) + 1 + strlen (f->name) + 1; + if (size > container_size) + container = (char *)alloca (size); + newdir = container; + + odir = u->dir; if (*odir == '\0' || (*odir == '/' && *(odir + 1) == '\0')) /* If ODIR is empty or just "/", simply append f->name to ODIR. (In the former case, to preserve u->dir being relative; in the latter case, to avoid double slash.) */ - sprintf (u->dir, "%s%s", odir, f->name); + sprintf (newdir, "%s%s", odir, f->name); else /* Else, use a separator. */ - sprintf (u->dir, "%s/%s", odir, f->name); + sprintf (newdir, "%s/%s", odir, f->name); + DEBUGP (("Composing new CWD relative to the initial directory.\n")); - DEBUGP ((" odir = '%s'\n f->name = '%s'\n u->dir = '%s'\n\n", - odir, f->name, u->dir)); - if (!accdir (u->dir, ALLABS)) + DEBUGP ((" odir = '%s'\n f->name = '%s'\n newdir = '%s'\n\n", + odir, f->name, newdir)); + if (!accdir (newdir, ALLABS)) { logprintf (LOG_VERBOSE, _("\ -Not descending to `%s' as it is excluded/not-included.\n"), u->dir); - u->dir = odir; +Not descending to `%s' as it is excluded/not-included.\n"), newdir); continue; } + con->st &= ~DONE_CWD; + + odir = xstrdup (u->dir); /* because url_set_dir will free + u->dir. */ + url_set_dir (u, newdir); ftp_retrieve_glob (u, con, GETALL); + url_set_dir (u, odir); + xfree (odir); + /* Set the time-stamp? */ - u->dir = odir; } + if (opt.quota && opt.downloaded > opt.quota) return QUOTEXC; else @@ -1533,9 +1591,9 @@ Not descending to `%s' as it is excluded/not-included.\n"), u->dir); get the listing, so that the time-stamp is heeded); if it's GLOBALL, use globbing; if it's GETALL, download the whole directory. */ static uerr_t -ftp_retrieve_glob (struct urlinfo *u, ccon *con, int action) +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; @@ -1548,8 +1606,7 @@ ftp_retrieve_glob (struct urlinfo *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)) @@ -1561,19 +1618,31 @@ ftp_retrieve_glob (struct urlinfo *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); if (matchres == -1) { - logprintf (LOG_NOTQUIET, "%s: %s\n", u->local, + logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno)); break; } @@ -1623,7 +1692,7 @@ ftp_retrieve_glob (struct urlinfo *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 urlinfo *u, int *dt) +ftp_loop (struct url *u, int *dt, struct url *proxy) { ccon con; /* FTP connection */ uerr_t res; @@ -1636,6 +1705,7 @@ ftp_loop (struct urlinfo *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 @@ -1652,8 +1722,8 @@ ftp_loop (struct urlinfo *u, int *dt) { char *filename = (opt.output_document ? xstrdup (opt.output_document) - : (u->local ? xstrdup (u->local) - : url_filename (u))); + : (con.target ? xstrdup (con.target) + : url_file_name (u))); res = ftp_index (filename, u, f); if (res == FTPOK && opt.verbose) { @@ -1687,8 +1757,8 @@ ftp_loop (struct urlinfo *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); @@ -1702,6 +1772,8 @@ ftp_loop (struct urlinfo *u, int *dt) CLOSE (RBUF_FD (&con.rbuf)); FREE_MAYBE (con.id); con.id = NULL; + FREE_MAYBE (con.target); + con.target = NULL; return res; }