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. */
34 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
54 char ftp_last_respline[128];
57 /* Get the response of FTP server and allocate enough room to handle
58 it. <CR> and <LF> characters are stripped from the line, and the
59 line is 0-terminated. All the response lines but the last one are
60 skipped. The last line is determined as described in RFC959. */
62 ftp_response (struct rbuf *rbuf, char **line)
67 *line = (char *)xmalloc (bufsize);
74 *line = (char *)xrealloc (*line, (bufsize <<= 1));
75 res = RBUF_READCHAR (rbuf, *line + i);
76 /* RES is number of bytes read. */
79 if ((*line)[i] == '\n')
83 if (i > 0 && (*line)[i - 1] == '\r')
84 (*line)[i - 1] = '\0';
91 if (opt.server_response)
92 logprintf (LOG_ALWAYS, "%s\n", *line);
94 DEBUGP (("%s\n", *line));
96 while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) &&
97 ISDIGIT ((*line)[2]) && (*line)[3] == ' '));
98 strncpy (ftp_last_respline, *line, sizeof (ftp_last_respline));
99 ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0';
103 /* Returns the malloc-ed FTP request, ending with <CR><LF>, printing
104 it if printing is required. If VALUE is NULL, just use
107 ftp_request (const char *command, const char *value)
109 char *res = (char *)xmalloc (strlen (command)
110 + (value ? (1 + strlen (value)) : 0)
112 sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : "");
113 if (opt.server_response)
115 /* Hack: don't print out password. */
116 if (strncmp (res, "PASS", 4) != 0)
117 logprintf (LOG_ALWAYS, "--> %s\n", res);
119 logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
122 DEBUGP (("\n--> %s\n", res));
127 const char *calculate_skey_response PARAMS ((int, const char *, const char *));
130 /* Sends the USER and PASS commands to the server, to control
131 connection socket csock. */
133 ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
136 char *request, *respline;
140 err = ftp_response (rbuf, &respline);
146 if (*respline != '2')
152 /* Send USER username. */
153 request = ftp_request ("USER", acc);
154 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
161 /* Get appropriate response. */
162 err = ftp_response (rbuf, &respline);
168 /* An unprobable possibility of logging without a password. */
169 if (*respline == '2')
174 /* Else, only response 3 is appropriate. */
175 if (*respline != '3')
178 return FTPLOGREFUSED;
182 static const char *skey_head[] = {
188 for (i = 0; i < ARRAY_SIZE (skey_head); i++)
190 if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
193 if (i < ARRAY_SIZE (skey_head))
196 int skey_sequence = 0;
198 for (cp = respline + strlen (skey_head[i]);
199 '0' <= *cp && *cp <= '9';
202 skey_sequence = skey_sequence * 10 + *cp - '0';
210 return FTPLOGREFUSED;
212 if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
217 #endif /* USE_OPIE */
219 /* Send PASS password. */
220 request = ftp_request ("PASS", pass);
221 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
228 /* Get appropriate response. */
229 err = ftp_response (rbuf, &respline);
235 if (*respline != '2')
247 ftp_eprt (struct rbuf *rbuf)
251 char *request, *respline;
255 char ipv6 [8 * (4 * 3 + 3) + 8];
258 /* Setting port to 0 lets the system choose a free port. */
260 err = bindport (&port, ip_default_family);
261 if (err != BINDOK) /* Bind the port. */
264 /* Get the address of this side of the connection. */
265 if (!conaddr (RBUF_FD (rbuf), &in_addr))
266 /* Huh? This is not BINDERR! */
268 inet_ntop (AF_INET6, &in_addr, ipv6, sizeof (ipv6));
270 /* Construct the argument of EPRT (of the form |2|IPv6.ascii|PORT.ascii|). */
271 bytes = alloca (3 + strlen (ipv6) + 1 + numdigit (port) + 1 + 1);
272 sprintf (bytes, "|2|%s|%u|", ipv6, port);
273 /* Send PORT request. */
274 request = ftp_request ("EPRT", bytes);
275 if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
282 /* Get appropriate response. */
283 err = ftp_response (rbuf, &respline);
290 if (*respline != '2')
301 /* Bind a port and send the appropriate PORT command to the FTP
302 server. Use acceptport after RETR, to get the socket of data
305 ftp_port (struct rbuf *rbuf)
308 char *request, *respline;
309 char bytes[6 * 4 +1];
312 ip4_address in_addr_4;
313 unsigned char *in_addr4_ptr = (unsigned char *)&in_addr_4;
319 Only try the Extented Version if we actually use IPv6
321 if (ip_default_family == AF_INET6)
323 err = ftp_eprt (rbuf);
328 /* Setting port to 0 lets the system choose a free port. */
331 err = bindport (&port, AF_INET);
335 /* Get the address of this side of the connection and convert it
337 if (!conaddr (RBUF_FD (rbuf), &in_addr))
338 /* Huh? This is not BINDERR! */
340 if (!map_ip_to_ipv4 (&in_addr, &in_addr_4))
343 /* Construct the argument of PORT (of the form a,b,c,d,e,f). Port
344 is unsigned short so (unsigned) (port & 0xff000) >> 8 is the same
347 sprintf (bytes, "%d,%d,%d,%d,%d,%d",
348 in_addr4_ptr[0], in_addr4_ptr[1], in_addr4_ptr[2], in_addr4_ptr[3],
349 port >> 8, port & 0xff);
350 /* Send PORT request. */
351 request = ftp_request ("PORT", bytes);
352 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
359 /* Get appropriate response. */
360 err = ftp_response (rbuf, &respline);
366 if (*respline != '2')
377 ftp_epsv (struct rbuf *rbuf, ip_address *addr, unsigned short *port,
382 char *request = ftp_request ("EPSV", typ);
383 if (0 > iwrite (RBUF_FD (rbuf), request, strlen (request)))
388 /* Get the server response. */
389 err = ftp_response (rbuf, &respline);
395 if (*respline != '2')
400 /* Parse the request. */
402 /* respline::=229 Entering Extended Passive Mode (|||6446|) */
403 for (s += 4; *s && !ISDIGIT (*s); s++);
407 for (; ISDIGIT (*s); s++)
408 *port = (*s - '0') + 10 * (*port);
410 /* Now we have the port but we need the IPv6 :-( */
412 wget_sockaddr remote;
413 int len = sizeof (remote);
414 struct sockaddr_in *ipv4_sock = ( struct sockaddr_in *)&remote;
415 getpeername (RBUF_FD (rbuf), (struct sockaddr*)&remote, &len);
416 switch(remote.sa.sa_family)
419 memcpy (addr, &remote.sin6.sin6_addr, 16);
422 map_ipv4_to_ip ((ip4_address *)&ipv4_sock->sin_addr, addr);
435 /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
436 transfer. Reads the response from server and parses it. Reads the
437 host and port addresses and returns them. */
439 ftp_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
441 char *request, *respline, *s;
444 unsigned char addr4[4];
447 if (ip_default_family == AF_INET6)
449 err = ftp_epsv (rbuf, addr, port, "2"); /* try IPv6 with EPSV */
452 err = ftp_epsv (rbuf, addr, port, "1"); /* try IPv4 with EPSV */
457 /* Form the request. */
458 request = ftp_request ("PASV", NULL);
460 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
467 /* Get the server response. */
468 err = ftp_response (rbuf, &respline);
474 if (*respline != '2')
479 /* Parse the request. */
480 /* respline::=227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
482 for (s += 4; *s && !ISDIGIT (*s); s++);
485 for (i = 0; i < 4; i++)
488 for (; ISDIGIT (*s); s++)
489 addr4[i] = (*s - '0') + 10 * addr4[i];
499 /* Eventually make an IPv4 in IPv6 adress if needed */
500 map_ipv4_to_ip ((ip4_address *)addr4, addr);
503 for (; ISDIGIT (*s); s++)
504 *port = (*s - '0') + 10 * (*port);
514 unsigned short port2 = 0;
515 for (; ISDIGIT (*s); s++)
516 port2 = (*s - '0') + 10 * port2;
517 *port = (*port) * 256 + port2;
523 /* Sends the TYPE request to the server. */
525 ftp_type (struct rbuf *rbuf, int type)
527 char *request, *respline;
532 /* Construct argument. */
535 /* Send TYPE request. */
536 request = ftp_request ("TYPE", stype);
537 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
544 /* Get appropriate response. */
545 err = ftp_response (rbuf, &respline);
551 if (*respline != '2')
554 return FTPUNKNOWNTYPE;
561 /* Changes the working directory by issuing a CWD command to the
564 ftp_cwd (struct rbuf *rbuf, const char *dir)
566 char *request, *respline;
570 /* Send CWD request. */
571 request = ftp_request ("CWD", dir);
572 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
579 /* Get appropriate response. */
580 err = ftp_response (rbuf, &respline);
586 if (*respline == '5')
591 if (*respline != '2')
601 /* Sends REST command to the FTP server. */
603 ftp_rest (struct rbuf *rbuf, long offset)
605 char *request, *respline;
608 static char numbuf[24]; /* Buffer for the number */
610 number_to_string (numbuf, offset);
611 request = ftp_request ("REST", numbuf);
612 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
619 /* Get appropriate response. */
620 err = ftp_response (rbuf, &respline);
626 if (*respline != '3')
636 /* Sends RETR command to the FTP server. */
638 ftp_retr (struct rbuf *rbuf, const char *file)
640 char *request, *respline;
644 /* Send RETR request. */
645 request = ftp_request ("RETR", file);
646 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
653 /* Get appropriate response. */
654 err = ftp_response (rbuf, &respline);
660 if (*respline == '5')
665 if (*respline != '1')
675 /* Sends the LIST command to the server. If FILE is NULL, send just
676 `LIST' (no space). */
678 ftp_list (struct rbuf *rbuf, const char *file)
680 char *request, *respline;
684 /* Send LIST request. */
685 request = ftp_request ("LIST", file);
686 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
693 /* Get appropriate respone. */
694 err = ftp_response (rbuf, &respline);
700 if (*respline == '5')
705 if (*respline != '1')
715 /* Sends the SYST command to the server. */
717 ftp_syst (struct rbuf *rbuf, enum stype *server_type)
719 char *request, *respline;
723 /* Send SYST request. */
724 request = ftp_request ("SYST", NULL);
725 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
733 /* Get appropriate response. */
734 err = ftp_response (rbuf, &respline);
740 if (*respline == '5')
746 /* Skip the number (215, but 200 (!!!) in case of VMS) */
747 strtok (respline, " ");
749 /* Which system type has been reported (we are interested just in the
750 first word of the server response)? */
751 request = strtok (NULL, " ");
753 if (!strcasecmp (request, "VMS"))
754 *server_type = ST_VMS;
755 else if (!strcasecmp (request, "UNIX"))
756 *server_type = ST_UNIX;
757 else if (!strcasecmp (request, "WINDOWS_NT"))
758 *server_type = ST_WINNT;
759 else if (!strcasecmp (request, "MACOS"))
760 *server_type = ST_MACOS;
762 *server_type = ST_OTHER;
769 /* Sends the PWD command to the server. */
771 ftp_pwd (struct rbuf *rbuf, char **pwd)
773 char *request, *respline;
777 /* Send PWD request. */
778 request = ftp_request ("PWD", NULL);
779 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
786 /* Get appropriate response. */
787 err = ftp_response (rbuf, &respline);
793 if (*respline == '5')
799 /* Skip the number (257), leading citation mark, trailing citation mark
800 and everything following it. */
801 strtok (respline, "\"");
802 request = strtok (NULL, "\"");
804 /* Has the `pwd' been already allocated? Free! */
807 *pwd = xstrdup (request);
814 /* Sends the SIZE command to the server, and returns the value in 'size'.
815 * If an error occurs, size is set to zero. */
817 ftp_size (struct rbuf *rbuf, const char *file, long int *size)
819 char *request, *respline;
823 /* Send PWD request. */
824 request = ftp_request ("SIZE", file);
825 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
833 /* Get appropriate response. */
834 err = ftp_response (rbuf, &respline);
841 if (*respline == '5')
844 * Probably means SIZE isn't supported on this server.
845 * Error is nonfatal since SIZE isn't in RFC 959
853 *size = strtol (respline + 4, NULL, 0);
857 * Couldn't parse the response for some reason. On the (few)
858 * tests I've done, the response is 213 <SIZE> with nothing else -
859 * maybe something a bit more resilient is necessary. It's not a
860 * fatal error, however.
872 /* If URL's params are of the form "type=X", return character X.
873 Otherwise, return 'I' (the default type). */
875 ftp_process_type (const char *params)
878 && 0 == strncasecmp (params, "type=", 5)
879 && params[5] != '\0')
880 return TOUPPER (params[5]);