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