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