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