#include "ftp.h"
#include "connect.h"
#include "host.h"
-#include "fnmatch.h"
#include "netrc.h"
+#include "convert.h" /* for downloaded_file */
+#include "recur.h" /* for INFINITE_RECURSION */
#ifndef errno
extern int errno;
#endif
+extern LARGE_INT total_downloaded_bytes;
+
/* File where the "ls -al" listing will be saved. */
#define LIST_FILENAME ".listing"
int st; /* connection status */
int cmd; /* command code */
struct rbuf rbuf; /* control connection buffer */
- long dltime; /* time of the download */
+ 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 */
return res;
}
+#ifdef ENABLE_IPV6
+static int
+getfamily (int fd)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ socklen_t len = sizeof (ss);
+
+ assert (fd >= 0);
+
+ if (getpeername (fd, sa, &len) < 0)
+ /* Mauro Tortonesi: HOW DO WE HANDLE THIS ERROR? */
+ abort ();
+
+ return sa->sa_family;
+}
+
+/*
+ * 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.
+ */
+static uerr_t
+ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, int *port)
+{
+ uerr_t err;
+ int family;
+
+ family = getfamily (rbuf->fd);
+ assert (family == AF_INET || family == AF_INET6);
+
+ /* 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. */
+ if (family == AF_INET6)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> EPSV ... ");
+ err = ftp_epsv (rbuf, addr, port);
+
+ /* If EPSV is not supported try LPSV */
+ if (err == FTPNOPASV)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> LPSV ... ");
+ err = ftp_lpsv (rbuf, addr, port);
+ }
+ }
+ else
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> PASV ... ");
+ err = ftp_pasv (rbuf, addr, port);
+ }
+
+ return err;
+}
+
+/*
+ * This function sets up an active data connection with the FTP server.
+ * It is merely a wrapper around ftp_eprt, ftp_lprt and ftp_port.
+ */
+static uerr_t
+ftp_do_port (struct rbuf *rbuf, int *local_sock)
+{
+ uerr_t err;
+ int family;
+
+ assert (rbuf != NULL);
+ assert (rbuf_initialized_p (rbuf));
+
+ family = getfamily (rbuf->fd);
+ assert (family == AF_INET || family == AF_INET6);
+
+ /* 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. */
+ if (family == AF_INET6)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> EPRT ... ");
+ err = ftp_eprt (rbuf, local_sock);
+
+ /* If EPRT is not supported try LPRT */
+ if (err == FTPPORTERR)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> LPRT ... ");
+ err = ftp_lprt (rbuf, local_sock);
+ }
+ }
+ else
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> PORT ... ");
+ err = ftp_port (rbuf, local_sock);
+ }
+
+ return err;
+}
+#else
+
+static uerr_t
+ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, int *port)
+{
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> PASV ... ");
+ return ftp_pasv (rbuf, addr, port);
+}
+
+static uerr_t
+ftp_do_port (struct rbuf *rbuf, int *local_sock)
+{
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> PORT ... ");
+ return ftp_port (rbuf, local_sock);
+}
+#endif
+
/* 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. */
static uerr_t
getftp (struct url *u, long *len, long restval, ccon *con)
{
- int csock, dtsock, res;
+ int csock, dtsock, local_sock, res;
uerr_t err;
FILE *fp;
char *user, *passwd, *respline;
assert (user && passwd);
dtsock = -1;
+ local_sock = -1;
con->dltime = 0;
if (!(cmd & DO_LOGIN))
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;
/* First: Establish the control connection. */
- al = lookup_host (host, 0);
- if (!al)
+ csock = connect_to_host (host, port);
+ if (csock == E_HOST)
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 errno == ECONNREFUSED ? CONREFUSED : CONERROR;
+ else if (csock < 0)
+ return CONNECT_ERROR (errno);
if (cmd & LEAVE_PENDING)
rbuf_initialize (&con->rbuf, csock);
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
case FTPSRVERR:
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("Error in server greeting.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Write failed, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
case FTPLOGREFUSED:
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("The server refuses login.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return FTPLOGREFUSED;
break;
case FTPLOGINC:
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("Login incorrect.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return FTPLOGINC;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
case FTPSRVERR :
/* PWD unsupported -- assume "/". */
- FREE_MAYBE (con->id);
+ xfree_null (con->id);
con->id = xstrdup ("/");
break;
case FTPOK:
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Write failed, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logprintf (LOG_NOTQUIET,
_("Unknown type `%c', closing control connection.\n"),
type_char);
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
case FTPOK:
A relative directory is one that does not begin with '/'
and, on non-Unix OS'es, one that doesn't begin with
- "<letter>:". */
+ "[a-z]:".
+
+ This is not done for OS400, which doesn't use
+ "/"-delimited directories, nor does it support directory
+ hierarchies. "CWD foo" followed by "CWD bar" leaves us
+ in "bar", not in "foo/bar", as would be customary
+ elsewhere. */
if (target[0] != '/'
&& !(con->rs != ST_UNIX
- && ISALPHA (target[0]) && target[1] == ':'))
+ && ISALPHA (target[0])
+ && target[1] == ':')
+ && con->rs != ST_OS400)
{
int idlen = strlen (con->id);
char *ntarget, *p;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Write failed, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logprintf (LOG_NOTQUIET, _("No such directory `%s'.\n\n"),
u->dir);
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
{
if (opt.ftp_pasv > 0)
{
- ip_address passive_addr;
- unsigned short passive_port;
- if (!opt.server_response)
- logputs (LOG_VERBOSE, "==> PASV ... ");
- err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
+ ip_address passive_addr;
+ int passive_port;
+ err = ftp_do_pasv (&con->rbuf, &passive_addr, &passive_port);
/* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
switch (err)
{
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Write failed, closing control connection.\n"));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
} /* switch(err) */
if (err==FTPOK)
{
- dtsock = connect_to_one (&passive_addr, passive_port, 1);
+ DEBUGP (("trying to connect to %s port %d\n",
+ pretty_print_address (&passive_addr),
+ passive_port));
+ dtsock = connect_to_ip (&passive_addr, passive_port, NULL);
if (dtsock < 0)
{
int save_errno = errno;
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
- logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"),
+ logprintf (LOG_VERBOSE, _("couldn't connect to %s port %hu: %s\n"),
pretty_print_address (&passive_addr), passive_port,
strerror (save_errno));
- return save_errno == ECONNREFUSED ? CONREFUSED : CONERROR;
+ return CONNECT_ERROR (save_errno);
}
pasv_mode_open = 1; /* Flag to avoid accept port */
if (!pasv_mode_open) /* Try to use a port command if PASV failed */
{
- if (!opt.server_response)
- logputs (LOG_VERBOSE, "==> PORT ... ");
- err = ftp_port (&con->rbuf);
+ err = ftp_do_port (&con->rbuf, &local_sock);
/* FTPRERR, WRITEFAILED, bindport (CONSOCKERR, CONPORTERR, BINDERR,
LISTENERR), HOSTERR, FTPPORTERR */
switch (err)
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Write failed, closing control connection.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
case CONSOCKERR:
logputs (LOG_VERBOSE, "\n");
logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logprintf (LOG_NOTQUIET, _("Bind error (%s).\n"),
strerror (errno));
- closeport (dtsock);
+ xclose (dtsock);
+ xclose (local_sock);
return err;
break;
case FTPPORTERR:
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("Invalid PORT.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Write failed, closing control connection.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logprintf (LOG_NOTQUIET,
_("\nREST failed; will not truncate `%s'.\n"),
con->target);
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return CONTNOTSUPPORTED;
}
request. */
if (opt.spider)
{
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return RETRFINISHED;
}
logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
}
}
+
err = ftp_retr (&con->rbuf, u->file);
/* FTPRERR, WRITEFAILED, FTPNSFOD */
switch (err)
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Write failed, closing control connection.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
case FTPNSFOD:
logputs (LOG_VERBOSE, "\n");
logprintf (LOG_NOTQUIET, _("No such file `%s'.\n\n"), u->file);
- closeport (dtsock);
+ xclose (dtsock);
+ xclose (local_sock);
return err;
break;
case FTPOK:
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Write failed, closing control connection.\n"));
- CLOSE (csock);
- closeport (dtsock);
+ xclose (csock);
+ xclose (dtsock);
+ xclose (local_sock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
logputs (LOG_VERBOSE, "\n");
logprintf (LOG_NOTQUIET, _("No such file or directory `%s'.\n\n"),
".");
- closeport (dtsock);
+ xclose (dtsock);
+ xclose (local_sock);
return err;
break;
case FTPOK:
expected_bytes = ftp_expected_bytes (ftp_last_respline);
} /* cmd & DO_LIST */
+ if (!(cmd & (DO_LIST | DO_RETR)) || (opt.spider && !(cmd & DO_LIST)))
+ return RETRFINISHED;
+
/* Some FTP servers return the total length of file after REST
command, others just return the remaining size. */
if (*len && restval && expected_bytes
}
/* If no transmission was required, then everything is OK. */
- if (!(cmd & (DO_LIST | DO_RETR)))
- return RETRFINISHED;
-
if (!pasv_mode_open) /* we are not using pasive mode so we need
to accept */
{
/* Open the data transmission socket by calling acceptport(). */
- err = acceptport (&dtsock);
+ err = acceptport (local_sock, &dtsock);
/* Possible errors: ACCEPTERR. */
if (err == ACCEPTERR)
{
if (!fp)
{
logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
- closeport (dtsock);
+ xclose (dtsock);
+ xclose (local_sock);
return FOPENERR;
}
}
tms = time_str (NULL);
tmrate = retr_rate (*len - restval, con->dltime, 0);
/* Close data connection socket. */
- closeport (dtsock);
+ xclose (dtsock);
+ xclose (local_sock);
/* Close the local file. */
{
/* Close or flush the file. We have to be careful to check for
if (flush_res == EOF)
res = -2;
}
+
/* If get_contents couldn't write to fp, bail out. */
if (res == -2)
{
logprintf (LOG_NOTQUIET, _("%s: %s, closing control connection.\n"),
con->target, strerror (errno));
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return FWRITEERR;
}
return FTPRETRINT, since there is a possibility that the
whole file was retrieved nevertheless (but that is for
ftp_loop_internal to decide). */
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
return FTPRETRINT;
} /* err != FTPOK */
{
/* I should probably send 'QUIT' and check for a reply, but this
is faster. #### Is it OK, though? */
- CLOSE (csock);
+ xclose (csock);
rbuf_uninitialize (&con->rbuf);
}
/* If it was a listing, and opt.server_response is true,
{
int count, orig_lp;
long restval, len;
- char *tms, *tmrate, *locf;
+ char *tms, *locf;
+ char *tmrate = NULL;
uerr_t err;
struct stat st;
if (!con->target)
- con->target = url_filename (u);
+ con->target = url_file_name (u);
if (opt.noclobber && file_exists_p (con->target))
{
switch (err)
{
- case HOSTERR: case CONREFUSED: case FWRITEERR: case FOPENERR:
+ case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR:
case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED:
/* Fatal errors, give up. */
return err;
}
/* Time? */
tms = time_str (NULL);
- tmrate = retr_rate (len - restval, con->dltime, 0);
+ if (!opt.spider)
+ 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. */
- downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
+ downloaded_file (FILE_DOWNLOADED_NORMALLY, locf);
if (con->st & ON_YOUR_OWN)
{
- CLOSE (RBUF_FD (&con->rbuf));
+ xclose (RBUF_FD (&con->rbuf));
rbuf_uninitialize (&con->rbuf);
}
- logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%ld]\n\n"),
- tms, tmrate, locf, len);
+ if (!opt.spider)
+ logprintf (LOG_VERBOSE, _("%s (%s) - `%s' saved [%ld]\n\n"),
+ tms, tmrate, locf, len);
if (!opt.verbose && !opt.quiet)
{
/* Need to hide the password from the URL. The `if' is here
/* --dont-remove-listing was specified, so do count this towards the
number of bytes and files downloaded. */
{
- downloaded_increase (len);
+ total_downloaded_bytes += len;
opt.numurls++;
}
by the more specific option --dont-remove-listing, and the code
to do this deletion is in another function. */
}
- else
+ else if (!opt.spider)
/* This is not a directory listing file. */
{
/* Unlike directory listing files, don't pretend normal files weren't
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. */
- downloaded_increase (len);
+ total_downloaded_bytes += len;
opt.numurls++;
if (opt.delete_after)
logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
}
}
-
+
/* Restore the original leave-pendingness. */
if (orig_lp)
con->cmd |= LEAVE_PENDING;
if (rbuf_initialized_p (&con->rbuf) && (con->st & ON_YOUR_OWN))
{
- CLOSE (RBUF_FD (&con->rbuf));
+ xclose (RBUF_FD (&con->rbuf));
rbuf_uninitialize (&con->rbuf);
}
return TRYLIMEXC;
/* 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_filename (u);
+ uf = url_file_name (u);
lf = file_merge (uf, LIST_FILENAME);
xfree (uf);
DEBUGP ((_("Using `%s' as listing tmp file.\n"), lf));
{
char *old_target, *ofile;
- if (downloaded_exceeds_quota ())
+ if (opt.quota && total_downloaded_bytes > opt.quota)
{
--depth;
return QUOTEXC;
ofile = xstrdup (u->file);
url_set_file (u, f->name);
- con->target = url_filename (u);
+ con->target = url_file_name (u);
err = RETROK;
dlthis = 1;
logprintf (LOG_NOTQUIET, _("%s: corrupt time-stamp.\n"), con->target);
if (f->perms && f->type == FT_PLAINFILE && dlthis)
- chmod (con->target, f->perms);
+ {
+ if (opt.preserve_perm)
+ chmod (con->target, f->perms);
+ }
else
DEBUGP (("Unrecognized permissions for %s.\n", con->target));
int size;
char *odir, *newdir;
- if (downloaded_exceeds_quota ())
+ if (opt.quota && total_downloaded_bytes > opt.quota)
break;
if (f->type != FT_DIRECTORY)
continue;
/* Set the time-stamp? */
}
- if (opt.quota && opt.downloaded > opt.quota)
+ if (opt.quota && total_downloaded_bytes > opt.quota)
return QUOTEXC;
else
return RETROK;
}
+/* Return non-zero if S has a leading '/' or contains '../' */
+static int
+has_insecure_name_p (const char *s)
+{
+ if (*s == '/')
+ return 1;
+
+ if (strstr(s, "../") != 0)
+ return 1;
+
+ return 0;
+}
/* A near-top-level function to retrieve the files in a directory.
The function calls ftp_get_listing, to get a linked list of files.
static uerr_t
ftp_retrieve_glob (struct url *u, ccon *con, int action)
{
- struct fileinfo *orig, *start;
+ struct fileinfo *f, *start;
uerr_t res;
con->cmd |= LEAVE_PENDING;
- res = ftp_get_listing (u, con, &orig);
+ res = ftp_get_listing (u, con, &start);
if (res != RETROK)
return res;
- start = orig;
/* First: weed out that do not conform the global rules given in
opt.accepts and opt.rejects. */
if (opt.accepts || opt.rejects)
{
- struct fileinfo *f = orig;
-
+ f = start;
while (f)
{
if (f->type != FT_DIRECTORY && !acceptable (f->name))
f = f->next;
}
}
+ /* Remove all files with possible harmful names */
+ f = start;
+ 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);
}
}
freefileinfo (start);
- if (downloaded_exceeds_quota ())
+ if (opt.quota && total_downloaded_bytes > opt.quota)
return QUOTEXC;
else
/* #### Should we return `res' here? */
if (res == RETROK)
{
- if (opt.htmlify)
+ if (opt.htmlify && !opt.spider)
{
char *filename = (opt.output_document
? xstrdup (opt.output_document)
: (con.target ? xstrdup (con.target)
- : url_filename (u)));
+ : url_file_name (u)));
res = ftp_index (filename, u, f);
if (res == FTPOK && opt.verbose)
{
*dt |= RETROKF;
/* If a connection was left, quench it. */
if (rbuf_initialized_p (&con.rbuf))
- CLOSE (RBUF_FD (&con.rbuf));
- FREE_MAYBE (con.id);
+ xclose (RBUF_FD (&con.rbuf));
+ xfree_null (con.id);
con.id = NULL;
- FREE_MAYBE (con.target);
+ xfree_null (con.target);
con.target = NULL;
return res;
}
struct fileinfo *next = f->next;
xfree (f->name);
- FREE_MAYBE (f->linkto);
+ xfree_null (f->linkto);
xfree (f);
if (next)