source: trunk/essentials/net-misc/wget/src/ftp-basic.c

Last change on this file was 3440, checked in by bird, 18 years ago

wget 1.10.2

File size: 28.2 KB
Line 
1/* Basic FTP routines.
2 Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
3
4This file is part of GNU Wget.
5
6GNU Wget is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11GNU Wget is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with Wget; if not, write to the Free Software
18Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20In addition, as a special exception, the Free Software Foundation
21gives permission to link the code of its release of Wget with the
22OpenSSL project's "OpenSSL" library (or with modified versions of it
23that use the same license as the "OpenSSL" library), and distribute
24the linked executables. You must obey the GNU General Public License
25in all respects for all of the code used other than "OpenSSL". If you
26modify this file, you may extend this exception to your version of the
27file, but you are not obligated to do so. If you do not wish to do
28so, delete this exception statement from your version. */
29
30#include <config.h>
31
32#include <assert.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <errno.h>
36
37#ifdef HAVE_STRING_H
38# include <string.h>
39#else
40# include <strings.h>
41#endif
42#ifdef HAVE_UNISTD_H
43# include <unistd.h>
44#endif
45#include <sys/types.h>
46
47#include "wget.h"
48#include "utils.h"
49#include "connect.h"
50#include "host.h"
51#include "ftp.h"
52#include "retr.h"
53
54char ftp_last_respline[128];
55
56
57
58/* Get the response of FTP server and allocate enough room to handle
59 it. <CR> and <LF> characters are stripped from the line, and the
60 line is 0-terminated. All the response lines but the last one are
61 skipped. The last line is determined as described in RFC959.
62
63 If the line is successfully read, FTPOK is returned, and *ret_line
64 is assigned a freshly allocated line. Otherwise, FTPRERR is
65 returned, and the value of *ret_line should be ignored. */
66
67uerr_t
68ftp_response (int fd, char **ret_line)
69{
70 while (1)
71 {
72 char *p;
73 char *line = fd_read_line (fd);
74 if (!line)
75 return FTPRERR;
76
77 /* Strip trailing CRLF before printing the line, so that
78 escnonprint doesn't include bogus \012 and \015. */
79 p = strchr (line, '\0');
80 if (p > line && p[-1] == '\n')
81 *--p = '\0';
82 if (p > line && p[-1] == '\r')
83 *--p = '\0';
84
85 if (opt.server_response)
86 logprintf (LOG_NOTQUIET, "%s\n", escnonprint (line));
87 else
88 DEBUGP (("%s\n", escnonprint (line)));
89
90 /* The last line of output is the one that begins with "ddd ". */
91 if (ISDIGIT (line[0]) && ISDIGIT (line[1]) && ISDIGIT (line[2])
92 && line[3] == ' ')
93 {
94 strncpy (ftp_last_respline, line, sizeof (ftp_last_respline));
95 ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0';
96 *ret_line = line;
97 return FTPOK;
98 }
99 xfree (line);
100 }
101}
102
103/* Returns the malloc-ed FTP request, ending with <CR><LF>, printing
104 it if printing is required. If VALUE is NULL, just use
105 command<CR><LF>. */
106static char *
107ftp_request (const char *command, const char *value)
108{
109 char *res;
110 if (value)
111 {
112 /* Check for newlines in VALUE (possibly injected by the %0A URL
113 escape) making the callers inadvertently send multiple FTP
114 commands at once. Without this check an attacker could
115 intentionally redirect to ftp://server/fakedir%0Acommand.../
116 and execute arbitrary FTP command on a remote FTP server. */
117 if (strpbrk (value, "\r\n"))
118 {
119 /* Copy VALUE to the stack and modify CR/LF to space. */
120 char *defanged, *p;
121 STRDUP_ALLOCA (defanged, value);
122 for (p = defanged; *p; p++)
123 if (*p == '\r' || *p == '\n')
124 *p = ' ';
125 DEBUGP (("\nDetected newlines in %s \"%s\"; changing to %s \"%s\"\n",
126 command, escnonprint (value), command, escnonprint (defanged)));
127 /* Make VALUE point to the defanged copy of the string. */
128 value = defanged;
129 }
130 res = concat_strings (command, " ", value, "\r\n", (char *) 0);
131 }
132 else
133 res = concat_strings (command, "\r\n", (char *) 0);
134 if (opt.server_response)
135 {
136 /* Hack: don't print out password. */
137 if (strncmp (res, "PASS", 4) != 0)
138 logprintf (LOG_ALWAYS, "--> %s\n", res);
139 else
140 logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n\n");
141 }
142 else
143 DEBUGP (("\n--> %s\n", res));
144 return res;
145}
146
147/* Sends the USER and PASS commands to the server, to control
148 connection socket csock. */
149uerr_t
150ftp_login (int csock, const char *acc, const char *pass)
151{
152 uerr_t err;
153 char *request, *respline;
154 int nwritten;
155
156 /* Get greeting. */
157 err = ftp_response (csock, &respline);
158 if (err != FTPOK)
159 return err;
160 if (*respline != '2')
161 {
162 xfree (respline);
163 return FTPSRVERR;
164 }
165 xfree (respline);
166 /* Send USER username. */
167 request = ftp_request ("USER", acc);
168 nwritten = fd_write (csock, request, strlen (request), -1.0);
169 if (nwritten < 0)
170 {
171 xfree (request);
172 return WRITEFAILED;
173 }
174 xfree (request);
175 /* Get appropriate response. */
176 err = ftp_response (csock, &respline);
177 if (err != FTPOK)
178 return err;
179 /* An unprobable possibility of logging without a password. */
180 if (*respline == '2')
181 {
182 xfree (respline);
183 return FTPOK;
184 }
185 /* Else, only response 3 is appropriate. */
186 if (*respline != '3')
187 {
188 xfree (respline);
189 return FTPLOGREFUSED;
190 }
191#ifdef ENABLE_OPIE
192 {
193 static const char *skey_head[] = {
194 "331 s/key ",
195 "331 opiekey "
196 };
197 int i;
198 const char *seed = NULL;
199
200 for (i = 0; i < countof (skey_head); i++)
201 {
202 int l = strlen (skey_head[i]);
203 if (0 == strncasecmp (skey_head[i], respline, l))
204 {
205 seed = respline + l;
206 break;
207 }
208 }
209 if (seed)
210 {
211 int skey_sequence = 0;
212
213 /* Extract the sequence from SEED. */
214 for (; ISDIGIT (*seed); seed++)
215 skey_sequence = 10 * skey_sequence + *seed - '0';
216 if (*seed == ' ')
217 ++seed;
218 else
219 {
220 xfree (respline);
221 return FTPLOGREFUSED;
222 }
223 /* Replace the password with the SKEY response to the
224 challenge. */
225 pass = skey_response (skey_sequence, seed, pass);
226 }
227 }
228#endif /* ENABLE_OPIE */
229 xfree (respline);
230 /* Send PASS password. */
231 request = ftp_request ("PASS", pass);
232 nwritten = fd_write (csock, request, strlen (request), -1.0);
233 if (nwritten < 0)
234 {
235 xfree (request);
236 return WRITEFAILED;
237 }
238 xfree (request);
239 /* Get appropriate response. */
240 err = ftp_response (csock, &respline);
241 if (err != FTPOK)
242 return err;
243 if (*respline != '2')
244 {
245 xfree (respline);
246 return FTPLOGINC;
247 }
248 xfree (respline);
249 /* All OK. */
250 return FTPOK;
251}
252
253static void
254ip_address_to_port_repr (const ip_address *addr, int port, char *buf,
255 size_t buflen)
256{
257 unsigned char *ptr;
258
259 assert (addr != NULL);
260 assert (addr->type == IPV4_ADDRESS);
261 assert (buf != NULL);
262 /* buf must contain the argument of PORT (of the form a,b,c,d,e,f). */
263 assert (buflen >= 6 * 4);
264
265 ptr = ADDRESS_IPV4_DATA (addr);
266 snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d", ptr[0], ptr[1],
267 ptr[2], ptr[3], (port & 0xff00) >> 8, port & 0xff);
268 buf[buflen - 1] = '\0';
269}
270
271/* Bind a port and send the appropriate PORT command to the FTP
272 server. Use acceptport after RETR, to get the socket of data
273 connection. */
274uerr_t
275ftp_port (int csock, int *local_sock)
276{
277 uerr_t err;
278 char *request, *respline;
279 ip_address addr;
280 int nwritten;
281 int port;
282 /* Must contain the argument of PORT (of the form a,b,c,d,e,f). */
283 char bytes[6 * 4 + 1];
284
285 /* Get the address of this side of the connection. */
286 if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
287 return FTPSYSERR;
288
289 assert (addr.type == IPV4_ADDRESS);
290
291 /* Setting port to 0 lets the system choose a free port. */
292 port = 0;
293
294 /* Bind the port. */
295 *local_sock = bind_local (&addr, &port);
296 if (*local_sock < 0)
297 return FTPSYSERR;
298
299 /* Construct the argument of PORT (of the form a,b,c,d,e,f). */
300 ip_address_to_port_repr (&addr, port, bytes, sizeof (bytes));
301
302 /* Send PORT request. */
303 request = ftp_request ("PORT", bytes);
304 nwritten = fd_write (csock, request, strlen (request), -1.0);
305 if (nwritten < 0)
306 {
307 xfree (request);
308 fd_close (*local_sock);
309 return WRITEFAILED;
310 }
311 xfree (request);
312
313 /* Get appropriate response. */
314 err = ftp_response (csock, &respline);
315 if (err != FTPOK)
316 {
317 fd_close (*local_sock);
318 return err;
319 }
320 if (*respline != '2')
321 {
322 xfree (respline);
323 fd_close (*local_sock);
324 return FTPPORTERR;
325 }
326 xfree (respline);
327 return FTPOK;
328}
329
330#ifdef ENABLE_IPV6
331static void
332ip_address_to_lprt_repr (const ip_address *addr, int port, char *buf,
333 size_t buflen)
334{
335 unsigned char *ptr;
336
337 assert (addr != NULL);
338 assert (addr->type == IPV4_ADDRESS || addr->type == IPV6_ADDRESS);
339 assert (buf != NULL);
340 /* buf must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
341 assert (buflen >= 21 * 4);
342
343 /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
344 switch (addr->type)
345 {
346 case IPV4_ADDRESS:
347 ptr = ADDRESS_IPV4_DATA (addr);
348 snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d", 4, 4,
349 ptr[0], ptr[1], ptr[2], ptr[3], 2,
350 (port & 0xff00) >> 8, port & 0xff);
351 buf[buflen - 1] = '\0';
352 break;
353 case IPV6_ADDRESS:
354 ptr = ADDRESS_IPV6_DATA (addr);
355 snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
356 6, 16, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7],
357 ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15], 2,
358 (port & 0xff00) >> 8, port & 0xff);
359 buf[buflen - 1] = '\0';
360 break;
361 }
362}
363
364/* Bind a port and send the appropriate PORT command to the FTP
365 server. Use acceptport after RETR, to get the socket of data
366 connection. */
367uerr_t
368ftp_lprt (int csock, int *local_sock)
369{
370 uerr_t err;
371 char *request, *respline;
372 ip_address addr;
373 int nwritten;
374 int port;
375 /* Must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
376 char bytes[21 * 4 + 1];
377
378 /* Get the address of this side of the connection. */
379 if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
380 return FTPSYSERR;
381
382 assert (addr.type == IPV4_ADDRESS || addr.type == IPV6_ADDRESS);
383
384 /* Setting port to 0 lets the system choose a free port. */
385 port = 0;
386
387 /* Bind the port. */
388 *local_sock = bind_local (&addr, &port);
389 if (*local_sock < 0)
390 return FTPSYSERR;
391
392 /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */
393 ip_address_to_lprt_repr (&addr, port, bytes, sizeof (bytes));
394
395 /* Send PORT request. */
396 request = ftp_request ("LPRT", bytes);
397 nwritten = fd_write (csock, request, strlen (request), -1.0);
398 if (nwritten < 0)
399 {
400 xfree (request);
401 fd_close (*local_sock);
402 return WRITEFAILED;
403 }
404 xfree (request);
405 /* Get appropriate response. */
406 err = ftp_response (csock, &respline);
407 if (err != FTPOK)
408 {
409 fd_close (*local_sock);
410 return err;
411 }
412 if (*respline != '2')
413 {
414 xfree (respline);
415 fd_close (*local_sock);
416 return FTPPORTERR;
417 }
418 xfree (respline);
419 return FTPOK;
420}
421
422static void
423ip_address_to_eprt_repr (const ip_address *addr, int port, char *buf,
424 size_t buflen)
425{
426 int afnum;
427
428 assert (addr != NULL);
429 assert (addr->type == IPV4_ADDRESS || addr->type == IPV6_ADDRESS);
430 assert (buf != NULL);
431 /* buf must contain the argument of EPRT (of the form |af|addr|port|).
432 * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr
433 * 1 char for af (1-2) and 5 chars for port (0-65535) */
434 assert (buflen >= 4 + INET6_ADDRSTRLEN + 1 + 5);
435
436 /* Construct the argument of EPRT (of the form |af|addr|port|). */
437 afnum = (addr->type == IPV4_ADDRESS ? 1 : 2);
438 snprintf (buf, buflen, "|%d|%s|%d|", afnum, pretty_print_address (addr), port);
439 buf[buflen - 1] = '\0';
440}
441
442/* Bind a port and send the appropriate PORT command to the FTP
443 server. Use acceptport after RETR, to get the socket of data
444 connection. */
445uerr_t
446ftp_eprt (int csock, int *local_sock)
447{
448 uerr_t err;
449 char *request, *respline;
450 ip_address addr;
451 int nwritten;
452 int port;
453 /* Must contain the argument of EPRT (of the form |af|addr|port|).
454 * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr
455 * 1 char for af (1-2) and 5 chars for port (0-65535) */
456 char bytes[4 + INET6_ADDRSTRLEN + 1 + 5 + 1];
457
458 /* Get the address of this side of the connection. */
459 if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL))
460 return FTPSYSERR;
461
462 assert (addr.type == IPV4_ADDRESS || addr.type == IPV6_ADDRESS);
463
464 /* Setting port to 0 lets the system choose a free port. */
465 port = 0;
466
467 /* Bind the port. */
468 *local_sock = bind_local (&addr, &port);
469 if (*local_sock < 0)
470 return FTPSYSERR;
471
472 /* Construct the argument of EPRT (of the form |af|addr|port|). */
473 ip_address_to_eprt_repr (&addr, port, bytes, sizeof (bytes));
474
475 /* Send PORT request. */
476 request = ftp_request ("EPRT", bytes);
477 nwritten = fd_write (csock, request, strlen (request), -1.0);
478 if (nwritten < 0)
479 {
480 xfree (request);
481 fd_close (*local_sock);
482 return WRITEFAILED;
483 }
484 xfree (request);
485 /* Get appropriate response. */
486 err = ftp_response (csock, &respline);
487 if (err != FTPOK)
488 {
489 fd_close (*local_sock);
490 return err;
491 }
492 if (*respline != '2')
493 {
494 xfree (respline);
495 fd_close (*local_sock);
496 return FTPPORTERR;
497 }
498 xfree (respline);
499 return FTPOK;
500}
501#endif
502
503/* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
504 transfer. Reads the response from server and parses it. Reads the
505 host and port addresses and returns them. */
506uerr_t
507ftp_pasv (int csock, ip_address *addr, int *port)
508{
509 char *request, *respline, *s;
510 int nwritten, i;
511 uerr_t err;
512 unsigned char tmp[6];
513
514 assert (addr != NULL);
515 assert (port != NULL);
516
517 xzero (*addr);
518
519 /* Form the request. */
520 request = ftp_request ("PASV", NULL);
521 /* And send it. */
522 nwritten = fd_write (csock, request, strlen (request), -1.0);
523 if (nwritten < 0)
524 {
525 xfree (request);
526 return WRITEFAILED;
527 }
528 xfree (request);
529 /* Get the server response. */
530 err = ftp_response (csock, &respline);
531 if (err != FTPOK)
532 return err;
533 if (*respline != '2')
534 {
535 xfree (respline);
536 return FTPNOPASV;
537 }
538 /* Parse the request. */
539 s = respline;
540 for (s += 4; *s && !ISDIGIT (*s); s++);
541 if (!*s)
542 return FTPINVPASV;
543 for (i = 0; i < 6; i++)
544 {
545 tmp[i] = 0;
546 for (; ISDIGIT (*s); s++)
547 tmp[i] = (*s - '0') + 10 * tmp[i];
548 if (*s == ',')
549 s++;
550 else if (i < 5)
551 {
552 /* When on the last number, anything can be a terminator. */
553 xfree (respline);
554 return FTPINVPASV;
555 }
556 }
557 xfree (respline);
558
559 addr->type = IPV4_ADDRESS;
560 memcpy (ADDRESS_IPV4_DATA (addr), tmp, 4);
561 *port = ((tmp[4] << 8) & 0xff00) + tmp[5];
562
563 return FTPOK;
564}
565
566#ifdef ENABLE_IPV6
567/* Similar to ftp_lprt, but uses `LPSV' to initiate the passive FTP
568 transfer. Reads the response from server and parses it. Reads the
569 host and port addresses and returns them. */
570uerr_t
571ftp_lpsv (int csock, ip_address *addr, int *port)
572{
573 char *request, *respline, *s;
574 int nwritten, i, af, addrlen, portlen;
575 uerr_t err;
576 unsigned char tmp[16];
577 unsigned char tmpprt[2];
578
579 assert (addr != NULL);
580 assert (port != NULL);
581
582 xzero (*addr);
583
584 /* Form the request. */
585 request = ftp_request ("LPSV", NULL);
586
587 /* And send it. */
588 nwritten = fd_write (csock, request, strlen (request), -1.0);
589 if (nwritten < 0)
590 {
591 xfree (request);
592 return WRITEFAILED;
593 }
594 xfree (request);
595
596 /* Get the server response. */
597 err = ftp_response (csock, &respline);
598 if (err != FTPOK)
599 return err;
600 if (*respline != '2')
601 {
602 xfree (respline);
603 return FTPNOPASV;
604 }
605
606 /* Parse the response. */
607 s = respline;
608 for (s += 4; *s && !ISDIGIT (*s); s++);
609 if (!*s)
610 return FTPINVPASV;
611
612 /* First, get the address family */
613 af = 0;
614 for (; ISDIGIT (*s); s++)
615 af = (*s - '0') + 10 * af;
616
617 if (af != 4 && af != 6)
618 {
619 xfree (respline);
620 return FTPINVPASV;
621 }
622
623 if (!*s || *s++ != ',')
624 {
625 xfree (respline);
626 return FTPINVPASV;
627 }
628
629 /* Then, get the address length */
630 addrlen = 0;
631 for (; ISDIGIT (*s); s++)
632 addrlen = (*s - '0') + 10 * addrlen;
633
634 if (!*s || *s++ != ',')
635 {
636 xfree (respline);
637 return FTPINVPASV;
638 }
639
640 if (addrlen > 16)
641 {
642 xfree (respline);
643 return FTPINVPASV;
644 }
645
646 if ((af == 4 && addrlen != 4)
647 || (af == 6 && addrlen != 16))
648 {
649 xfree (respline);
650 return FTPINVPASV;
651 }
652
653 /* Now, we get the actual address */
654 for (i = 0; i < addrlen; i++)
655 {
656 tmp[i] = 0;
657 for (; ISDIGIT (*s); s++)
658 tmp[i] = (*s - '0') + 10 * tmp[i];
659 if (*s == ',')
660 s++;
661 else
662 {
663 xfree (respline);
664 return FTPINVPASV;
665 }
666 }
667
668 /* Now, get the port length */
669 portlen = 0;
670 for (; ISDIGIT (*s); s++)
671 portlen = (*s - '0') + 10 * portlen;
672
673 if (!*s || *s++ != ',')
674 {
675 xfree (respline);
676 return FTPINVPASV;
677 }
678
679 if (portlen > 2)
680 {
681 xfree (respline);
682 return FTPINVPASV;
683 }
684
685 /* Finally, we get the port number */
686 tmpprt[0] = 0;
687 for (; ISDIGIT (*s); s++)
688 tmpprt[0] = (*s - '0') + 10 * tmpprt[0];
689
690 if (!*s || *s++ != ',')
691 {
692 xfree (respline);
693 return FTPINVPASV;
694 }
695
696 tmpprt[1] = 0;
697 for (; ISDIGIT (*s); s++)
698 tmpprt[1] = (*s - '0') + 10 * tmpprt[1];
699
700 assert (s != NULL);
701
702 if (af == 4)
703 {
704 addr->type = IPV4_ADDRESS;
705 memcpy (ADDRESS_IPV4_DATA (addr), tmp, 4);
706 *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
707 DEBUGP (("lpsv addr is: %s\n", pretty_print_address(addr)));
708 DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
709 DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
710 DEBUGP (("*port is: %d\n", *port));
711 }
712 else
713 {
714 assert (af == 6);
715 addr->type = IPV6_ADDRESS;
716 memcpy (ADDRESS_IPV6_DATA (addr), tmp, 16);
717 *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1];
718 DEBUGP (("lpsv addr is: %s\n", pretty_print_address(addr)));
719 DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0]));
720 DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1]));
721 DEBUGP (("*port is: %d\n", *port));
722 }
723
724 xfree (respline);
725 return FTPOK;
726}
727
728/* Similar to ftp_eprt, but uses `EPSV' to initiate the passive FTP
729 transfer. Reads the response from server and parses it. Reads the
730 host and port addresses and returns them. */
731uerr_t
732ftp_epsv (int csock, ip_address *ip, int *port)
733{
734 char *request, *respline, *start, delim, *s;
735 int nwritten, i;
736 uerr_t err;
737 int tport;
738
739 assert (ip != NULL);
740 assert (port != NULL);
741
742 /* IP already contains the IP address of the control connection's
743 peer, so we don't need to call socket_ip_address here. */
744
745 /* Form the request. */
746 /* EPSV 1 means that we ask for IPv4 and EPSV 2 means that we ask for IPv6. */
747 request = ftp_request ("EPSV", (ip->type == IPV4_ADDRESS ? "1" : "2"));
748
749 /* And send it. */
750 nwritten = fd_write (csock, request, strlen (request), -1.0);
751 if (nwritten < 0)
752 {
753 xfree (request);
754 return WRITEFAILED;
755 }
756 xfree (request);
757
758 /* Get the server response. */
759 err = ftp_response (csock, &respline);
760 if (err != FTPOK)
761 return err;
762 if (*respline != '2')
763 {
764 xfree (respline);
765 return FTPNOPASV;
766 }
767
768 assert (respline != NULL);
769
770 DEBUGP(("respline is %s\n", respline));
771
772 /* Parse the response. */
773 s = respline;
774
775 /* Skip the useless stuff and get what's inside the parentheses */
776 start = strchr (respline, '(');
777 if (start == NULL)
778 {
779 xfree (respline);
780 return FTPINVPASV;
781 }
782
783 /* Skip the first two void fields */
784 s = start + 1;
785 delim = *s++;
786 if (delim < 33 || delim > 126)
787 {
788 xfree (respline);
789 return FTPINVPASV;
790 }
791
792 for (i = 0; i < 2; i++)
793 {
794 if (*s++ != delim)
795 {
796 xfree (respline);
797 return FTPINVPASV;
798 }
799 }
800
801 /* Finally, get the port number */
802 tport = 0;
803 for (i = 1; ISDIGIT (*s); s++)
804 {
805 if (i > 5)
806 {
807 xfree (respline);
808 return FTPINVPASV;
809 }
810 tport = (*s - '0') + 10 * tport;
811 }
812
813 /* Make sure that the response terminates correcty */
814 if (*s++ != delim)
815 {
816 xfree (respline);
817 return FTPINVPASV;
818 }
819
820 if (*s++ != ')')
821 {
822 xfree (respline);
823 return FTPINVPASV;
824 }
825
826 *port = tport;
827
828 xfree (respline);
829 return FTPOK;
830}
831#endif
832
833/* Sends the TYPE request to the server. */
834uerr_t
835ftp_type (int csock, int type)
836{
837 char *request, *respline;
838 int nwritten;
839 uerr_t err;
840 char stype[2];
841
842 /* Construct argument. */
843 stype[0] = type;
844 stype[1] = 0;
845 /* Send TYPE request. */
846 request = ftp_request ("TYPE", stype);
847 nwritten = fd_write (csock, request, strlen (request), -1.0);
848 if (nwritten < 0)
849 {
850 xfree (request);
851 return WRITEFAILED;
852 }
853 xfree (request);
854 /* Get appropriate response. */
855 err = ftp_response (csock, &respline);
856 if (err != FTPOK)
857 return err;
858 if (*respline != '2')
859 {
860 xfree (respline);
861 return FTPUNKNOWNTYPE;
862 }
863 xfree (respline);
864 /* All OK. */
865 return FTPOK;
866}
867
868/* Changes the working directory by issuing a CWD command to the
869 server. */
870uerr_t
871ftp_cwd (int csock, const char *dir)
872{
873 char *request, *respline;
874 int nwritten;
875 uerr_t err;
876
877 /* Send CWD request. */
878 request = ftp_request ("CWD", dir);
879 nwritten = fd_write (csock, request, strlen (request), -1.0);
880 if (nwritten < 0)
881 {
882 xfree (request);
883 return WRITEFAILED;
884 }
885 xfree (request);
886 /* Get appropriate response. */
887 err = ftp_response (csock, &respline);
888 if (err != FTPOK)
889 return err;
890 if (*respline == '5')
891 {
892 xfree (respline);
893 return FTPNSFOD;
894 }
895 if (*respline != '2')
896 {
897 xfree (respline);
898 return FTPRERR;
899 }
900 xfree (respline);
901 /* All OK. */
902 return FTPOK;
903}
904
905/* Sends REST command to the FTP server. */
906uerr_t
907ftp_rest (int csock, wgint offset)
908{
909 char *request, *respline;
910 int nwritten;
911 uerr_t err;
912
913 request = ftp_request ("REST", number_to_static_string (offset));
914 nwritten = fd_write (csock, request, strlen (request), -1.0);
915 if (nwritten < 0)
916 {
917 xfree (request);
918 return WRITEFAILED;
919 }
920 xfree (request);
921 /* Get appropriate response. */
922 err = ftp_response (csock, &respline);
923 if (err != FTPOK)
924 return err;
925 if (*respline != '3')
926 {
927 xfree (respline);
928 return FTPRESTFAIL;
929 }
930 xfree (respline);
931 /* All OK. */
932 return FTPOK;
933}
934
935/* Sends RETR command to the FTP server. */
936uerr_t
937ftp_retr (int csock, const char *file)
938{
939 char *request, *respline;
940 int nwritten;
941 uerr_t err;
942
943 /* Send RETR request. */
944 request = ftp_request ("RETR", file);
945 nwritten = fd_write (csock, request, strlen (request), -1.0);
946 if (nwritten < 0)
947 {
948 xfree (request);
949 return WRITEFAILED;
950 }
951 xfree (request);
952 /* Get appropriate response. */
953 err = ftp_response (csock, &respline);
954 if (err != FTPOK)
955 return err;
956 if (*respline == '5')
957 {
958 xfree (respline);
959 return FTPNSFOD;
960 }
961 if (*respline != '1')
962 {
963 xfree (respline);
964 return FTPRERR;
965 }
966 xfree (respline);
967 /* All OK. */
968 return FTPOK;
969}
970
971/* Sends the LIST command to the server. If FILE is NULL, send just
972 `LIST' (no space). */
973uerr_t
974ftp_list (int csock, const char *file)
975{
976 char *request, *respline;
977 int nwritten;
978 uerr_t err;
979
980 /* Send LIST request. */
981 request = ftp_request ("LIST", file);
982 nwritten = fd_write (csock, request, strlen (request), -1.0);
983 if (nwritten < 0)
984 {
985 xfree (request);
986 return WRITEFAILED;
987 }
988 xfree (request);
989 /* Get appropriate respone. */
990 err = ftp_response (csock, &respline);
991 if (err != FTPOK)
992 return err;
993 if (*respline == '5')
994 {
995 xfree (respline);
996 return FTPNSFOD;
997 }
998 if (*respline != '1')
999 {
1000 xfree (respline);
1001 return FTPRERR;
1002 }
1003 xfree (respline);
1004 /* All OK. */
1005 return FTPOK;
1006}
1007
1008/* Sends the SYST command to the server. */
1009uerr_t
1010ftp_syst (int csock, enum stype *server_type)
1011{
1012 char *request, *respline;
1013 int nwritten;
1014 uerr_t err;
1015
1016 /* Send SYST request. */
1017 request = ftp_request ("SYST", NULL);
1018 nwritten = fd_write (csock, request, strlen (request), -1.0);
1019 if (nwritten < 0)
1020 {
1021 xfree (request);
1022 return WRITEFAILED;
1023 }
1024 xfree (request);
1025
1026 /* Get appropriate response. */
1027 err = ftp_response (csock, &respline);
1028 if (err != FTPOK)
1029 return err;
1030 if (*respline == '5')
1031 {
1032 xfree (respline);
1033 return FTPSRVERR;
1034 }
1035
1036 /* Skip the number (215, but 200 (!!!) in case of VMS) */
1037 strtok (respline, " ");
1038
1039 /* Which system type has been reported (we are interested just in the
1040 first word of the server response)? */
1041 request = strtok (NULL, " ");
1042
1043 if (!strcasecmp (request, "VMS"))
1044 *server_type = ST_VMS;
1045 else if (!strcasecmp (request, "UNIX"))
1046 *server_type = ST_UNIX;
1047 else if (!strcasecmp (request, "WINDOWS_NT")
1048 || !strcasecmp (request, "WINDOWS2000"))
1049 *server_type = ST_WINNT;
1050 else if (!strcasecmp (request, "MACOS"))
1051 *server_type = ST_MACOS;
1052 else if (!strcasecmp (request, "OS/400"))
1053 *server_type = ST_OS400;
1054 else
1055 *server_type = ST_OTHER;
1056
1057 xfree (respline);
1058 /* All OK. */
1059 return FTPOK;
1060}
1061
1062/* Sends the PWD command to the server. */
1063uerr_t
1064ftp_pwd (int csock, char **pwd)
1065{
1066 char *request, *respline;
1067 int nwritten;
1068 uerr_t err;
1069
1070 /* Send PWD request. */
1071 request = ftp_request ("PWD", NULL);
1072 nwritten = fd_write (csock, request, strlen (request), -1.0);
1073 if (nwritten < 0)
1074 {
1075 xfree (request);
1076 return WRITEFAILED;
1077 }
1078 xfree (request);
1079 /* Get appropriate response. */
1080 err = ftp_response (csock, &respline);
1081 if (err != FTPOK)
1082 return err;
1083 if (*respline == '5')
1084 {
1085 err:
1086 xfree (respline);
1087 return FTPSRVERR;
1088 }
1089
1090 /* Skip the number (257), leading citation mark, trailing citation mark
1091 and everything following it. */
1092 strtok (respline, "\"");
1093 request = strtok (NULL, "\"");
1094 if (!request)
1095 /* Treat the malformed response as an error, which the caller has
1096 to handle gracefully anyway. */
1097 goto err;
1098
1099 /* Has the `pwd' been already allocated? Free! */
1100 xfree_null (*pwd);
1101
1102 *pwd = xstrdup (request);
1103
1104 xfree (respline);
1105 /* All OK. */
1106 return FTPOK;
1107}
1108
1109/* Sends the SIZE command to the server, and returns the value in 'size'.
1110 * If an error occurs, size is set to zero. */
1111uerr_t
1112ftp_size (int csock, const char *file, wgint *size)
1113{
1114 char *request, *respline;
1115 int nwritten;
1116 uerr_t err;
1117
1118 /* Send PWD request. */
1119 request = ftp_request ("SIZE", file);
1120 nwritten = fd_write (csock, request, strlen (request), -1.0);
1121 if (nwritten < 0)
1122 {
1123 xfree (request);
1124 *size = 0;
1125 return WRITEFAILED;
1126 }
1127 xfree (request);
1128 /* Get appropriate response. */
1129 err = ftp_response (csock, &respline);
1130 if (err != FTPOK)
1131 {
1132 *size = 0;
1133 return err;
1134 }
1135 if (*respline == '5')
1136 {
1137 /*
1138 * Probably means SIZE isn't supported on this server.
1139 * Error is nonfatal since SIZE isn't in RFC 959
1140 */
1141 xfree (respline);
1142 *size = 0;
1143 return FTPOK;
1144 }
1145
1146 errno = 0;
1147 *size = str_to_wgint (respline + 4, NULL, 10);
1148 if (errno)
1149 {
1150 /*
1151 * Couldn't parse the response for some reason. On the (few)
1152 * tests I've done, the response is 213 <SIZE> with nothing else -
1153 * maybe something a bit more resilient is necessary. It's not a
1154 * fatal error, however.
1155 */
1156 xfree (respline);
1157 *size = 0;
1158 return FTPOK;
1159 }
1160
1161 xfree (respline);
1162 /* All OK. */
1163 return FTPOK;
1164}
1165
1166/* If URL's params are of the form "type=X", return character X.
1167 Otherwise, return 'I' (the default type). */
1168char
1169ftp_process_type (const char *params)
1170{
1171 if (params
1172 && 0 == strncasecmp (params, "type=", 5)
1173 && params[5] != '\0')
1174 return TOUPPER (params[5]);
1175 else
1176 return 'I';
1177}
Note: See TracBrowser for help on using the repository browser.