blob: 4521abfcffd7a3f98c283e67f2904d0827f0435d [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>
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2, see file LICENSE in this source tree.
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +02007 *
8 * Copyright (C) 2010 Bradley M. Kuhn <bkuhn@ebb.org>
9 * Kuhn's copyrights are licensed GPLv2-or-later. File as a whole remains GPLv2.
Eric Andersen96700832000-09-04 15:15:55 +000010 */
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000011#include "libbb.h"
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +000012
Eric Andersen79757c92001-04-05 21:45:54 +000013struct host_info {
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +000014 // May be used if we ever will want to free() all xstrdup()s...
15 /* char *allocated; */
Denis Vlasenko818322b2007-09-24 18:27:04 +000016 const char *path;
17 const char *user;
18 char *host;
19 int port;
20 smallint is_ftp;
Eric Andersen79757c92001-04-05 21:45:54 +000021};
22
Denis Vlasenko77105632007-09-24 15:04:00 +000023
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020024/* Globals */
Denis Vlasenko77105632007-09-24 15:04:00 +000025struct globals {
26 off_t content_len; /* Content-length of the file */
27 off_t beg_range; /* Range at which continue begins */
28#if ENABLE_FEATURE_WGET_STATUSBAR
Denis Vlasenko77105632007-09-24 15:04:00 +000029 off_t transferred; /* Number of bytes transferred so far */
30 const char *curfile; /* Name of current file being transferred */
Magnus Dammf5914992009-11-08 16:34:43 +010031 bb_progress_t pmt;
Denis Vlasenko77105632007-09-24 15:04:00 +000032#endif
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020033#if ENABLE_FEATURE_WGET_TIMEOUT
34 unsigned timeout_seconds;
35#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +020036 smallint chunked; /* chunked transfer encoding */
37 smallint got_clen; /* got content-length: from server */
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010038} FIX_ALIASING;
Denis Vlasenko77105632007-09-24 15:04:00 +000039#define G (*(struct globals*)&bb_common_bufsiz1)
40struct BUG_G_too_big {
Denis Vlasenko6b404432008-01-07 16:13:14 +000041 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
Denis Vlasenko77105632007-09-24 15:04:00 +000042};
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020043#define INIT_G() do { \
44 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;) \
45} while (0)
Denis Vlasenko77105632007-09-24 15:04:00 +000046
47
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020048/* Must match option string! */
49enum {
50 WGET_OPT_CONTINUE = (1 << 0),
51 WGET_OPT_SPIDER = (1 << 1),
52 WGET_OPT_QUIET = (1 << 2),
53 WGET_OPT_OUTNAME = (1 << 3),
54 WGET_OPT_PREFIX = (1 << 4),
55 WGET_OPT_PROXY = (1 << 5),
56 WGET_OPT_USER_AGENT = (1 << 6),
57 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7),
58 WGET_OPT_RETRIES = (1 << 8),
59 WGET_OPT_PASSIVE = (1 << 9),
60 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
61 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
62};
63
64enum {
65 PROGRESS_START = -1,
66 PROGRESS_END = 0,
67 PROGRESS_BUMP = 1,
68};
Denis Vlasenko9cade082006-11-21 10:43:02 +000069#if ENABLE_FEATURE_WGET_STATUSBAR
Denis Vlasenko00d84172008-11-24 07:34:42 +000070static void progress_meter(int flag)
Denis Vlasenko47ddd012007-09-24 18:24:17 +000071{
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020072 if (option_mask32 & WGET_OPT_QUIET)
73 return;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000074
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020075 if (flag == PROGRESS_START)
Magnus Dammf5914992009-11-08 16:34:43 +010076 bb_progress_init(&G.pmt);
Denis Vlasenko47ddd012007-09-24 18:24:17 +000077
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +010078 bb_progress_update(&G.pmt, G.curfile, G.beg_range, G.transferred,
Denys Vlasenkoc5bbd5d2010-07-12 03:27:09 +020079 G.chunked ? 0 : G.beg_range + G.transferred + G.content_len);
Denis Vlasenko47ddd012007-09-24 18:24:17 +000080
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020081 if (flag == PROGRESS_END) {
Denys Vlasenko19ced5c2010-06-06 21:53:09 +020082 bb_putchar_stderr('\n');
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +010083 G.transferred = 0;
Denis Vlasenko47ddd012007-09-24 18:24:17 +000084 }
85}
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020086#else
Denis Vlasenko00d84172008-11-24 07:34:42 +000087static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
Eric Andersenb520e082000-10-03 00:21:45 +000088#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +000089
Denis Vlasenko47ddd012007-09-24 18:24:17 +000090
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +020091/* IPv6 knows scoped address types i.e. link and site local addresses. Link
92 * local addresses can have a scope identifier to specify the
93 * interface/link an address is valid on (e.g. fe80::1%eth0). This scope
94 * identifier is only valid on a single node.
95 *
96 * RFC 4007 says that the scope identifier MUST NOT be sent across the wire,
97 * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers
98 * in the Host header as invalid requests, see
99 * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
100 */
101static void strip_ipv6_scope_id(char *host)
102{
103 char *scope, *cp;
104
105 /* bbox wget actually handles IPv6 addresses without [], like
106 * wget "http://::1/xxx", but this is not standard.
107 * To save code, _here_ we do not support it. */
108
109 if (host[0] != '[')
110 return; /* not IPv6 */
111
112 scope = strchr(host, '%');
113 if (!scope)
114 return;
115
116 /* Remove the IPv6 zone identifier from the host address */
117 cp = strchr(host, ']');
118 if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
119 /* malformed address (not "[xx]:nn" or "[xx]") */
120 return;
121 }
122
123 /* cp points to "]...", scope points to "%eth0]..." */
124 overlapping_strcpy(scope, cp);
125}
126
Denis Vlasenko12d21292007-06-27 21:40:07 +0000127/* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
128 * and a short count if an eof or non-interrupt error is encountered. */
129static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
Matt Kraai854125f2001-05-09 19:15:46 +0000130{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000131 size_t ret;
132 char *p = (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000133
134 do {
135 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000136 errno = 0;
Denis Vlasenko12d21292007-06-27 21:40:07 +0000137 ret = fread(p, 1, nmemb, stream);
138 p += ret;
139 nmemb -= ret;
140 } while (nmemb && ferror(stream) && errno == EINTR);
Matt Kraai854125f2001-05-09 19:15:46 +0000141
Denis Vlasenko12d21292007-06-27 21:40:07 +0000142 return p - (char*)ptr;
Matt Kraai854125f2001-05-09 19:15:46 +0000143}
144
Denis Vlasenko12d21292007-06-27 21:40:07 +0000145/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
Matt Kraai854125f2001-05-09 19:15:46 +0000146 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
147static char *safe_fgets(char *s, int size, FILE *stream)
148{
149 char *ret;
150
151 do {
152 clearerr(stream);
Denis Vlasenko00d84172008-11-24 07:34:42 +0000153 errno = 0;
Matt Kraai854125f2001-05-09 19:15:46 +0000154 ret = fgets(s, size, stream);
155 } while (ret == NULL && ferror(stream) && errno == EINTR);
156
157 return ret;
158}
159
Denis Vlasenko9cade082006-11-21 10:43:02 +0000160#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denis Vlasenko12d21292007-06-27 21:40:07 +0000161/* Base64-encode character string. buf is assumed to be char buf[512]. */
162static char *base64enc_512(char buf[512], const char *str)
Denis Vlasenko3526a132006-09-09 12:20:57 +0000163{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000164 unsigned len = strlen(str);
165 if (len > 512/4*3 - 10) /* paranoia */
166 len = 512/4*3 - 10;
167 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64);
Rob Landley76ef08c2006-06-13 16:44:26 +0000168 return buf;
Eric Andersen79757c92001-04-05 21:45:54 +0000169}
170#endif
171
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200172static char* sanitize_string(char *s)
173{
174 unsigned char *p = (void *) s;
175 while (*p >= ' ')
176 p++;
177 *p = '\0';
178 return s;
179}
180
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000181static FILE *open_socket(len_and_sockaddr *lsa)
182{
183 FILE *fp;
184
185 /* glibc 2.4 seems to try seeking on it - ??! */
186 /* hopefully it understands what ESPIPE means... */
187 fp = fdopen(xconnect_stream(lsa), "r+");
188 if (fp == NULL)
189 bb_perror_msg_and_die("fdopen");
190
191 return fp;
192}
193
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000194static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf)
195{
196 int result;
197 if (s1) {
198 if (!s2) s2 = "";
199 fprintf(fp, "%s%s\r\n", s1, s2);
200 fflush(fp);
201 }
202
203 do {
204 char *buf_ptr;
205
206 if (fgets(buf, 510, fp) == NULL) {
207 bb_perror_msg_and_die("error getting response");
208 }
209 buf_ptr = strstr(buf, "\r\n");
210 if (buf_ptr) {
211 *buf_ptr = '\0';
212 }
213 } while (!isdigit(buf[0]) || buf[3] != ' ');
214
215 buf[3] = '\0';
Denys Vlasenko77832482010-08-12 14:14:45 +0200216 result = xatoi_positive(buf);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000217 buf[3] = ' ';
218 return result;
219}
220
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000221static void parse_url(char *src_url, struct host_info *h)
222{
223 char *url, *p, *sp;
224
225 /* h->allocated = */ url = xstrdup(src_url);
226
227 if (strncmp(url, "http://", 7) == 0) {
228 h->port = bb_lookup_port("http", "tcp", 80);
229 h->host = url + 7;
230 h->is_ftp = 0;
231 } else if (strncmp(url, "ftp://", 6) == 0) {
232 h->port = bb_lookup_port("ftp", "tcp", 21);
233 h->host = url + 6;
234 h->is_ftp = 1;
235 } else
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200236 bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000237
238 // FYI:
239 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
240 // 'GET /?var=a/b HTTP 1.0'
241 // and saves 'index.html?var=a%2Fb' (we save 'b')
242 // wget 'http://busybox.net?login=john@doe':
243 // request: 'GET /?login=john@doe HTTP/1.0'
244 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
245 // wget 'http://busybox.net#test/test':
246 // request: 'GET / HTTP/1.0'
247 // saves: 'index.html' (we save 'test')
248 //
249 // We also don't add unique .N suffix if file exists...
250 sp = strchr(h->host, '/');
251 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
252 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
253 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000254 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000255 } else if (*sp == '/') {
256 *sp = '\0';
257 h->path = sp + 1;
258 } else { // '#' or '?'
259 // http://busybox.net?login=john@doe is a valid URL
260 // memmove converts to:
261 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000262 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000263 h->host--;
264 sp[-1] = '\0';
265 h->path = sp;
266 }
267
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200268 // We used to set h->user to NULL here, but this interferes
269 // with handling of code 302 ("object was moved")
270
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000271 sp = strrchr(h->host, '@');
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000272 if (sp != NULL) {
273 h->user = h->host;
274 *sp = '\0';
275 h->host = sp + 1;
276 }
277
278 sp = h->host;
279}
280
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000281static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
282{
283 char *s, *hdrval;
284 int c;
285
286 /* *istrunc = 0; */
287
288 /* retrieve header line */
289 if (fgets(buf, bufsiz, fp) == NULL)
290 return NULL;
291
292 /* see if we are at the end of the headers */
293 for (s = buf; *s == '\r'; ++s)
294 continue;
295 if (*s == '\n')
296 return NULL;
297
298 /* convert the header name to lower case */
Denys Vlasenko48363312010-04-04 15:29:32 +0200299 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s) {
300 /* tolower for "A-Z", no-op for "0-9a-z-." */
301 *s = (*s | 0x20);
302 }
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000303
304 /* verify we are at the end of the header name */
305 if (*s != ':')
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200306 bb_error_msg_and_die("bad header line: %s", sanitize_string(buf));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000307
308 /* locate the start of the header value */
309 *s++ = '\0';
310 hdrval = skip_whitespace(s);
311
312 /* locate the end of header */
313 while (*s && *s != '\r' && *s != '\n')
314 ++s;
315
316 /* end of header found */
317 if (*s) {
318 *s = '\0';
319 return hdrval;
320 }
321
Denys Vlasenko7f432802009-06-28 01:02:24 +0200322 /* Rats! The buffer isn't big enough to hold the entire header value */
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000323 while (c = getc(fp), c != EOF && c != '\n')
324 continue;
325 /* *istrunc = 1; */
326 return hdrval;
327}
328
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000329#if ENABLE_FEATURE_WGET_LONG_OPTIONS
330static char *URL_escape(const char *str)
331{
332 /* URL encode, see RFC 2396 */
333 char *dst;
334 char *res = dst = xmalloc(strlen(str) * 3 + 1);
335 unsigned char c;
336
337 while (1) {
338 c = *str++;
339 if (c == '\0'
340 /* || strchr("!&'()*-.=_~", c) - more code */
341 || c == '!'
342 || c == '&'
343 || c == '\''
344 || c == '('
345 || c == ')'
346 || c == '*'
347 || c == '-'
348 || c == '.'
349 || c == '='
350 || c == '_'
351 || c == '~'
352 || (c >= '0' && c <= '9')
353 || ((c|0x20) >= 'a' && (c|0x20) <= 'z')
354 ) {
355 *dst++ = c;
356 if (c == '\0')
357 return res;
358 } else {
359 *dst++ = '%';
360 *dst++ = bb_hexdigits_upcase[c >> 4];
361 *dst++ = bb_hexdigits_upcase[c & 0xf];
362 }
363 }
364}
365#endif
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000366
Denys Vlasenko7f432802009-06-28 01:02:24 +0200367static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
368{
369 char buf[512];
370 FILE *sfp;
371 char *str;
372 int port;
373
374 if (!target->user)
375 target->user = xstrdup("anonymous:busybox@");
376
377 sfp = open_socket(lsa);
378 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200379 bb_error_msg_and_die("%s", sanitize_string(buf+4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200380
381 /*
382 * Splitting username:password pair,
383 * trying to log in
384 */
385 str = strchr(target->user, ':');
386 if (str)
387 *str++ = '\0';
388 switch (ftpcmd("USER ", target->user, sfp, buf)) {
389 case 230:
390 break;
391 case 331:
392 if (ftpcmd("PASS ", str, sfp, buf) == 230)
393 break;
394 /* fall through (failed login) */
395 default:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200396 bb_error_msg_and_die("ftp login: %s", sanitize_string(buf+4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200397 }
398
399 ftpcmd("TYPE I", NULL, sfp, buf);
400
401 /*
402 * Querying file size
403 */
404 if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100405 G.content_len = BB_STRTOOFF(buf+4, NULL, 10);
406 if (G.content_len < 0 || errno) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200407 bb_error_msg_and_die("SIZE value is garbage");
408 }
409 G.got_clen = 1;
410 }
411
412 /*
413 * Entering passive mode
414 */
415 if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
416 pasv_error:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200417 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200418 }
419 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
420 // Server's IP is N1.N2.N3.N4 (we ignore it)
421 // Server's port for data connection is P1*256+P2
422 str = strrchr(buf, ')');
423 if (str) str[0] = '\0';
424 str = strrchr(buf, ',');
425 if (!str) goto pasv_error;
426 port = xatou_range(str+1, 0, 255);
427 *str = '\0';
428 str = strrchr(buf, ',');
429 if (!str) goto pasv_error;
430 port += xatou_range(str+1, 0, 255) * 256;
431 set_nport(lsa, htons(port));
432
433 *dfpp = open_socket(lsa);
434
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100435 if (G.beg_range) {
436 sprintf(buf, "REST %"OFF_FMT"u", G.beg_range);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200437 if (ftpcmd(buf, NULL, sfp, buf) == 350)
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100438 G.content_len -= G.beg_range;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200439 }
440
441 if (ftpcmd("RETR ", target->path, sfp, buf) > 150)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200442 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200443
444 return sfp;
445}
446
Denys Vlasenko7f432802009-06-28 01:02:24 +0200447static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
448{
449 char buf[512];
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200450#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
451# if ENABLE_FEATURE_WGET_TIMEOUT
452 unsigned second_cnt;
453# endif
454 struct pollfd polldata;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200455
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200456 polldata.fd = fileno(dfp);
457 polldata.events = POLLIN | POLLPRI;
Denys Vlasenkoda0df472010-08-08 04:21:50 +0200458 ndelay_on(polldata.fd);
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200459#endif
460 progress_meter(PROGRESS_START);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200461
462 if (G.chunked)
463 goto get_clen;
464
465 /* Loops only if chunked */
466 while (1) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100467 while (1) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200468 int n;
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100469 unsigned rdsz;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200470
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100471 rdsz = sizeof(buf);
472 if (G.got_clen) {
Denys Vlasenkod2c879d2009-12-11 14:12:28 +0100473 if (G.content_len < (off_t)sizeof(buf)) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100474 if ((int)G.content_len <= 0)
475 break;
476 rdsz = (unsigned)G.content_len;
477 }
478 }
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200479#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
480# if ENABLE_FEATURE_WGET_TIMEOUT
481 second_cnt = G.timeout_seconds;
482# endif
483 while (1) {
484 if (safe_poll(&polldata, 1, 1000) != 0)
485 break; /* error, EOF, or data is available */
486# if ENABLE_FEATURE_WGET_TIMEOUT
487 if (second_cnt != 0 && --second_cnt == 0) {
488 progress_meter(PROGRESS_END);
489 bb_perror_msg_and_die("download timed out");
490 }
491# endif
492 /* Needed for "stalled" indicator */
493 progress_meter(PROGRESS_BUMP);
494 }
495#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +0200496 n = safe_fread(buf, rdsz, dfp);
497 if (n <= 0) {
498 if (ferror(dfp)) {
499 /* perror will not work: ferror doesn't set errno */
500 bb_error_msg_and_die(bb_msg_read_error);
501 }
502 break;
503 }
504 xwrite(output_fd, buf, n);
505#if ENABLE_FEATURE_WGET_STATUSBAR
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100506 G.transferred += n;
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200507 progress_meter(PROGRESS_BUMP);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200508#endif
509 if (G.got_clen)
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100510 G.content_len -= n;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200511 }
512
513 if (!G.chunked)
514 break;
515
516 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
517 get_clen:
518 safe_fgets(buf, sizeof(buf), dfp);
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100519 G.content_len = STRTOOFF(buf, NULL, 16);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200520 /* FIXME: error check? */
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100521 if (G.content_len == 0)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200522 break; /* all done! */
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100523 G.got_clen = 1;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200524 }
525
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200526 progress_meter(PROGRESS_END);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200527}
528
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000529int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000530int wget_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen96700832000-09-04 15:15:55 +0000531{
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000532 char buf[512];
Eric Andersen79757c92001-04-05 21:45:54 +0000533 struct host_info server, target;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000534 len_and_sockaddr *lsa;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000535 unsigned opt;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200536 int redir_limit;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200537 char *proxy = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000538 char *dir_prefix = NULL;
539#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000540 char *post_data;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000541 char *extra_headers = NULL;
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000542 llist_t *headers_llist = NULL;
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000543#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +0200544 FILE *sfp; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000545 FILE *dfp; /* socket to ftp server (data) */
546 char *fname_out; /* where to direct output (-O) */
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000547 int output_fd = -1;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200548 bool use_proxy; /* Use proxies if env vars are set */
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000549 const char *proxy_flag = "on"; /* Use proxies if env vars are set */
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000550 const char *user_agent = "Wget";/* "User-Agent" header field */
Denis Vlasenko77105632007-09-24 15:04:00 +0000551
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000552 static const char keywords[] ALIGN1 =
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000553 "content-length\0""transfer-encoding\0""chunked\0""location\0";
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000554 enum {
555 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
556 };
Bernhard Reutner-Fischer289e86a2006-08-20 20:01:24 +0000557#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000558 static const char wget_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000559 /* name, has_arg, val */
560 "continue\0" No_argument "c"
561 "spider\0" No_argument "s"
562 "quiet\0" No_argument "q"
563 "output-document\0" Required_argument "O"
564 "directory-prefix\0" Required_argument "P"
565 "proxy\0" Required_argument "Y"
566 "user-agent\0" Required_argument "U"
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200567#if ENABLE_FEATURE_WGET_TIMEOUT
568 "timeout\0" Required_argument "T"
569#endif
Denis Vlasenko50af9262009-03-02 15:08:06 +0000570 /* Ignored: */
571 // "tries\0" Required_argument "t"
Denis Vlasenko50af9262009-03-02 15:08:06 +0000572 /* Ignored (we always use PASV): */
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000573 "passive-ftp\0" No_argument "\xff"
574 "header\0" Required_argument "\xfe"
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000575 "post-data\0" Required_argument "\xfd"
Bernhard Reutner-Fischer3fdba182010-02-10 19:37:29 +0100576 /* Ignored (we don't do ssl) */
577 "no-check-certificate\0" No_argument "\xfc"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000578 ;
Denis Vlasenko77105632007-09-24 15:04:00 +0000579#endif
580
581 INIT_G();
582
583#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000584 applet_long_options = wget_longopts;
Bernhard Reutner-Fischer8d3a6f72006-05-31 14:11:38 +0000585#endif
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000586 /* server.allocated = target.allocated = NULL; */
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200587 opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
588 opt = getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000589 &fname_out, &dir_prefix,
Denis Vlasenko540ab702008-06-29 00:32:35 +0000590 &proxy_flag, &user_agent,
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200591 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
592 NULL /* -t RETRIES */
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000593 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
594 IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000595 );
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000596#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denis Vlasenko7534e082006-10-23 23:21:58 +0000597 if (headers_llist) {
598 int size = 1;
599 char *cp;
Denis Vlasenko8d9f4952007-04-08 15:08:42 +0000600 llist_t *ll = headers_llist;
Denis Vlasenko7534e082006-10-23 23:21:58 +0000601 while (ll) {
602 size += strlen(ll->data) + 2;
603 ll = ll->link;
604 }
605 extra_headers = cp = xmalloc(size);
Glenn L McGrath514aeab2003-12-19 12:08:56 +0000606 while (headers_llist) {
Denis Vlasenkod50dda82008-06-15 05:40:56 +0000607 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
Eric Andersen96700832000-09-04 15:15:55 +0000608 }
609 }
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000610#endif
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000611
Denys Vlasenko7f432802009-06-28 01:02:24 +0200612 /* TODO: compat issue: should handle "wget URL1 URL2..." */
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200613
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200614 target.user = NULL;
Eric Andersen79757c92001-04-05 21:45:54 +0000615 parse_url(argv[optind], &target);
Eric Andersen79757c92001-04-05 21:45:54 +0000616
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000617 /* Use the proxy if necessary */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200618 use_proxy = (strcmp(proxy_flag, "off") != 0);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000619 if (use_proxy) {
Robert Griebld7760112002-05-14 23:36:45 +0000620 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200621 if (proxy && proxy[0]) {
Denys Vlasenko81fe2b12010-02-11 04:23:43 +0100622 server.user = NULL;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000623 parse_url(proxy, &server);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000624 } else {
625 use_proxy = 0;
626 }
Robert Griebld7760112002-05-14 23:36:45 +0000627 }
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200628 if (!use_proxy) {
629 server.port = target.port;
630 if (ENABLE_FEATURE_IPV6) {
631 server.host = xstrdup(target.host);
632 } else {
633 server.host = target.host;
634 }
635 }
636
637 if (ENABLE_FEATURE_IPV6)
638 strip_ipv6_scope_id(target.host);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000639
Denis Vlasenko818322b2007-09-24 18:27:04 +0000640 /* Guess an output filename, if there was no -O FILE */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000641 if (!(opt & WGET_OPT_OUTNAME)) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000642 fname_out = bb_get_last_path_component_nostrip(target.path);
643 /* handle "wget http://kernel.org//" */
644 if (fname_out[0] == '/' || !fname_out[0])
Denis Vlasenkob6aae0f2007-01-29 22:51:25 +0000645 fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000646 /* -P DIR is considered only if there was no -O FILE */
647 if (dir_prefix)
Matt Kraai0382eb82001-07-19 19:13:55 +0000648 fname_out = concat_path_file(dir_prefix, fname_out);
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000649 } else {
650 if (LONE_DASH(fname_out)) {
651 /* -O - */
652 output_fd = 1;
653 opt &= ~WGET_OPT_CONTINUE;
654 }
Eric Andersen29edd002000-12-09 16:55:35 +0000655 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000656#if ENABLE_FEATURE_WGET_STATUSBAR
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100657 G.curfile = bb_get_last_path_component_nostrip(fname_out);
Denis Vlasenko818322b2007-09-24 18:27:04 +0000658#endif
659
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000660 /* Impossible?
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000661 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100662 bb_error_msg_and_die("can't specify continue (-c) without a filename (-O)");
Denys Vlasenko7f432802009-06-28 01:02:24 +0200663 */
Eric Andersen29edd002000-12-09 16:55:35 +0000664
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000665 /* Determine where to start transfer */
Denis Vlasenko4e4662c2006-11-23 13:10:23 +0000666 if (opt & WGET_OPT_CONTINUE) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000667 output_fd = open(fname_out, O_WRONLY);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000668 if (output_fd >= 0) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100669 G.beg_range = xlseek(output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000670 }
671 /* File doesn't exist. We do not create file here yet.
Denys Vlasenko7f432802009-06-28 01:02:24 +0200672 * We are not sure it exists on remove side */
Eric Andersen96700832000-09-04 15:15:55 +0000673 }
674
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200675 redir_limit = 5;
676 resolve_lsa:
Denis Vlasenko42823d52007-02-04 02:39:08 +0000677 lsa = xhost2sockaddr(server.host, server.port);
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000678 if (!(opt & WGET_OPT_QUIET)) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200679 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
680 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
681 free(s);
Eric Andersene6dc4392003-10-31 09:31:46 +0000682 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200683 establish_session:
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000684 if (use_proxy || !target.is_ftp) {
Eric Andersen79757c92001-04-05 21:45:54 +0000685 /*
686 * HTTP session
687 */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200688 char *str;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200689 int status;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200690
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200691 /* Open socket to http server */
692 sfp = open_socket(lsa);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200693
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200694 /* Send HTTP request */
695 if (use_proxy) {
696 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
697 target.is_ftp ? "f" : "ht", target.host,
698 target.path);
699 } else {
700 if (opt & WGET_OPT_POST_DATA)
701 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
702 else
703 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
704 }
Glenn L McGrathe7bdfcc2003-08-28 22:03:19 +0000705
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200706 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
707 target.host, user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000708
Denis Vlasenko9cade082006-11-21 10:43:02 +0000709#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200710 if (target.user) {
711 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
712 base64enc_512(buf, target.user));
713 }
714 if (use_proxy && server.user) {
715 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
716 base64enc_512(buf, server.user));
717 }
Eric Andersen79757c92001-04-05 21:45:54 +0000718#endif
719
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100720 if (G.beg_range)
721 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000722#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200723 if (extra_headers)
724 fputs(extra_headers, sfp);
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000725
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200726 if (opt & WGET_OPT_POST_DATA) {
727 char *estr = URL_escape(post_data);
728 fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n");
729 fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s",
730 (int) strlen(estr), estr);
731 /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/
732 /*fprintf(sfp, "%s\r\n", estr);*/
733 free(estr);
734 } else
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000735#endif
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200736 { /* If "Connection:" is needed, document why */
737 fprintf(sfp, /* "Connection: close\r\n" */ "\r\n");
738 }
Eric Andersen79757c92001-04-05 21:45:54 +0000739
Nguyễn Thái Ngọc Duyebec11d2010-09-23 15:18:41 +0200740 fflush(sfp);
741
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200742 /*
743 * Retrieve HTTP response line and check for "200" status code.
744 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000745 read_response:
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200746 if (fgets(buf, sizeof(buf), sfp) == NULL)
747 bb_error_msg_and_die("no response from server");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000748
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200749 str = buf;
750 str = skip_non_whitespace(str);
751 str = skip_whitespace(str);
752 // FIXME: no error check
753 // xatou wouldn't work: "200 OK"
754 status = atoi(str);
755 switch (status) {
756 case 0:
757 case 100:
758 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL)
759 /* eat all remaining headers */;
760 goto read_response;
761 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000762/*
763Response 204 doesn't say "null file", it says "metadata
764has changed but data didn't":
765
766"10.2.5 204 No Content
767The server has fulfilled the request but does not need to return
768an entity-body, and might want to return updated metainformation.
769The response MAY include new or updated metainformation in the form
770of entity-headers, which if present SHOULD be associated with
771the requested variant.
772
773If the client is a user agent, it SHOULD NOT change its document
774view from that which caused the request to be sent. This response
775is primarily intended to allow input for actions to take place
776without causing a change to the user agent's active document view,
777although any new or updated metainformation SHOULD be applied
778to the document currently in the user agent's active view.
779
780The 204 response MUST NOT include a message-body, and thus
781is always terminated by the first empty line after the header fields."
782
783However, in real world it was observed that some web servers
784(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
785*/
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200786 case 204:
787 break;
788 case 300: /* redirection */
789 case 301:
790 case 302:
791 case 303:
792 break;
793 case 206:
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100794 if (G.beg_range)
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000795 break;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200796 /* fall through */
797 default:
798 bb_error_msg_and_die("server returned error: %s", sanitize_string(buf));
799 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000800
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200801 /*
802 * Retrieve HTTP headers.
803 */
804 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) {
805 /* gethdr converted "FOO:" string to lowercase */
Matthijs van de Water0d586662009-08-22 20:19:48 +0200806 smalluint key;
807 /* strip trailing whitespace */
808 char *s = strchrnul(str, '\0') - 1;
809 while (s >= str && (*s == ' ' || *s == '\t')) {
810 *s = '\0';
811 s--;
812 }
813 key = index_in_strings(keywords, buf) + 1;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200814 if (key == KEY_content_length) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100815 G.content_len = BB_STRTOOFF(str, NULL, 10);
816 if (G.content_len < 0 || errno) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200817 bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
Eric Andersen79757c92001-04-05 21:45:54 +0000818 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200819 G.got_clen = 1;
820 continue;
821 }
822 if (key == KEY_transfer_encoding) {
823 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
824 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
825 G.chunked = G.got_clen = 1;
826 }
827 if (key == KEY_location && status >= 300) {
828 if (--redir_limit == 0)
829 bb_error_msg_and_die("too many redirections");
830 fclose(sfp);
831 G.got_clen = 0;
832 G.chunked = 0;
833 if (str[0] == '/')
834 /* free(target.allocated); */
835 target.path = /* target.allocated = */ xstrdup(str+1);
836 /* lsa stays the same: it's on the same server */
837 else {
838 parse_url(str, &target);
839 if (!use_proxy) {
840 server.host = target.host;
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200841 /* strip_ipv6_scope_id(target.host); - no! */
842 /* we assume remote never gives us IPv6 addr with scope id */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200843 server.port = target.port;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000844 free(lsa);
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200845 goto resolve_lsa;
846 } /* else: lsa stays the same: we use proxy */
Eric Andersen79757c92001-04-05 21:45:54 +0000847 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200848 goto establish_session;
Eric Andersen79757c92001-04-05 21:45:54 +0000849 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200850 }
851// if (status >= 300)
852// bb_error_msg_and_die("bad redirection (no Location: header from server)");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000853
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200854 /* For HTTP, data is pumped over the same connection */
Eric Andersen79757c92001-04-05 21:45:54 +0000855 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000856
857 } else {
Eric Andersen79757c92001-04-05 21:45:54 +0000858 /*
859 * FTP session
860 */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200861 sfp = prepare_ftp_session(&dfp, &target, lsa);
Eric Andersen96700832000-09-04 15:15:55 +0000862 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000863
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000864 if (opt & WGET_OPT_SPIDER) {
865 if (ENABLE_FEATURE_CLEAN_UP)
866 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +0000867 return EXIT_SUCCESS;
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +0000868 }
Eric Andersen79757c92001-04-05 21:45:54 +0000869
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000870 if (output_fd < 0) {
871 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
872 /* compat with wget: -O FILE can overwrite */
873 if (opt & WGET_OPT_OUTNAME)
874 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
875 output_fd = xopen(fname_out, o_flags);
876 }
Denis Vlasenkof8aa1092006-10-01 10:58:54 +0000877
Denys Vlasenko7f432802009-06-28 01:02:24 +0200878 retrieve_file_data(dfp, output_fd);
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100879 xclose(output_fd);
Rob Landley19a39402006-06-13 17:10:26 +0000880
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200881 if (dfp != sfp) {
882 /* It's ftp. Close it properly */
Eric Andersen79757c92001-04-05 21:45:54 +0000883 fclose(dfp);
884 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200885 bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4));
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100886 /* ftpcmd("QUIT", NULL, sfp, buf); - why bother? */
Eric Andersen79757c92001-04-05 21:45:54 +0000887 }
Denis Vlasenko77105632007-09-24 15:04:00 +0000888
889 return EXIT_SUCCESS;
Eric Andersen96700832000-09-04 15:15:55 +0000890}