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