blob: 5b73b933b7ffbef16e70ee983f2bebabd92db663 [file] [log] [blame]
Eric Andersen96700832000-09-04 15:15:55 +00001/* vi: set sw=4 ts=4: */
2/*
Eric Andersen79757c92001-04-05 21:45:54 +00003 * wget - retrieve a file using HTTP or FTP
Eric Andersen96700832000-09-04 15:15:55 +00004 *
Eric Andersen4e573f42000-11-14 23:29:24 +00005 * Chip Rosenthal Covad Communications <chip@laserlink.net>
Eric Andersenb520e082000-10-03 00:21:45 +00006 *
Denis Vlasenkodb12d1d2008-12-07 00:52:58 +00007 * Licensed under GPLv2, see file LICENSE in this tarball for details.
Eric Andersen96700832000-09-04 15:15:55 +00008 */
Denis Vlasenkob6adbf12007-05-26 19:00:18 +00009#include "libbb.h"
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +000010
Eric Andersen79757c92001-04-05 21:45:54 +000011struct host_info {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +000012 // May be used if we ever will want to free() all xstrdup()s...
13 /* char *allocated; */
Denis Vlasenko818322b2007-09-24 18:27:04 +000014 const char *path;
15 const char *user;
16 char *host;
17 int port;
18 smallint is_ftp;
Eric Andersen79757c92001-04-05 21:45:54 +000019};
20
Denis Vlasenko77105632007-09-24 15:04:00 +000021
22/* Globals (can be accessed from signal handlers) */
23struct globals {
24 off_t content_len; /* Content-length of the file */
25 off_t beg_range; /* Range at which continue begins */
26#if ENABLE_FEATURE_WGET_STATUSBAR
Denis Vlasenko77105632007-09-24 15:04:00 +000027 off_t transferred; /* Number of bytes transferred so far */
28 const char *curfile; /* Name of current file being transferred */
Magnus Dammf5914992009-11-08 16:34:43 +010029 bb_progress_t pmt;
Denis Vlasenko77105632007-09-24 15:04:00 +000030#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +020031 smallint chunked; /* chunked transfer encoding */
32 smallint got_clen; /* got content-length: from server */
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010033} FIX_ALIASING;
Denis Vlasenko77105632007-09-24 15:04:00 +000034#define G (*(struct globals*)&bb_common_bufsiz1)
35struct BUG_G_too_big {
Denis Vlasenko6b404432008-01-07 16:13:14 +000036 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
Denis Vlasenko77105632007-09-24 15:04:00 +000037};
Denis Vlasenko77105632007-09-24 15:04:00 +000038#define INIT_G() do { } while (0)
39
40
Denis Vlasenko9cade082006-11-21 10:43:02 +000041#if ENABLE_FEATURE_WGET_STATUSBAR
Denis Vlasenko47ddd012007-09-24 18:24:17 +000042
Denis Vlasenko00d84172008-11-24 07:34:42 +000043static void progress_meter(int flag)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000044{
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000045 /* We can be called from signal handler */
46 int save_errno = errno;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000047
Denis Vlasenko00d84172008-11-24 07:34:42 +000048 if (flag == -1) { /* first call to progress_meter */
Magnus Dammf5914992009-11-08 16:34:43 +010049 bb_progress_init(&G.pmt);
Denis Vlasenko47ddd012007-09-24 18:24:17 +000050 }
51
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +010052 bb_progress_update(&G.pmt, G.curfile, G.beg_range, G.transferred,
53 G.chunked ? 0 : G.content_len + G.beg_range);
Denis Vlasenko47ddd012007-09-24 18:24:17 +000054
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000055 if (flag == 0) {
Denis Vlasenko00d84172008-11-24 07:34:42 +000056 /* last call to progress_meter */
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000057 alarm(0);
Denis Vlasenko4daad902007-09-27 10:20:47 +000058 fputc('\n', stderr);
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +010059 G.transferred = 0;
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000060 } else {
Denis Vlasenko00d84172008-11-24 07:34:42 +000061 if (flag == -1) { /* first call to progress_meter */
62 signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000063 }
64 alarm(1);
Denis Vlasenko47ddd012007-09-24 18:24:17 +000065 }
Denis Vlasenkoa7ce2072007-09-24 18:25:08 +000066
67 errno = save_errno;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000068}
Magnus Dammf5914992009-11-08 16:34:43 +010069
Denis Vlasenko47ddd012007-09-24 18:24:17 +000070#else /* FEATURE_WGET_STATUSBAR */
71
Denis Vlasenko00d84172008-11-24 07:34:42 +000072static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
Denis Vlasenko47ddd012007-09-24 18:24:17 +000073
Eric Andersenb520e082000-10-03 00:21:45 +000074#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +000075
Denis Vlasenko47ddd012007-09-24 18:24:17 +000076
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +020077/* IPv6 knows scoped address types i.e. link and site local addresses. Link
78 * local addresses can have a scope identifier to specify the
79 * interface/link an address is valid on (e.g. fe80::1%eth0). This scope
80 * identifier is only valid on a single node.
81 *
82 * RFC 4007 says that the scope identifier MUST NOT be sent across the wire,
83 * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers
84 * in the Host header as invalid requests, see
85 * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
86 */
87static void strip_ipv6_scope_id(char *host)
88{
89 char *scope, *cp;
90
91 /* bbox wget actually handles IPv6 addresses without [], like
92 * wget "http://::1/xxx", but this is not standard.
93 * To save code, _here_ we do not support it. */
94
95 if (host[0] != '[')
96 return; /* not IPv6 */
97
98 scope = strchr(host, '%');
99 if (!scope)
100 return;
101
102 /* Remove the IPv6 zone identifier from the host address */
103 cp = strchr(host, ']');
104 if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
105 /* malformed address (not "[xx]:nn" or "[xx]") */
106 return;
107 }
108
109 /* cp points to "]...", scope points to "%eth0]..." */
110 overlapping_strcpy(scope, cp);
111}
112
Denis Vlasenko12d21292007-06-27 21:40:07 +0000113/* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
114 * and a short count if an eof or non-interrupt error is encountered. */
115static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
Matt Kraai854125f2001-05-09 19:15:46 +0000116{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000117 size_t ret;
118 char *p = (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000119
120 do {
121 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000122 errno = 0;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000123 ret = fread(p, 1, nmemb, stream);
124 p += ret;
125 nmemb -= ret;
126 } while (nmemb && ferror(stream) && errno == EINTR);
Matt Kraai854125f2001-05-09 19:15:46 +0000127
Denis Vlasenko12d21292007-06-27 21:40:07 +0000128 return p - (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000129}
130
Denis Vlasenko12d21292007-06-27 21:40:07 +0000131/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
Matt Kraai854125f2001-05-09 19:15:46 +0000132 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
133static char *safe_fgets(char *s, int size, FILE *stream)
134{
135 char *ret;
136
137 do {
138 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000139 errno = 0;
Matt Kraai854125f2001-05-09 19:15:46 +0000140 ret = fgets(s, size, stream);
141 } while (ret == NULL && ferror(stream) && errno == EINTR);
142
143 return ret;
144}
145
Denis Vlasenko9cade082006-11-21 10:43:02 +0000146#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denis Vlasenko12d21292007-06-27 21:40:07 +0000147/* Base64-encode character string. buf is assumed to be char buf[512]. */
148static char *base64enc_512(char buf[512], const char *str)
Denis Vlasenko3526a132006-09-09 12:20:57 +0000149{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000150 unsigned len = strlen(str);
151 if (len > 512/4*3 - 10) /* paranoia */
152 len = 512/4*3 - 10;
153 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
Rob Landley76ef08c2006-06-13 16:44:26 +0000154 return buf;
Eric Andersen79757c92001-04-05 21:45:54 +0000155}
156#endif
157
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200158static char* sanitize_string(char *s)
159{
160 unsigned char *p = (void *) s;
161 while (*p >= ' ')
162 p++;
163 *p = '\0';
164 return s;
165}
166
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000167static FILE *open_socket(len_and_sockaddr *lsa)
168{
169 FILE *fp;
170
171 /* glibc 2.4 seems to try seeking on it - ??! */
172 /* hopefully it understands what ESPIPE means... */
173 fp = fdopen(xconnect_stream(lsa), "r+");
174 if (fp == NULL)
175 bb_perror_msg_and_die("fdopen");
176
177 return fp;
178}
179
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000180static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
181{
182 int result;
183 if (s1) {
184 if (!s2) s2 = "";
185 fprintf(fp, "%s%s\r\n", s1, s2);
186 fflush(fp);
187 }
188
189 do {
190 char *buf_ptr;
191
192 if (fgets(buf, 510, fp) == NULL) {
193 bb_perror_msg_and_die("error getting response");
194 }
195 buf_ptr = strstr(buf, "\r\n");
196 if (buf_ptr) {
197 *buf_ptr = '\0';
198 }
199 } while (!isdigit(buf[0]) || buf[3] != ' ');
200
201 buf[3] = '\0';
202 result = xatoi_u(buf);
203 buf[3] = ' ';
204 return result;
205}
206
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000207static void parse_url(char *src_url, struct host_info *h)
208{
209 char *url, *p, *sp;
210
211 /* h->allocated = */ url = xstrdup(src_url);
212
213 if (strncmp(url, "http://", 7) == 0) {
214 h->port = bb_lookup_port("http", "tcp", 80);
215 h->host = url + 7;
216 h->is_ftp = 0;
217 } else if (strncmp(url, "ftp://", 6) == 0) {
218 h->port = bb_lookup_port("ftp", "tcp", 21);
219 h->host = url + 6;
220 h->is_ftp = 1;
221 } else
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200222 bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000223
224 // FYI:
225 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
226 // 'GET /?var=a/b HTTP 1.0'
227 // and saves 'index.html?var=a%2Fb' (we save 'b')
228 // wget 'http://busybox.net?login=john@doe':
229 // request: 'GET /?login=john@doe HTTP/1.0'
230 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
231 // wget 'http://busybox.net#test/test':
232 // request: 'GET / HTTP/1.0'
233 // saves: 'index.html' (we save 'test')
234 //
235 // We also don't add unique .N suffix if file exists...
236 sp = strchr(h->host, '/');
237 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
238 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
239 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000240 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000241 } else if (*sp == '/') {
242 *sp = '\0';
243 h->path = sp + 1;
244 } else { // '#' or '?'
245 // http://busybox.net?login=john@doe is a valid URL
246 // memmove converts to:
247 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000248 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000249 h->host--;
250 sp[-1] = '\0';
251 h->path = sp;
252 }
253
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200254 // We used to set h->user to NULL here, but this interferes
255 // with handling of code 302 ("object was moved")
256
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000257 sp = strrchr(h->host, '@');
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000258 if (sp != NULL) {
259 h->user = h->host;
260 *sp = '\0';
261 h->host = sp + 1;
262 }
263
264 sp = h->host;
265}
266
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000267static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
268{
269 char *s, *hdrval;
270 int c;
271
272 /* *istrunc = 0; */
273
274 /* retrieve header line */
275 if (fgets(buf, bufsiz, fp) == NULL)
276 return NULL;
277
278 /* see if we are at the end of the headers */
279 for (s = buf; *s == '\r'; ++s)
280 continue;
281 if (*s == '\n')
282 return NULL;
283
284 /* convert the header name to lower case */
285 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s)
286 *s = tolower(*s);
287
288 /* verify we are at the end of the header name */
289 if (*s != ':')
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200290 bb_error_msg_and_die("bad header line: %s", sanitize_string(buf));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000291
292 /* locate the start of the header value */
293 *s++ = '\0';
294 hdrval = skip_whitespace(s);
295
296 /* locate the end of header */
297 while (*s && *s != '\r' && *s != '\n')
298 ++s;
299
300 /* end of header found */
301 if (*s) {
302 *s = '\0';
303 return hdrval;
304 }
305
Denys Vlasenko7f432802009-06-28 01:02:24 +0200306 /* Rats! The buffer isn't big enough to hold the entire header value */
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000307 while (c = getc(fp), c != EOF && c != '\n')
308 continue;
309 /* *istrunc = 1; */
310 return hdrval;
311}
312
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000313#if ENABLE_FEATURE_WGET_LONG_OPTIONS
314static char *URL_escape(const char *str)
315{
316 /* URL encode, see RFC 2396 */
317 char *dst;
318 char *res = dst = xmalloc(strlen(str) * 3 + 1);
319 unsigned char c;
320
321 while (1) {
322 c = *str++;
323 if (c == '\0'
324 /* || strchr("!&'()*-.=_~", c) - more code */
325 || c == '!'
326 || c == '&'
327 || c == '\''
328 || c == '('
329 || c == ')'
330 || c == '*'
331 || c == '-'
332 || c == '.'
333 || c == '='
334 || c == '_'
335 || c == '~'
336 || (c >= '0' && c <= '9')
337 || ((c|0x20) >= 'a' && (c|0x20) <= 'z')
338 ) {
339 *dst++ = c;
340 if (c == '\0')
341 return res;
342 } else {
343 *dst++ = '%';
344 *dst++ = bb_hexdigits_upcase[c >> 4];
345 *dst++ = bb_hexdigits_upcase[c & 0xf];
346 }
347 }
348}
349#endif
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000350
Denys Vlasenko7f432802009-06-28 01:02:24 +0200351static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
352{
353 char buf[512];
354 FILE *sfp;
355 char *str;
356 int port;
357
358 if (!target->user)
359 target->user = xstrdup("anonymous:busybox@");
360
361 sfp = open_socket(lsa);
362 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200363 bb_error_msg_and_die("%s", sanitize_string(buf+4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200364
365 /*
366 * Splitting username:password pair,
367 * trying to log in
368 */
369 str = strchr(target->user, ':');
370 if (str)
371 *str++ = '\0';
372 switch (ftpcmd("USER ", target->user, sfp, buf)) {
373 case 230:
374 break;
375 case 331:
376 if (ftpcmd("PASS ", str, sfp, buf) == 230)
377 break;
378 /* fall through (failed login) */
379 default:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200380 bb_error_msg_and_die("ftp login: %s", sanitize_string(buf+4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200381 }
382
383 ftpcmd("TYPE I", NULL, sfp, buf);
384
385 /*
386 * Querying file size
387 */
388 if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100389 G.content_len = BB_STRTOOFF(buf+4, NULL, 10);
390 if (G.content_len < 0 || errno) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200391 bb_error_msg_and_die("SIZE value is garbage");
392 }
393 G.got_clen = 1;
394 }
395
396 /*
397 * Entering passive mode
398 */
399 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
400 pasv_error:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200401 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200402 }
403 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
404 // Server's IP is N1.N2.N3.N4 (we ignore it)
405 // Server's port for data connection is P1*256+P2
406 str = strrchr(buf, ')');
407 if (str) str[0] = '\0';
408 str = strrchr(buf, ',');
409 if (!str) goto pasv_error;
410 port = xatou_range(str+1, 0, 255);
411 *str = '\0';
412 str = strrchr(buf, ',');
413 if (!str) goto pasv_error;
414 port += xatou_range(str+1, 0, 255) * 256;
415 set_nport(lsa, htons(port));
416
417 *dfpp = open_socket(lsa);
418
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100419 if (G.beg_range) {
420 sprintf(buf, "REST %"OFF_FMT"u", G.beg_range);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200421 if (ftpcmd(buf, NULL, sfp, buf) == 350)
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100422 G.content_len -= G.beg_range;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200423 }
424
425 if (ftpcmd("RETR ", target->path, sfp, buf) > 150)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200426 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200427
428 return sfp;
429}
430
431/* Must match option string! */
432enum {
433 WGET_OPT_CONTINUE = (1 << 0),
434 WGET_OPT_SPIDER = (1 << 1),
435 WGET_OPT_QUIET = (1 << 2),
436 WGET_OPT_OUTNAME = (1 << 3),
437 WGET_OPT_PREFIX = (1 << 4),
438 WGET_OPT_PROXY = (1 << 5),
439 WGET_OPT_USER_AGENT = (1 << 6),
440 WGET_OPT_RETRIES = (1 << 7),
441 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 8),
442 WGET_OPT_PASSIVE = (1 << 9),
443 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
444 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
445};
446
447static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
448{
449 char buf[512];
450
451 if (!(option_mask32 & WGET_OPT_QUIET))
452 progress_meter(-1);
453
454 if (G.chunked)
455 goto get_clen;
456
457 /* Loops only if chunked */
458 while (1) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100459 while (1) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200460 int n;
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100461 unsigned rdsz;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200462
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100463 rdsz = sizeof(buf);
464 if (G.got_clen) {
Denys Vlasenkod2c879d2009-12-11 14:12:28 +0100465 if (G.content_len < (off_t)sizeof(buf)) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100466 if ((int)G.content_len <= 0)
467 break;
468 rdsz = (unsigned)G.content_len;
469 }
470 }
Denys Vlasenko7f432802009-06-28 01:02:24 +0200471 n = safe_fread(buf, rdsz, dfp);
472 if (n <= 0) {
473 if (ferror(dfp)) {
474 /* perror will not work: ferror doesn't set errno */
475 bb_error_msg_and_die(bb_msg_read_error);
476 }
477 break;
478 }
479 xwrite(output_fd, buf, n);
480#if ENABLE_FEATURE_WGET_STATUSBAR
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100481 G.transferred += n;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200482#endif
483 if (G.got_clen)
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100484 G.content_len -= n;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200485 }
486
487 if (!G.chunked)
488 break;
489
490 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
491 get_clen:
492 safe_fgets(buf, sizeof(buf), dfp);
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100493 G.content_len = STRTOOFF(buf, NULL, 16);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200494 /* FIXME: error check? */
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100495 if (G.content_len == 0)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200496 break; /* all done! */
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100497 G.got_clen = 1;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200498 }
499
500 if (!(option_mask32 & WGET_OPT_QUIET))
501 progress_meter(0);
502}
503
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000504int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000505int wget_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen96700832000-09-04 15:15:55 +0000506{
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000507 char buf[512];
Eric Andersen79757c92001-04-05 21:45:54 +0000508 struct host_info server, target;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000509 len_and_sockaddr *lsa;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000510 unsigned opt;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200511 int redir_limit;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200512 char *proxy = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000513 char *dir_prefix = NULL;
514#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000515 char *post_data;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000516 char *extra_headers = NULL;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000517 llist_t *headers_llist = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000518#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +0200519 FILE *sfp; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000520 FILE *dfp; /* socket to ftp server (data) */
521 char *fname_out; /* where to direct output (-O) */
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000522 int output_fd = -1;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200523 bool use_proxy; /* Use proxies if env vars are set */
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000524 const char *proxy_flag = "on"; /* Use proxies if env vars are set */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000525 const char *user_agent = "Wget";/* "User-Agent" header field */
Denis Vlasenko77105632007-09-24 15:04:00 +0000526
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000527 static const char keywords[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000528 "content-length\0""transfer-encoding\0""chunked\0""location\0";
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000529 enum {
530 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
531 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000532#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000533 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000534 /* name, has_arg, val */
535 "continue\0" No_argument "c"
536 "spider\0" No_argument "s"
537 "quiet\0" No_argument "q"
538 "output-document\0" Required_argument "O"
539 "directory-prefix\0" Required_argument "P"
540 "proxy\0" Required_argument "Y"
541 "user-agent\0" Required_argument "U"
Denis Vlasenko50af9262009-03-02 15:08:06 +0000542 /* Ignored: */
543 // "tries\0" Required_argument "t"
544 // "timeout\0" Required_argument "T"
545 /* Ignored (we always use PASV): */
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000546 "passive-ftp\0" No_argument "\xff"
547 "header\0" Required_argument "\xfe"
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000548 "post-data\0" Required_argument "\xfd"
Bernhard Reutner-Fischer3fdba182010-02-10 19:37:29 +0100549 /* Ignored (we don't do ssl) */
550 "no-check-certificate\0" No_argument "\xfc"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000551 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000552#endif
553
554 INIT_G();
555
556#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000557 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000558#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000559 /* server.allocated = target.allocated = NULL; */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000560 opt_complementary = "-1" IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denis Vlasenko540ab702008-06-29 00:32:35 +0000561 opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000562 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000563 &proxy_flag, &user_agent,
564 NULL, /* -t RETRIES */
565 NULL /* -T NETWORK_READ_TIMEOUT */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000566 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
567 IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000568 );
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000569#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000570 if (headers_llist) {
571 int size = 1;
572 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000573 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000574 while (ll) {
575 size += strlen(ll->data) + 2;
576 ll = ll->link;
577 }
578 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000579 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000580 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000581 }
582 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000583#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000584
Denys Vlasenko7f432802009-06-28 01:02:24 +0200585 /* TODO: compat issue: should handle "wget URL1 URL2..." */
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200586
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200587 target.user = NULL;
Eric Andersen79757c92001-04-05 21:45:54 +0000588 parse_url(argv[optind], &target);
Eric Andersen79757c92001-04-05 21:45:54 +0000589
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000590 /* Use the proxy if necessary */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200591 use_proxy = (strcmp(proxy_flag, "off") != 0);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000592 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000593 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200594 if (proxy && proxy[0]) {
Denys Vlasenko81fe2b12010-02-11 04:23:43 +0100595 server.user = NULL;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000596 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000597 } else {
598 use_proxy = 0;
599 }
Robert Griebld7760112002-05-14 23:36:45 +0000600 }
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200601 if (!use_proxy) {
602 server.port = target.port;
603 if (ENABLE_FEATURE_IPV6) {
604 server.host = xstrdup(target.host);
605 } else {
606 server.host = target.host;
607 }
608 }
609
610 if (ENABLE_FEATURE_IPV6)
611 strip_ipv6_scope_id(target.host);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000612
Denis Vlasenko818322b2007-09-24 18:27:04 +0000613 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000614 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000615 fname_out = bb_get_last_path_component_nostrip(target.path);
616 /* handle "wget http://kernel.org//" */
617 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000618 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000619 /* -P DIR is considered only if there was no -O FILE */
620 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000621 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000622 } else {
623 if (LONE_DASH(fname_out)) {
624 /* -O - */
625 output_fd = 1;
626 opt &= ~WGET_OPT_CONTINUE;
627 }
Eric Andersen29edd002000-12-09 16:55:35 +0000628 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000629#if ENABLE_FEATURE_WGET_STATUSBAR
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100630 G.curfile = bb_get_last_path_component_nostrip(fname_out);
Denis Vlasenko818322b2007-09-24 18:27:04 +0000631#endif
632
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000633 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000634 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100635 bb_error_msg_and_die("can't specify continue (-c) without a filename (-O)");
Denys Vlasenko7f432802009-06-28 01:02:24 +0200636 */
Eric Andersen29edd002000-12-09 16:55:35 +0000637
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000638 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000639 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000640 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000641 if (output_fd >= 0) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100642 G.beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000643 }
644 /* File doesn't exist. We do not create file here yet.
Denys Vlasenko7f432802009-06-28 01:02:24 +0200645 * We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000646 }
647
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200648 redir_limit = 5;
649 resolve_lsa:
Denis Vlasenko42823d52007-02-04 02:39:08 +0000650 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000651 if (!(opt & WGET_OPT_QUIET)) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200652 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
653 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
654 free(s);
Eric Andersene6dc4392003-10-31 09:31:46 +0000655 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200656 establish_session:
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000657 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000658 /*
659 * HTTP session
660 */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200661 char *str;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200662 int status;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200663
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200664 /* Open socket to http server */
665 sfp = open_socket(lsa);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200666
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200667 /* Send HTTP request */
668 if (use_proxy) {
669 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
670 target.is_ftp ? "f" : "ht", target.host,
671 target.path);
672 } else {
673 if (opt & WGET_OPT_POST_DATA)
674 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
675 else
676 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
677 }
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000678
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200679 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
680 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000681
Denis Vlasenko9cade082006-11-21 10:43:02 +0000682#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200683 if (target.user) {
684 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
685 base64enc_512(buf, target.user));
686 }
687 if (use_proxy && server.user) {
688 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
689 base64enc_512(buf, server.user));
690 }
Eric Andersen79757c92001-04-05 21:45:54 +0000691#endif
692
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100693 if (G.beg_range)
694 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000695#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200696 if (extra_headers)
697 fputs(extra_headers, sfp);
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000698
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200699 if (opt & WGET_OPT_POST_DATA) {
700 char *estr = URL_escape(post_data);
701 fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
702 fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
703 (int) strlen(estr), estr);
704 /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
705 /*fprintf(sfp, "%s\r\n", estr);*/
706 free(estr);
707 } else
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000708#endif
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200709 { /* If "Connection:" is needed, document why */
710 fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
711 }
Eric Andersen79757c92001-04-05 21:45:54 +0000712
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200713 /*
714 * Retrieve HTTP response line and check for "200" status code.
715 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000716 read_response:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200717 if (fgets(buf, sizeof(buf), sfp) == NULL)
718 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000719
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200720 str = buf;
721 str = skip_non_whitespace(str);
722 str = skip_whitespace(str);
723 // FIXME: no error check
724 // xatou wouldn't work: "200 OK"
725 status = atoi(str);
726 switch (status) {
727 case 0:
728 case 100:
729 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
730 /* eat all remaining headers */;
731 goto read_response;
732 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000733/*
734Response 204 doesn't say "null file", it says "metadata
735has changed but data didn't":
736
737"10.2.5 204 No Content
738The server has fulfilled the request but does not need to return
739an entity-body, and might want to return updated metainformation.
740The response MAY include new or updated metainformation in the form
741of entity-headers, which if present SHOULD be associated with
742the requested variant.
743
744If the client is a user agent, it SHOULD NOT change its document
745view from that which caused the request to be sent. This response
746is primarily intended to allow input for actions to take place
747without causing a change to the user agent's active document view,
748although any new or updated metainformation SHOULD be applied
749to the document currently in the user agent's active view.
750
751The 204 response MUST NOT include a message-body, and thus
752is always terminated by the first empty line after the header fields."
753
754However, in real world it was observed that some web servers
755(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
756*/
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200757 case 204:
758 break;
759 case 300: /* redirection */
760 case 301:
761 case 302:
762 case 303:
763 break;
764 case 206:
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100765 if (G.beg_range)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000766 break;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200767 /* fall through */
768 default:
769 bb_error_msg_and_die("server returned error: %s", sanitize_string(buf));
770 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000771
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200772 /*
773 * Retrieve HTTP headers.
774 */
775 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
776 /* gethdr converted "FOO:" string to lowercase */
Matthijs van de Water0d586662009-08-22 20:19:48 +0200777 smalluint key;
778 /* strip trailing whitespace */
779 char *s = strchrnul(str, '\0') - 1;
780 while (s >= str && (*s == ' ' || *s == '\t')) {
781 *s = '\0';
782 s--;
783 }
784 key = index_in_strings(keywords, buf) + 1;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200785 if (key == KEY_content_length) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100786 G.content_len = BB_STRTOOFF(str, NULL, 10);
787 if (G.content_len < 0 || errno) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200788 bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
Eric Andersen79757c92001-04-05 21:45:54 +0000789 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200790 G.got_clen = 1;
791 continue;
792 }
793 if (key == KEY_transfer_encoding) {
794 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
795 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
796 G.chunked = G.got_clen = 1;
797 }
798 if (key == KEY_location && status >= 300) {
799 if (--redir_limit == 0)
800 bb_error_msg_and_die("too many redirections");
801 fclose(sfp);
802 G.got_clen = 0;
803 G.chunked = 0;
804 if (str[0] == '/')
805 /* free(target.allocated); */
806 target.path = /* target.allocated = */ xstrdup(str+1);
807 /* lsa stays the same: it's on the same server */
808 else {
809 parse_url(str, &target);
810 if (!use_proxy) {
811 server.host = target.host;
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200812 /* strip_ipv6_scope_id(target.host); - no! */
813 /* we assume remote never gives us IPv6 addr with scope id */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200814 server.port = target.port;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000815 free(lsa);
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200816 goto resolve_lsa;
817 } /* else: lsa stays the same: we use proxy */
Eric Andersen79757c92001-04-05 21:45:54 +0000818 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200819 goto establish_session;
Eric Andersen79757c92001-04-05 21:45:54 +0000820 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200821 }
822// if (status >= 300)
823// bb_error_msg_and_die("bad redirection (no Location: header from server)");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000824
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200825 /* For HTTP, data is pumped over the same connection */
Eric Andersen79757c92001-04-05 21:45:54 +0000826 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000827
828 } else {
Eric Andersen79757c92001-04-05 21:45:54 +0000829 /*
830 * FTP session
831 */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200832 sfp = prepare_ftp_session(&dfp, &target, lsa);
Eric Andersen96700832000-09-04 15:15:55 +0000833 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000834
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000835 if (opt & WGET_OPT_SPIDER) {
836 if (ENABLE_FEATURE_CLEAN_UP)
837 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000838 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000839 }
Eric Andersen79757c92001-04-05 21:45:54 +0000840
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000841 if (output_fd < 0) {
842 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
843 /* compat with wget: -O FILE can overwrite */
844 if (opt & WGET_OPT_OUTNAME)
845 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
846 output_fd = xopen(fname_out, o_flags);
847 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000848
Denys Vlasenko7f432802009-06-28 01:02:24 +0200849 retrieve_file_data(dfp, output_fd);
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100850 xclose(output_fd);
Rob Landley19a39402006-06-13 17:10:26 +0000851
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200852 if (dfp != sfp) {
853 /* It's ftp. Close it properly */
Eric Andersen79757c92001-04-05 21:45:54 +0000854 fclose(dfp);
855 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200856 bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4));
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100857 /* ftpcmd("QUIT", NULL, sfp, buf); - why bother? */
Eric Andersen79757c92001-04-05 21:45:54 +0000858 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000859
860 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000861}