]> sjero.net Git - wget/blob - src/ftp-basic.c
[svn] Declare h_errno in host.c, but not elsewhere.
[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 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25
26 #ifdef HAVE_STRING_H
27 # include <string.h>
28 #else
29 # include <strings.h>
30 #endif
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <sys/types.h>
35
36 #ifdef WINDOWS
37 # include <winsock.h>
38 #endif
39
40 #include "wget.h"
41 #include "utils.h"
42 #include "rbuf.h"
43 #include "connect.h"
44 #include "host.h"
45 #include "ftp.h"
46
47 char ftp_last_respline[128];
48
49 \f
50 /* Get the response of FTP server and allocate enough room to handle
51    it.  <CR> and <LF> characters are stripped from the line, and the
52    line is 0-terminated.  All the response lines but the last one are
53    skipped.  The last line is determined as described in RFC959.  */
54 uerr_t
55 ftp_response (struct rbuf *rbuf, char **line)
56 {
57   int i;
58   int bufsize = 40;
59
60   *line = (char *)xmalloc (bufsize);
61   do
62     {
63       for (i = 0; 1; i++)
64         {
65           int res;
66           if (i > bufsize - 1)
67             *line = (char *)xrealloc (*line, (bufsize <<= 1));
68           res = RBUF_READCHAR (rbuf, *line + i);
69           /* RES is number of bytes read.  */
70           if (res == 1)
71             {
72               if ((*line)[i] == '\n')
73                 {
74                   (*line)[i] = '\0';
75                   /* Get rid of \r.  */
76                   if (i > 0 && (*line)[i - 1] == '\r')
77                     (*line)[i - 1] = '\0';
78                   break;
79                 }
80             }
81           else
82             return FTPRERR;
83         }
84       if (opt.server_response)
85         logprintf (LOG_ALWAYS, "%s\n", *line);
86       else
87         DEBUGP (("%s\n", *line));
88     }
89   while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) &&
90            ISDIGIT ((*line)[2]) && (*line)[3] == ' '));
91   strncpy (ftp_last_respline, *line, sizeof (ftp_last_respline));
92   ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0';
93   return FTPOK;
94 }
95
96 /* Returns the malloc-ed FTP request, ending with <CR><LF>, printing
97    it if printing is required.  If VALUE is NULL, just use
98    command<CR><LF>.  */
99 static char *
100 ftp_request (const char *command, const char *value)
101 {
102   char *res = (char *)xmalloc (strlen (command)
103                                + (value ? (1 + strlen (value)) : 0)
104                                + 2 + 1);
105   sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : "");
106   if (opt.server_response)
107     {
108       /* Hack: don't print out password.  */
109       if (strncmp (res, "PASS", 4) != 0)
110         logprintf (LOG_ALWAYS, "--> %s\n", res);
111       else
112         logputs (LOG_ALWAYS, "--> PASS Turtle Power!\n");
113     }
114   else
115     DEBUGP (("\n--> %s\n", res));
116   return res;
117 }
118
119 #ifdef USE_OPIE
120 const char *calculate_skey_response PARAMS ((int, const char *, const char *));
121 #endif
122
123 /* Sends the USER and PASS commands to the server, to control
124    connection socket csock.  */
125 uerr_t
126 ftp_login (struct rbuf *rbuf, const char *acc, const char *pass)
127 {
128   uerr_t err;
129   char *request, *respline;
130   int nwritten;
131
132   /* Get greeting.  */
133   err = ftp_response (rbuf, &respline);
134   if (err != FTPOK)
135     {
136       xfree (respline);
137       return err;
138     }
139   if (*respline != '2')
140     {
141       xfree (respline);
142       return FTPSRVERR;
143     }
144   xfree (respline);
145   /* Send USER username.  */
146   request = ftp_request ("USER", acc);
147   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
148   if (nwritten < 0)
149     {
150       xfree (request);
151       return WRITEFAILED;
152     }
153   xfree (request);
154   /* Get appropriate response.  */
155   err = ftp_response (rbuf, &respline);
156   if (err != FTPOK)
157     {
158       xfree (respline);
159       return err;
160     }
161   /* An unprobable possibility of logging without a password.  */
162   if (*respline == '2')
163     {
164       xfree (respline);
165       return FTPOK;
166     }
167   /* Else, only response 3 is appropriate.  */
168   if (*respline != '3')
169     {
170       xfree (respline);
171       return FTPLOGREFUSED;
172     }
173 #ifdef USE_OPIE
174   {
175     static const char *skey_head[] = {
176       "331 s/key ",
177       "331 opiekey "
178     };
179     int i;
180
181     for (i = 0; i < ARRAY_SIZE (skey_head); i++)
182       {
183         if (strncasecmp (skey_head[i], respline, strlen (skey_head[i])) == 0)
184           break;
185       }
186     if (i < ARRAY_SIZE (skey_head))
187       {
188         const char *cp;
189         int skey_sequence = 0;
190
191         for (cp = respline + strlen (skey_head[i]);
192              '0' <= *cp && *cp <= '9';
193              cp++)
194           {
195             skey_sequence = skey_sequence * 10 + *cp - '0';
196           }
197         if (*cp == ' ')
198           cp++;
199         else
200           {
201           bad:
202             xfree (respline);
203             return FTPLOGREFUSED;
204           }
205         if ((cp = calculate_skey_response (skey_sequence, cp, pass)) == 0)
206           goto bad;
207         pass = cp;
208       }
209   }
210 #endif /* USE_OPIE */
211   xfree (respline);
212   /* Send PASS password.  */
213   request = ftp_request ("PASS", pass);
214   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
215   if (nwritten < 0)
216     {
217       xfree (request);
218       return WRITEFAILED;
219     }
220   xfree (request);
221   /* Get appropriate response.  */
222   err = ftp_response (rbuf, &respline);
223   if (err != FTPOK)
224     {
225       xfree (respline);
226       return err;
227     }
228   if (*respline != '2')
229     {
230       xfree (respline);
231       return FTPLOGINC;
232     }
233   xfree (respline);
234   /* All OK.  */
235   return FTPOK;
236 }
237
238 /* Bind a port and send the appropriate PORT command to the FTP
239    server.  Use acceptport after RETR, to get the socket of data
240    connection.  */
241 uerr_t
242 ftp_port (struct rbuf *rbuf)
243 {
244   uerr_t err;
245   char *request, *respline, *bytes;
246   unsigned char *in_addr;
247   int nwritten;
248   unsigned short port;
249
250   /* Setting port to 0 lets the system choose a free port.  */
251   port = 0;
252   /* Bind the port.  */
253   err = bindport (&port);
254   if (err != BINDOK)
255     return err;
256   /* Get the address of this side of the connection.  */
257   if (!(in_addr = conaddr (RBUF_FD (rbuf))))
258     return BINDERR;
259   /* Construct the argument of PORT (of the form a,b,c,d,e,f).  */
260   bytes = (char *)alloca (6 * 4 + 1);
261   sprintf (bytes, "%d,%d,%d,%d,%d,%d", in_addr[0], in_addr[1],
262           in_addr[2], in_addr[3], (unsigned) (port & 0xff00) >> 8,
263           port & 0xff);
264   /* Send PORT request.  */
265   request = ftp_request ("PORT", bytes);
266   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
267   if (nwritten < 0)
268     {
269       xfree (request);
270       return WRITEFAILED;
271     }
272   xfree (request);
273   /* Get appropriate response.  */
274   err = ftp_response (rbuf, &respline);
275   if (err != FTPOK)
276     {
277       xfree (respline);
278       return err;
279     }
280   if (*respline != '2')
281     {
282       xfree (respline);
283       return FTPPORTERR;
284     }
285   xfree (respline);
286   return FTPOK;
287 }
288
289 /* Similar to ftp_port, but uses `PASV' to initiate the passive FTP
290    transfer.  Reads the response from server and parses it.  Reads the
291    host and port addresses and returns them.  */
292 uerr_t
293 ftp_pasv (struct rbuf *rbuf, unsigned char *addr)
294 {
295   char *request, *respline, *s;
296   int nwritten, i;
297   uerr_t err;
298
299   /* Form the request.  */
300   request = ftp_request ("PASV", NULL);
301   /* And send it.  */
302   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
303   if (nwritten < 0)
304     {
305       xfree (request);
306       return WRITEFAILED;
307     }
308   xfree (request);
309   /* Get the server response.  */
310   err = ftp_response (rbuf, &respline);
311   if (err != FTPOK)
312     {
313       xfree (respline);
314       return err;
315     }
316   if (*respline != '2')
317     {
318       xfree (respline);
319       return FTPNOPASV;
320     }
321   /* Parse the request.  */
322   s = respline;
323   for (s += 4; *s && !ISDIGIT (*s); s++);
324   if (!*s)
325     return FTPINVPASV;
326   for (i = 0; i < 6; i++)
327     {
328       addr[i] = 0;
329       for (; ISDIGIT (*s); s++)
330         addr[i] = (*s - '0') + 10 * addr[i];
331       if (*s == ',')
332         s++;
333       else if (i < 5)
334         {
335           /* When on the last number, anything can be a terminator.  */
336           xfree (respline);
337           return FTPINVPASV;
338         }
339     }
340   xfree (respline);
341   return FTPOK;
342 }
343
344 /* Sends the TYPE request to the server.  */
345 uerr_t
346 ftp_type (struct rbuf *rbuf, int type)
347 {
348   char *request, *respline;
349   int nwritten;
350   uerr_t err;
351   char stype[2];
352
353   /* Construct argument.  */
354   stype[0] = type;
355   stype[1] = 0;
356   /* Send TYPE request.  */
357   request = ftp_request ("TYPE", stype);
358   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
359   if (nwritten < 0)
360     {
361       xfree (request);
362       return WRITEFAILED;
363     }
364   xfree (request);
365   /* Get appropriate response.  */
366   err = ftp_response (rbuf, &respline);
367   if (err != FTPOK)
368     {
369       xfree (respline);
370       return err;
371     }
372   if (*respline != '2')
373     {
374       xfree (respline);
375       return FTPUNKNOWNTYPE;
376     }
377   xfree (respline);
378   /* All OK.  */
379   return FTPOK;
380 }
381
382 /* Changes the working directory by issuing a CWD command to the
383    server.  */
384 uerr_t
385 ftp_cwd (struct rbuf *rbuf, const char *dir)
386 {
387   char *request, *respline;
388   int nwritten;
389   uerr_t err;
390
391   /* Send CWD request.  */
392   request = ftp_request ("CWD", dir);
393   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
394   if (nwritten < 0)
395     {
396       xfree (request);
397       return WRITEFAILED;
398     }
399   xfree (request);
400   /* Get appropriate response.  */
401   err = ftp_response (rbuf, &respline);
402   if (err != FTPOK)
403     {
404       xfree (respline);
405       return err;
406     }
407   if (*respline == '5')
408     {
409       xfree (respline);
410       return FTPNSFOD;
411     }
412   if (*respline != '2')
413     {
414       xfree (respline);
415       return FTPRERR;
416     }
417   xfree (respline);
418   /* All OK.  */
419   return FTPOK;
420 }
421
422 /* Sends REST command to the FTP server.  */
423 uerr_t
424 ftp_rest (struct rbuf *rbuf, long offset)
425 {
426   char *request, *respline;
427   int nwritten;
428   uerr_t err;
429   static char numbuf[20]; /* Buffer for the number */
430
431   long_to_string (numbuf, offset);
432   request = ftp_request ("REST", numbuf);
433   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
434   if (nwritten < 0)
435     {
436       xfree (request);
437       return WRITEFAILED;
438     }
439   xfree (request);
440   /* Get appropriate response.  */
441   err = ftp_response (rbuf, &respline);
442   if (err != FTPOK)
443     {
444       xfree (respline);
445       return err;
446     }
447   if (*respline != '3')
448     {
449       xfree (respline);
450       return FTPRESTFAIL;
451     }
452   xfree (respline);
453   /* All OK.  */
454   return FTPOK;
455 }
456
457 /* Sends RETR command to the FTP server.  */
458 uerr_t
459 ftp_retr (struct rbuf *rbuf, const char *file)
460 {
461   char *request, *respline;
462   int nwritten;
463   uerr_t err;
464
465   /* Send RETR request.  */
466   request = ftp_request ("RETR", file);
467   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
468   if (nwritten < 0)
469     {
470       xfree (request);
471       return WRITEFAILED;
472     }
473   xfree (request);
474   /* Get appropriate response.  */
475   err = ftp_response (rbuf, &respline);
476   if (err != FTPOK)
477     {
478       xfree (respline);
479       return err;
480     }
481   if (*respline == '5')
482     {
483       xfree (respline);
484       return FTPNSFOD;
485     }
486   if (*respline != '1')
487     {
488       xfree (respline);
489       return FTPRERR;
490     }
491   xfree (respline);
492   /* All OK.  */
493   return FTPOK;
494 }
495
496 /* Sends the LIST command to the server.  If FILE is NULL, send just
497    `LIST' (no space).  */
498 uerr_t
499 ftp_list (struct rbuf *rbuf, const char *file)
500 {
501   char *request, *respline;
502   int nwritten;
503   uerr_t err;
504
505   /* Send LIST request.  */
506   request = ftp_request ("LIST", file);
507   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
508   if (nwritten < 0)
509     {
510       xfree (request);
511       return WRITEFAILED;
512     }
513   xfree (request);
514   /* Get appropriate respone.  */
515   err = ftp_response (rbuf, &respline);
516   if (err != FTPOK)
517     {
518       xfree (respline);
519       return err;
520     }
521   if (*respline == '5')
522     {
523       xfree (respline);
524       return FTPNSFOD;
525     }
526   if (*respline != '1')
527     {
528       xfree (respline);
529       return FTPRERR;
530     }
531   xfree (respline);
532   /* All OK.  */
533   return FTPOK;
534 }
535
536 /* Sends the SYST command to the server. */
537 uerr_t
538 ftp_syst (struct rbuf *rbuf, enum stype *server_type)
539 {
540   char *request, *respline;
541   int nwritten;
542   uerr_t err;
543
544   /* Send SYST request.  */
545   request = ftp_request ("SYST", NULL);
546   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
547   if (nwritten < 0)
548     {
549       xfree (request);
550       return WRITEFAILED;
551     }
552   xfree (request);
553
554   /* Get appropriate response.  */
555   err = ftp_response (rbuf, &respline);
556   if (err != FTPOK)
557     {
558       xfree (respline);
559       return err;
560     }
561   if (*respline == '5')
562     {
563       xfree (respline);
564       return FTPSRVERR;
565     }
566
567   /* Skip the number (215, but 200 (!!!) in case of VMS) */
568   strtok (respline, " ");
569   
570   /* Which system type has been reported (we are interested just in the
571      first word of the server response)?  */
572   request = strtok (NULL, " ");
573
574   if (!strcasecmp (request, "VMS"))
575     *server_type = ST_VMS;
576   else if (!strcasecmp (request, "UNIX"))
577     *server_type = ST_UNIX;
578   else if (!strcasecmp (request, "WINDOWS_NT"))
579     *server_type = ST_WINNT;
580   else if (!strcasecmp (request, "MACOS"))
581     *server_type = ST_MACOS;
582   else
583     *server_type = ST_OTHER;
584
585   xfree (respline);
586   /* All OK.  */
587   return FTPOK;
588 }
589
590 /* Sends the PWD command to the server. */
591 uerr_t
592 ftp_pwd (struct rbuf *rbuf, char **pwd)
593 {
594   char *request, *respline;
595   int nwritten;
596   uerr_t err;
597
598   /* Send PWD request.  */
599   request = ftp_request ("PWD", NULL);
600   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
601   if (nwritten < 0)
602     {
603       xfree (request);
604       return WRITEFAILED;
605     }
606   xfree (request);
607   /* Get appropriate response.  */
608   err = ftp_response (rbuf, &respline);
609   if (err != FTPOK)
610     {
611       xfree (respline);
612       return err;
613     }
614   if (*respline == '5')
615     {
616       xfree (respline);
617       return FTPSRVERR;
618     }
619
620   /* Skip the number (257), leading citation mark, trailing citation mark
621      and everything following it. */
622   strtok (respline, "\"");
623   request = strtok (NULL, "\"");
624   
625   /* Has the `pwd' been already allocated?  Free! */
626   FREE_MAYBE (*pwd);
627
628   *pwd = xstrdup (request);
629
630   xfree (respline);
631   /* All OK.  */
632   return FTPOK;
633 }
634
635 /* Sends the SIZE command to the server, and returns the value in 'size'.
636  * If an error occurs, size is set to zero. */
637 uerr_t
638 ftp_size (struct rbuf *rbuf, const char *file, long int *size)
639 {
640   char *request, *respline;
641   int nwritten;
642   uerr_t err;
643
644   /* Send PWD request.  */
645   request = ftp_request ("SIZE", file);
646   nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
647   if (nwritten < 0)
648     {
649       xfree (request);
650       *size = 0;
651       return WRITEFAILED;
652     }
653   xfree (request);
654   /* Get appropriate response.  */
655   err = ftp_response (rbuf, &respline);
656   if (err != FTPOK)
657     {
658       xfree (respline);
659       *size = 0;
660       return err;
661     }
662   if (*respline == '5')
663     {
664       /* 
665        * Probably means SIZE isn't supported on this server.
666        * Error is nonfatal since SIZE isn't in RFC 959 
667        */
668       xfree (respline);
669       *size = 0;
670       return FTPOK;
671     }
672
673   errno = 0;
674   *size = strtol (respline + 4, NULL, 0);
675   if (errno) 
676     {
677       /* 
678        * Couldn't parse the response for some reason.  On the (few)
679        * tests I've done, the response is 213 <SIZE> with nothing else -
680        * maybe something a bit more resilient is necessary.  It's not a
681        * fatal error, however.
682        */
683       xfree (respline);
684       *size = 0;
685       return FTPOK;
686     }
687
688   xfree (respline);
689   /* All OK.  */
690   return FTPOK;
691 }
692
693 /* If URL's params are of the form "type=X", return character X.
694    Otherwise, return 'I' (the default type).  */
695 char
696 ftp_process_type (const char *params)
697 {
698   if (params
699       && 0 == strncasecmp (params, "type=", 5)
700       && params[5] != '\0')
701     return TOUPPER (params[5]);
702   else
703     return 'I';
704 }