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>
47 char ftp_last_respline[128];
50 /* Get the response of FTP server and allocate enough room to handle
51 it. <CR> and <LF> characters are stripped from the line, and the
52 line is 0-terminated. All the response lines but the last one are
53 skipped. The last line is determined as described in RFC959. */
55 ftp_response (struct rbuf *rbuf, char **line)
60 *line = (char *)xmalloc (bufsize);
67 *line = (char *)xrealloc (*line, (bufsize <<= 1));
68 res = RBUF_READCHAR (rbuf, *line + i);
69 /* RES is number of bytes read. */
72 if ((*line)[i] == '\n')
76 if (i > 0 && (*line)[i - 1] == '\r')
77 (*line)[i - 1] = '\0';
84 if (opt.server_response)
85 logprintf (LOG_ALWAYS, "%s\n", *line);
87 DEBUGP (("%s\n", *line));
89 while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) &&
90 ISDIGIT ((*line)[2]) && (*line)[3] == ' '));
91 strncpy (ftp_last_respline, *line, sizeof (ftp_last_respline));
92 ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0';
96 /* Returns the malloc-ed FTP request, ending with <CR><LF>, printing
97 it if printing is required. If VALUE is NULL, just use
100 ftp_request (const char *command, const char *value)
102 char *res = (char *)xmalloc (strlen (command)
103 + (value ? (1 + strlen (value)) : 0)
105 sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : "");
106 if (opt.server_response)
108 /* Hack: don't print out password. */
109 if (strncmp (res, "PASS", 4) != 0)
110 logprintf (LOG_ALWAYS, "--> %s\n", res);
112 logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
115 DEBUGP (("\n--> %s\n", res));
120 const char *calculate_skey_response PARAMS ((int, const char *, const char *));
123 /* Sends the USER and PASS commands to the server, to control
124 connection socket csock. */
126 ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
129 char *request, *respline;
133 err = ftp_response (rbuf, &respline);
139 if (*respline != '2')
145 /* Send USER username. */
146 request = ftp_request ("USER", acc);
147 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
154 /* Get appropriate response. */
155 err = ftp_response (rbuf, &respline);
161 /* An unprobable possibility of logging without a password. */
162 if (*respline == '2')
167 /* Else, only response 3 is appropriate. */
168 if (*respline != '3')
171 return FTPLOGREFUSED;
175 static const char *skey_head[] = {
181 for (i = 0; i < ARRAY_SIZE (skey_head); i++)
183 if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
186 if (i < ARRAY_SIZE (skey_head))
189 int skey_sequence = 0;
191 for (cp = respline + strlen (skey_head[i]);
192 '0' <= *cp && *cp <= '9';
195 skey_sequence = skey_sequence * 10 + *cp - '0';
203 return FTPLOGREFUSED;
205 if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
210 #endif /* USE_OPIE */
212 /* Send PASS password. */
213 request = ftp_request ("PASS", pass);
214 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
221 /* Get appropriate response. */
222 err = ftp_response (rbuf, &respline);
228 if (*respline != '2')
238 /* Bind a port and send the appropriate PORT command to the FTP
239 server. Use acceptport after RETR, to get the socket of data
242 ftp_port (struct rbuf *rbuf)
245 char *request, *respline, *bytes;
246 unsigned char *in_addr;
250 /* Setting port to 0 lets the system choose a free port. */
253 err = bindport (&port);
256 /* Get the address of this side of the connection. */
257 if (!(in_addr = conaddr (RBUF_FD (rbuf))))
259 /* Construct the argument of PORT (of the form a,b,c,d,e,f). */
260 bytes = (char *)alloca (6 * 4 + 1);
261 sprintf (bytes, "%d,%d,%d,%d,%d,%d", in_addr[0], in_addr[1],
262 in_addr[2], in_addr[3], (unsigned) (port & 0xff00) >> 8,
264 /* Send PORT request. */
265 request = ftp_request ("PORT", bytes);
266 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
273 /* Get appropriate response. */
274 err = ftp_response (rbuf, &respline);
280 if (*respline != '2')
289 /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
290 transfer. Reads the response from server and parses it. Reads the
291 host and port addresses and returns them. */
293 ftp_pasv (struct rbuf *rbuf, unsigned char *addr)
295 char *request, *respline, *s;
299 /* Form the request. */
300 request = ftp_request ("PASV", NULL);
302 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
309 /* Get the server response. */
310 err = ftp_response (rbuf, &respline);
316 if (*respline != '2')
321 /* Parse the request. */
323 for (s += 4; *s && !ISDIGIT (*s); s++);
326 for (i = 0; i < 6; i++)
329 for (; ISDIGIT (*s); s++)
330 addr[i] = (*s - '0') + 10 * addr[i];
335 /* When on the last number, anything can be a terminator. */
344 /* Sends the TYPE request to the server. */
346 ftp_type (struct rbuf *rbuf, int type)
348 char *request, *respline;
353 /* Construct argument. */
356 /* Send TYPE request. */
357 request = ftp_request ("TYPE", stype);
358 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
365 /* Get appropriate response. */
366 err = ftp_response (rbuf, &respline);
372 if (*respline != '2')
375 return FTPUNKNOWNTYPE;
382 /* Changes the working directory by issuing a CWD command to the
385 ftp_cwd (struct rbuf *rbuf, const char *dir)
387 char *request, *respline;
391 /* Send CWD request. */
392 request = ftp_request ("CWD", dir);
393 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
400 /* Get appropriate response. */
401 err = ftp_response (rbuf, &respline);
407 if (*respline == '5')
412 if (*respline != '2')
422 /* Sends REST command to the FTP server. */
424 ftp_rest (struct rbuf *rbuf, long offset)
426 char *request, *respline;
429 static char numbuf[24]; /* Buffer for the number */
431 number_to_string (numbuf, offset);
432 request = ftp_request ("REST", numbuf);
433 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
440 /* Get appropriate response. */
441 err = ftp_response (rbuf, &respline);
447 if (*respline != '3')
457 /* Sends RETR command to the FTP server. */
459 ftp_retr (struct rbuf *rbuf, const char *file)
461 char *request, *respline;
465 /* Send RETR request. */
466 request = ftp_request ("RETR", file);
467 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
474 /* Get appropriate response. */
475 err = ftp_response (rbuf, &respline);
481 if (*respline == '5')
486 if (*respline != '1')
496 /* Sends the LIST command to the server. If FILE is NULL, send just
497 `LIST' (no space). */
499 ftp_list (struct rbuf *rbuf, const char *file)
501 char *request, *respline;
505 /* Send LIST request. */
506 request = ftp_request ("LIST", file);
507 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
514 /* Get appropriate respone. */
515 err = ftp_response (rbuf, &respline);
521 if (*respline == '5')
526 if (*respline != '1')
536 /* Sends the SYST command to the server. */
538 ftp_syst (struct rbuf *rbuf, enum stype *server_type)
540 char *request, *respline;
544 /* Send SYST request. */
545 request = ftp_request ("SYST", NULL);
546 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
554 /* Get appropriate response. */
555 err = ftp_response (rbuf, &respline);
561 if (*respline == '5')
567 /* Skip the number (215, but 200 (!!!) in case of VMS) */
568 strtok (respline, " ");
570 /* Which system type has been reported (we are interested just in the
571 first word of the server response)? */
572 request = strtok (NULL, " ");
574 if (!strcasecmp (request, "VMS"))
575 *server_type = ST_VMS;
576 else if (!strcasecmp (request, "UNIX"))
577 *server_type = ST_UNIX;
578 else if (!strcasecmp (request, "WINDOWS_NT"))
579 *server_type = ST_WINNT;
580 else if (!strcasecmp (request, "MACOS"))
581 *server_type = ST_MACOS;
583 *server_type = ST_OTHER;
590 /* Sends the PWD command to the server. */
592 ftp_pwd (struct rbuf *rbuf, char **pwd)
594 char *request, *respline;
598 /* Send PWD request. */
599 request = ftp_request ("PWD", NULL);
600 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
607 /* Get appropriate response. */
608 err = ftp_response (rbuf, &respline);
614 if (*respline == '5')
620 /* Skip the number (257), leading citation mark, trailing citation mark
621 and everything following it. */
622 strtok (respline, "\"");
623 request = strtok (NULL, "\"");
625 /* Has the `pwd' been already allocated? Free! */
628 *pwd = xstrdup (request);
635 /* Sends the SIZE command to the server, and returns the value in 'size'.
636 * If an error occurs, size is set to zero. */
638 ftp_size (struct rbuf *rbuf, const char *file, long int *size)
640 char *request, *respline;
644 /* Send PWD request. */
645 request = ftp_request ("SIZE", file);
646 nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
654 /* Get appropriate response. */
655 err = ftp_response (rbuf, &respline);
662 if (*respline == '5')
665 * Probably means SIZE isn't supported on this server.
666 * Error is nonfatal since SIZE isn't in RFC 959
674 *size = strtol (respline + 4, NULL, 0);
678 * Couldn't parse the response for some reason. On the (few)
679 * tests I've done, the response is 213 <SIZE> with nothing else -
680 * maybe something a bit more resilient is necessary. It's not a
681 * fatal error, however.
693 /* If URL's params are of the form "type=X", return character X.
694 Otherwise, return 'I' (the default type). */
696 ftp_process_type (const char *params)
699 && 0 == strncasecmp (params, "type=", 5)
700 && params[5] != '\0')
701 return TOUPPER (params[5]);