2 Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
4 This file is part of GNU Wget.
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables. You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL". If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so. If you do not wish to do
28 so, delete this exception statement from your version. */
44 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
64 char ftp_last_respline[128];
67 /* Get the response of FTP server and allocate enough room to handle
68 it. <CR> and <LF> characters are stripped from the line, and the
69 line is 0-terminated. All the response lines but the last one are
70 skipped. The last line is determined as described in RFC959. */
72 ftp_response (struct rbuf *rbuf, char **line)
77 *line = (char *)xmalloc (bufsize);
84 *line = (char *)xrealloc (*line, (bufsize <<= 1));
85 res = RBUF_READCHAR (rbuf, *line + i);
86 /* RES is number of bytes read. */
89 if ((*line)[i] == '\n')
93 if (i > 0 && (*line)[i - 1] == '\r')
94 (*line)[i - 1] = '\0';
101 if (opt.server_response)
102 logprintf (LOG_ALWAYS, "%s\n", *line);
104 DEBUGP (("%s\n", *line));
106 while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) &&
107 ISDIGIT ((*line)[2]) && (*line)[3] == ' '));
108 strncpy (ftp_last_respline, *line, sizeof (ftp_last_respline));
109 ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0';
113 /* Returns the malloc-ed FTP request, ending with <CR><LF>, printing
114 it if printing is required. If VALUE is NULL, just use
117 ftp_request (const char *command, const char *value)
119 char *res = (char *)xmalloc (strlen (command)
120 + (value ? (1 + strlen (value)) : 0)
122 sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : "");
123 if (opt.server_response)
125 /* Hack: don't print out password. */
126 if (strncmp (res, "PASS", 4) != 0)
127 logprintf (LOG_ALWAYS, "--> %s\n", res);
129 logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
132 DEBUGP (("\n--> %s\n", res));
137 const char *calculate_skey_response PARAMS ((int, const char *, const char *));
140 /* Sends the USER and PASS commands to the server, to control
141 connection socket csock. */
143 ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
146 char *request, *respline;
150 err = ftp_response (rbuf, &respline);
156 if (*respline != '2')
162 /* Send USER username. */
163 request = ftp_request ("USER", acc);
164 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
171 /* Get appropriate response. */
172 err = ftp_response (rbuf, &respline);
178 /* An unprobable possibility of logging without a password. */
179 if (*respline == '2')
184 /* Else, only response 3 is appropriate. */
185 if (*respline != '3')
188 return FTPLOGREFUSED;
192 static const char *skey_head[] = {
198 for (i = 0; i < countof (skey_head); i++)
200 if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
203 if (i < countof (skey_head))
206 int skey_sequence = 0;
208 for (cp = respline + strlen (skey_head[i]);
209 '0' <= *cp && *cp <= '9';
212 skey_sequence = skey_sequence * 10 + *cp - '0';
220 return FTPLOGREFUSED;
222 if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
227 #endif /* USE_OPIE */
229 /* Send PASS password. */
230 request = ftp_request ("PASS", pass);
231 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
238 /* Get appropriate response. */
239 err = ftp_response (rbuf, &respline);
245 if (*respline != '2')
257 ftp_eprt (struct rbuf *rbuf)
261 char *request, *respline;
265 char ipv6 [8 * (4 * 3 + 3) + 8];
268 /* Setting port to 0 lets the system choose a free port. */
270 err = bindport (&port, ip_default_family);
271 if (err != BINDOK) /* Bind the port. */
274 /* Get the address of this side of the connection. */
275 if (!conaddr (RBUF_FD (rbuf), &in_addr))
276 /* Huh? This is not BINDERR! */
278 inet_ntop (AF_INET6, &in_addr, ipv6, sizeof (ipv6));
280 /* Construct the argument of EPRT (of the form |2|IPv6.ascii|PORT.ascii|). */
281 bytes = alloca (3 + strlen (ipv6) + 1 + numdigit (port) + 1 + 1);
282 sprintf (bytes, "|2|%s|%u|", ipv6, port);
283 /* Send PORT request. */
284 request = ftp_request ("EPRT", bytes);
285 if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
292 /* Get appropriate response. */
293 err = ftp_response (rbuf, &respline);
300 if (*respline != '2')
311 /* Bind a port and send the appropriate PORT command to the FTP
312 server. Use acceptport after RETR, to get the socket of data
315 ftp_port (struct rbuf *rbuf)
318 char *request, *respline;
319 char bytes[6 * 4 +1];
322 ip4_address in_addr_4;
323 unsigned char *in_addr4_ptr = (unsigned char *)&in_addr_4;
329 Only try the Extented Version if we actually use IPv6
331 if (ip_default_family == AF_INET6)
333 err = ftp_eprt (rbuf);
338 /* Setting port to 0 lets the system choose a free port. */
341 err = bindport (&port, AF_INET);
345 /* Get the address of this side of the connection and convert it
347 if (!conaddr (RBUF_FD (rbuf), &in_addr))
348 /* Huh? This is not BINDERR! */
350 if (!map_ip_to_ipv4 (&in_addr, &in_addr_4))
353 /* Construct the argument of PORT (of the form a,b,c,d,e,f). Port
354 is unsigned short so (unsigned) (port & 0xff000) >> 8 is the same
357 sprintf (bytes, "%d,%d,%d,%d,%d,%d",
358 in_addr4_ptr[0], in_addr4_ptr[1], in_addr4_ptr[2], in_addr4_ptr[3],
359 port >> 8, port & 0xff);
360 /* Send PORT request. */
361 request = ftp_request ("PORT", bytes);
362 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
369 /* Get appropriate response. */
370 err = ftp_response (rbuf, &respline);
376 if (*respline != '2')
387 ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port,
392 char *request = ftp_request ("EPSV", typ);
393 if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
398 /* Get the server response. */
399 err = ftp_response (rbuf, &respline);
405 if (*respline != '2')
410 /* Parse the request. */
412 /* respline::=229 Entering Extended Passive Mode (|||6446|) */
413 for (s += 4; *s && !ISDIGIT (*s); s++);
417 for (; ISDIGIT (*s); s++)
418 *port = (*s - '0') + 10 * (*port);
420 /* Now we have the port but we need the IPv6 :-( */
422 wget_sockaddr remote;
423 int len = sizeof (remote);
424 struct sockaddr_in *ipv4_sock = ( struct sockaddr_in *)&remote;
425 getpeername (RBUF_FD (rbuf), (struct sockaddr*)&remote, &len);
426 switch(remote.sa.sa_family)
429 memcpy (addr, &remote.sin6.sin6_addr, 16);
432 map_ipv4_to_ip ((ip4_address *)&ipv4_sock->sin_addr, addr);
445 /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
446 transfer. Reads the response from server and parses it. Reads the
447 host and port addresses and returns them. */
449 ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
451 char *request, *respline, *s;
454 unsigned char addr4[4];
457 if (ip_default_family == AF_INET6)
459 err = ftp_epsv (rbuf, addr, port, "2"); /* try IPv6 with EPSV */
462 err = ftp_epsv (rbuf, addr, port, "1"); /* try IPv4 with EPSV */
467 /* Form the request. */
468 request = ftp_request ("PASV", NULL);
470 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
477 /* Get the server response. */
478 err = ftp_response (rbuf, &respline);
484 if (*respline != '2')
489 /* Parse the request. */
490 /* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
492 for (s += 4; *s && !ISDIGIT (*s); s++);
495 for (i = 0; i < 4; i++)
498 for (; ISDIGIT (*s); s++)
499 addr4[i] = (*s - '0') + 10 * addr4[i];
509 /* Eventually make an IPv4 in IPv6 adress if needed */
510 map_ipv4_to_ip ((ip4_address *)addr4, addr);
513 for (; ISDIGIT (*s); s++)
514 *port = (*s - '0') + 10 * (*port);
524 unsigned short port2 = 0;
525 for (; ISDIGIT (*s); s++)
526 port2 = (*s - '0') + 10 * port2;
527 *port = (*port) * 256 + port2;
533 /* Sends the TYPE request to the server. */
535 ftp_type (struct rbuf *rbuf, int type)
537 char *request, *respline;
542 /* Construct argument. */
545 /* Send TYPE request. */
546 request = ftp_request ("TYPE", stype);
547 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
554 /* Get appropriate response. */
555 err = ftp_response (rbuf, &respline);
561 if (*respline != '2')
564 return FTPUNKNOWNTYPE;
571 /* Changes the working directory by issuing a CWD command to the
574 ftp_cwd (struct rbuf *rbuf, const char *dir)
576 char *request, *respline;
580 /* Send CWD request. */
581 request = ftp_request ("CWD", dir);
582 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
589 /* Get appropriate response. */
590 err = ftp_response (rbuf, &respline);
596 if (*respline == '5')
601 if (*respline != '2')
611 /* Sends REST command to the FTP server. */
613 ftp_rest (struct rbuf *rbuf, long offset)
615 char *request, *respline;
618 static char numbuf[24]; /* Buffer for the number */
620 number_to_string (numbuf, offset);
621 request = ftp_request ("REST", numbuf);
622 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
629 /* Get appropriate response. */
630 err = ftp_response (rbuf, &respline);
636 if (*respline != '3')
646 /* Sends RETR command to the FTP server. */
648 ftp_retr (struct rbuf *rbuf, const char *file)
650 char *request, *respline;
654 /* Send RETR request. */
655 request = ftp_request ("RETR", file);
656 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
663 /* Get appropriate response. */
664 err = ftp_response (rbuf, &respline);
670 if (*respline == '5')
675 if (*respline != '1')
685 /* Sends the LIST command to the server. If FILE is NULL, send just
686 `LIST' (no space). */
688 ftp_list (struct rbuf *rbuf, const char *file)
690 char *request, *respline;
694 /* Send LIST request. */
695 request = ftp_request ("LIST", file);
696 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
703 /* Get appropriate respone. */
704 err = ftp_response (rbuf, &respline);
710 if (*respline == '5')
715 if (*respline != '1')
725 /* Sends the SYST command to the server. */
727 ftp_syst (struct rbuf *rbuf, enum stype *server_type)
729 char *request, *respline;
733 /* Send SYST request. */
734 request = ftp_request ("SYST", NULL);
735 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
743 /* Get appropriate response. */
744 err = ftp_response (rbuf, &respline);
750 if (*respline == '5')
756 /* Skip the number (215, but 200 (!!!) in case of VMS) */
757 strtok (respline, " ");
759 /* Which system type has been reported (we are interested just in the
760 first word of the server response)? */
761 request = strtok (NULL, " ");
763 if (!strcasecmp (request, "VMS"))
764 *server_type = ST_VMS;
765 else if (!strcasecmp (request, "UNIX"))
766 *server_type = ST_UNIX;
767 else if (!strcasecmp (request, "WINDOWS_NT"))
768 *server_type = ST_WINNT;
769 else if (!strcasecmp (request, "MACOS"))
770 *server_type = ST_MACOS;
771 else if (!strcasecmp (request, "OS/400"))
772 *server_type = ST_OS400;
774 *server_type = ST_OTHER;
781 /* Sends the PWD command to the server. */
783 ftp_pwd (struct rbuf *rbuf, char **pwd)
785 char *request, *respline;
789 /* Send PWD request. */
790 request = ftp_request ("PWD", NULL);
791 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
798 /* Get appropriate response. */
799 err = ftp_response (rbuf, &respline);
805 if (*respline == '5')
811 /* Skip the number (257), leading citation mark, trailing citation mark
812 and everything following it. */
813 strtok (respline, "\"");
814 request = strtok (NULL, "\"");
816 /* Has the `pwd' been already allocated? Free! */
819 *pwd = xstrdup (request);
826 /* Sends the SIZE command to the server, and returns the value in 'size'.
827 * If an error occurs, size is set to zero. */
829 ftp_size (struct rbuf *rbuf, const char *file, long int *size)
831 char *request, *respline;
835 /* Send PWD request. */
836 request = ftp_request ("SIZE", file);
837 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
845 /* Get appropriate response. */
846 err = ftp_response (rbuf, &respline);
853 if (*respline == '5')
856 * Probably means SIZE isn't supported on this server.
857 * Error is nonfatal since SIZE isn't in RFC 959
865 *size = strtol (respline + 4, NULL, 0);
869 * Couldn't parse the response for some reason. On the (few)
870 * tests I've done, the response is 213 <SIZE> with nothing else -
871 * maybe something a bit more resilient is necessary. It's not a
872 * fatal error, however.
884 /* If URL's params are of the form "type=X", return character X.
885 Otherwise, return 'I' (the default type). */
887 ftp_process_type (const char *params)
890 && 0 == strncasecmp (params, "type=", 5)
891 && params[5] != '\0')
892 return TOUPPER (params[5]);