blob: f744ea2de2b17d2e3593ae9c3c7b5cde4a95226e [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 Vlasenkoe2e55b02011-03-21 00:37:05 +010011
12//usage:#define wget_trivial_usage
13//usage: IF_FEATURE_WGET_LONG_OPTIONS(
14//usage: "[-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]\n"
15//usage: " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n"
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +020016/* Since we ignore these opts, we don't show them in --help */
17/* //usage: " [--no-check-certificate] [--no-cache]" */
18//usage: " [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
Denys Vlasenkoe2e55b02011-03-21 00:37:05 +010019//usage: )
20//usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS(
21//usage: "[-csq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]"
22//usage: IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
23//usage: )
24//usage:#define wget_full_usage "\n\n"
25//usage: "Retrieve files via HTTP or FTP\n"
Denys Vlasenkoe2e55b02011-03-21 00:37:05 +010026//usage: "\n -s Spider mode - only check file existence"
27//usage: "\n -c Continue retrieval of aborted transfer"
28//usage: "\n -q Quiet"
29//usage: "\n -P DIR Save to DIR (default .)"
30//usage: IF_FEATURE_WGET_TIMEOUT(
31//usage: "\n -T SEC Network read timeout is SEC seconds"
32//usage: )
33//usage: "\n -O FILE Save to FILE ('-' for stdout)"
34//usage: "\n -U STR Use STR for User-Agent header"
35//usage: "\n -Y Use proxy ('on' or 'off')"
36
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000037#include "libbb.h"
Denis Vlasenkoa552eeb2006-09-26 09:22:12 +000038
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +020039#if 0
40# define log_io(...) bb_error_msg(__VA_ARGS__)
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +010041# define SENDFMT(fp, fmt, ...) \
42 do { \
43 log_io("> " fmt, ##__VA_ARGS__); \
44 fprintf(fp, fmt, ##__VA_ARGS__); \
45 } while (0);
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +020046#else
47# define log_io(...) ((void)0)
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +010048# define SENDFMT(fp, fmt, ...) fprintf(fp, fmt, ##__VA_ARGS__)
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +020049#endif
Denys Vlasenkof836f012011-02-10 23:02:28 +010050
51
Eric Andersen79757c92001-04-05 21:45:54 +000052struct host_info {
Denys Vlasenkoa3661092011-02-13 02:33:11 +010053 char *allocated;
Denis Vlasenko818322b2007-09-24 18:27:04 +000054 const char *path;
Denys Vlasenkod353bff2014-02-03 14:09:42 +010055 char *user;
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +010056 const char *protocol;
Denis Vlasenko818322b2007-09-24 18:27:04 +000057 char *host;
58 int port;
Eric Andersen79757c92001-04-05 21:45:54 +000059};
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +010060static const char P_FTP[] = "ftp";
61static const char P_HTTP[] = "http";
62static const char P_HTTPS[] = "https";
Eric Andersen79757c92001-04-05 21:45:54 +000063
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +010064#if ENABLE_FEATURE_WGET_LONG_OPTIONS
65/* User-specified headers prevent using our corresponding built-in headers. */
66enum {
67 HDR_HOST = (1<<0),
68 HDR_USER_AGENT = (1<<1),
69 HDR_RANGE = (1<<2),
70 HDR_AUTH = (1<<3) * ENABLE_FEATURE_WGET_AUTHENTICATION,
71 HDR_PROXY_AUTH = (1<<4) * ENABLE_FEATURE_WGET_AUTHENTICATION,
72};
73static const char wget_user_headers[] ALIGN1 =
74 "Host:\0"
75 "User-Agent:\0"
76 "Range:\0"
77# if ENABLE_FEATURE_WGET_AUTHENTICATION
78 "Authorization:\0"
79 "Proxy-Authorization:\0"
80# endif
81 ;
82# define USR_HEADER_HOST (G.user_headers & HDR_HOST)
83# define USR_HEADER_USER_AGENT (G.user_headers & HDR_USER_AGENT)
84# define USR_HEADER_RANGE (G.user_headers & HDR_RANGE)
85# define USR_HEADER_AUTH (G.user_headers & HDR_AUTH)
86# define USR_HEADER_PROXY_AUTH (G.user_headers & HDR_PROXY_AUTH)
87#else /* No long options, no user-headers :( */
88# define USR_HEADER_HOST 0
89# define USR_HEADER_USER_AGENT 0
90# define USR_HEADER_RANGE 0
91# define USR_HEADER_AUTH 0
92# define USR_HEADER_PROXY_AUTH 0
93#endif
Denis Vlasenko77105632007-09-24 15:04:00 +000094
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +020095/* Globals */
Denis Vlasenko77105632007-09-24 15:04:00 +000096struct globals {
97 off_t content_len; /* Content-length of the file */
98 off_t beg_range; /* Range at which continue begins */
99#if ENABLE_FEATURE_WGET_STATUSBAR
Denis Vlasenko77105632007-09-24 15:04:00 +0000100 off_t transferred; /* Number of bytes transferred so far */
101 const char *curfile; /* Name of current file being transferred */
Magnus Dammf5914992009-11-08 16:34:43 +0100102 bb_progress_t pmt;
Denis Vlasenko77105632007-09-24 15:04:00 +0000103#endif
Denys Vlasenko982e87f2013-07-30 11:52:58 +0200104 char *dir_prefix;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100105#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Denys Vlasenko982e87f2013-07-30 11:52:58 +0200106 char *post_data;
107 char *extra_headers;
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100108 unsigned char user_headers; /* Headers mentioned by the user */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100109#endif
Denys Vlasenko982e87f2013-07-30 11:52:58 +0200110 char *fname_out; /* where to direct output (-O) */
111 const char *proxy_flag; /* Use proxies if env vars are set */
112 const char *user_agent; /* "User-Agent" header field */
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200113#if ENABLE_FEATURE_WGET_TIMEOUT
114 unsigned timeout_seconds;
Lauri Kasanend074b412013-10-12 21:47:07 +0200115 bool connecting;
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200116#endif
Denys Vlasenko2384a352011-02-15 00:58:36 +0100117 int output_fd;
118 int o_flags;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200119 smallint chunked; /* chunked transfer encoding */
120 smallint got_clen; /* got content-length: from server */
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100121 /* Local downloads do benefit from big buffer.
122 * With 512 byte buffer, it was measured to be
123 * an order of magnitude slower than with big one.
124 */
125 uint64_t just_to_align_next_member;
126 char wget_buf[CONFIG_FEATURE_COPYBUF_KB*1024];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100127} FIX_ALIASING;
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100128#define G (*ptr_to_globals)
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200129#define INIT_G() do { \
Denys Vlasenko982e87f2013-07-30 11:52:58 +0200130 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200131} while (0)
Guilherme Maciel Ferreira840ef172013-10-16 14:43:30 +0200132#define FINI_G() do { \
133 FREE_PTR_TO_GLOBALS(); \
134} while (0)
Denis Vlasenko77105632007-09-24 15:04:00 +0000135
136
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200137/* Must match option string! */
138enum {
139 WGET_OPT_CONTINUE = (1 << 0),
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200140 WGET_OPT_SPIDER = (1 << 1),
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200141 WGET_OPT_QUIET = (1 << 2),
142 WGET_OPT_OUTNAME = (1 << 3),
143 WGET_OPT_PREFIX = (1 << 4),
144 WGET_OPT_PROXY = (1 << 5),
145 WGET_OPT_USER_AGENT = (1 << 6),
146 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7),
147 WGET_OPT_RETRIES = (1 << 8),
148 WGET_OPT_PASSIVE = (1 << 9),
149 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
150 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
151};
152
153enum {
154 PROGRESS_START = -1,
155 PROGRESS_END = 0,
156 PROGRESS_BUMP = 1,
157};
Denis Vlasenko9cade082006-11-21 10:43:02 +0000158#if ENABLE_FEATURE_WGET_STATUSBAR
Denis Vlasenko00d84172008-11-24 07:34:42 +0000159static void progress_meter(int flag)
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000160{
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200161 if (option_mask32 & WGET_OPT_QUIET)
162 return;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000163
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200164 if (flag == PROGRESS_START)
Denys Vlasenkod55e1392011-02-11 18:56:13 +0100165 bb_progress_init(&G.pmt, G.curfile);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000166
Denys Vlasenko2384a352011-02-15 00:58:36 +0100167 bb_progress_update(&G.pmt,
168 G.beg_range,
169 G.transferred,
170 (G.chunked || !G.got_clen) ? 0 : G.beg_range + G.transferred + G.content_len
171 );
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000172
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200173 if (flag == PROGRESS_END) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100174 bb_progress_free(&G.pmt);
Denys Vlasenko19ced5c2010-06-06 21:53:09 +0200175 bb_putchar_stderr('\n');
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100176 G.transferred = 0;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000177 }
178}
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200179#else
Denis Vlasenko00d84172008-11-24 07:34:42 +0000180static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
Eric Andersenb520e082000-10-03 00:21:45 +0000181#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000182
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000183
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200184/* IPv6 knows scoped address types i.e. link and site local addresses. Link
185 * local addresses can have a scope identifier to specify the
186 * interface/link an address is valid on (e.g. fe80::1%eth0). This scope
187 * identifier is only valid on a single node.
188 *
189 * RFC 4007 says that the scope identifier MUST NOT be sent across the wire,
190 * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers
191 * in the Host header as invalid requests, see
192 * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
193 */
194static void strip_ipv6_scope_id(char *host)
195{
196 char *scope, *cp;
197
198 /* bbox wget actually handles IPv6 addresses without [], like
199 * wget "http://::1/xxx", but this is not standard.
200 * To save code, _here_ we do not support it. */
201
202 if (host[0] != '[')
203 return; /* not IPv6 */
204
205 scope = strchr(host, '%');
206 if (!scope)
207 return;
208
209 /* Remove the IPv6 zone identifier from the host address */
210 cp = strchr(host, ']');
211 if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
212 /* malformed address (not "[xx]:nn" or "[xx]") */
213 return;
214 }
215
216 /* cp points to "]...", scope points to "%eth0]..." */
217 overlapping_strcpy(scope, cp);
218}
219
Denis Vlasenko9cade082006-11-21 10:43:02 +0000220#if ENABLE_FEATURE_WGET_AUTHENTICATION
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100221/* Base64-encode character string. */
222static char *base64enc(const char *str)
Denis Vlasenko3526a132006-09-09 12:20:57 +0000223{
Denis Vlasenko12d21292007-06-27 21:40:07 +0000224 unsigned len = strlen(str);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100225 if (len > sizeof(G.wget_buf)/4*3 - 10) /* paranoia */
226 len = sizeof(G.wget_buf)/4*3 - 10;
227 bb_uuencode(G.wget_buf, str, len, bb_uuenc_tbl_base64);
228 return G.wget_buf;
Eric Andersen79757c92001-04-05 21:45:54 +0000229}
230#endif
231
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200232static char* sanitize_string(char *s)
233{
234 unsigned char *p = (void *) s;
235 while (*p >= ' ')
236 p++;
237 *p = '\0';
238 return s;
239}
240
Lauri Kasanend074b412013-10-12 21:47:07 +0200241#if ENABLE_FEATURE_WGET_TIMEOUT
242static void alarm_handler(int sig UNUSED_PARAM)
243{
244 /* This is theoretically unsafe (uses stdio and malloc in signal handler) */
245 if (G.connecting)
246 bb_error_msg_and_die("download timed out");
247}
248#endif
249
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000250static FILE *open_socket(len_and_sockaddr *lsa)
251{
Lauri Kasanend074b412013-10-12 21:47:07 +0200252 int fd;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000253 FILE *fp;
254
Lauri Kasanend074b412013-10-12 21:47:07 +0200255 IF_FEATURE_WGET_TIMEOUT(alarm(G.timeout_seconds); G.connecting = 1;)
256 fd = xconnect_stream(lsa);
257 IF_FEATURE_WGET_TIMEOUT(G.connecting = 0;)
258
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000259 /* glibc 2.4 seems to try seeking on it - ??! */
260 /* hopefully it understands what ESPIPE means... */
Lauri Kasanend074b412013-10-12 21:47:07 +0200261 fp = fdopen(fd, "r+");
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100262 if (!fp)
Denys Vlasenkodee0fc92011-02-10 10:01:49 +0100263 bb_perror_msg_and_die(bb_msg_memory_exhausted);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000264
265 return fp;
266}
267
Denys Vlasenkof836f012011-02-10 23:02:28 +0100268/* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */
Lauri Kasanend074b412013-10-12 21:47:07 +0200269/* FIXME: does not respect FEATURE_WGET_TIMEOUT and -T N: */
Denys Vlasenkof836f012011-02-10 23:02:28 +0100270static char fgets_and_trim(FILE *fp)
271{
272 char c;
273 char *buf_ptr;
274
275 if (fgets(G.wget_buf, sizeof(G.wget_buf) - 1, fp) == NULL)
276 bb_perror_msg_and_die("error getting response");
277
278 buf_ptr = strchrnul(G.wget_buf, '\n');
279 c = *buf_ptr;
280 *buf_ptr = '\0';
281 buf_ptr = strchrnul(G.wget_buf, '\r');
282 *buf_ptr = '\0';
283
284 log_io("< %s", G.wget_buf);
285
286 return c;
287}
288
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100289static int ftpcmd(const char *s1, const char *s2, FILE *fp)
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000290{
291 int result;
292 if (s1) {
Denys Vlasenkof836f012011-02-10 23:02:28 +0100293 if (!s2)
294 s2 = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000295 fprintf(fp, "%s%s\r\n", s1, s2);
296 fflush(fp);
Denys Vlasenkof836f012011-02-10 23:02:28 +0100297 log_io("> %s%s", s1, s2);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000298 }
299
300 do {
Denys Vlasenkof836f012011-02-10 23:02:28 +0100301 fgets_and_trim(fp);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100302 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000303
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100304 G.wget_buf[3] = '\0';
305 result = xatoi_positive(G.wget_buf);
306 G.wget_buf[3] = ' ';
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000307 return result;
308}
309
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100310static void parse_url(const char *src_url, struct host_info *h)
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000311{
312 char *url, *p, *sp;
313
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100314 free(h->allocated);
315 h->allocated = url = xstrdup(src_url);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000316
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100317 h->protocol = P_FTP;
318 p = strstr(url, "://");
319 if (p) {
320 *p = '\0';
321 h->host = p + 3;
322 if (strcmp(url, P_FTP) == 0) {
323 h->port = bb_lookup_port(P_FTP, "tcp", 21);
324 } else
325 if (strcmp(url, P_HTTPS) == 0) {
326 h->port = bb_lookup_port(P_HTTPS, "tcp", 443);
327 h->protocol = P_HTTPS;
328 } else
329 if (strcmp(url, P_HTTP) == 0) {
Lauri Kasanen4967a412013-12-17 19:03:41 +0100330 http:
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100331 h->port = bb_lookup_port(P_HTTP, "tcp", 80);
332 h->protocol = P_HTTP;
333 } else {
334 *p = ':';
335 bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
336 }
337 } else {
Lauri Kasanen4967a412013-12-17 19:03:41 +0100338 // GNU wget is user-friendly and falls back to http://
339 h->host = url;
340 goto http;
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100341 }
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000342
343 // FYI:
344 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
345 // 'GET /?var=a/b HTTP 1.0'
346 // and saves 'index.html?var=a%2Fb' (we save 'b')
347 // wget 'http://busybox.net?login=john@doe':
348 // request: 'GET /?login=john@doe HTTP/1.0'
349 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
350 // wget 'http://busybox.net#test/test':
351 // request: 'GET / HTTP/1.0'
352 // saves: 'index.html' (we save 'test')
353 //
354 // We also don't add unique .N suffix if file exists...
355 sp = strchr(h->host, '/');
356 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
357 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
358 if (!sp) {
Denis Vlasenko818322b2007-09-24 18:27:04 +0000359 h->path = "";
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000360 } else if (*sp == '/') {
361 *sp = '\0';
362 h->path = sp + 1;
363 } else { // '#' or '?'
364 // http://busybox.net?login=john@doe is a valid URL
365 // memmove converts to:
366 // http:/busybox.nett?login=john@doe...
Denis Vlasenko818322b2007-09-24 18:27:04 +0000367 memmove(h->host - 1, h->host, sp - h->host);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000368 h->host--;
369 sp[-1] = '\0';
370 h->path = sp;
371 }
372
373 sp = strrchr(h->host, '@');
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000374 if (sp != NULL) {
Denys Vlasenkodd1061b2011-09-11 21:04:02 +0200375 // URL-decode "user:password" string before base64-encoding:
376 // wget http://test:my%20pass@example.com should send
377 // Authorization: Basic dGVzdDpteSBwYXNz
378 // which decodes to "test:my pass".
379 // Standard wget and curl do this too.
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000380 *sp = '\0';
Denys Vlasenkod353bff2014-02-03 14:09:42 +0100381 free(h->user);
382 h->user = xstrdup(percent_decode_in_place(h->host, /*strict:*/ 0));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000383 h->host = sp + 1;
384 }
Denys Vlasenkod353bff2014-02-03 14:09:42 +0100385 /* else: h->user remains NULL, or as set by original request
386 * before redirect (if we are here after a redirect).
387 */
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000388}
389
Denys Vlasenkof836f012011-02-10 23:02:28 +0100390static char *gethdr(FILE *fp)
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000391{
392 char *s, *hdrval;
393 int c;
394
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000395 /* retrieve header line */
Denys Vlasenkof836f012011-02-10 23:02:28 +0100396 c = fgets_and_trim(fp);
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000397
Denys Vlasenkof836f012011-02-10 23:02:28 +0100398 /* end of the headers? */
399 if (G.wget_buf[0] == '\0')
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000400 return NULL;
401
402 /* convert the header name to lower case */
Denys Vlasenkoea267d52013-07-01 15:01:50 +0200403 for (s = G.wget_buf; isalnum(*s) || *s == '-' || *s == '.' || *s == '_'; ++s) {
404 /*
405 * No-op for 20-3f and 60-7f. "0-9a-z-." are in these ranges.
406 * 40-5f range ("@A-Z[\]^_") maps to 60-7f.
407 * "A-Z" maps to "a-z".
408 * "@[\]" can't occur in header names.
409 * "^_" maps to "~,DEL" (which is wrong).
410 * "^" was never seen yet, "_" was seen from web.archive.org
411 * (x-archive-orig-x_commoncrawl_Signature: HEXSTRING).
412 */
Denys Vlasenkof836f012011-02-10 23:02:28 +0100413 *s |= 0x20;
Denys Vlasenko48363312010-04-04 15:29:32 +0200414 }
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000415
416 /* verify we are at the end of the header name */
417 if (*s != ':')
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100418 bb_error_msg_and_die("bad header line: %s", sanitize_string(G.wget_buf));
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000419
420 /* locate the start of the header value */
421 *s++ = '\0';
422 hdrval = skip_whitespace(s);
423
Denys Vlasenkof836f012011-02-10 23:02:28 +0100424 if (c != '\n') {
425 /* Rats! The buffer isn't big enough to hold the entire header value */
426 while (c = getc(fp), c != EOF && c != '\n')
427 continue;
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000428 }
429
Denis Vlasenko47ddd012007-09-24 18:24:17 +0000430 return hdrval;
431}
432
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200433static void reset_beg_range_to_zero(void)
434{
Denys Vlasenko61441242012-06-17 19:52:25 +0200435 bb_error_msg("restart failed");
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200436 G.beg_range = 0;
437 xlseek(G.output_fd, 0, SEEK_SET);
Denys Vlasenko61441242012-06-17 19:52:25 +0200438 /* Done at the end instead: */
439 /* ftruncate(G.output_fd, 0); */
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200440}
441
Denys Vlasenko7f432802009-06-28 01:02:24 +0200442static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
443{
Denys Vlasenko7f432802009-06-28 01:02:24 +0200444 FILE *sfp;
445 char *str;
446 int port;
447
448 if (!target->user)
449 target->user = xstrdup("anonymous:busybox@");
450
451 sfp = open_socket(lsa);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100452 if (ftpcmd(NULL, NULL, sfp) != 220)
453 bb_error_msg_and_die("%s", sanitize_string(G.wget_buf + 4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200454
455 /*
456 * Splitting username:password pair,
457 * trying to log in
458 */
459 str = strchr(target->user, ':');
460 if (str)
461 *str++ = '\0';
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100462 switch (ftpcmd("USER ", target->user, sfp)) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200463 case 230:
464 break;
465 case 331:
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100466 if (ftpcmd("PASS ", str, sfp) == 230)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200467 break;
468 /* fall through (failed login) */
469 default:
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100470 bb_error_msg_and_die("ftp login: %s", sanitize_string(G.wget_buf + 4));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200471 }
472
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100473 ftpcmd("TYPE I", NULL, sfp);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200474
475 /*
476 * Querying file size
477 */
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100478 if (ftpcmd("SIZE ", target->path, sfp) == 213) {
479 G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10);
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100480 if (G.content_len < 0 || errno) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200481 bb_error_msg_and_die("SIZE value is garbage");
482 }
483 G.got_clen = 1;
484 }
485
486 /*
487 * Entering passive mode
488 */
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100489 if (ftpcmd("PASV", NULL, sfp) != 227) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200490 pasv_error:
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100491 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(G.wget_buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200492 }
493 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
494 // Server's IP is N1.N2.N3.N4 (we ignore it)
495 // Server's port for data connection is P1*256+P2
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100496 str = strrchr(G.wget_buf, ')');
Denys Vlasenko7f432802009-06-28 01:02:24 +0200497 if (str) str[0] = '\0';
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100498 str = strrchr(G.wget_buf, ',');
Denys Vlasenko7f432802009-06-28 01:02:24 +0200499 if (!str) goto pasv_error;
500 port = xatou_range(str+1, 0, 255);
501 *str = '\0';
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100502 str = strrchr(G.wget_buf, ',');
Denys Vlasenko7f432802009-06-28 01:02:24 +0200503 if (!str) goto pasv_error;
504 port += xatou_range(str+1, 0, 255) * 256;
Denys Vlasenkoca183112011-04-07 17:52:20 +0200505 set_nport(&lsa->u.sa, htons(port));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200506
507 *dfpp = open_socket(lsa);
508
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +0200509 if (G.beg_range != 0) {
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100510 sprintf(G.wget_buf, "REST %"OFF_FMT"u", G.beg_range);
511 if (ftpcmd(G.wget_buf, NULL, sfp) == 350)
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100512 G.content_len -= G.beg_range;
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200513 else
514 reset_beg_range_to_zero();
Denys Vlasenko7f432802009-06-28 01:02:24 +0200515 }
516
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100517 if (ftpcmd("RETR ", target->path, sfp) > 150)
518 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(G.wget_buf));
Denys Vlasenko7f432802009-06-28 01:02:24 +0200519
520 return sfp;
521}
522
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100523static int spawn_https_helper(const char *host, unsigned port)
524{
525 char *allocated = NULL;
526 int sp[2];
527 int pid;
528
529 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0)
530 /* Kernel can have AF_UNIX support disabled */
531 bb_perror_msg_and_die("socketpair");
532
533 if (!strchr(host, ':'))
534 host = allocated = xasprintf("%s:%u", host, port);
535
536 pid = BB_MMU ? xfork() : xvfork();
537 if (pid == 0) {
538 /* Child */
539 char *argv[6];
540
541 close(sp[0]);
542 xmove_fd(sp[1], 0);
543 xdup2(0, 1);
544 /*
545 * TODO: develop a tiny ssl/tls helper (using matrixssl?),
546 * try to exec it here before falling back to big fat openssl.
547 */
548 /*
549 * openssl s_client -quiet -connect www.kernel.org:443 2>/dev/null
550 * It prints some debug stuff on stderr, don't know how to suppress it.
551 * Work around by dev-nulling stderr. We lose all error messages :(
552 */
553 xmove_fd(2, 3);
554 xopen("/dev/null", O_RDWR);
555 argv[0] = (char*)"openssl";
556 argv[1] = (char*)"s_client";
557 argv[2] = (char*)"-quiet";
558 argv[3] = (char*)"-connect";
559 argv[4] = (char*)host;
560 argv[5] = NULL;
561 BB_EXECVP(argv[0], argv);
562 xmove_fd(3, 2);
563 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
564 /* notreached */
565 }
566
Denys Vlasenko53315572014-02-23 23:39:47 +0100567 /* Parent */
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100568 free(allocated);
569 close(sp[1]);
570 return sp[0];
571}
572
Denys Vlasenko53315572014-02-23 23:39:47 +0100573/* See networking/ssl_helper/README */
574#define SSL_HELPER 0
575
576#if SSL_HELPER
577static void spawn_https_helper1(int network_fd)
578{
579 int sp[2];
580 int pid;
581
582 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0)
583 /* Kernel can have AF_UNIX support disabled */
584 bb_perror_msg_and_die("socketpair");
585
586 pid = BB_MMU ? xfork() : xvfork();
587 if (pid == 0) {
588 /* Child */
589 char *argv[3];
590
591 close(sp[0]);
592 xmove_fd(sp[1], 0);
593 xdup2(0, 1);
594 xmove_fd(network_fd, 3);
595 /*
596 * A simple ssl/tls helper
597 */
598 argv[0] = (char*)"ssl_helper";
599 argv[1] = (char*)"-d3";
600 argv[2] = NULL;
601 BB_EXECVP(argv[0], argv);
602 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
603 /* notreached */
604 }
605
606 /* Parent */
607 close(sp[1]);
608 xmove_fd(sp[0], network_fd);
609}
610#endif
611
Denys Vlasenko2384a352011-02-15 00:58:36 +0100612static void NOINLINE retrieve_file_data(FILE *dfp)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200613{
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200614#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
615# if ENABLE_FEATURE_WGET_TIMEOUT
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200616 unsigned second_cnt = G.timeout_seconds;
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200617# endif
618 struct pollfd polldata;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200619
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200620 polldata.fd = fileno(dfp);
621 polldata.events = POLLIN | POLLPRI;
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200622#endif
623 progress_meter(PROGRESS_START);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200624
625 if (G.chunked)
626 goto get_clen;
627
628 /* Loops only if chunked */
629 while (1) {
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100630
631#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
632 /* Must use nonblocking I/O, otherwise fread will loop
633 * and *block* until it reads full buffer,
634 * which messes up progress bar and/or timeout logic.
635 * Because of nonblocking I/O, we need to dance
636 * very carefully around EAGAIN. See explanation at
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200637 * clearerr() calls.
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100638 */
639 ndelay_on(polldata.fd);
640#endif
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100641 while (1) {
Denys Vlasenko7f432802009-06-28 01:02:24 +0200642 int n;
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100643 unsigned rdsz;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200644
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200645#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
Denys Vlasenko8766a792011-02-11 21:42:00 +0100646 /* fread internally uses read loop, which in our case
647 * is usually exited when we get EAGAIN.
648 * In this case, libc sets error marker on the stream.
649 * Need to clear it before next fread to avoid possible
650 * rare false positive ferror below. Rare because usually
651 * fread gets more than zero bytes, and we don't fall
652 * into if (n <= 0) ...
653 */
654 clearerr(dfp);
Denys Vlasenkof9af3752011-02-11 22:01:33 +0100655#endif
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200656 errno = 0;
657 rdsz = sizeof(G.wget_buf);
658 if (G.got_clen) {
659 if (G.content_len < (off_t)sizeof(G.wget_buf)) {
660 if ((int)G.content_len <= 0)
661 break;
662 rdsz = (unsigned)G.content_len;
663 }
664 }
Denys Vlasenko0fac2f72011-02-10 09:55:05 +0100665 n = fread(G.wget_buf, 1, rdsz, dfp);
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200666
667 if (n > 0) {
668 xwrite(G.output_fd, G.wget_buf, n);
669#if ENABLE_FEATURE_WGET_STATUSBAR
670 G.transferred += n;
671#endif
672 if (G.got_clen) {
673 G.content_len -= n;
674 if (G.content_len == 0)
675 break;
676 }
677#if ENABLE_FEATURE_WGET_TIMEOUT
678 second_cnt = G.timeout_seconds;
679#endif
Denys Vlasenkofaa9e942014-03-27 16:50:29 +0100680 goto bump;
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200681 }
682
683 /* n <= 0.
684 * man fread:
Denys Vlasenko8766a792011-02-11 21:42:00 +0100685 * If error occurs, or EOF is reached, the return value
686 * is a short item count (or zero).
687 * fread does not distinguish between EOF and error.
688 */
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200689 if (errno != EAGAIN) {
690 if (ferror(dfp)) {
691 progress_meter(PROGRESS_END);
Denys Vlasenko8766a792011-02-11 21:42:00 +0100692 bb_perror_msg_and_die(bb_msg_read_error);
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200693 }
Denys Vlasenko8766a792011-02-11 21:42:00 +0100694 break; /* EOF, not error */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200695 }
Denys Vlasenko8766a792011-02-11 21:42:00 +0100696
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200697#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
698 /* It was EAGAIN. There is no data. Wait up to one second
699 * then abort if timed out, or update the bar and try reading again.
700 */
701 if (safe_poll(&polldata, 1, 1000) == 0) {
702# if ENABLE_FEATURE_WGET_TIMEOUT
703 if (second_cnt != 0 && --second_cnt == 0) {
704 progress_meter(PROGRESS_END);
705 bb_error_msg_and_die("download timed out");
706 }
707# endif
708 /* We used to loop back to poll here,
709 * but there is no great harm in letting fread
710 * to try reading anyway.
711 */
712 }
Denys Vlasenkofaa9e942014-03-27 16:50:29 +0100713#endif
714 bump:
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200715 /* Need to do it _every_ second for "stalled" indicator
716 * to be shown properly.
717 */
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200718 progress_meter(PROGRESS_BUMP);
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200719 } /* while (reading data) */
720
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100721#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
722 clearerr(dfp);
Denys Vlasenko88ad9da2011-02-11 23:06:21 +0100723 ndelay_off(polldata.fd); /* else fgets can get very unhappy */
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100724#endif
Denys Vlasenko7f432802009-06-28 01:02:24 +0200725 if (!G.chunked)
726 break;
727
Denys Vlasenkoc60f4462011-02-11 22:23:23 +0100728 fgets_and_trim(dfp); /* Eat empty line */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200729 get_clen:
Denys Vlasenkof836f012011-02-10 23:02:28 +0100730 fgets_and_trim(dfp);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100731 G.content_len = STRTOOFF(G.wget_buf, NULL, 16);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200732 /* FIXME: error check? */
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100733 if (G.content_len == 0)
Denys Vlasenko7f432802009-06-28 01:02:24 +0200734 break; /* all done! */
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +0100735 G.got_clen = 1;
Denys Vlasenkob7812ce2012-09-03 12:49:30 +0200736 /*
737 * Note that fgets may result in some data being buffered in dfp.
738 * We loop back to fread, which will retrieve this data.
739 * Also note that code has to be arranged so that fread
740 * is done _before_ one-second poll wait - poll doesn't know
741 * about stdio buffering and can result in spurious one second waits!
742 */
Denys Vlasenko7f432802009-06-28 01:02:24 +0200743 }
744
Denys Vlasenko61441242012-06-17 19:52:25 +0200745 /* If -c failed, we restart from the beginning,
746 * but we do not truncate file then, we do it only now, at the end.
747 * This lets user to ^C if his 99% complete 10 GB file download
748 * failed to restart *without* losing the almost complete file.
749 */
750 {
751 off_t pos = lseek(G.output_fd, 0, SEEK_CUR);
752 if (pos != (off_t)-1)
753 ftruncate(G.output_fd, pos);
754 }
755
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100756 /* Draw full bar and free its resources */
Denys Vlasenko2384a352011-02-15 00:58:36 +0100757 G.chunked = 0; /* makes it show 100% even for chunked download */
758 G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */
Bradley M. Kuhnc97131c2010-08-08 02:51:20 +0200759 progress_meter(PROGRESS_END);
Denys Vlasenko7f432802009-06-28 01:02:24 +0200760}
761
Pere Orga53695632011-02-16 20:09:36 +0100762static void download_one_url(const char *url)
Eric Andersen96700832000-09-04 15:15:55 +0000763{
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100764 bool use_proxy; /* Use proxies if env vars are set */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200765 int redir_limit;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100766 len_and_sockaddr *lsa;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200767 FILE *sfp; /* socket to web/ftp server */
Denis Vlasenkoa36535b2007-09-27 15:07:23 +0000768 FILE *dfp; /* socket to ftp server (data) */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100769 char *proxy = NULL;
770 char *fname_out_alloc;
Denys Vlasenko93b4a602011-12-18 05:11:56 +0100771 char *redirected_path = NULL;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100772 struct host_info server;
773 struct host_info target;
Denis Vlasenko77105632007-09-24 15:04:00 +0000774
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100775 server.allocated = NULL;
776 target.allocated = NULL;
777 server.user = NULL;
Vladimir Dronnikovbe168b12009-10-05 02:18:01 +0200778 target.user = NULL;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100779
780 parse_url(url, &target);
Eric Andersen79757c92001-04-05 21:45:54 +0000781
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000782 /* Use the proxy if necessary */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100783 use_proxy = (strcmp(G.proxy_flag, "off") != 0);
Glenn L McGrathf1c4b112004-02-22 00:27:34 +0000784 if (use_proxy) {
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100785 proxy = getenv(target.protocol == P_FTP ? "ftp_proxy" : "http_proxy");
786//FIXME: what if protocol is https? Ok to use http_proxy?
Denys Vlasenko2384a352011-02-15 00:58:36 +0100787 use_proxy = (proxy && proxy[0]);
788 if (use_proxy)
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +0000789 parse_url(proxy, &server);
Robert Griebld7760112002-05-14 23:36:45 +0000790 }
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200791 if (!use_proxy) {
792 server.port = target.port;
793 if (ENABLE_FEATURE_IPV6) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100794 //free(server.allocated); - can't be non-NULL
795 server.host = server.allocated = xstrdup(target.host);
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +0200796 } else {
797 server.host = target.host;
798 }
799 }
800
801 if (ENABLE_FEATURE_IPV6)
802 strip_ipv6_scope_id(target.host);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000803
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100804 /* If there was no -O FILE, guess output filename */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100805 fname_out_alloc = NULL;
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +0100806 if (!(option_mask32 & WGET_OPT_OUTNAME)) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100807 G.fname_out = bb_get_last_path_component_nostrip(target.path);
Denis Vlasenko818322b2007-09-24 18:27:04 +0000808 /* handle "wget http://kernel.org//" */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100809 if (G.fname_out[0] == '/' || !G.fname_out[0])
810 G.fname_out = (char*)"index.html";
Denis Vlasenko818322b2007-09-24 18:27:04 +0000811 /* -P DIR is considered only if there was no -O FILE */
Denys Vlasenkoaacd4482012-06-17 20:21:30 +0200812 if (G.dir_prefix)
813 G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out);
Denys Vlasenko625f2182011-03-21 00:29:37 +0100814 else {
Denys Vlasenkoaacd4482012-06-17 20:21:30 +0200815 /* redirects may free target.path later, need to make a copy */
816 G.fname_out = fname_out_alloc = xstrdup(G.fname_out);
Denys Vlasenko625f2182011-03-21 00:29:37 +0100817 }
Eric Andersen29edd002000-12-09 16:55:35 +0000818 }
Denis Vlasenko818322b2007-09-24 18:27:04 +0000819#if ENABLE_FEATURE_WGET_STATUSBAR
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100820 G.curfile = bb_get_last_path_component_nostrip(G.fname_out);
Denis Vlasenko818322b2007-09-24 18:27:04 +0000821#endif
822
Bernhard Reutner-Fischer7e8a53a2007-04-10 09:37:29 +0000823 /* Determine where to start transfer */
Denys Vlasenko2384a352011-02-15 00:58:36 +0100824 G.beg_range = 0;
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100825 if (option_mask32 & WGET_OPT_CONTINUE) {
Denys Vlasenko2384a352011-02-15 00:58:36 +0100826 G.output_fd = open(G.fname_out, O_WRONLY);
827 if (G.output_fd >= 0) {
828 G.beg_range = xlseek(G.output_fd, 0, SEEK_END);
Denis Vlasenkoa94554d2006-09-23 17:49:09 +0000829 }
830 /* File doesn't exist. We do not create file here yet.
Denys Vlasenkoa84eadf2011-02-12 23:40:31 +0100831 * We are not sure it exists on remote side */
Eric Andersen96700832000-09-04 15:15:55 +0000832 }
833
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200834 redir_limit = 5;
835 resolve_lsa:
Denis Vlasenko42823d52007-02-04 02:39:08 +0000836 lsa = xhost2sockaddr(server.host, server.port);
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100837 if (!(option_mask32 & WGET_OPT_QUIET)) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200838 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
839 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
840 free(s);
Eric Andersene6dc4392003-10-31 09:31:46 +0000841 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200842 establish_session:
Denys Vlasenko2384a352011-02-15 00:58:36 +0100843 /*G.content_len = 0; - redundant, got_clen = 0 is enough */
844 G.got_clen = 0;
845 G.chunked = 0;
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100846 if (use_proxy || target.protocol != P_FTP) {
Eric Andersen79757c92001-04-05 21:45:54 +0000847 /*
848 * HTTP session
849 */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200850 char *str;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200851 int status;
Denys Vlasenko7f432802009-06-28 01:02:24 +0200852
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100853 /* Open socket to http(s) server */
854 if (target.protocol == P_HTTPS) {
Denys Vlasenko53315572014-02-23 23:39:47 +0100855/* openssl-based helper
Denys Vlasenkoa2796222014-02-24 17:20:40 +0100856 * Inconvenient API since we can't give it an open fd
Denys Vlasenko53315572014-02-23 23:39:47 +0100857 */
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100858 int fd = spawn_https_helper(server.host, server.port);
859 sfp = fdopen(fd, "r+");
860 if (!sfp)
861 bb_perror_msg_and_die(bb_msg_memory_exhausted);
862 } else
863 sfp = open_socket(lsa);
Denys Vlasenko53315572014-02-23 23:39:47 +0100864#if SSL_HELPER
865 if (target.protocol == P_HTTPS)
866 spawn_https_helper1(fileno(sfp));
867#endif
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200868 /* Send HTTP request */
869 if (use_proxy) {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100870 SENDFMT(sfp, "GET %s://%s/%s HTTP/1.1\r\n",
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100871 target.protocol, target.host,
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200872 target.path);
873 } else {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100874 SENDFMT(sfp, "%s /%s HTTP/1.1\r\n",
Denys Vlasenko8b7e8ae2014-02-22 14:12:29 +0100875 (option_mask32 & WGET_OPT_POST_DATA) ? "POST" : "GET",
876 target.path);
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200877 }
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100878 if (!USR_HEADER_HOST)
879 SENDFMT(sfp, "Host: %s\r\n", target.host);
880 if (!USR_HEADER_USER_AGENT)
881 SENDFMT(sfp, "User-Agent: %s\r\n", G.user_agent);
Eric Andersen79757c92001-04-05 21:45:54 +0000882
Denys Vlasenko9213a552011-02-10 13:23:45 +0100883 /* Ask server to close the connection as soon as we are done
884 * (IOW: we do not intend to send more requests)
885 */
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100886 SENDFMT(sfp, "Connection: close\r\n");
Denys Vlasenko9213a552011-02-10 13:23:45 +0100887
Denis Vlasenko9cade082006-11-21 10:43:02 +0000888#if ENABLE_FEATURE_WGET_AUTHENTICATION
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100889 if (target.user && !USR_HEADER_AUTH) {
890 SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100891 base64enc(target.user));
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200892 }
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100893 if (use_proxy && server.user && !USR_HEADER_PROXY_AUTH) {
894 SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n",
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100895 base64enc(server.user));
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200896 }
Eric Andersen79757c92001-04-05 21:45:54 +0000897#endif
898
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100899 if (G.beg_range != 0 && !USR_HEADER_RANGE)
900 SENDFMT(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
Denys Vlasenko9213a552011-02-10 13:23:45 +0100901
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000902#if ENABLE_FEATURE_WGET_LONG_OPTIONS
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100903 if (G.extra_headers) {
904 log_io(G.extra_headers);
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100905 fputs(G.extra_headers, sfp);
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100906 }
Denis Vlasenko5a2ad692009-03-04 14:13:37 +0000907
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100908 if (option_mask32 & WGET_OPT_POST_DATA) {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100909 SENDFMT(sfp,
Denys Vlasenko9213a552011-02-10 13:23:45 +0100910 "Content-Type: application/x-www-form-urlencoded\r\n"
911 "Content-Length: %u\r\n"
912 "\r\n"
913 "%s",
Vitaly Magerya700fbc32011-03-27 22:33:13 +0200914 (int) strlen(G.post_data), G.post_data
Denys Vlasenko9213a552011-02-10 13:23:45 +0100915 );
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200916 } else
Denis Vlasenkoc8400a22006-10-25 00:33:44 +0000917#endif
Denys Vlasenko9213a552011-02-10 13:23:45 +0100918 {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +0100919 SENDFMT(sfp, "\r\n");
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200920 }
Eric Andersen79757c92001-04-05 21:45:54 +0000921
Nguyễn Thái Ngọc Duyebec11d2010-09-23 15:18:41 +0200922 fflush(sfp);
923
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200924 /*
925 * Retrieve HTTP response line and check for "200" status code.
926 */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000927 read_response:
Denys Vlasenkof836f012011-02-10 23:02:28 +0100928 fgets_and_trim(sfp);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000929
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100930 str = G.wget_buf;
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200931 str = skip_non_whitespace(str);
932 str = skip_whitespace(str);
933 // FIXME: no error check
934 // xatou wouldn't work: "200 OK"
935 status = atoi(str);
936 switch (status) {
937 case 0:
938 case 100:
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100939 while (gethdr(sfp) != NULL)
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200940 /* eat all remaining headers */;
941 goto read_response;
942 case 200:
Denis Vlasenko50b5cac2008-06-22 16:28:02 +0000943/*
944Response 204 doesn't say "null file", it says "metadata
945has changed but data didn't":
946
947"10.2.5 204 No Content
948The server has fulfilled the request but does not need to return
949an entity-body, and might want to return updated metainformation.
950The response MAY include new or updated metainformation in the form
951of entity-headers, which if present SHOULD be associated with
952the requested variant.
953
954If the client is a user agent, it SHOULD NOT change its document
955view from that which caused the request to be sent. This response
956is primarily intended to allow input for actions to take place
957without causing a change to the user agent's active document view,
958although any new or updated metainformation SHOULD be applied
959to the document currently in the user agent's active view.
960
961The 204 response MUST NOT include a message-body, and thus
962is always terminated by the first empty line after the header fields."
963
964However, in real world it was observed that some web servers
965(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
966*/
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200967 case 204:
Denys Vlasenkobf146b82012-06-13 17:31:07 +0200968 if (G.beg_range != 0) {
969 /* "Range:..." was not honored by the server.
970 * Restart download from the beginning.
971 */
972 reset_beg_range_to_zero();
973 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200974 break;
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200975 case 300: /* redirection */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200976 case 301:
977 case 302:
978 case 303:
979 break;
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +0200980 case 206: /* Partial Content */
981 if (G.beg_range != 0)
982 /* "Range:..." worked. Good. */
Denis Vlasenko023b57d2006-10-15 17:05:55 +0000983 break;
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +0200984 /* Partial Content even though we did not ask for it??? */
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200985 /* fall through */
986 default:
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +0100987 bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf));
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200988 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000989
Denys Vlasenkof1fab092009-06-28 03:33:57 +0200990 /*
991 * Retrieve HTTP headers.
992 */
Denys Vlasenkoa3661092011-02-13 02:33:11 +0100993 while ((str = gethdr(sfp)) != NULL) {
994 static const char keywords[] ALIGN1 =
995 "content-length\0""transfer-encoding\0""location\0";
996 enum {
997 KEY_content_length = 1, KEY_transfer_encoding, KEY_location
998 };
Matthijs van de Water0d586662009-08-22 20:19:48 +0200999 smalluint key;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001000
1001 /* gethdr converted "FOO:" string to lowercase */
1002
Matthijs van de Water0d586662009-08-22 20:19:48 +02001003 /* strip trailing whitespace */
1004 char *s = strchrnul(str, '\0') - 1;
1005 while (s >= str && (*s == ' ' || *s == '\t')) {
1006 *s = '\0';
1007 s--;
1008 }
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +01001009 key = index_in_strings(keywords, G.wget_buf) + 1;
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001010 if (key == KEY_content_length) {
Denys Vlasenkoa3aa3e32009-12-11 12:36:10 +01001011 G.content_len = BB_STRTOOFF(str, NULL, 10);
1012 if (G.content_len < 0 || errno) {
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001013 bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
Eric Andersen79757c92001-04-05 21:45:54 +00001014 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001015 G.got_clen = 1;
1016 continue;
1017 }
1018 if (key == KEY_transfer_encoding) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001019 if (strcmp(str_tolower(str), "chunked") != 0)
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001020 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001021 G.chunked = 1;
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001022 }
1023 if (key == KEY_location && status >= 300) {
1024 if (--redir_limit == 0)
1025 bb_error_msg_and_die("too many redirections");
1026 fclose(sfp);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001027 if (str[0] == '/') {
Denys Vlasenko93b4a602011-12-18 05:11:56 +01001028 free(redirected_path);
1029 target.path = redirected_path = xstrdup(str+1);
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001030 /* lsa stays the same: it's on the same server */
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001031 } else {
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001032 parse_url(str, &target);
1033 if (!use_proxy) {
Denys Vlasenkod353bff2014-02-03 14:09:42 +01001034 /* server.user remains untouched */
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001035 free(server.allocated);
Pere Orga57b49092011-02-14 23:56:07 +01001036 server.allocated = NULL;
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001037 server.host = target.host;
Denys Vlasenko7d5ddf12009-06-30 20:36:27 +02001038 /* strip_ipv6_scope_id(target.host); - no! */
1039 /* we assume remote never gives us IPv6 addr with scope id */
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001040 server.port = target.port;
Denis Vlasenko6536a9b2007-01-12 10:35:23 +00001041 free(lsa);
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001042 goto resolve_lsa;
1043 } /* else: lsa stays the same: we use proxy */
Eric Andersen79757c92001-04-05 21:45:54 +00001044 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001045 goto establish_session;
Eric Andersen79757c92001-04-05 21:45:54 +00001046 }
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001047 }
1048// if (status >= 300)
1049// bb_error_msg_and_die("bad redirection (no Location: header from server)");
Eric Andersenc7bda1c2004-03-15 08:29:22 +00001050
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001051 /* For HTTP, data is pumped over the same connection */
Eric Andersen79757c92001-04-05 21:45:54 +00001052 dfp = sfp;
Denis Vlasenko96e9d3c2006-10-07 14:28:55 +00001053 } else {
Eric Andersen79757c92001-04-05 21:45:54 +00001054 /*
1055 * FTP session
1056 */
Denys Vlasenko7f432802009-06-28 01:02:24 +02001057 sfp = prepare_ftp_session(&dfp, &target, lsa);
Eric Andersen96700832000-09-04 15:15:55 +00001058 }
Denis Vlasenko77105632007-09-24 15:04:00 +00001059
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001060 free(lsa);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001061
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001062 if (!(option_mask32 & WGET_OPT_SPIDER)) {
Denys Vlasenko2384a352011-02-15 00:58:36 +01001063 if (G.output_fd < 0)
1064 G.output_fd = xopen(G.fname_out, G.o_flags);
1065 retrieve_file_data(dfp);
1066 if (!(option_mask32 & WGET_OPT_OUTNAME)) {
1067 xclose(G.output_fd);
1068 G.output_fd = -1;
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001069 }
Bernhard Reutner-Fischer2e75dcc2007-04-05 10:31:47 +00001070 }
Eric Andersen79757c92001-04-05 21:45:54 +00001071
Denys Vlasenkof1fab092009-06-28 03:33:57 +02001072 if (dfp != sfp) {
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001073 /* It's ftp. Close data connection properly */
Eric Andersen79757c92001-04-05 21:45:54 +00001074 fclose(dfp);
Denys Vlasenkodf4e16c2011-02-10 06:29:06 +01001075 if (ftpcmd(NULL, NULL, sfp) != 226)
1076 bb_error_msg_and_die("ftp error: %s", sanitize_string(G.wget_buf + 4));
1077 /* ftpcmd("QUIT", NULL, sfp); - why bother? */
Eric Andersen79757c92001-04-05 21:45:54 +00001078 }
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001079 fclose(sfp);
Denis Vlasenko77105632007-09-24 15:04:00 +00001080
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001081 free(server.allocated);
1082 free(target.allocated);
Denys Vlasenkod353bff2014-02-03 14:09:42 +01001083 free(server.user);
1084 free(target.user);
Denys Vlasenko9a5b7f62011-02-13 02:49:43 +01001085 free(fname_out_alloc);
Denys Vlasenko93b4a602011-12-18 05:11:56 +01001086 free(redirected_path);
Eric Andersen96700832000-09-04 15:15:55 +00001087}
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001088
1089int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1090int wget_main(int argc UNUSED_PARAM, char **argv)
1091{
1092#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1093 static const char wget_longopts[] ALIGN1 =
1094 /* name, has_arg, val */
1095 "continue\0" No_argument "c"
1096//FIXME: -s isn't --spider, it's --save-headers!
1097 "spider\0" No_argument "s"
1098 "quiet\0" No_argument "q"
1099 "output-document\0" Required_argument "O"
1100 "directory-prefix\0" Required_argument "P"
1101 "proxy\0" Required_argument "Y"
1102 "user-agent\0" Required_argument "U"
1103#if ENABLE_FEATURE_WGET_TIMEOUT
1104 "timeout\0" Required_argument "T"
1105#endif
1106 /* Ignored: */
1107 // "tries\0" Required_argument "t"
1108 /* Ignored (we always use PASV): */
1109 "passive-ftp\0" No_argument "\xff"
1110 "header\0" Required_argument "\xfe"
1111 "post-data\0" Required_argument "\xfd"
1112 /* Ignored (we don't do ssl) */
1113 "no-check-certificate\0" No_argument "\xfc"
Vladimir Dronnikovf5abc782012-06-13 17:29:41 +02001114 /* Ignored (we don't support caching) */
1115 "no-cache\0" No_argument "\xfb"
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001116 ;
1117#endif
1118
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001119#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1120 llist_t *headers_llist = NULL;
1121#endif
1122
1123 INIT_G();
1124
Lauri Kasanend074b412013-10-12 21:47:07 +02001125#if ENABLE_FEATURE_WGET_TIMEOUT
1126 G.timeout_seconds = 900;
1127 signal(SIGALRM, alarm_handler);
1128#endif
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001129 G.proxy_flag = "on"; /* use proxies if env vars are set */
1130 G.user_agent = "Wget"; /* "User-Agent" header field */
1131
1132#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1133 applet_long_options = wget_longopts;
1134#endif
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001135 opt_complementary = "-1"
1136 IF_FEATURE_WGET_TIMEOUT(":T+")
1137 IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001138 getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
1139 &G.fname_out, &G.dir_prefix,
1140 &G.proxy_flag, &G.user_agent,
1141 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
1142 NULL /* -t RETRIES */
1143 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
1144 IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data)
1145 );
1146 argv += optind;
1147
1148#if ENABLE_FEATURE_WGET_LONG_OPTIONS
1149 if (headers_llist) {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001150 int size = 0;
1151 char *hdr;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001152 llist_t *ll = headers_llist;
1153 while (ll) {
1154 size += strlen(ll->data) + 2;
1155 ll = ll->link;
1156 }
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001157 G.extra_headers = hdr = xmalloc(size + 1);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001158 while (headers_llist) {
Bernhard Reutner-Fischerd7bfee12015-02-18 20:41:02 +01001159 int bit;
1160 const char *words;
1161
1162 size = sprintf(hdr, "%s\r\n",
1163 (char*)llist_pop(&headers_llist));
1164 /* a bit like index_in_substrings but don't match full key */
1165 bit = 1;
1166 words = wget_user_headers;
1167 while (*words) {
1168 if (strstr(hdr, words) == hdr) {
1169 G.user_headers |= bit;
1170 break;
1171 }
1172 bit <<= 1;
1173 words += strlen(words) + 1;
1174 }
1175 hdr += size;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001176 }
1177 }
1178#endif
1179
Denys Vlasenko2384a352011-02-15 00:58:36 +01001180 G.output_fd = -1;
1181 G.o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
1182 if (G.fname_out) { /* -O FILE ? */
1183 if (LONE_DASH(G.fname_out)) { /* -O - ? */
1184 G.output_fd = 1;
1185 option_mask32 &= ~WGET_OPT_CONTINUE;
1186 }
1187 /* compat with wget: -O FILE can overwrite */
1188 G.o_flags = O_WRONLY | O_CREAT | O_TRUNC;
1189 }
1190
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001191 while (*argv)
Pere Orga53695632011-02-16 20:09:36 +01001192 download_one_url(*argv++);
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001193
Denys Vlasenko28556b92011-02-15 11:03:53 +01001194 if (G.output_fd >= 0)
1195 xclose(G.output_fd);
1196
Guilherme Maciel Ferreira840ef172013-10-16 14:43:30 +02001197#if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_WGET_LONG_OPTIONS
1198 free(G.extra_headers);
1199#endif
1200 FINI_G();
1201
Pere Orga53695632011-02-16 20:09:36 +01001202 return EXIT_SUCCESS;
Denys Vlasenkoa3661092011-02-13 02:33:11 +01001203}