/* Basic FTP routines.
Copyright (C) 1995, 1996, 1997, 1998, 2000 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
+along with Wget; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
+#include <errno.h>
+
#ifdef HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
-#include <ctype.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <sys/types.h>
+/* For inet_ntop. */
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
#ifdef WINDOWS
# include <winsock.h>
#endif
#include "host.h"
#include "ftp.h"
-#ifndef errno
-extern int errno;
-#endif
-#ifndef h_errno
-extern int h_errno;
-#endif
-
char ftp_last_respline[128];
\f
do
{
for (i = 0; 1; i++)
- {
- int res;
- if (i > bufsize - 1)
- *line = (char *)xrealloc (*line, (bufsize <<= 1));
- res = RBUF_READCHAR (rbuf, *line + i);
- /* RES is number of bytes read. */
- if (res == 1)
- {
- if ((*line)[i] == '\n')
- {
- (*line)[i] = '\0';
- /* Get rid of \r. */
- if (i > 0 && (*line)[i - 1] == '\r')
- (*line)[i - 1] = '\0';
- break;
- }
- }
- else
- return FTPRERR;
- }
+ {
+ int res;
+ if (i > bufsize - 1)
+ *line = (char *)xrealloc (*line, (bufsize <<= 1));
+ res = RBUF_READCHAR (rbuf, *line + i);
+ /* RES is number of bytes read. */
+ if (res == 1)
+ {
+ if ((*line)[i] == '\n')
+ {
+ (*line)[i] = '\0';
+ /* Get rid of \r. */
+ if (i > 0 && (*line)[i - 1] == '\r')
+ (*line)[i - 1] = '\0';
+ break;
+ }
+ }
+ else
+ return FTPRERR;
+ }
if (opt.server_response)
- logprintf (LOG_ALWAYS, "%s\n", *line);
+ logprintf (LOG_ALWAYS, "%s\n", *line);
else
- DEBUGP (("%s\n", *line));
+ DEBUGP (("%s\n", *line));
}
while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) &&
- ISDIGIT ((*line)[2]) && (*line)[3] == ' '));
+ ISDIGIT ((*line)[2]) && (*line)[3] == ' '));
strncpy (ftp_last_respline, *line, sizeof (ftp_last_respline));
ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0';
return FTPOK;
ftp_request (const char *command, const char *value)
{
char *res = (char *)xmalloc (strlen (command)
- + (value ? (1 + strlen (value)) : 0)
- + 2 + 1);
+ + (value ? (1 + strlen (value)) : 0)
+ + 2 + 1);
sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : "");
if (opt.server_response)
{
/* Hack: don't print out password. */
if (strncmp (res, "PASS", 4) != 0)
- logprintf (LOG_ALWAYS, "--> %s\n", res);
+ logprintf (LOG_ALWAYS, "--> %s\n", res);
else
- logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
+ logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
}
else
DEBUGP (("\n--> %s\n", res));
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline != '2')
{
- free (respline);
+ xfree (respline);
return FTPSRVERR;
}
- free (respline);
+ xfree (respline);
/* Send USER username. */
request = ftp_request ("USER", acc);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
/* An unprobable possibility of logging without a password. */
if (*respline == '2')
{
- free (respline);
+ xfree (respline);
return FTPOK;
}
/* Else, only response 3 is appropriate. */
if (*respline != '3')
{
- free (respline);
+ xfree (respline);
return FTPLOGREFUSED;
}
#ifdef USE_OPIE
for (i = 0; i < ARRAY_SIZE (skey_head); i++)
{
- if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
- break;
+ if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
+ break;
}
if (i < ARRAY_SIZE (skey_head))
{
- const char *cp;
- int skey_sequence = 0;
-
- for (cp = respline + strlen (skey_head[i]);
- '0' <= *cp && *cp <= '9';
- cp++)
- {
- skey_sequence = skey_sequence * 10 + *cp - '0';
- }
- if (*cp == ' ')
- cp++;
- else
- {
- bad:
- free (respline);
- return FTPLOGREFUSED;
- }
- if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
- goto bad;
- pass = cp;
+ const char *cp;
+ int skey_sequence = 0;
+
+ for (cp = respline + strlen (skey_head[i]);
+ '0' <= *cp && *cp <= '9';
+ cp++)
+ {
+ skey_sequence = skey_sequence * 10 + *cp - '0';
+ }
+ if (*cp == ' ')
+ cp++;
+ else
+ {
+ bad:
+ xfree (respline);
+ return FTPLOGREFUSED;
+ }
+ if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
+ goto bad;
+ pass = cp;
}
}
#endif /* USE_OPIE */
- free (respline);
+ xfree (respline);
/* Send PASS password. */
request = ftp_request ("PASS", pass);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline != '2')
{
- free (respline);
+ xfree (respline);
return FTPLOGINC;
}
- free (respline);
+ xfree (respline);
/* All OK. */
return FTPOK;
}
+#ifdef INET6
+uerr_t
+ftp_eprt (struct rbuf *rbuf)
+{
+ uerr_t err;
+
+ char *request, *respline;
+ ip_address in_addr;
+ unsigned short port;
+
+ char ipv6 [8 * (4 * 3 + 3) + 8];
+ char *bytes;
+
+ /* Setting port to 0 lets the system choose a free port. */
+ port = 0;
+ err = bindport (&port, ip_default_family);
+ if (err != BINDOK) /* Bind the port. */
+ return err;
+
+ /* Get the address of this side of the connection. */
+ if (!conaddr (RBUF_FD (rbuf), &in_addr))
+ /* Huh? This is not BINDERR! */
+ return BINDERR;
+ inet_ntop (AF_INET6, &in_addr, ipv6, sizeof (ipv6));
+
+ /* Construct the argument of EPRT (of the form |2|IPv6.ascii|PORT.ascii|). */
+ bytes = alloca (3 + strlen (ipv6) + 1 + numdigit (port) + 1 + 1);
+ sprintf (bytes, "|2|%s|%u|", ipv6, port);
+ /* Send PORT request. */
+ request = ftp_request ("EPRT", bytes);
+ if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
+ {
+ closeport (port);
+ xfree (request);
+ return WRITEFAILED;
+ }
+ xfree (request);
+ /* Get appropriate response. */
+ err = ftp_response (rbuf, &respline);
+ if (err != FTPOK)
+ {
+ closeport (port);
+ xfree (respline);
+ return err;
+ }
+ if (*respline != '2')
+ {
+ closeport (port);
+ xfree (respline);
+ return FTPPORTERR;
+ }
+ xfree (respline);
+ return FTPOK;
+}
+#endif
+
/* Bind a port and send the appropriate PORT command to the FTP
server. Use acceptport after RETR, to get the socket of data
connection. */
ftp_port (struct rbuf *rbuf)
{
uerr_t err;
- char *request, *respline, *bytes;
- unsigned char *in_addr;
+ char *request, *respline;
+ char bytes[6 * 4 +1];
+
+ ip_address in_addr;
+ ip4_address in_addr_4;
+ unsigned char *in_addr4_ptr = (unsigned char *)&in_addr_4;
+
int nwritten;
unsigned short port;
-
+#ifdef INET6
+ /*
+ Only try the Extented Version if we actually use IPv6
+ */
+ if (ip_default_family == AF_INET6)
+ {
+ err = ftp_eprt (rbuf);
+ if (err == FTPOK)
+ return err;
+ }
+#endif
/* Setting port to 0 lets the system choose a free port. */
port = 0;
- /* Bind the port. */
- err = bindport (&port);
+
+ err = bindport (&port, AF_INET);
if (err != BINDOK)
return err;
- /* Get the address of this side of the connection. */
- if (!(in_addr = conaddr (RBUF_FD (rbuf))))
- return HOSTERR;
- /* Construct the argument of PORT (of the form a,b,c,d,e,f). */
- bytes = (char *)alloca (6 * 4 + 1);
- sprintf (bytes, "%d,%d,%d,%d,%d,%d", in_addr[0], in_addr[1],
- in_addr[2], in_addr[3], (unsigned) (port & 0xff00) >> 8,
- port & 0xff);
+
+ /* Get the address of this side of the connection and convert it
+ (back) to IPv4. */
+ if (!conaddr (RBUF_FD (rbuf), &in_addr))
+ /* Huh? This is not BINDERR! */
+ return BINDERR;
+ if (!map_ip_to_ipv4 (&in_addr, &in_addr_4))
+ return BINDERR;
+
+ /* Construct the argument of PORT (of the form a,b,c,d,e,f). Port
+ is unsigned short so (unsigned) (port & 0xff000) >> 8 is the same
+ like port >> 8
+ */
+ sprintf (bytes, "%d,%d,%d,%d,%d,%d",
+ in_addr4_ptr[0], in_addr4_ptr[1], in_addr4_ptr[2], in_addr4_ptr[3],
+ port >> 8, port & 0xff);
/* Send PORT request. */
request = ftp_request ("PORT", bytes);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline != '2')
{
- free (respline);
+ xfree (respline);
return FTPPORTERR;
}
- free (respline);
+ xfree (respline);
return FTPOK;
}
+#ifdef INET6
+uerr_t
+ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port,
+ char *typ)
+{
+ int err;
+ char *s, *respline;
+ char *request = ftp_request ("EPSV", typ);
+ if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
+ {
+ xfree (request);
+ return WRITEFAILED;
+ }
+ /* Get the server response. */
+ err = ftp_response (rbuf, &respline);
+ if (err != FTPOK)
+ {
+ xfree (respline);
+ return err;
+ }
+ if (*respline != '2')
+ {
+ xfree (respline);
+ return FTPNOPASV;
+ }
+ /* Parse the request. */
+ s = respline;
+ /* respline::=229 Entering Extended Passive Mode (|||6446|) */
+ for (s += 4; *s && !ISDIGIT (*s); s++);
+ if (!*s)
+ return FTPINVPASV;
+ *port=0;
+ for (; ISDIGIT (*s); s++)
+ *port = (*s - '0') + 10 * (*port);
+ xfree (respline);
+ /* Now we have the port but we need the IPv6 :-( */
+ {
+ wget_sockaddr remote;
+ int len = sizeof (remote);
+ struct sockaddr_in *ipv4_sock = ( struct sockaddr_in *)&remote;
+ getpeername (RBUF_FD (rbuf), (struct sockaddr*)&remote, &len);
+ switch(remote.sa.sa_family)
+ {
+ case AF_INET6:
+ memcpy (addr, &remote.sin6.sin6_addr, 16);
+ break;
+ case AF_INET:
+ map_ipv4_to_ip ((ip4_address *)&ipv4_sock->sin_addr, addr);
+ break;
+ default:
+ abort();
+ return FTPINVPASV;
+ /* realy bad */
+ }
+ }
+ return FTPOK;
+}
+#endif
+
+
/* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
transfer. Reads the response from server and parses it. Reads the
host and port addresses and returns them. */
uerr_t
-ftp_pasv (struct rbuf *rbuf, unsigned char *addr)
+ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
{
char *request, *respline, *s;
int nwritten, i;
uerr_t err;
+ unsigned char addr4[4];
+#ifdef INET6
+ if (ip_default_family == AF_INET6)
+ {
+ err = ftp_epsv (rbuf, addr, port, "2"); /* try IPv6 with EPSV */
+ if (FTPOK == err)
+ return FTPOK;
+ err = ftp_epsv (rbuf, addr, port, "1"); /* try IPv4 with EPSV */
+ if (FTPOK == err)
+ return FTPOK;
+ }
+#endif
/* Form the request. */
request = ftp_request ("PASV", NULL);
/* And send it. */
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get the server response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline != '2')
{
- free (respline);
+ xfree (respline);
return FTPNOPASV;
}
/* Parse the request. */
+ /* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
s = respline;
for (s += 4; *s && !ISDIGIT (*s); s++);
if (!*s)
return FTPINVPASV;
- for (i = 0; i < 6; i++)
+ for (i = 0; i < 4; i++)
{
- addr[i] = 0;
+ addr4[i] = 0;
for (; ISDIGIT (*s); s++)
- addr[i] = (*s - '0') + 10 * addr[i];
+ addr4[i] = (*s - '0') + 10 * addr4[i];
if (*s == ',')
- s++;
- else if (i < 5)
- {
- /* When on the last number, anything can be a terminator. */
- free (respline);
- return FTPINVPASV;
- }
+ s++;
+ else
+ {
+ xfree (respline);
+ return FTPINVPASV;
+ }
+ }
+
+ /* Eventually make an IPv4 in IPv6 adress if needed */
+ map_ipv4_to_ip ((ip4_address *)addr4, addr);
+
+ *port=0;
+ for (; ISDIGIT (*s); s++)
+ *port = (*s - '0') + 10 * (*port);
+ if (*s == ',')
+ s++;
+ else
+ {
+ xfree (respline);
+ return FTPINVPASV;
}
- free (respline);
+
+ {
+ unsigned short port2 = 0;
+ for (; ISDIGIT (*s); s++)
+ port2 = (*s - '0') + 10 * port2;
+ *port = (*port) * 256 + port2;
+ }
+ xfree (respline);
return FTPOK;
}
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline != '2')
{
- free (respline);
+ xfree (respline);
return FTPUNKNOWNTYPE;
}
- free (respline);
+ xfree (respline);
/* All OK. */
return FTPOK;
}
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline == '5')
{
- free (respline);
+ xfree (respline);
return FTPNSFOD;
}
if (*respline != '2')
{
- free (respline);
+ xfree (respline);
return FTPRERR;
}
- free (respline);
+ xfree (respline);
/* All OK. */
return FTPOK;
}
char *request, *respline;
int nwritten;
uerr_t err;
- static char numbuf[20]; /* Buffer for the number */
+ static char numbuf[24]; /* Buffer for the number */
- long_to_string (numbuf, offset);
+ number_to_string (numbuf, offset);
request = ftp_request ("REST", numbuf);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline != '3')
{
- free (respline);
+ xfree (respline);
return FTPRESTFAIL;
}
- free (respline);
+ xfree (respline);
/* All OK. */
return FTPOK;
}
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline == '5')
{
- free (respline);
+ xfree (respline);
return FTPNSFOD;
}
if (*respline != '1')
{
- free (respline);
+ xfree (respline);
return FTPRERR;
}
- free (respline);
+ xfree (respline);
/* All OK. */
return FTPOK;
}
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate respone. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline == '5')
{
- free (respline);
+ xfree (respline);
return FTPNSFOD;
}
if (*respline != '1')
{
- free (respline);
+ xfree (respline);
return FTPRERR;
}
- free (respline);
+ xfree (respline);
/* All OK. */
return FTPOK;
}
/* Sends the SYST command to the server. */
uerr_t
-ftp_syst (struct rbuf *rbuf, enum stype *host_type)
+ftp_syst (struct rbuf *rbuf, enum stype *server_type)
{
char *request, *respline;
int nwritten;
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
+
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline == '5')
{
- free (respline);
+ xfree (respline);
return FTPSRVERR;
}
request = strtok (NULL, " ");
if (!strcasecmp (request, "VMS"))
- *host_type = ST_VMS;
+ *server_type = ST_VMS;
+ else if (!strcasecmp (request, "UNIX"))
+ *server_type = ST_UNIX;
+ else if (!strcasecmp (request, "WINDOWS_NT"))
+ *server_type = ST_WINNT;
+ else if (!strcasecmp (request, "MACOS"))
+ *server_type = ST_MACOS;
else
- if (!strcasecmp (request, "UNIX"))
- *host_type = ST_UNIX;
- else
- *host_type = ST_OTHER;
+ *server_type = ST_OTHER;
- free (respline);
+ xfree (respline);
/* All OK. */
return FTPOK;
}
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
- free (request);
+ xfree (request);
return WRITEFAILED;
}
- free (request);
+ xfree (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
- free (respline);
+ xfree (respline);
return err;
}
if (*respline == '5')
{
- free (respline);
+ xfree (respline);
return FTPSRVERR;
}
strtok (respline, "\"");
request = strtok (NULL, "\"");
- /* Has the `pwd' been already allocated? Free! */
- if (*pwd) free (*pwd);
+ /* Has the `pwd' been already allocated? Free! */
+ FREE_MAYBE (*pwd);
*pwd = xstrdup (request);
- free (respline);
+ xfree (respline);
/* All OK. */
return FTPOK;
}
+
+/* Sends the SIZE command to the server, and returns the value in 'size'.
+ * If an error occurs, size is set to zero. */
+uerr_t
+ftp_size (struct rbuf *rbuf, const char *file, long int *size)
+{
+ char *request, *respline;
+ int nwritten;
+ uerr_t err;
+
+ /* Send PWD request. */
+ request = ftp_request ("SIZE", file);
+ nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
+ if (nwritten < 0)
+ {
+ xfree (request);
+ *size = 0;
+ return WRITEFAILED;
+ }
+ xfree (request);
+ /* Get appropriate response. */
+ err = ftp_response (rbuf, &respline);
+ if (err != FTPOK)
+ {
+ xfree (respline);
+ *size = 0;
+ return err;
+ }
+ if (*respline == '5')
+ {
+ /*
+ * Probably means SIZE isn't supported on this server.
+ * Error is nonfatal since SIZE isn't in RFC 959
+ */
+ xfree (respline);
+ *size = 0;
+ return FTPOK;
+ }
+
+ errno = 0;
+ *size = strtol (respline + 4, NULL, 0);
+ if (errno)
+ {
+ /*
+ * Couldn't parse the response for some reason. On the (few)
+ * tests I've done, the response is 213 <SIZE> with nothing else -
+ * maybe something a bit more resilient is necessary. It's not a
+ * fatal error, however.
+ */
+ xfree (respline);
+ *size = 0;
+ return FTPOK;
+ }
+
+ xfree (respline);
+ /* All OK. */
+ return FTPOK;
+}
+
+/* If URL's params are of the form "type=X", return character X.
+ Otherwise, return 'I' (the default type). */
+char
+ftp_process_type (const char *params)
+{
+ if (params
+ && 0 == strncasecmp (params, "type=", 5)
+ && params[5] != '\0')
+ return TOUPPER (params[5]);
+ else
+ return 'I';
+}