/* File Transfer Protocol support.
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
- 2004, 2005, 2006, 2007, 2008 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.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <strings.h>
+#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include "netrc.h"
#include "convert.h" /* for downloaded_file */
#include "recur.h" /* for INFINITE_RECURSION */
+#include "warc.h"
#ifdef __VMS
# include "vms.h"
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 */
} 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
}
#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.
*/
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)
{
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.
*/
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)
{
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));
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. */
+ and closes the control connection in case of error. If warc_tmp
+ is non-NULL, the downloaded data will be written there as well. */
static uerr_t
-getftp (struct url *u, wgint *len, wgint restval, ccon *con)
+getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread,
+ wgint restval, ccon *con, int count, FILE *warc_tmp)
{
int csock, dtsock, local_sock, res;
uerr_t err = RETROK; /* appease the compiler */
FILE *fp;
- char *user, *passwd, *respline;
- char *tms;
- const char *tmrate;
+ char *respline, *tms;
+ const char *user, *passwd, *tmrate;
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;
/* Make sure that at least *something* is requested. */
assert ((cmd & (DO_LIST | DO_CWD | DO_RETR | DO_LOGIN)) != 0);
+ *qtyread = restval;
+
user = u->user;
passwd = u->passwd;
search_netrc (u->host, (const char **)&user, (const char **)&passwd, 1);
{
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 = concat_strings (user, "@", u->host, (char *) 0);
- }
/* Login to the server: */
csock = connect_to_host (host, port);
if (csock == E_HOST)
- return HOSTERR;
+ return HOSTERR;
else if (csock < 0)
- return (retryable_socket_connect_error (errno)
- ? CONERROR : CONIMPOSSIBLE);
+ return (retryable_socket_connect_error (errno)
+ ? CONERROR : CONIMPOSSIBLE);
if (cmd & LEAVE_PENDING)
con->csock = csock;
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);
-
if (con->proxy)
- xfree (logname);
+ {
+ /* If proxy is in use, log in as username@target-site. */
+ char *logname = concat_strings (user, "@", u->host, (char *) 0);
+ err = ftp_login (csock, logname, passwd);
+ xfree (logname);
+ }
+ else
+ err = ftp_login (csock, user, passwd);
/* FTPRERR, FTPSRVERR, WRITEFAILED, FTPLOGREFUSED, FTPLOGINC */
switch (err)
*/
/* 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, '[');
logputs (LOG_VERBOSE, _("==> CWD not needed.\n"));
else
{
- char *targ;
- int cwd_count;
- int cwd_end;
- int cwd_start;
+ const char *targ = NULL;
+ int cwd_count;
+ int cwd_end;
+ int cwd_start;
char *target = u->dir;
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
#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
}
if (!opt.server_response)
- logprintf (LOG_VERBOSE, "==> CWD (%d) %s ... ",
- cwd_count, escnonprint (target));
+ logprintf (LOG_VERBOSE, "==> CWD (%d) %s ... ", cwd_count,
+ quotearg_style (escape_quoting_style, target));
err = ftp_cwd (csock, targ);
/* FTPRERR, WRITEFAILED, FTPNSFOD */
switch (err)
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;
else /* do not CWD */
logputs (LOG_VERBOSE, _("==> CWD not required.\n"));
- if ((cmd & DO_RETR) && *len == 0)
+ if ((cmd & DO_RETR) && passed_expected_bytes == 0)
{
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);
+ err = ftp_size (csock, u->file, &expected_bytes);
/* FTPRERR */
switch (err)
{
con->csock = -1;
return err;
case FTPOK:
+ got_expected_bytes = true;
/* Everything is OK. */
break;
default:
abort ();
}
if (!opt.server_response)
- logprintf (LOG_VERBOSE, *len ? "%s\n" : _("done.\n"),
- number_to_static_string (*len));
+ logprintf (LOG_VERBOSE, expected_bytes ? "%s\n" : _("done.\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. */
} /* 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)
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);
{
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));
}
}
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;
case FTPOK:
- if (getenv( "FTP_DELETE") != NULL)
- {
- err = ftp_dele (csock, u->file);
- }
break;
default:
abort ();
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)
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;
}
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)))
/* Some FTP servers return the total length of file after REST
command, others just return the remaining size. */
- if (*len && restval && expected_bytes
- && (expected_bytes == *len - restval))
+ if (passed_expected_bytes && restval && expected_bytes
+ && (expected_bytes == passed_expected_bytes - restval))
{
DEBUGP (("Lying FTP server found, adjusting.\n"));
- expected_bytes = *len;
+ expected_bytes = passed_expected_bytes;
}
/* If no transmission was required, then everything is OK. */
if (dtsock < 0)
{
logprintf (LOG_NOTQUIET, "accept: %s\n", strerror (errno));
- return err;
+ return CONERROR;
}
}
/* 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
#ifdef __VMS
char *targ;
- targ = ods_conform( con->target);
+ targ = ods_conform (con->target);
if (targ != con->target)
{
- xfree( con->target);
+ xfree (con->target);
con->target = targ;
}
#endif /* def __VMS */
-
+
mkalldirs (con->target);
if (opt.backups)
rotate_backups (con->target);
Elsewhere, define a constant "binary" flag.
Isn't it nice to have distinct text and binary file types?
*/
-# define BIN_TYPE_TRANSFER (type_char != 'A')
+/* 2011-09-30 SMS.
+ Added listing files to the set of non-"binary" (text, Stream_LF)
+ files. (Wget works either way, but other programs, like, say, text
+ editors, work better on listing files which have text attributes.)
+ Now we use "binary" attributes for a binary ("IMAGE") transfer,
+ unless "--ftp-stmlf" was specified, and we always use non-"binary"
+ (text, Stream_LF) attributes for a listing file, or for an ASCII
+ transfer.
+ Tidied the VMS-specific BIN_TYPE_xxx macros, and changed the call to
+ fopen_excl() (restored?) to use BIN_TYPE_FILE instead of "true".
+*/
#ifdef __VMS
+# define BIN_TYPE_TRANSFER (type_char != 'A')
+# define BIN_TYPE_FILE \
+ ((!(cmd & DO_LIST)) && BIN_TYPE_TRANSFER && (opt.ftp_stmlf == 0))
# define FOPEN_OPT_ARGS "fop=sqo", "acc", acc_cb, &open_id
# define FOPEN_OPT_ARGS_BIN "ctx=bin,stm", "rfm=fix", "mrs=512" FOPEN_OPT_ARGS
-# define BIN_TYPE_FILE (BIN_TYPE_TRANSFER && (opt.ftp_stmlf == 0))
#else /* def __VMS */
-# define BIN_TYPE_FILE 1
+# define BIN_TYPE_FILE true
#endif /* def __VMS [else] */
-
+
if (restval && !(con->cmd & DO_LIST))
{
#ifdef __VMS
#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;
}
else
{
- fp = fopen_excl (con->target, true);
+ fp = fopen_excl (con->target, BIN_TYPE_FILE);
if (!fp && errno == EEXIST)
{
/* We cannot just invent a new name and use it (which is
else
fp = output_stream;
- if (*len)
+ if (passed_expected_bytes)
{
- print_length (*len, restval, true);
- expected_bytes = *len; /* for fd_read_body's progress bar */
+ print_length (passed_expected_bytes, restval, true);
+ expected_bytes = passed_expected_bytes;
+ /* for fd_read_body's progress bar */
}
else if (expected_bytes)
print_length (expected_bytes, restval, false);
flags = 0;
if (restval && rest_failed)
flags |= rb_skip_startpos;
- *len = restval;
rd_size = 0;
res = fd_read_body (dtsock, fp,
expected_bytes ? expected_bytes - restval : 0,
- restval, &rd_size, len, &con->dltime, flags);
+ restval, &rd_size, qtyread, &con->dltime, flags, warc_tmp);
tms = datetime_str (time (NULL));
tmrate = retr_rate (rd_size, con->dltime);
if (!output_stream || con->cmd & DO_LIST)
fclose (fp);
- /* If fd_read_body couldn't write to fp, bail out. */
- if (res == -2)
+ /* If fd_read_body couldn't write to fp or warc_tmp, bail out. */
+ if (res == -2 || (warc_tmp != NULL && res == -3))
{
logprintf (LOG_NOTQUIET, _("%s: %s, closing control connection.\n"),
con->target, strerror (errno));
fd_close (csock);
con->csock = -1;
fd_close (dtsock);
- return FWRITEERR;
+ if (res == -2)
+ return FWRITEERR;
+ else if (res == -3)
+ return WARC_TMP_FWRITEERR;
}
else if (res == -1)
{
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);
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;
+ wgint restval, len = 0, qtyread = 0;
char *tms, *locf;
const char *tmrate = NULL;
uerr_t err;
struct_stat st;
+ /* Declare WARC variables. */
+ bool warc_enabled = (opt.warc_filename != NULL);
+ FILE *warc_tmp = NULL;
+ ip_address *warc_ip = NULL;
+
/* Get the target, and set the name for the message accordingly. */
if ((f == NULL) && (con->target))
{
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
locf = opt.output_document;
}
- 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;
}
orig_lp = con->cmd & LEAVE_PENDING ? 1 : 0;
+ /* For file RETR requests, we can write a WARC record.
+ We record the file contents to a temporary file. */
+ if (warc_enabled && (con->cmd & DO_RETR))
+ {
+ warc_tmp = warc_tempfile ();
+ if (warc_tmp == NULL)
+ return WARC_TMP_FOPENERR;
+
+ if (!con->proxy && con->csock != -1)
+ {
+ warc_ip = (ip_address *) alloca (sizeof (ip_address));
+ socket_ip_address (con->csock, warc_ip, ENDPOINT_PEER);
+ }
+ }
+
/* THE loop. */
do
{
first attempt to clobber existing data.) */
restval = st.st_size;
else if (count > 1)
- restval = len; /* start where the previous run left off */
+ restval = qtyread; /* start where the previous run left off */
else
restval = 0;
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
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, restval, con);
+
+ /* If we are working on a WARC record, getftp should also write
+ to the warc_tmp file. */
+ err = getftp (u, len, &qtyread, restval, con, count, warc_tmp);
if (con->csock == -1)
con->st &= ~DONE_CWD;
{
case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR:
case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
+ case UNLINKERR: case WARC_TMP_FWRITEERR:
/* Fatal errors, give up. */
+ if (warc_tmp != NULL)
+ fclose (warc_tmp);
return err;
case CONSOCKERR: case CONERROR: case FTPSRVERR: case FTPRERR:
case WRITEFAILED: case FTPUNKNOWNTYPE: case FTPSYSERR:
{
/* 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;
case FTPRETRINT:
/* If the control connection was closed, the retrieval
will be considered OK if f->size == len. */
- if (!f || len != f->size)
+ if (!f || qtyread != f->size)
{
printwhat (count, opt.ntry);
continue;
}
tms = datetime_str (time (NULL));
if (!opt.spider)
- tmrate = retr_rate (len - restval, con->dltime);
+ tmrate = retr_rate (qtyread - restval, con->dltime);
/* If we get out of the switch above without continue'ing, we've
successfully downloaded a file. Remember this fact. */
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 (qtyread));
+ }
if (!opt.verbose && !opt.quiet)
{
/* Need to hide the password from the URL. The `if' is here
time. */
char *hurl = url_string (u, URL_AUTH_HIDE_PASSWD);
logprintf (LOG_NONVERBOSE, "%s URL: %s [%s] -> \"%s\" [%d]\n",
- tms, hurl, number_to_static_string (len), locf, count);
+ tms, hurl, number_to_static_string (qtyread), locf, count);
xfree (hurl);
}
+ if (warc_enabled && (con->cmd & DO_RETR))
+ {
+ /* Create and store a WARC resource record for the retrieved file. */
+ bool warc_res;
+
+ warc_res = warc_write_resource_record (NULL, u->url, NULL, NULL,
+ warc_ip, NULL, warc_tmp, -1);
+ if (! warc_res)
+ return WARC_ERR;
+
+ /* warc_write_resource_record has also closed warc_tmp. */
+ }
+
if ((con->cmd & DO_LIST))
/* This is a directory listing file. */
{
/* --dont-remove-listing was specified, so do count this towards the
number of bytes and files downloaded. */
{
- total_downloaded_bytes += len;
- opt.numurls++;
+ total_downloaded_bytes += qtyread;
+ numurls++;
}
/* Deletion of listing files is not controlled by --delete-after, but
downloaded if they're going to be deleted. People seeding proxies,
for instance, may want to know how many bytes and files they've
downloaded through it. */
- total_downloaded_bytes += len;
- opt.numurls++;
+ 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"));
con->cmd |= LEAVE_PENDING;
else
con->cmd &= ~LEAVE_PENDING;
+
+ if (local_file)
+ *local_file = xstrdup (locf);
+
return RETROK;
} while (!opt.ntry || (count < opt.ntry));
/* 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"), lf));
+ DEBUGP ((_("Using %s as listing tmp file.\n"), quote (lf)));
- con->target = lf;
- err = ftp_loop_internal (u, NULL, con);
+ con->target = xstrdup (lf);
+ xfree (lf);
+ err = ftp_loop_internal (u, NULL, con, NULL);
+ lf = xstrdup (con->target);
+ 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;
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;
/* 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
{
{
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)
} /* 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 */
{
if (dlthis)
- err = ftp_loop_internal (u, f, con);
+ err = ftp_loop_internal (u, f, con, NULL);
} /* opt.retr_symlinks */
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. */
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"),
- escnonprint (f->name));
+ quote (f->name));
break;
} /* switch */
* appropriate. (Do the test once, and save the result.)
*/
- /* #### This code repeats in http.c and ftp.c. Move it to a
- function! */
- actual_target = NULL;
- if (opt.output_document)
- {
- if (output_stream_regular)
- actual_target = opt.output_document;
- }
- else
- actual_target = con->target;
+ set_local_file (&actual_target, con->target);
- /* If downloading a plain file, set valid (non-zero) permissions. */
- if (dlthis && (actual_target != NULL) && (f->type == FT_PLAINFILE))
+ /* If downloading a plain file, and the user requested it, then
+ set valid (non-zero) permissions. */
+ if (dlthis && (actual_target != NULL) &&
+ (f->type == FT_PLAINFILE) && opt.preserve_perm)
{
if (f->perms)
chmod (actual_target, f->perms);
/* Set the time-stamp information to the local file. Symlinks
are not to be stamped because it sets the stamp on the
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))
}
else if (f->tstamp == -1)
logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"),
- actual_target);
+ actual_target);
}
xfree (con->target);
xfree (ofile);
/* Break on fatals. */
- if (err == QUOTEXC || err == HOSTERR || err == FWRITEERR)
+ if (err == QUOTEXC || err == HOSTERR || err == FWRITEERR
+ || err == WARC_ERR || err == WARC_TMP_FOPENERR
+ || err == WARC_TMP_FWRITEERR)
break;
con->cmd &= ~ (DO_CWD | DO_LOGIN);
f = f->next;
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;
}
{
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
{
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
if (matchres == -1)
{
logprintf (LOG_NOTQUIET, _("Error matching %s against %s: %s\n"),
- u->file, escnonprint (f->name), strerror (errno));
+ u->file, quotearg_style (escape_quoting_style, f->name),
+ strerror (errno));
break;
}
if (matchres == FNM_NOMATCH)
}
else if (action == GLOB_GETONE)
{
+#ifdef __VMS
+ /* 2009-09-09 SMS.
+ * Odd-ball compiler ("HP C V7.3-009 on OpenVMS Alpha V7.3-2")
+ * bug causes spurious %CC-E-BADCONDIT complaint with this
+ * "?:" statement. (Different linkage attributes for strcmp()
+ * and strcasecmp().) Converting to "if" changes the
+ * complaint to %CC-W-PTRMISMATCH on "cmp = strcmp;". Adding
+ * the senseless type cast clears the complaint, and looks
+ * harmless.
+ */
+ int (*cmp) (const char *, const char *)
+ = opt.ignore_case ? strcasecmp : (int (*)())strcmp;
+#else /* def __VMS */
int (*cmp) (const char *, const char *)
= opt.ignore_case ? strcasecmp : strcmp;
+#endif /* def __VMS [else] */
f = start;
while (f)
{
if (start)
{
/* Just get everything. */
- ftp_retrieve_list (u, start, con);
+ res = ftp_retrieve_list (u, start, con);
}
else
{
/* 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 if (action == GLOB_GETONE) /* GLOB_GETONE or GLOB_GETALL */
{
/* 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;
}
if (opt.quota && total_downloaded_bytes > opt.quota)
return QUOTEXC;
else
- /* #### Should we return `res' here? */
- return RETROK;
+ return res;
}
/* The wrapper that calls an appropriate routine according to contents
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;
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)
{
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);
}
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;