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