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