#include "ftp.h"
#include "connect.h"
#include "host.h"
-#include "fnmatch.h"
#include "netrc.h"
#include "convert.h" /* for downloaded_file */
extern int errno;
#endif
+extern LARGE_INT total_downloaded_bytes;
+
/* File where the "ls -al" listing will be saved. */
#define LIST_FILENAME ".listing"
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, unsigned short *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)
+{
+ 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);
+
+ /* If EPRT is not supported try LPRT */
+ if (err == FTPPORTERR)
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> LPRT ... ");
+ err = ftp_lprt (rbuf);
+ }
+ }
+ else
+ {
+ if (!opt.server_response)
+ logputs (LOG_VERBOSE, "==> PORT ... ");
+ err = ftp_port (rbuf);
+ }
+
+ return err;
+}
+#else
+#define ftp_do_pasv ftp_pasv
+#define ftp_do_port ftp_port
+#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. */
unsigned short passive_port;
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PASV ... ");
- err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
+ err = ftp_do_pasv (&con->rbuf, &passive_addr, &passive_port);
/* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
switch (err)
{
} /* switch(err) */
if (err==FTPOK)
{
+ DEBUGP (("trying to connect to %s port %d\n",
+ pretty_print_address (&passive_addr),
+ passive_port));
dtsock = connect_to_one (&passive_addr, passive_port, 1);
if (dtsock < 0)
{
int save_errno = errno;
CLOSE (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 CONNECT_ERROR (save_errno);
{
if (!opt.server_response)
logputs (LOG_VERBOSE, "==> PORT ... ");
- err = ftp_port (&con->rbuf);
+ err = ftp_do_port (&con->rbuf);
/* FTPRERR, WRITEFAILED, bindport (CONSOCKERR, CONPORTERR, BINDERR,
LISTENERR), HOSTERR, FTPPORTERR */
switch (err)
logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
}
}
+
err = ftp_retr (&con->rbuf, u->file);
/* FTPRERR, WRITEFAILED, FTPNSFOD */
switch (err)
if (flush_res == EOF)
res = -2;
}
+
/* If get_contents couldn't write to fp, bail out. */
if (res == -2)
{
/* --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++;
}
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;
{
char *old_target, *ofile;
- if (downloaded_exceeds_quota ())
+ if (opt.quota && total_downloaded_bytes > opt.quota)
{
--depth;
return QUOTEXC;
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 *f, *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)
{
- f = orig;
+ f = start;
while (f)
{
if (f->type != FT_DIRECTORY && !acceptable (f->name))
}
}
/* Remove all files with possible harmful names */
- f = orig;
+ f = start;
while (f)
{
- if (has_insecure_name_p(f->name))
+ if (has_insecure_name_p (f->name))
{
logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
f = delelement (f, &start);
}
}
freefileinfo (start);
- if (downloaded_exceeds_quota ())
+ if (opt.quota && total_downloaded_bytes > opt.quota)
return QUOTEXC;
else
/* #### Should we return `res' here? */