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