blob: d1d85230cab59092b2e041333f2ac493084164b5 [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>
Denys Vlasenkofb132e42010-10-29 11:46:52 +02009 * Kuhn's copyrights are licensed GPLv2-or-later. File as a whole remains GPLv2.
Eric Andersen96700832000-09-04 15:15:55 +000010 */
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020011//config:config WGET
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020012//config: bool "wget (35 kb)"
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020013//config: default y
14//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020015//config: wget is a utility for non-interactive download of files from HTTP
16//config: and FTP servers.
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020017//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +010018//config:config FEATURE_WGET_LONG_OPTIONS
19//config: bool "Enable long options"
20//config: default y
21//config: depends on WGET && LONG_OPTS
22//config:
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020023//config:config FEATURE_WGET_STATUSBAR
Denys Vlasenkof5604222017-01-10 14:58:54 +010024//config: bool "Enable progress bar (+2k)"
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020025//config: default y
26//config: depends on WGET
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020027//config:
28//config:config FEATURE_WGET_AUTHENTICATION
29//config: bool "Enable HTTP authentication"
30//config: default y
31//config: depends on WGET
32//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020033//config: Support authenticated HTTP transfers.
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020034//config:
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020035//config:config FEATURE_WGET_TIMEOUT
36//config: bool "Enable timeout option -T SEC"
37//config: default y
38//config: depends on WGET
39//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020040//config: Supports network read and connect timeouts for wget,
41//config: so that wget will give up and timeout, through the -T
42//config: command line option.
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020043//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020044//config: Currently only connect and network data read timeout are
45//config: supported (i.e., timeout is not applied to the DNS query). When
46//config: FEATURE_WGET_LONG_OPTIONS is also enabled, the --timeout option
47//config: will work in addition to -T.
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020048//config:
Denys Vlasenko9a647c32017-01-23 01:08:16 +010049//config:config FEATURE_WGET_HTTPS
50//config: bool "Support HTTPS using internal TLS code"
51//config: default y
52//config: depends on WGET
53//config: select TLS
54//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020055//config: wget will use internal TLS code to connect to https:// URLs.
56//config: Note:
57//config: On NOMMU machines, ssl_helper applet should be available
58//config: in the $PATH for this to work. Make sure to select that applet.
Denys Vlasenko9a647c32017-01-23 01:08:16 +010059//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020060//config: Note: currently, TLS code only makes TLS I/O work, it
61//config: does *not* check that the peer is who it claims to be, etc.
62//config: IOW: it uses peer-supplied public keys to establish encryption
63//config: and signing keys, then encrypts and signs outgoing data and
64//config: decrypts incoming data.
65//config: It does not check signature hashes on the incoming data:
66//config: this means that attackers manipulating TCP packets can
67//config: send altered data and we unknowingly receive garbage.
68//config: (This check might be relatively easy to add).
69//config: It does not check public key's certificate:
70//config: this means that the peer may be an attacker impersonating
71//config: the server we think we are talking to.
Denys Vlasenko67f6db62017-01-30 16:27:37 +010072//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020073//config: If you think this is unacceptable, consider this. As more and more
74//config: servers switch to HTTPS-only operation, without such "crippled"
75//config: TLS code it is *impossible* to simply download a kernel source
76//config: from kernel.org. Which can in real world translate into
77//config: "my small automatic tooling to build cross-compilers from sources
78//config: no longer works, I need to additionally keep a local copy
79//config: of ~4 megabyte source tarball of a SSL library and ~2 megabyte
80//config: source of wget, need to compile and built both before I can
81//config: download anything. All this despite the fact that the build
82//config: is done in a QEMU sandbox on a machine with absolutely nothing
83//config: worth stealing, so I don't care if someone would go to a lot
84//config: of trouble to intercept my HTTPS download to send me an altered
85//config: kernel tarball".
Denys Vlasenko67f6db62017-01-30 16:27:37 +010086//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020087//config: If you still think this is unacceptable, send patches.
Denys Vlasenko67f6db62017-01-30 16:27:37 +010088//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +020089//config: If you still think this is unacceptable, do not want to send
90//config: patches, but do want to waste bandwidth expaining how wrong
91//config: it is, you will be ignored.
Denys Vlasenko67f6db62017-01-30 16:27:37 +010092//config:
Denys Vlasenko2007ef52015-10-07 02:40:53 +020093//config:config FEATURE_WGET_OPENSSL
94//config: bool "Try to connect to HTTPS using openssl"
95//config: default y
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020096//config: depends on WGET
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020097//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020098//config: Try to use openssl to handle HTTPS.
Denys Vlasenko1c6c6702015-10-07 01:39:40 +020099//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200100//config: OpenSSL has a simple SSL client for debug purposes.
101//config: If you select this option, wget will effectively run:
102//config: "openssl s_client -quiet -connect hostname:443
103//config: -servername hostname 2>/dev/null" and pipe its data
104//config: through it. -servername is not used if hostname is numeric.
105//config: Note inconvenient API: host resolution is done twice,
106//config: and there is no guarantee openssl's idea of IPv6 address
107//config: format is the same as ours.
108//config: Another problem is that s_client prints debug information
109//config: to stderr, and it needs to be suppressed. This means
110//config: all error messages get suppressed too.
111//config: openssl is also a big binary, often dynamically linked
112//config: against ~15 libraries.
Denys Vlasenko1c6c6702015-10-07 01:39:40 +0200113//config:
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200114//config: If openssl can't be executed, internal TLS code will be used
115//config: (if you enabled it); if openssl can be executed but fails later,
116//config: wget can't detect this, and download will fail.
Denys Vlasenko1c6c6702015-10-07 01:39:40 +0200117
118//applet:IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP))
119
120//kbuild:lib-$(CONFIG_WGET) += wget.o
121
Denys Vlasenkoe2e55b02011-03-21 00:37:05 +0100122//usage:#define wget_trivial_usage
123//usage: IF_FEATURE_WGET_LONG_OPTIONS(
Denys Vlasenko2972e2c2016-10-04 04:23:09 +0200124//usage: "[-c|--continue] [--spider] [-q|--quiet] [-O|--output-document FILE]\n"
Denys Vlasenkoe2e55b02011-03-21 00:37:05 +0100125//usage: " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n"
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +0200126/* Since we ignore these opts, we don't show them in --help */
Denys Vlasenko92e1b082015-10-20 21:51:52 +0200127/* //usage: " [--no-check-certificate] [--no-cache] [--passive-ftp] [-t TRIES]" */
128/* //usage: " [-nv] [-nc] [-nH] [-np]" */
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100129//usage: " [-S|--server-response] [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
Denys Vlasenkoe2e55b02011-03-21 00:37:05 +0100130//usage: )
131//usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS(
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100132//usage: "[-cq] [-O FILE] [-Y on/off] [-P DIR] [-S] [-U AGENT]"
Denys Vlasenkoe2e55b02011-03-21 00:37:05 +0100133//usage: IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
134//usage: )
135//usage:#define wget_full_usage "\n\n"
136//usage: "Retrieve files via HTTP or FTP\n"
Denys Vlasenko2972e2c2016-10-04 04:23:09 +0200137//usage: IF_FEATURE_WGET_LONG_OPTIONS(
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100138//usage: "\n --spider Only check URL existence: $? is 0 if exists"
Denys Vlasenkoe2e55b02011-03-21 00:37:05 +0100139//usage: )
Denys Vlasenko2972e2c2016-10-04 04:23:09 +0200140//usage: "\n -c Continue retrieval of aborted transfer"
141//usage: "\n -q Quiet"
142//usage: "\n -P DIR Save to DIR (default .)"
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100143//usage: "\n -S Show server response"
Denys Vlasenko2972e2c2016-10-04 04:23:09 +0200144//usage: IF_FEATURE_WGET_TIMEOUT(
145//usage: "\n -T SEC Network read timeout is SEC seconds"
146//usage: )
147//usage: "\n -O FILE Save to FILE ('-' for stdout)"
148//usage: "\n -U STR Use STR for User-Agent header"
149//usage: "\n -Y on/off Use proxy"
Denys Vlasenkoe2e55b02011-03-21 00:37:05 +0100150
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000151#include "libbb.h"
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +0000152
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +0200153#if 0
154# define log_io(...) bb_error_msg(__VA_ARGS__)
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100155# define SENDFMT(fp, fmt, ...) \
156 do { \
157 log_io("> " fmt, ##__VA_ARGS__); \
158 fprintf(fp, fmt, ##__VA_ARGS__); \
159 } while (0);
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +0200160#else
161# define log_io(...) ((void)0)
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100162# define SENDFMT(fp, fmt, ...) fprintf(fp, fmt, ##__VA_ARGS__)
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +0200163#endif
Denys Vlasenkof836f012011-02-10 23:02:28 +0100164
165
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100166#define SSL_SUPPORTED (ENABLE_FEATURE_WGET_OPENSSL || ENABLE_FEATURE_WGET_HTTPS)
Denys Vlasenkoa6f86512017-01-11 20:16:45 +0100167
Eric Andersen79757c92001-04-05 21:45:54 +0000168struct host_info {
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100169 char *allocated;
Denis Vlasenko818322b2007-09-24 18:27:04 +0000170 const char *path;
Denys Vlasenkod353bff2014-02-03 14:09:42 +0100171 char *user;
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100172 const char *protocol;
Denis Vlasenko818322b2007-09-24 18:27:04 +0000173 char *host;
174 int port;
Eric Andersen79757c92001-04-05 21:45:54 +0000175};
Denys Vlasenko3e134eb2016-04-22 18:09:21 +0200176static const char P_FTP[] ALIGN1 = "ftp";
177static const char P_HTTP[] ALIGN1 = "http";
Denys Vlasenkoa6f86512017-01-11 20:16:45 +0100178#if SSL_SUPPORTED
Denys Vlasenko3e134eb2016-04-22 18:09:21 +0200179static const char P_HTTPS[] ALIGN1 = "https";
Ron Yorston4d0c1ea2015-10-12 10:51:25 +0100180#endif
Eric Andersen79757c92001-04-05 21:45:54 +0000181
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100182#if ENABLE_FEATURE_WGET_LONG_OPTIONS
183/* User-specified headers prevent using our corresponding built-in headers. */
184enum {
185 HDR_HOST = (1<<0),
186 HDR_USER_AGENT = (1<<1),
187 HDR_RANGE = (1<<2),
188 HDR_AUTH = (1<<3) * ENABLE_FEATURE_WGET_AUTHENTICATION,
189 HDR_PROXY_AUTH = (1<<4) * ENABLE_FEATURE_WGET_AUTHENTICATION,
190};
191static const char wget_user_headers[] ALIGN1 =
192 "Host:\0"
193 "User-Agent:\0"
194 "Range:\0"
195# if ENABLE_FEATURE_WGET_AUTHENTICATION
196 "Authorization:\0"
197 "Proxy-Authorization:\0"
198# endif
199 ;
200# define USR_HEADER_HOST (G.user_headers & HDR_HOST)
201# define USR_HEADER_USER_AGENT (G.user_headers & HDR_USER_AGENT)
202# define USR_HEADER_RANGE (G.user_headers & HDR_RANGE)
203# define USR_HEADER_AUTH (G.user_headers & HDR_AUTH)
204# define USR_HEADER_PROXY_AUTH (G.user_headers & HDR_PROXY_AUTH)
205#else /* No long options, no user-headers :( */
206# define USR_HEADER_HOST 0
207# define USR_HEADER_USER_AGENT 0
208# define USR_HEADER_RANGE 0
209# define USR_HEADER_AUTH 0
210# define USR_HEADER_PROXY_AUTH 0
211#endif
Denis Vlasenko77105632007-09-24 15:04:00 +0000212
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200213/* Globals */
Denis Vlasenko77105632007-09-24 15:04:00 +0000214struct globals {
215 off_t content_len; /* Content-length of the file */
216 off_t beg_range; /* Range at which continue begins */
217#if ENABLE_FEATURE_WGET_STATUSBAR
Denis Vlasenko77105632007-09-24 15:04:00 +0000218 off_t transferred; /* Number of bytes transferred so far */
219 const char *curfile; /* Name of current file being transferred */
Magnus Dammf5914992009-11-08 16:34:43 +0100220 bb_progress_t pmt;
Denis Vlasenko77105632007-09-24 15:04:00 +0000221#endif
Denys Vlasenko982e87f2013-07-30 11:52:58 +0200222 char *dir_prefix;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100223#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denys Vlasenko982e87f2013-07-30 11:52:58 +0200224 char *post_data;
225 char *extra_headers;
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100226 unsigned char user_headers; /* Headers mentioned by the user */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100227#endif
Denys Vlasenko982e87f2013-07-30 11:52:58 +0200228 char *fname_out; /* where to direct output (-O) */
229 const char *proxy_flag; /* Use proxies if env vars are set */
230 const char *user_agent; /* "User-Agent" header field */
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200231#if ENABLE_FEATURE_WGET_TIMEOUT
232 unsigned timeout_seconds;
Denys Vlasenko6701e912016-03-17 15:58:16 +0100233 bool die_if_timed_out;
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200234#endif
Denys Vlasenko2384a352011-02-15 00:58:36 +0100235 int output_fd;
236 int o_flags;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200237 smallint chunked; /* chunked transfer encoding */
238 smallint got_clen; /* got content-length: from server */
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100239 /* Local downloads do benefit from big buffer.
240 * With 512 byte buffer, it was measured to be
241 * an order of magnitude slower than with big one.
242 */
243 uint64_t just_to_align_next_member;
244 char wget_buf[CONFIG_FEATURE_COPYBUF_KB*1024];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100245} FIX_ALIASING;
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100246#define G (*ptr_to_globals)
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200247#define INIT_G() do { \
Denys Vlasenko982e87f2013-07-30 11:52:58 +0200248 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200249} while (0)
Guilherme Maciel Ferreira840ef172013-10-16 14:43:30 +0200250#define FINI_G() do { \
251 FREE_PTR_TO_GLOBALS(); \
252} while (0)
Denis Vlasenko77105632007-09-24 15:04:00 +0000253
254
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200255/* Must match option string! */
256enum {
257 WGET_OPT_CONTINUE = (1 << 0),
Denys Vlasenko2972e2c2016-10-04 04:23:09 +0200258 WGET_OPT_QUIET = (1 << 1),
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100259 WGET_OPT_SERVER_RESPONSE = (1 << 2),
260 WGET_OPT_OUTNAME = (1 << 3),
261 WGET_OPT_PREFIX = (1 << 4),
262 WGET_OPT_PROXY = (1 << 5),
263 WGET_OPT_USER_AGENT = (1 << 6),
264 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7),
265 WGET_OPT_RETRIES = (1 << 8),
266 WGET_OPT_nsomething = (1 << 9),
267 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
268 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
269 WGET_OPT_SPIDER = (1 << 12) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200270};
271
272enum {
273 PROGRESS_START = -1,
274 PROGRESS_END = 0,
275 PROGRESS_BUMP = 1,
276};
Denis Vlasenko9cade082006-11-21 10:43:02 +0000277#if ENABLE_FEATURE_WGET_STATUSBAR
Denis Vlasenko00d84172008-11-24 07:34:42 +0000278static void progress_meter(int flag)
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000279{
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200280 if (option_mask32 & WGET_OPT_QUIET)
281 return;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000282
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200283 if (flag == PROGRESS_START)
Denys Vlasenkod55e1392011-02-11 18:56:13 +0100284 bb_progress_init(&G.pmt, G.curfile);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000285
Denys Vlasenko2384a352011-02-15 00:58:36 +0100286 bb_progress_update(&G.pmt,
287 G.beg_range,
288 G.transferred,
289 (G.chunked || !G.got_clen) ? 0 : G.beg_range + G.transferred + G.content_len
290 );
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000291
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200292 if (flag == PROGRESS_END) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100293 bb_progress_free(&G.pmt);
Denys Vlasenko19ced5c2010-06-06 21:53:09 +0200294 bb_putchar_stderr('\n');
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100295 G.transferred = 0;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000296 }
297}
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200298#else
Denis Vlasenko00d84172008-11-24 07:34:42 +0000299static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
Eric Andersenb520e082000-10-03 00:21:45 +0000300#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000301
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000302
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200303/* IPv6 knows scoped address types i.e. link and site local addresses. Link
304 * local addresses can have a scope identifier to specify the
305 * interface/link an address is valid on (e.g. fe80::1%eth0). This scope
306 * identifier is only valid on a single node.
307 *
308 * RFC 4007 says that the scope identifier MUST NOT be sent across the wire,
309 * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers
310 * in the Host header as invalid requests, see
311 * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
312 */
313static void strip_ipv6_scope_id(char *host)
314{
315 char *scope, *cp;
316
317 /* bbox wget actually handles IPv6 addresses without [], like
318 * wget "http://::1/xxx", but this is not standard.
319 * To save code, _here_ we do not support it. */
320
321 if (host[0] != '[')
322 return; /* not IPv6 */
323
324 scope = strchr(host, '%');
325 if (!scope)
326 return;
327
328 /* Remove the IPv6 zone identifier from the host address */
329 cp = strchr(host, ']');
330 if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
331 /* malformed address (not "[xx]:nn" or "[xx]") */
332 return;
333 }
334
335 /* cp points to "]...", scope points to "%eth0]..." */
336 overlapping_strcpy(scope, cp);
337}
338
Denis Vlasenko9cade082006-11-21 10:43:02 +0000339#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100340/* Base64-encode character string. */
341static char *base64enc(const char *str)
Denis Vlasenko3526a132006-09-09 12:20:57 +0000342{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000343 unsigned len = strlen(str);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100344 if (len > sizeof(G.wget_buf)/4*3 - 10) /* paranoia */
345 len = sizeof(G.wget_buf)/4*3 - 10;
346 bb_uuencode(G.wget_buf, str, len, bb_uuenc_tbl_base64);
347 return G.wget_buf;
Eric Andersen79757c92001-04-05 21:45:54 +0000348}
349#endif
350
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200351static char* sanitize_string(char *s)
352{
353 unsigned char *p = (void *) s;
354 while (*p >= ' ')
355 p++;
356 *p = '\0';
357 return s;
358}
359
Lauri Kasanend074b412013-10-12 21:47:07 +0200360#if ENABLE_FEATURE_WGET_TIMEOUT
361static void alarm_handler(int sig UNUSED_PARAM)
362{
363 /* This is theoretically unsafe (uses stdio and malloc in signal handler) */
Denys Vlasenko6701e912016-03-17 15:58:16 +0100364 if (G.die_if_timed_out)
Lauri Kasanend074b412013-10-12 21:47:07 +0200365 bb_error_msg_and_die("download timed out");
366}
Denys Vlasenko6701e912016-03-17 15:58:16 +0100367static void set_alarm(void)
368{
369 if (G.timeout_seconds) {
370 alarm(G.timeout_seconds);
371 G.die_if_timed_out = 1;
372 }
373}
374# define clear_alarm() ((void)(G.die_if_timed_out = 0))
375#else
376# define set_alarm() ((void)0)
377# define clear_alarm() ((void)0)
Lauri Kasanend074b412013-10-12 21:47:07 +0200378#endif
379
Denys Vlasenkoed727612016-07-25 21:34:57 +0200380#if ENABLE_FEATURE_WGET_OPENSSL
381/*
382 * is_ip_address() attempts to verify whether or not a string
383 * contains an IPv4 or IPv6 address (vs. an FQDN). The result
384 * of inet_pton() can be used to determine this.
385 *
386 * TODO add proper error checking when inet_pton() returns -1
387 * (some form of system error has occurred, and errno is set)
388 */
389static int is_ip_address(const char *string)
390{
391 struct sockaddr_in sa;
392
393 int result = inet_pton(AF_INET, string, &(sa.sin_addr));
394# if ENABLE_FEATURE_IPV6
395 if (result == 0) {
396 struct sockaddr_in6 sa6;
397 result = inet_pton(AF_INET6, string, &(sa6.sin6_addr));
398 }
399# endif
400 return (result == 1);
401}
402#endif
403
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000404static FILE *open_socket(len_and_sockaddr *lsa)
405{
Lauri Kasanend074b412013-10-12 21:47:07 +0200406 int fd;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000407 FILE *fp;
408
Denys Vlasenko6701e912016-03-17 15:58:16 +0100409 set_alarm();
Lauri Kasanend074b412013-10-12 21:47:07 +0200410 fd = xconnect_stream(lsa);
Denys Vlasenko6701e912016-03-17 15:58:16 +0100411 clear_alarm();
Lauri Kasanend074b412013-10-12 21:47:07 +0200412
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000413 /* glibc 2.4 seems to try seeking on it - ??! */
414 /* hopefully it understands what ESPIPE means... */
Lauri Kasanend074b412013-10-12 21:47:07 +0200415 fp = fdopen(fd, "r+");
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100416 if (!fp)
Denys Vlasenkodee0fc92011-02-10 10:01:49 +0100417 bb_perror_msg_and_die(bb_msg_memory_exhausted);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000418
419 return fp;
420}
421
Denys Vlasenkof836f012011-02-10 23:02:28 +0100422/* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100423static char fgets_and_trim(FILE *fp, const char *fmt)
Denys Vlasenkof836f012011-02-10 23:02:28 +0100424{
425 char c;
426 char *buf_ptr;
427
Denys Vlasenko6701e912016-03-17 15:58:16 +0100428 set_alarm();
Denys Vlasenkof836f012011-02-10 23:02:28 +0100429 if (fgets(G.wget_buf, sizeof(G.wget_buf) - 1, fp) == NULL)
430 bb_perror_msg_and_die("error getting response");
Denys Vlasenko6701e912016-03-17 15:58:16 +0100431 clear_alarm();
Denys Vlasenkof836f012011-02-10 23:02:28 +0100432
433 buf_ptr = strchrnul(G.wget_buf, '\n');
434 c = *buf_ptr;
435 *buf_ptr = '\0';
436 buf_ptr = strchrnul(G.wget_buf, '\r');
437 *buf_ptr = '\0';
438
439 log_io("< %s", G.wget_buf);
440
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100441 if (fmt && (option_mask32 & WGET_OPT_SERVER_RESPONSE))
442 fprintf(stderr, fmt, G.wget_buf);
443
Denys Vlasenkof836f012011-02-10 23:02:28 +0100444 return c;
445}
446
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100447static int ftpcmd(const char *s1, const char *s2, FILE *fp)
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000448{
449 int result;
450 if (s1) {
Denys Vlasenkof836f012011-02-10 23:02:28 +0100451 if (!s2)
452 s2 = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000453 fprintf(fp, "%s%s\r\n", s1, s2);
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100454 /* With --server-response, wget also shows its ftp commands */
455 if (option_mask32 & WGET_OPT_SERVER_RESPONSE)
456 fprintf(stderr, "--> %s%s\n\n", s1, s2);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000457 fflush(fp);
Denys Vlasenkof836f012011-02-10 23:02:28 +0100458 log_io("> %s%s", s1, s2);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000459 }
460
461 do {
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100462 fgets_and_trim(fp, "%s\n");
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100463 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000464
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100465 G.wget_buf[3] = '\0';
466 result = xatoi_positive(G.wget_buf);
467 G.wget_buf[3] = ' ';
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000468 return result;
469}
470
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100471static void parse_url(const char *src_url, struct host_info *h)
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000472{
473 char *url, *p, *sp;
474
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100475 free(h->allocated);
476 h->allocated = url = xstrdup(src_url);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000477
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100478 h->protocol = P_FTP;
479 p = strstr(url, "://");
480 if (p) {
481 *p = '\0';
482 h->host = p + 3;
483 if (strcmp(url, P_FTP) == 0) {
484 h->port = bb_lookup_port(P_FTP, "tcp", 21);
485 } else
Denys Vlasenkoa6f86512017-01-11 20:16:45 +0100486#if SSL_SUPPORTED
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100487 if (strcmp(url, P_HTTPS) == 0) {
488 h->port = bb_lookup_port(P_HTTPS, "tcp", 443);
489 h->protocol = P_HTTPS;
490 } else
Ron Yorston4d0c1ea2015-10-12 10:51:25 +0100491#endif
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100492 if (strcmp(url, P_HTTP) == 0) {
Lauri Kasanen4967a412013-12-17 19:03:41 +0100493 http:
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100494 h->port = bb_lookup_port(P_HTTP, "tcp", 80);
495 h->protocol = P_HTTP;
496 } else {
497 *p = ':';
498 bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
499 }
500 } else {
Lauri Kasanen4967a412013-12-17 19:03:41 +0100501 // GNU wget is user-friendly and falls back to http://
502 h->host = url;
503 goto http;
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100504 }
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000505
506 // FYI:
507 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
Denys Vlasenkoa0aae9f2017-01-20 14:12:10 +0100508 // 'GET /?var=a/b HTTP/1.0'
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000509 // and saves 'index.html?var=a%2Fb' (we save 'b')
510 // wget 'http://busybox.net?login=john@doe':
511 // request: 'GET /?login=john@doe HTTP/1.0'
512 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
513 // wget 'http://busybox.net#test/test':
514 // request: 'GET / HTTP/1.0'
515 // saves: 'index.html' (we save 'test')
516 //
517 // We also don't add unique .N suffix if file exists...
518 sp = strchr(h->host, '/');
519 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
520 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
521 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000522 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000523 } else if (*sp == '/') {
524 *sp = '\0';
525 h->path = sp + 1;
526 } else { // '#' or '?'
527 // http://busybox.net?login=john@doe is a valid URL
528 // memmove converts to:
529 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000530 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000531 h->host--;
532 sp[-1] = '\0';
533 h->path = sp;
534 }
535
536 sp = strrchr(h->host, '@');
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000537 if (sp != NULL) {
Denys Vlasenkodd1061b2011-09-11 21:04:02 +0200538 // URL-decode "user:password" string before base64-encoding:
539 // wget http://test:my%20pass@example.com should send
540 // Authorization: Basic dGVzdDpteSBwYXNz
541 // which decodes to "test:my pass".
542 // Standard wget and curl do this too.
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000543 *sp = '\0';
Denys Vlasenkod353bff2014-02-03 14:09:42 +0100544 free(h->user);
545 h->user = xstrdup(percent_decode_in_place(h->host, /*strict:*/ 0));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000546 h->host = sp + 1;
547 }
Denys Vlasenkod353bff2014-02-03 14:09:42 +0100548 /* else: h->user remains NULL, or as set by original request
549 * before redirect (if we are here after a redirect).
550 */
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000551}
552
Denys Vlasenkof836f012011-02-10 23:02:28 +0100553static char *gethdr(FILE *fp)
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000554{
555 char *s, *hdrval;
556 int c;
557
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000558 /* retrieve header line */
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100559 c = fgets_and_trim(fp, " %s\n");
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000560
Denys Vlasenkof836f012011-02-10 23:02:28 +0100561 /* end of the headers? */
562 if (G.wget_buf[0] == '\0')
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000563 return NULL;
564
565 /* convert the header name to lower case */
Denys Vlasenkoea267d52013-07-01 15:01:50 +0200566 for (s = G.wget_buf; isalnum(*s) || *s == '-' || *s == '.' || *s == '_'; ++s) {
567 /*
568 * No-op for 20-3f and 60-7f. "0-9a-z-." are in these ranges.
569 * 40-5f range ("@A-Z[\]^_") maps to 60-7f.
570 * "A-Z" maps to "a-z".
571 * "@[\]" can't occur in header names.
572 * "^_" maps to "~,DEL" (which is wrong).
573 * "^" was never seen yet, "_" was seen from web.archive.org
574 * (x-archive-orig-x_commoncrawl_Signature: HEXSTRING).
575 */
Denys Vlasenkof836f012011-02-10 23:02:28 +0100576 *s |= 0x20;
Denys Vlasenko48363312010-04-04 15:29:32 +0200577 }
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000578
579 /* verify we are at the end of the header name */
580 if (*s != ':')
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100581 bb_error_msg_and_die("bad header line: %s", sanitize_string(G.wget_buf));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000582
583 /* locate the start of the header value */
584 *s++ = '\0';
585 hdrval = skip_whitespace(s);
586
Denys Vlasenkof836f012011-02-10 23:02:28 +0100587 if (c != '\n') {
588 /* Rats! The buffer isn't big enough to hold the entire header value */
589 while (c = getc(fp), c != EOF && c != '\n')
590 continue;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000591 }
592
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000593 return hdrval;
594}
595
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200596static void reset_beg_range_to_zero(void)
597{
Denys Vlasenko61441242012-06-17 19:52:25 +0200598 bb_error_msg("restart failed");
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200599 G.beg_range = 0;
600 xlseek(G.output_fd, 0, SEEK_SET);
Denys Vlasenko61441242012-06-17 19:52:25 +0200601 /* Done at the end instead: */
602 /* ftruncate(G.output_fd, 0); */
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200603}
604
Denys Vlasenko7f432802009-06-28 01:02:24 +0200605static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
606{
Denys Vlasenko7f432802009-06-28 01:02:24 +0200607 FILE *sfp;
608 char *str;
609 int port;
610
611 if (!target->user)
612 target->user = xstrdup("anonymous:busybox@");
613
614 sfp = open_socket(lsa);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100615 if (ftpcmd(NULL, NULL, sfp) != 220)
616 bb_error_msg_and_die("%s", sanitize_string(G.wget_buf + 4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200617
618 /*
619 * Splitting username:password pair,
620 * trying to log in
621 */
622 str = strchr(target->user, ':');
623 if (str)
624 *str++ = '\0';
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100625 switch (ftpcmd("USER ", target->user, sfp)) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200626 case 230:
627 break;
628 case 331:
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100629 if (ftpcmd("PASS ", str, sfp) == 230)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200630 break;
631 /* fall through (failed login) */
632 default:
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100633 bb_error_msg_and_die("ftp login: %s", sanitize_string(G.wget_buf + 4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200634 }
635
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100636 ftpcmd("TYPE I", NULL, sfp);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200637
638 /*
639 * Querying file size
640 */
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100641 if (ftpcmd("SIZE ", target->path, sfp) == 213) {
642 G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10);
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100643 if (G.content_len < 0 || errno) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200644 bb_error_msg_and_die("SIZE value is garbage");
645 }
646 G.got_clen = 1;
647 }
648
649 /*
650 * Entering passive mode
651 */
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100652 if (ftpcmd("PASV", NULL, sfp) != 227) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200653 pasv_error:
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100654 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(G.wget_buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200655 }
656 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
657 // Server's IP is N1.N2.N3.N4 (we ignore it)
658 // Server's port for data connection is P1*256+P2
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100659 str = strrchr(G.wget_buf, ')');
Denys Vlasenko7f432802009-06-28 01:02:24 +0200660 if (str) str[0] = '\0';
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100661 str = strrchr(G.wget_buf, ',');
Denys Vlasenko7f432802009-06-28 01:02:24 +0200662 if (!str) goto pasv_error;
663 port = xatou_range(str+1, 0, 255);
664 *str = '\0';
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100665 str = strrchr(G.wget_buf, ',');
Denys Vlasenko7f432802009-06-28 01:02:24 +0200666 if (!str) goto pasv_error;
667 port += xatou_range(str+1, 0, 255) * 256;
Denys Vlasenkoca183112011-04-07 17:52:20 +0200668 set_nport(&lsa->u.sa, htons(port));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200669
670 *dfpp = open_socket(lsa);
671
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +0200672 if (G.beg_range != 0) {
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100673 sprintf(G.wget_buf, "REST %"OFF_FMT"u", G.beg_range);
674 if (ftpcmd(G.wget_buf, NULL, sfp) == 350)
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100675 G.content_len -= G.beg_range;
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200676 else
677 reset_beg_range_to_zero();
Denys Vlasenko7f432802009-06-28 01:02:24 +0200678 }
679
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100680 if (ftpcmd("RETR ", target->path, sfp) > 150)
681 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(G.wget_buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200682
683 return sfp;
684}
685
Denys Vlasenko1c6c6702015-10-07 01:39:40 +0200686#if ENABLE_FEATURE_WGET_OPENSSL
Denys Vlasenko2007ef52015-10-07 02:40:53 +0200687static int spawn_https_helper_openssl(const char *host, unsigned port)
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100688{
689 char *allocated = NULL;
Denys Vlasenkoed727612016-07-25 21:34:57 +0200690 char *servername;
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100691 int sp[2];
692 int pid;
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100693 IF_FEATURE_WGET_HTTPS(volatile int child_failed = 0;)
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100694
695 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0)
696 /* Kernel can have AF_UNIX support disabled */
697 bb_perror_msg_and_die("socketpair");
698
699 if (!strchr(host, ':'))
700 host = allocated = xasprintf("%s:%u", host, port);
Denys Vlasenkoed727612016-07-25 21:34:57 +0200701 servername = xstrdup(host);
702 strrchr(servername, ':')[0] = '\0';
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100703
Denys Vlasenko2007ef52015-10-07 02:40:53 +0200704 fflush_all();
705 pid = xvfork();
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100706 if (pid == 0) {
707 /* Child */
Denys Vlasenkoed727612016-07-25 21:34:57 +0200708 char *argv[8];
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100709
710 close(sp[0]);
711 xmove_fd(sp[1], 0);
712 xdup2(0, 1);
713 /*
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100714 * openssl s_client -quiet -connect www.kernel.org:443 2>/dev/null
715 * It prints some debug stuff on stderr, don't know how to suppress it.
716 * Work around by dev-nulling stderr. We lose all error messages :(
717 */
718 xmove_fd(2, 3);
719 xopen("/dev/null", O_RDWR);
Denys Vlasenkoed727612016-07-25 21:34:57 +0200720 memset(&argv, 0, sizeof(argv));
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100721 argv[0] = (char*)"openssl";
722 argv[1] = (char*)"s_client";
723 argv[2] = (char*)"-quiet";
724 argv[3] = (char*)"-connect";
725 argv[4] = (char*)host;
Denys Vlasenkoed727612016-07-25 21:34:57 +0200726 /*
727 * Per RFC 6066 Section 3, the only permitted values in the
728 * TLS server_name (SNI) field are FQDNs (DNS hostnames).
729 * IPv4 and IPv6 addresses, port numbers are not allowed.
730 */
731 if (!is_ip_address(servername)) {
732 argv[5] = (char*)"-servername";
733 argv[6] = (char*)servername;
734 }
735
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100736 BB_EXECVP(argv[0], argv);
737 xmove_fd(3, 2);
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100738# if ENABLE_FEATURE_WGET_HTTPS
Denys Vlasenko2007ef52015-10-07 02:40:53 +0200739 child_failed = 1;
740 xfunc_die();
741# else
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100742 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
Denys Vlasenko2007ef52015-10-07 02:40:53 +0200743# endif
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100744 /* notreached */
745 }
746
Denys Vlasenko53315572014-02-23 23:39:47 +0100747 /* Parent */
Denys Vlasenkoed727612016-07-25 21:34:57 +0200748 free(servername);
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100749 free(allocated);
750 close(sp[1]);
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100751# if ENABLE_FEATURE_WGET_HTTPS
Denys Vlasenko2007ef52015-10-07 02:40:53 +0200752 if (child_failed) {
753 close(sp[0]);
754 return -1;
755 }
756# endif
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100757 return sp[0];
758}
Denys Vlasenko1c6c6702015-10-07 01:39:40 +0200759#endif
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100760
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100761#if ENABLE_FEATURE_WGET_HTTPS
762static void spawn_ssl_client(const char *host, int network_fd)
Denys Vlasenko53315572014-02-23 23:39:47 +0100763{
764 int sp[2];
765 int pid;
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100766 char *servername, *p;
767
768 servername = xstrdup(host);
769 p = strrchr(servername, ':');
770 if (p) *p = '\0';
Denys Vlasenko53315572014-02-23 23:39:47 +0100771
772 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0)
773 /* Kernel can have AF_UNIX support disabled */
774 bb_perror_msg_and_die("socketpair");
775
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100776 fflush_all();
Denys Vlasenko53315572014-02-23 23:39:47 +0100777 pid = BB_MMU ? xfork() : xvfork();
778 if (pid == 0) {
779 /* Child */
Denys Vlasenko53315572014-02-23 23:39:47 +0100780 close(sp[0]);
781 xmove_fd(sp[1], 0);
782 xdup2(0, 1);
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100783 if (BB_MMU) {
784 tls_state_t *tls = new_tls_state();
785 tls->ifd = tls->ofd = network_fd;
786 tls_handshake(tls, servername);
787 tls_run_copy_loop(tls);
788 exit(0);
789 } else {
790 char *argv[5];
791 xmove_fd(network_fd, 3);
792 argv[0] = (char*)"ssl_client";
793 argv[1] = (char*)"-s3";
794 //TODO: if (!is_ip_address(servername))...
795 argv[2] = (char*)"-n";
796 argv[3] = servername;
797 argv[4] = NULL;
798 BB_EXECVP(argv[0], argv);
799 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
800 }
Denys Vlasenko53315572014-02-23 23:39:47 +0100801 /* notreached */
802 }
803
804 /* Parent */
Denys Vlasenko9a647c32017-01-23 01:08:16 +0100805 free(servername);
Denys Vlasenko53315572014-02-23 23:39:47 +0100806 close(sp[1]);
807 xmove_fd(sp[0], network_fd);
808}
809#endif
810
Denys Vlasenko2384a352011-02-15 00:58:36 +0100811static void NOINLINE retrieve_file_data(FILE *dfp)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200812{
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200813#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
814# if ENABLE_FEATURE_WGET_TIMEOUT
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200815 unsigned second_cnt = G.timeout_seconds;
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200816# endif
817 struct pollfd polldata;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200818
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200819 polldata.fd = fileno(dfp);
820 polldata.events = POLLIN | POLLPRI;
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200821#endif
822 progress_meter(PROGRESS_START);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200823
824 if (G.chunked)
825 goto get_clen;
826
827 /* Loops only if chunked */
828 while (1) {
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100829
830#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
831 /* Must use nonblocking I/O, otherwise fread will loop
832 * and *block* until it reads full buffer,
833 * which messes up progress bar and/or timeout logic.
834 * Because of nonblocking I/O, we need to dance
835 * very carefully around EAGAIN. See explanation at
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200836 * clearerr() calls.
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100837 */
838 ndelay_on(polldata.fd);
839#endif
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100840 while (1) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200841 int n;
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100842 unsigned rdsz;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200843
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200844#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
Denys Vlasenko8766a792011-02-11 21:42:00 +0100845 /* fread internally uses read loop, which in our case
846 * is usually exited when we get EAGAIN.
847 * In this case, libc sets error marker on the stream.
848 * Need to clear it before next fread to avoid possible
849 * rare false positive ferror below. Rare because usually
850 * fread gets more than zero bytes, and we don't fall
851 * into if (n <= 0) ...
852 */
853 clearerr(dfp);
Denys Vlasenkof9af3752011-02-11 22:01:33 +0100854#endif
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200855 errno = 0;
856 rdsz = sizeof(G.wget_buf);
857 if (G.got_clen) {
858 if (G.content_len < (off_t)sizeof(G.wget_buf)) {
859 if ((int)G.content_len <= 0)
860 break;
861 rdsz = (unsigned)G.content_len;
862 }
863 }
Denys Vlasenko0fac2f72011-02-10 09:55:05 +0100864 n = fread(G.wget_buf, 1, rdsz, dfp);
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200865
866 if (n > 0) {
867 xwrite(G.output_fd, G.wget_buf, n);
868#if ENABLE_FEATURE_WGET_STATUSBAR
869 G.transferred += n;
870#endif
871 if (G.got_clen) {
872 G.content_len -= n;
873 if (G.content_len == 0)
874 break;
875 }
876#if ENABLE_FEATURE_WGET_TIMEOUT
877 second_cnt = G.timeout_seconds;
878#endif
Denys Vlasenkofaa9e942014-03-27 16:50:29 +0100879 goto bump;
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200880 }
881
882 /* n <= 0.
883 * man fread:
Denys Vlasenko8766a792011-02-11 21:42:00 +0100884 * If error occurs, or EOF is reached, the return value
885 * is a short item count (or zero).
886 * fread does not distinguish between EOF and error.
887 */
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200888 if (errno != EAGAIN) {
889 if (ferror(dfp)) {
890 progress_meter(PROGRESS_END);
Denys Vlasenko8766a792011-02-11 21:42:00 +0100891 bb_perror_msg_and_die(bb_msg_read_error);
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200892 }
Denys Vlasenko8766a792011-02-11 21:42:00 +0100893 break; /* EOF, not error */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200894 }
Denys Vlasenko8766a792011-02-11 21:42:00 +0100895
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200896#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
897 /* It was EAGAIN. There is no data. Wait up to one second
898 * then abort if timed out, or update the bar and try reading again.
899 */
900 if (safe_poll(&polldata, 1, 1000) == 0) {
901# if ENABLE_FEATURE_WGET_TIMEOUT
902 if (second_cnt != 0 && --second_cnt == 0) {
903 progress_meter(PROGRESS_END);
904 bb_error_msg_and_die("download timed out");
905 }
906# endif
907 /* We used to loop back to poll here,
908 * but there is no great harm in letting fread
909 * to try reading anyway.
910 */
911 }
Denys Vlasenkofaa9e942014-03-27 16:50:29 +0100912#endif
913 bump:
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200914 /* Need to do it _every_ second for "stalled" indicator
915 * to be shown properly.
916 */
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200917 progress_meter(PROGRESS_BUMP);
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200918 } /* while (reading data) */
919
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100920#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
921 clearerr(dfp);
Denys Vlasenko88ad9da2011-02-11 23:06:21 +0100922 ndelay_off(polldata.fd); /* else fgets can get very unhappy */
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100923#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +0200924 if (!G.chunked)
925 break;
926
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100927 fgets_and_trim(dfp, NULL); /* Eat empty line */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200928 get_clen:
Denys Vlasenkodff9fef2017-01-24 21:41:43 +0100929 fgets_and_trim(dfp, NULL);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100930 G.content_len = STRTOOFF(G.wget_buf, NULL, 16);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200931 /* FIXME: error check? */
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100932 if (G.content_len == 0)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200933 break; /* all done! */
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100934 G.got_clen = 1;
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200935 /*
936 * Note that fgets may result in some data being buffered in dfp.
937 * We loop back to fread, which will retrieve this data.
938 * Also note that code has to be arranged so that fread
939 * is done _before_ one-second poll wait - poll doesn't know
940 * about stdio buffering and can result in spurious one second waits!
941 */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200942 }
943
Denys Vlasenko61441242012-06-17 19:52:25 +0200944 /* If -c failed, we restart from the beginning,
945 * but we do not truncate file then, we do it only now, at the end.
946 * This lets user to ^C if his 99% complete 10 GB file download
947 * failed to restart *without* losing the almost complete file.
948 */
949 {
950 off_t pos = lseek(G.output_fd, 0, SEEK_CUR);
951 if (pos != (off_t)-1)
952 ftruncate(G.output_fd, pos);
953 }
954
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100955 /* Draw full bar and free its resources */
Denys Vlasenko2384a352011-02-15 00:58:36 +0100956 G.chunked = 0; /* makes it show 100% even for chunked download */
957 G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200958 progress_meter(PROGRESS_END);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200959}
960
Pere Orga53695632011-02-16 20:09:36 +0100961static void download_one_url(const char *url)
Eric Andersen96700832000-09-04 15:15:55 +0000962{
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100963 bool use_proxy; /* Use proxies if env vars are set */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200964 int redir_limit;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100965 len_and_sockaddr *lsa;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200966 FILE *sfp; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000967 FILE *dfp; /* socket to ftp server (data) */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100968 char *proxy = NULL;
969 char *fname_out_alloc;
Denys Vlasenko93b4a602011-12-18 05:11:56 +0100970 char *redirected_path = NULL;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100971 struct host_info server;
972 struct host_info target;
Denis Vlasenko77105632007-09-24 15:04:00 +0000973
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100974 server.allocated = NULL;
975 target.allocated = NULL;
976 server.user = NULL;
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200977 target.user = NULL;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100978
979 parse_url(url, &target);
Eric Andersen79757c92001-04-05 21:45:54 +0000980
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000981 /* Use the proxy if necessary */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100982 use_proxy = (strcmp(G.proxy_flag, "off") != 0);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000983 if (use_proxy) {
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100984 proxy = getenv(target.protocol == P_FTP ? "ftp_proxy" : "http_proxy");
985//FIXME: what if protocol is https? Ok to use http_proxy?
Denys Vlasenko2384a352011-02-15 00:58:36 +0100986 use_proxy = (proxy && proxy[0]);
987 if (use_proxy)
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000988 parse_url(proxy, &server);
Robert Griebld7760112002-05-14 23:36:45 +0000989 }
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200990 if (!use_proxy) {
991 server.port = target.port;
992 if (ENABLE_FEATURE_IPV6) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100993 //free(server.allocated); - can't be non-NULL
994 server.host = server.allocated = xstrdup(target.host);
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200995 } else {
996 server.host = target.host;
997 }
998 }
999
1000 if (ENABLE_FEATURE_IPV6)
1001 strip_ipv6_scope_id(target.host);
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001002
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001003 /* If there was no -O FILE, guess output filename */
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001004 fname_out_alloc = NULL;
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001005 if (!(option_mask32 & WGET_OPT_OUTNAME)) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001006 G.fname_out = bb_get_last_path_component_nostrip(target.path);
Denis Vlasenko818322b2007-09-24 18:27:04 +00001007 /* handle "wget http://kernel.org//" */
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001008 if (G.fname_out[0] == '/' || !G.fname_out[0])
1009 G.fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +00001010 /* -P DIR is considered only if there was no -O FILE */
Denys Vlasenkoaacd4482012-06-17 20:21:30 +02001011 if (G.dir_prefix)
1012 G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out);
Denys Vlasenko625f2182011-03-21 00:29:37 +01001013 else {
Denys Vlasenkoaacd4482012-06-17 20:21:30 +02001014 /* redirects may free target.path later, need to make a copy */
1015 G.fname_out = fname_out_alloc = xstrdup(G.fname_out);
Denys Vlasenko625f2182011-03-21 00:29:37 +01001016 }
Eric Andersen29edd002000-12-09 16:55:35 +00001017 }
Denis Vlasenko818322b2007-09-24 18:27:04 +00001018#if ENABLE_FEATURE_WGET_STATUSBAR
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001019 G.curfile = bb_get_last_path_component_nostrip(G.fname_out);
Denis Vlasenko818322b2007-09-24 18:27:04 +00001020#endif
1021
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +00001022 /* Determine where to start transfer */
Denys Vlasenko2384a352011-02-15 00:58:36 +01001023 G.beg_range = 0;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001024 if (option_mask32 & WGET_OPT_CONTINUE) {
Denys Vlasenko2384a352011-02-15 00:58:36 +01001025 G.output_fd = open(G.fname_out, O_WRONLY);
1026 if (G.output_fd >= 0) {
1027 G.beg_range = xlseek(G.output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +00001028 }
1029 /* File doesn't exist. We do not create file here yet.
Denys Vlasenkoa84eadf2011-02-12 23:40:31 +01001030 * We are not sure it exists on remote side */
Eric Andersen96700832000-09-04 15:15:55 +00001031 }
1032
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001033 redir_limit = 5;
1034 resolve_lsa:
Denis Vlasenko42823d52007-02-04 02:39:08 +00001035 lsa = xhost2sockaddr(server.host, server.port);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001036 if (!(option_mask32 & WGET_OPT_QUIET)) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001037 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
1038 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
1039 free(s);
Eric Andersene6dc4392003-10-31 09:31:46 +00001040 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001041 establish_session:
Denys Vlasenko2384a352011-02-15 00:58:36 +01001042 /*G.content_len = 0; - redundant, got_clen = 0 is enough */
1043 G.got_clen = 0;
1044 G.chunked = 0;
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +01001045 if (use_proxy || target.protocol != P_FTP) {
Eric Andersen79757c92001-04-05 21:45:54 +00001046 /*
1047 * HTTP session
1048 */
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001049 char *str;
Denys Vlasenko7f432802009-06-28 01:02:24 +02001050 int status;
Denys Vlasenko7f432802009-06-28 01:02:24 +02001051
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +01001052 /* Open socket to http(s) server */
Denys Vlasenko1c6c6702015-10-07 01:39:40 +02001053#if ENABLE_FEATURE_WGET_OPENSSL
Denys Vlasenko9a647c32017-01-23 01:08:16 +01001054 /* openssl (and maybe internal TLS) support is configured */
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +01001055 if (target.protocol == P_HTTPS) {
Denys Vlasenko1c6c6702015-10-07 01:39:40 +02001056 /* openssl-based helper
1057 * Inconvenient API since we can't give it an open fd
1058 */
Denys Vlasenko2007ef52015-10-07 02:40:53 +02001059 int fd = spawn_https_helper_openssl(server.host, server.port);
Denys Vlasenko9a647c32017-01-23 01:08:16 +01001060# if ENABLE_FEATURE_WGET_HTTPS
1061 if (fd < 0) { /* no openssl? try internal */
Denys Vlasenko2007ef52015-10-07 02:40:53 +02001062 sfp = open_socket(lsa);
Denys Vlasenko9a647c32017-01-23 01:08:16 +01001063 spawn_ssl_client(server.host, fileno(sfp));
Denys Vlasenko2007ef52015-10-07 02:40:53 +02001064 goto socket_opened;
1065 }
1066# else
1067 /* We don't check for exec("openssl") failure in this case */
1068# endif
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +01001069 sfp = fdopen(fd, "r+");
1070 if (!sfp)
1071 bb_perror_msg_and_die(bb_msg_memory_exhausted);
Denys Vlasenko2007ef52015-10-07 02:40:53 +02001072 goto socket_opened;
1073 }
1074 sfp = open_socket(lsa);
1075 socket_opened:
Denys Vlasenko9a647c32017-01-23 01:08:16 +01001076#elif ENABLE_FEATURE_WGET_HTTPS
1077 /* Only internal TLS support is configured */
Denys Vlasenko2007ef52015-10-07 02:40:53 +02001078 sfp = open_socket(lsa);
Denys Vlasenko53315572014-02-23 23:39:47 +01001079 if (target.protocol == P_HTTPS)
Denys Vlasenko9a647c32017-01-23 01:08:16 +01001080 spawn_ssl_client(server.host, fileno(sfp));
Denys Vlasenko2007ef52015-10-07 02:40:53 +02001081#else
1082 /* ssl (https) support is not configured */
1083 sfp = open_socket(lsa);
Denys Vlasenko53315572014-02-23 23:39:47 +01001084#endif
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001085 /* Send HTTP request */
1086 if (use_proxy) {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001087 SENDFMT(sfp, "GET %s://%s/%s HTTP/1.1\r\n",
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +01001088 target.protocol, target.host,
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001089 target.path);
1090 } else {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001091 SENDFMT(sfp, "%s /%s HTTP/1.1\r\n",
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +01001092 (option_mask32 & WGET_OPT_POST_DATA) ? "POST" : "GET",
1093 target.path);
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001094 }
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001095 if (!USR_HEADER_HOST)
1096 SENDFMT(sfp, "Host: %s\r\n", target.host);
1097 if (!USR_HEADER_USER_AGENT)
1098 SENDFMT(sfp, "User-Agent: %s\r\n", G.user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +00001099
Denys Vlasenko9213a552011-02-10 13:23:45 +01001100 /* Ask server to close the connection as soon as we are done
1101 * (IOW: we do not intend to send more requests)
1102 */
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001103 SENDFMT(sfp, "Connection: close\r\n");
Denys Vlasenko9213a552011-02-10 13:23:45 +01001104
Denis Vlasenko9cade082006-11-21 10:43:02 +00001105#if ENABLE_FEATURE_WGET_AUTHENTICATION
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001106 if (target.user && !USR_HEADER_AUTH) {
1107 SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +01001108 base64enc(target.user));
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001109 }
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001110 if (use_proxy && server.user && !USR_HEADER_PROXY_AUTH) {
1111 SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n",
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +01001112 base64enc(server.user));
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001113 }
Eric Andersen79757c92001-04-05 21:45:54 +00001114#endif
1115
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001116 if (G.beg_range != 0 && !USR_HEADER_RANGE)
1117 SENDFMT(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
Denys Vlasenko9213a552011-02-10 13:23:45 +01001118
Denis Vlasenkoc8400a22006-10-25 00:33:44 +00001119#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001120 if (G.extra_headers) {
1121 log_io(G.extra_headers);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001122 fputs(G.extra_headers, sfp);
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001123 }
Denis Vlasenko5a2ad692009-03-04 14:13:37 +00001124
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001125 if (option_mask32 & WGET_OPT_POST_DATA) {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001126 SENDFMT(sfp,
Denys Vlasenko9213a552011-02-10 13:23:45 +01001127 "Content-Type: application/x-www-form-urlencoded\r\n"
1128 "Content-Length: %u\r\n"
1129 "\r\n"
1130 "%s",
Vitaly Magerya700fbc32011-03-27 22:33:13 +02001131 (int) strlen(G.post_data), G.post_data
Denys Vlasenko9213a552011-02-10 13:23:45 +01001132 );
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001133 } else
Denis Vlasenkoc8400a22006-10-25 00:33:44 +00001134#endif
Denys Vlasenko9213a552011-02-10 13:23:45 +01001135 {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001136 SENDFMT(sfp, "\r\n");
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001137 }
Eric Andersen79757c92001-04-05 21:45:54 +00001138
Nguyễn Thái Ngọc Duyebec11d2010-09-23 15:18:41 +02001139 fflush(sfp);
Denys Vlasenkoa6f86512017-01-11 20:16:45 +01001140
Denys Vlasenko4e08a122017-01-16 17:31:05 +01001141/* Tried doing this unconditionally.
1142 * Cloudflare and nginx/1.11.5 are shocked to see SHUT_WR on non-HTTPS.
1143 */
Denys Vlasenkoa6f86512017-01-11 20:16:45 +01001144#if SSL_SUPPORTED
1145 if (target.protocol == P_HTTPS) {
1146 /* If we use SSL helper, keeping our end of the socket open for writing
1147 * makes our end (i.e. the same fd!) readable (EAGAIN instead of EOF)
1148 * even after child closes its copy of the fd.
1149 * This helps:
1150 */
1151 shutdown(fileno(sfp), SHUT_WR);
1152 }
1153#endif
Nguyễn Thái Ngọc Duyebec11d2010-09-23 15:18:41 +02001154
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001155 /*
1156 * Retrieve HTTP response line and check for "200" status code.
1157 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +00001158 read_response:
Denys Vlasenkodff9fef2017-01-24 21:41:43 +01001159 fgets_and_trim(sfp, " %s\n");
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001160
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +01001161 str = G.wget_buf;
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001162 str = skip_non_whitespace(str);
1163 str = skip_whitespace(str);
1164 // FIXME: no error check
1165 // xatou wouldn't work: "200 OK"
1166 status = atoi(str);
1167 switch (status) {
1168 case 0:
1169 case 100:
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001170 while (gethdr(sfp) != NULL)
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001171 /* eat all remaining headers */;
1172 goto read_response;
Denys Vlasenko9ff910d2016-08-31 13:28:53 +02001173
1174 /* Success responses */
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001175 case 200:
Denys Vlasenko9ff910d2016-08-31 13:28:53 +02001176 /* fall through */
1177 case 201: /* 201 Created */
1178/* "The request has been fulfilled and resulted in a new resource being created" */
Denys Vlasenkoef159702016-09-01 11:16:22 +02001179 /* Standard wget is reported to treat this as success */
Denys Vlasenko9ff910d2016-08-31 13:28:53 +02001180 /* fall through */
1181 case 202: /* 202 Accepted */
1182/* "The request has been accepted for processing, but the processing has not been completed" */
1183 /* Treat as success: fall through */
1184 case 203: /* 203 Non-Authoritative Information */
1185/* "Use of this response code is not required and is only appropriate when the response would otherwise be 200 (OK)" */
1186 /* fall through */
1187 case 204: /* 204 No Content */
Denis Vlasenko50b5cac2008-06-22 16:28:02 +00001188/*
1189Response 204 doesn't say "null file", it says "metadata
1190has changed but data didn't":
1191
1192"10.2.5 204 No Content
1193The server has fulfilled the request but does not need to return
1194an entity-body, and might want to return updated metainformation.
1195The response MAY include new or updated metainformation in the form
1196of entity-headers, which if present SHOULD be associated with
1197the requested variant.
1198
1199If the client is a user agent, it SHOULD NOT change its document
1200view from that which caused the request to be sent. This response
1201is primarily intended to allow input for actions to take place
1202without causing a change to the user agent's active document view,
1203although any new or updated metainformation SHOULD be applied
1204to the document currently in the user agent's active view.
1205
1206The 204 response MUST NOT include a message-body, and thus
1207is always terminated by the first empty line after the header fields."
1208
1209However, in real world it was observed that some web servers
1210(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
1211*/
Denys Vlasenkobf146b82012-06-13 17:31:07 +02001212 if (G.beg_range != 0) {
1213 /* "Range:..." was not honored by the server.
1214 * Restart download from the beginning.
1215 */
1216 reset_beg_range_to_zero();
1217 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001218 break;
Denys Vlasenko9ff910d2016-08-31 13:28:53 +02001219 /* 205 Reset Content ?? what to do on this ?? */
1220
Denys Vlasenkofb132e42010-10-29 11:46:52 +02001221 case 300: /* redirection */
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001222 case 301:
1223 case 302:
1224 case 303:
1225 break;
Denys Vlasenko9ff910d2016-08-31 13:28:53 +02001226
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +02001227 case 206: /* Partial Content */
1228 if (G.beg_range != 0)
1229 /* "Range:..." worked. Good. */
Denis Vlasenko023b57d2006-10-15 17:05:55 +00001230 break;
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +02001231 /* Partial Content even though we did not ask for it??? */
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001232 /* fall through */
1233 default:
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +01001234 bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf));
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001235 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001236
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001237 /*
1238 * Retrieve HTTP headers.
1239 */
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001240 while ((str = gethdr(sfp)) != NULL) {
1241 static const char keywords[] ALIGN1 =
1242 "content-length\0""transfer-encoding\0""location\0";
1243 enum {
1244 KEY_content_length = 1, KEY_transfer_encoding, KEY_location
1245 };
Matthijs van de Water0d586662009-08-22 20:19:48 +02001246 smalluint key;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001247
1248 /* gethdr converted "FOO:" string to lowercase */
1249
Matthijs van de Water0d586662009-08-22 20:19:48 +02001250 /* strip trailing whitespace */
1251 char *s = strchrnul(str, '\0') - 1;
1252 while (s >= str && (*s == ' ' || *s == '\t')) {
1253 *s = '\0';
1254 s--;
1255 }
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +01001256 key = index_in_strings(keywords, G.wget_buf) + 1;
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001257 if (key == KEY_content_length) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +01001258 G.content_len = BB_STRTOOFF(str, NULL, 10);
1259 if (G.content_len < 0 || errno) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001260 bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
Eric Andersen79757c92001-04-05 21:45:54 +00001261 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001262 G.got_clen = 1;
1263 continue;
1264 }
1265 if (key == KEY_transfer_encoding) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001266 if (strcmp(str_tolower(str), "chunked") != 0)
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001267 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001268 G.chunked = 1;
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001269 }
1270 if (key == KEY_location && status >= 300) {
1271 if (--redir_limit == 0)
1272 bb_error_msg_and_die("too many redirections");
1273 fclose(sfp);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001274 if (str[0] == '/') {
Denys Vlasenko93b4a602011-12-18 05:11:56 +01001275 free(redirected_path);
1276 target.path = redirected_path = xstrdup(str+1);
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001277 /* lsa stays the same: it's on the same server */
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001278 } else {
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001279 parse_url(str, &target);
1280 if (!use_proxy) {
Denys Vlasenkod353bff2014-02-03 14:09:42 +01001281 /* server.user remains untouched */
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001282 free(server.allocated);
Pere Orga57b49092011-02-14 23:56:07 +01001283 server.allocated = NULL;
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001284 server.host = target.host;
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +02001285 /* strip_ipv6_scope_id(target.host); - no! */
1286 /* we assume remote never gives us IPv6 addr with scope id */
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001287 server.port = target.port;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001288 free(lsa);
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001289 goto resolve_lsa;
1290 } /* else: lsa stays the same: we use proxy */
Eric Andersen79757c92001-04-05 21:45:54 +00001291 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001292 goto establish_session;
Eric Andersen79757c92001-04-05 21:45:54 +00001293 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001294 }
1295// if (status >= 300)
1296// bb_error_msg_and_die("bad redirection (no Location: header from server)");
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001297
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001298 /* For HTTP, data is pumped over the same connection */
Eric Andersen79757c92001-04-05 21:45:54 +00001299 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +00001300 } else {
Eric Andersen79757c92001-04-05 21:45:54 +00001301 /*
1302 * FTP session
1303 */
Denys Vlasenko7f432802009-06-28 01:02:24 +02001304 sfp = prepare_ftp_session(&dfp, &target, lsa);
Eric Andersen96700832000-09-04 15:15:55 +00001305 }
Denis Vlasenko77105632007-09-24 15:04:00 +00001306
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001307 free(lsa);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001308
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001309 if (!(option_mask32 & WGET_OPT_SPIDER)) {
Denys Vlasenko2384a352011-02-15 00:58:36 +01001310 if (G.output_fd < 0)
1311 G.output_fd = xopen(G.fname_out, G.o_flags);
1312 retrieve_file_data(dfp);
1313 if (!(option_mask32 & WGET_OPT_OUTNAME)) {
1314 xclose(G.output_fd);
1315 G.output_fd = -1;
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001316 }
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +00001317 }
Eric Andersen79757c92001-04-05 21:45:54 +00001318
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001319 if (dfp != sfp) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001320 /* It's ftp. Close data connection properly */
Eric Andersen79757c92001-04-05 21:45:54 +00001321 fclose(dfp);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +01001322 if (ftpcmd(NULL, NULL, sfp) != 226)
1323 bb_error_msg_and_die("ftp error: %s", sanitize_string(G.wget_buf + 4));
1324 /* ftpcmd("QUIT", NULL, sfp); - why bother? */
Eric Andersen79757c92001-04-05 21:45:54 +00001325 }
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001326 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +00001327
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001328 free(server.allocated);
1329 free(target.allocated);
Denys Vlasenkod353bff2014-02-03 14:09:42 +01001330 free(server.user);
1331 free(target.user);
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001332 free(fname_out_alloc);
Denys Vlasenko93b4a602011-12-18 05:11:56 +01001333 free(redirected_path);
Eric Andersen96700832000-09-04 15:15:55 +00001334}
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001335
1336int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1337int wget_main(int argc UNUSED_PARAM, char **argv)
1338{
1339#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1340 static const char wget_longopts[] ALIGN1 =
1341 /* name, has_arg, val */
1342 "continue\0" No_argument "c"
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001343 "quiet\0" No_argument "q"
Denys Vlasenkodff9fef2017-01-24 21:41:43 +01001344 "server-response\0" No_argument "S"
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001345 "output-document\0" Required_argument "O"
1346 "directory-prefix\0" Required_argument "P"
1347 "proxy\0" Required_argument "Y"
1348 "user-agent\0" Required_argument "U"
Denys Vlasenko92e1b082015-10-20 21:51:52 +02001349IF_FEATURE_WGET_TIMEOUT(
1350 "timeout\0" Required_argument "T")
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001351 /* Ignored: */
Denys Vlasenko92e1b082015-10-20 21:51:52 +02001352IF_DESKTOP( "tries\0" Required_argument "t")
1353 "header\0" Required_argument "\xff"
1354 "post-data\0" Required_argument "\xfe"
Denys Vlasenko2972e2c2016-10-04 04:23:09 +02001355 "spider\0" No_argument "\xfd"
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001356 /* Ignored (we always use PASV): */
Denys Vlasenko92e1b082015-10-20 21:51:52 +02001357IF_DESKTOP( "passive-ftp\0" No_argument "\xf0")
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001358 /* Ignored (we don't do ssl) */
Denys Vlasenko92e1b082015-10-20 21:51:52 +02001359IF_DESKTOP( "no-check-certificate\0" No_argument "\xf0")
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +02001360 /* Ignored (we don't support caching) */
Denys Vlasenko92e1b082015-10-20 21:51:52 +02001361IF_DESKTOP( "no-cache\0" No_argument "\xf0")
1362IF_DESKTOP( "no-verbose\0" No_argument "\xf0")
1363IF_DESKTOP( "no-clobber\0" No_argument "\xf0")
1364IF_DESKTOP( "no-host-directories\0" No_argument "\xf0")
1365IF_DESKTOP( "no-parent\0" No_argument "\xf0")
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001366 ;
Denys Vlasenko036585a2017-08-08 16:38:18 +02001367# define GETOPT32 getopt32long
1368# define LONGOPTS ,wget_longopts
1369#else
1370# define GETOPT32 getopt32
1371# define LONGOPTS
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001372#endif
1373
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001374#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1375 llist_t *headers_llist = NULL;
1376#endif
1377
1378 INIT_G();
1379
Lauri Kasanend074b412013-10-12 21:47:07 +02001380#if ENABLE_FEATURE_WGET_TIMEOUT
1381 G.timeout_seconds = 900;
1382 signal(SIGALRM, alarm_handler);
1383#endif
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001384 G.proxy_flag = "on"; /* use proxies if env vars are set */
1385 G.user_agent = "Wget"; /* "User-Agent" header field */
1386
1387#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001388#endif
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001389 GETOPT32(argv, "^"
1390 "cqSO:P:Y:U:T:+"
Denys Vlasenko92e1b082015-10-20 21:51:52 +02001391 /*ignored:*/ "t:"
1392 /*ignored:*/ "n::"
1393 /* wget has exactly four -n<letter> opts, all of which we can ignore:
1394 * -nv --no-verbose: be moderately quiet (-q is full quiet)
1395 * -nc --no-clobber: abort if exists, neither download to FILE.n nor overwrite FILE
1396 * -nH --no-host-directories: wget -r http://host/ won't create host/
1397 * -np --no-parent
1398 * "n::" above says that we accept -n[ARG].
1399 * Specifying "n:" would be a bug: "-n ARG" would eat ARG!
1400 */
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001401 "\0"
1402 "-1" /* at least one URL */
1403 IF_FEATURE_WGET_LONG_OPTIONS(":\xff::") /* --header is a list */
Denys Vlasenko036585a2017-08-08 16:38:18 +02001404 LONGOPTS
Denys Vlasenko92e1b082015-10-20 21:51:52 +02001405 , &G.fname_out, &G.dir_prefix,
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001406 &G.proxy_flag, &G.user_agent,
1407 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
Denys Vlasenko92e1b082015-10-20 21:51:52 +02001408 NULL, /* -t RETRIES */
1409 NULL /* -n[ARG] */
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001410 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
1411 IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data)
1412 );
Denys Vlasenko2972e2c2016-10-04 04:23:09 +02001413#if 0 /* option bits debug */
1414 if (option_mask32 & WGET_OPT_RETRIES) bb_error_msg("-t NUM");
1415 if (option_mask32 & WGET_OPT_nsomething) bb_error_msg("-nsomething");
1416 if (option_mask32 & WGET_OPT_HEADER) bb_error_msg("--header");
1417 if (option_mask32 & WGET_OPT_POST_DATA) bb_error_msg("--post-data");
1418 if (option_mask32 & WGET_OPT_SPIDER) bb_error_msg("--spider");
1419 exit(0);
1420#endif
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001421 argv += optind;
1422
1423#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1424 if (headers_llist) {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001425 int size = 0;
1426 char *hdr;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001427 llist_t *ll = headers_llist;
1428 while (ll) {
1429 size += strlen(ll->data) + 2;
1430 ll = ll->link;
1431 }
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001432 G.extra_headers = hdr = xmalloc(size + 1);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001433 while (headers_llist) {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001434 int bit;
1435 const char *words;
1436
1437 size = sprintf(hdr, "%s\r\n",
1438 (char*)llist_pop(&headers_llist));
1439 /* a bit like index_in_substrings but don't match full key */
1440 bit = 1;
1441 words = wget_user_headers;
1442 while (*words) {
1443 if (strstr(hdr, words) == hdr) {
1444 G.user_headers |= bit;
1445 break;
1446 }
1447 bit <<= 1;
1448 words += strlen(words) + 1;
1449 }
1450 hdr += size;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001451 }
1452 }
1453#endif
1454
Denys Vlasenko2384a352011-02-15 00:58:36 +01001455 G.output_fd = -1;
1456 G.o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
1457 if (G.fname_out) { /* -O FILE ? */
1458 if (LONE_DASH(G.fname_out)) { /* -O - ? */
1459 G.output_fd = 1;
1460 option_mask32 &= ~WGET_OPT_CONTINUE;
1461 }
1462 /* compat with wget: -O FILE can overwrite */
1463 G.o_flags = O_WRONLY | O_CREAT | O_TRUNC;
1464 }
1465
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001466 while (*argv)
Pere Orga53695632011-02-16 20:09:36 +01001467 download_one_url(*argv++);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001468
Denys Vlasenko28556b92011-02-15 11:03:53 +01001469 if (G.output_fd >= 0)
1470 xclose(G.output_fd);
1471
Guilherme Maciel Ferreira840ef172013-10-16 14:43:30 +02001472#if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_WGET_LONG_OPTIONS
1473 free(G.extra_headers);
1474#endif
1475 FINI_G();
1476
Pere Orga53695632011-02-16 20:09:36 +01001477 return EXIT_SUCCESS;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001478}