blob: 2b9ba55c36f9f296b770df5ebdaac845479f4dfa [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]) {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000595 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000596 } else {
597 use_proxy = 0;
598 }
Robert Griebld7760112002-05-14 23:36:45 +0000599 }
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200600 if (!use_proxy) {
601 server.port = target.port;
602 if (ENABLE_FEATURE_IPV6) {
603 server.host = xstrdup(target.host);
604 } else {
605 server.host = target.host;
606 }
607 }
608
609 if (ENABLE_FEATURE_IPV6)
610 strip_ipv6_scope_id(target.host);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000611
Denis Vlasenko818322b2007-09-24 18:27:04 +0000612 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000613 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000614 fname_out = bb_get_last_path_component_nostrip(target.path);
615 /* handle "wget http://kernel.org//" */
616 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000617 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000618 /* -P DIR is considered only if there was no -O FILE */
619 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000620 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000621 } else {
622 if (LONE_DASH(fname_out)) {
623 /* -O - */
624 output_fd = 1;
625 opt &= ~WGET_OPT_CONTINUE;
626 }
Eric Andersen29edd002000-12-09 16:55:35 +0000627 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000628#if ENABLE_FEATURE_WGET_STATUSBAR
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100629 G.curfile = bb_get_last_path_component_nostrip(fname_out);
Denis Vlasenko818322b2007-09-24 18:27:04 +0000630#endif
631
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000632 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000633 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100634 bb_error_msg_and_die("can't specify continue (-c) without a filename (-O)");
Denys Vlasenko7f432802009-06-28 01:02:24 +0200635 */
Eric Andersen29edd002000-12-09 16:55:35 +0000636
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000637 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000638 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000639 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000640 if (output_fd >= 0) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100641 G.beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000642 }
643 /* File doesn't exist. We do not create file here yet.
Denys Vlasenko7f432802009-06-28 01:02:24 +0200644 * We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000645 }
646
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200647 redir_limit = 5;
648 resolve_lsa:
Denis Vlasenko42823d52007-02-04 02:39:08 +0000649 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000650 if (!(opt & WGET_OPT_QUIET)) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200651 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
652 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
653 free(s);
Eric Andersene6dc4392003-10-31 09:31:46 +0000654 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200655 establish_session:
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000656 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000657 /*
658 * HTTP session
659 */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200660 char *str;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200661 int status;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200662
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200663 /* Open socket to http server */
664 sfp = open_socket(lsa);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200665
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200666 /* Send HTTP request */
667 if (use_proxy) {
668 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
669 target.is_ftp ? "f" : "ht", target.host,
670 target.path);
671 } else {
672 if (opt & WGET_OPT_POST_DATA)
673 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
674 else
675 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
676 }
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000677
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200678 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
679 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000680
Denis Vlasenko9cade082006-11-21 10:43:02 +0000681#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200682 if (target.user) {
683 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
684 base64enc_512(buf, target.user));
685 }
686 if (use_proxy && server.user) {
687 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
688 base64enc_512(buf, server.user));
689 }
Eric Andersen79757c92001-04-05 21:45:54 +0000690#endif
691
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100692 if (G.beg_range)
693 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000694#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200695 if (extra_headers)
696 fputs(extra_headers, sfp);
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000697
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200698 if (opt & WGET_OPT_POST_DATA) {
699 char *estr = URL_escape(post_data);
700 fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
701 fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
702 (int) strlen(estr), estr);
703 /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
704 /*fprintf(sfp, "%s\r\n", estr);*/
705 free(estr);
706 } else
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000707#endif
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200708 { /* If "Connection:" is needed, document why */
709 fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
710 }
Eric Andersen79757c92001-04-05 21:45:54 +0000711
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200712 /*
713 * Retrieve HTTP response line and check for "200" status code.
714 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000715 read_response:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200716 if (fgets(buf, sizeof(buf), sfp) == NULL)
717 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000718
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200719 str = buf;
720 str = skip_non_whitespace(str);
721 str = skip_whitespace(str);
722 // FIXME: no error check
723 // xatou wouldn't work: "200 OK"
724 status = atoi(str);
725 switch (status) {
726 case 0:
727 case 100:
728 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
729 /* eat all remaining headers */;
730 goto read_response;
731 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000732/*
733Response 204 doesn't say "null file", it says "metadata
734has changed but data didn't":
735
736"10.2.5 204 No Content
737The server has fulfilled the request but does not need to return
738an entity-body, and might want to return updated metainformation.
739The response MAY include new or updated metainformation in the form
740of entity-headers, which if present SHOULD be associated with
741the requested variant.
742
743If the client is a user agent, it SHOULD NOT change its document
744view from that which caused the request to be sent. This response
745is primarily intended to allow input for actions to take place
746without causing a change to the user agent's active document view,
747although any new or updated metainformation SHOULD be applied
748to the document currently in the user agent's active view.
749
750The 204 response MUST NOT include a message-body, and thus
751is always terminated by the first empty line after the header fields."
752
753However, in real world it was observed that some web servers
754(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
755*/
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200756 case 204:
757 break;
758 case 300: /* redirection */
759 case 301:
760 case 302:
761 case 303:
762 break;
763 case 206:
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100764 if (G.beg_range)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000765 break;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200766 /* fall through */
767 default:
768 bb_error_msg_and_die("server returned error: %s", sanitize_string(buf));
769 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000770
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200771 /*
772 * Retrieve HTTP headers.
773 */
774 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
775 /* gethdr converted "FOO:" string to lowercase */
Matthijs van de Water0d586662009-08-22 20:19:48 +0200776 smalluint key;
777 /* strip trailing whitespace */
778 char *s = strchrnul(str, '\0') - 1;
779 while (s >= str && (*s == ' ' || *s == '\t')) {
780 *s = '\0';
781 s--;
782 }
783 key = index_in_strings(keywords, buf) + 1;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200784 if (key == KEY_content_length) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100785 G.content_len = BB_STRTOOFF(str, NULL, 10);
786 if (G.content_len < 0 || errno) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200787 bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
Eric Andersen79757c92001-04-05 21:45:54 +0000788 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200789 G.got_clen = 1;
790 continue;
791 }
792 if (key == KEY_transfer_encoding) {
793 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
794 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
795 G.chunked = G.got_clen = 1;
796 }
797 if (key == KEY_location && status >= 300) {
798 if (--redir_limit == 0)
799 bb_error_msg_and_die("too many redirections");
800 fclose(sfp);
801 G.got_clen = 0;
802 G.chunked = 0;
803 if (str[0] == '/')
804 /* free(target.allocated); */
805 target.path = /* target.allocated = */ xstrdup(str+1);
806 /* lsa stays the same: it's on the same server */
807 else {
808 parse_url(str, &target);
809 if (!use_proxy) {
810 server.host = target.host;
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200811 /* strip_ipv6_scope_id(target.host); - no! */
812 /* we assume remote never gives us IPv6 addr with scope id */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200813 server.port = target.port;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000814 free(lsa);
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200815 goto resolve_lsa;
816 } /* else: lsa stays the same: we use proxy */
Eric Andersen79757c92001-04-05 21:45:54 +0000817 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200818 goto establish_session;
Eric Andersen79757c92001-04-05 21:45:54 +0000819 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200820 }
821// if (status >= 300)
822// bb_error_msg_and_die("bad redirection (no Location: header from server)");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000823
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200824 /* For HTTP, data is pumped over the same connection */
Eric Andersen79757c92001-04-05 21:45:54 +0000825 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000826
827 } else {
Eric Andersen79757c92001-04-05 21:45:54 +0000828 /*
829 * FTP session
830 */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200831 sfp = prepare_ftp_session(&dfp, &target, lsa);
Eric Andersen96700832000-09-04 15:15:55 +0000832 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000833
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000834 if (opt & WGET_OPT_SPIDER) {
835 if (ENABLE_FEATURE_CLEAN_UP)
836 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000837 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000838 }
Eric Andersen79757c92001-04-05 21:45:54 +0000839
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000840 if (output_fd < 0) {
841 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
842 /* compat with wget: -O FILE can overwrite */
843 if (opt & WGET_OPT_OUTNAME)
844 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
845 output_fd = xopen(fname_out, o_flags);
846 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000847
Denys Vlasenko7f432802009-06-28 01:02:24 +0200848 retrieve_file_data(dfp, output_fd);
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100849 xclose(output_fd);
Rob Landley19a39402006-06-13 17:10:26 +0000850
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200851 if (dfp != sfp) {
852 /* It's ftp. Close it properly */
Eric Andersen79757c92001-04-05 21:45:54 +0000853 fclose(dfp);
854 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200855 bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4));
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100856 /* ftpcmd("QUIT", NULL, sfp, buf); - why bother? */
Eric Andersen79757c92001-04-05 21:45:54 +0000857 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000858
859 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000860}