1 /* vi: set sw=4 ts=4: */
3 * wget - retrieve a file using HTTP or FTP
5 * Chip Rosenthal Covad Communications <chip@laserlink.net>
6 * Licensed under GPLv2, see file LICENSE in this source tree.
8 * Copyright (C) 2010 Bradley M. Kuhn <bkuhn@ebb.org>
9 * Kuhn's copyrights are licensed GPLv2-or-later. File as a whole remains GPLv2.
13 //#define log_io(...) bb_error_msg(__VA_ARGS__)
14 #define log_io(...) ((void)0)
29 off_t content_len; /* Content-length of the file */
30 off_t beg_range; /* Range at which continue begins */
31 #if ENABLE_FEATURE_WGET_STATUSBAR
32 off_t transferred; /* Number of bytes transferred so far */
33 const char *curfile; /* Name of current file being transferred */
37 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
41 char *fname_out; /* where to direct output (-O) */
42 const char *proxy_flag; /* Use proxies if env vars are set */
43 const char *user_agent; /* "User-Agent" header field */
44 #if ENABLE_FEATURE_WGET_TIMEOUT
45 unsigned timeout_seconds;
47 smallint chunked; /* chunked transfer encoding */
48 smallint got_clen; /* got content-length: from server */
49 /* Local downloads do benefit from big buffer.
50 * With 512 byte buffer, it was measured to be
51 * an order of magnitude slower than with big one.
53 uint64_t just_to_align_next_member;
54 char wget_buf[CONFIG_FEATURE_COPYBUF_KB*1024];
56 #define G (*ptr_to_globals)
57 #define INIT_G() do { \
58 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
59 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;) \
63 /* Must match option string! */
65 WGET_OPT_CONTINUE = (1 << 0),
66 WGET_OPT_SPIDER = (1 << 1),
67 WGET_OPT_QUIET = (1 << 2),
68 WGET_OPT_OUTNAME = (1 << 3),
69 WGET_OPT_PREFIX = (1 << 4),
70 WGET_OPT_PROXY = (1 << 5),
71 WGET_OPT_USER_AGENT = (1 << 6),
72 WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7),
73 WGET_OPT_RETRIES = (1 << 8),
74 WGET_OPT_PASSIVE = (1 << 9),
75 WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
76 WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
84 #if ENABLE_FEATURE_WGET_STATUSBAR
85 static void progress_meter(int flag)
87 if (option_mask32 & WGET_OPT_QUIET)
90 if (flag == PROGRESS_START)
91 bb_progress_init(&G.pmt, G.curfile);
93 bb_progress_update(&G.pmt, G.beg_range, G.transferred,
94 G.chunked ? 0 : G.beg_range + G.transferred + G.content_len);
96 if (flag == PROGRESS_END) {
97 bb_progress_free(&G.pmt);
98 bb_putchar_stderr('\n');
103 static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
107 /* IPv6 knows scoped address types i.e. link and site local addresses. Link
108 * local addresses can have a scope identifier to specify the
109 * interface/link an address is valid on (e.g. fe80::1%eth0). This scope
110 * identifier is only valid on a single node.
112 * RFC 4007 says that the scope identifier MUST NOT be sent across the wire,
113 * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers
114 * in the Host header as invalid requests, see
115 * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
117 static void strip_ipv6_scope_id(char *host)
121 /* bbox wget actually handles IPv6 addresses without [], like
122 * wget "http://::1/xxx", but this is not standard.
123 * To save code, _here_ we do not support it. */
126 return; /* not IPv6 */
128 scope = strchr(host, '%');
132 /* Remove the IPv6 zone identifier from the host address */
133 cp = strchr(host, ']');
134 if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
135 /* malformed address (not "[xx]:nn" or "[xx]") */
139 /* cp points to "]...", scope points to "%eth0]..." */
140 overlapping_strcpy(scope, cp);
143 #if 0 /* were needed when we used signal-driven progress bar */
144 /* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
145 * and a short count if an eof or non-interrupt error is encountered. */
146 static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
149 char *p = (char*)ptr;
154 ret = fread(p, 1, nmemb, stream);
157 } while (nmemb && ferror(stream) && errno == EINTR);
159 return p - (char*)ptr;
162 /* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
163 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
164 static char *safe_fgets(char *s, int size, FILE *stream)
171 ret = fgets(s, size, stream);
172 } while (ret == NULL && ferror(stream) && errno == EINTR);
178 #if ENABLE_FEATURE_WGET_AUTHENTICATION
179 /* Base64-encode character string. */
180 static char *base64enc(const char *str)
182 unsigned len = strlen(str);
183 if (len > sizeof(G.wget_buf)/4*3 - 10) /* paranoia */
184 len = sizeof(G.wget_buf)/4*3 - 10;
185 bb_uuencode(G.wget_buf, str, len, bb_uuenc_tbl_base64);
190 static char* sanitize_string(char *s)
192 unsigned char *p = (void *) s;
199 static FILE *open_socket(len_and_sockaddr *lsa)
203 /* glibc 2.4 seems to try seeking on it - ??! */
204 /* hopefully it understands what ESPIPE means... */
205 fp = fdopen(xconnect_stream(lsa), "r+");
207 bb_perror_msg_and_die(bb_msg_memory_exhausted);
212 /* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */
213 static char fgets_and_trim(FILE *fp)
218 if (fgets(G.wget_buf, sizeof(G.wget_buf) - 1, fp) == NULL)
219 bb_perror_msg_and_die("error getting response");
221 buf_ptr = strchrnul(G.wget_buf, '\n');
224 buf_ptr = strchrnul(G.wget_buf, '\r');
227 log_io("< %s", G.wget_buf);
232 static int ftpcmd(const char *s1, const char *s2, FILE *fp)
238 fprintf(fp, "%s%s\r\n", s1, s2);
240 log_io("> %s%s", s1, s2);
245 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
247 G.wget_buf[3] = '\0';
248 result = xatoi_positive(G.wget_buf);
253 static void parse_url(const char *src_url, struct host_info *h)
258 h->allocated = url = xstrdup(src_url);
260 if (strncmp(url, "http://", 7) == 0) {
261 h->port = bb_lookup_port("http", "tcp", 80);
264 } else if (strncmp(url, "ftp://", 6) == 0) {
265 h->port = bb_lookup_port("ftp", "tcp", 21);
269 bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
272 // "Real" wget 'http://busybox.net?var=a/b' sends this request:
273 // 'GET /?var=a/b HTTP 1.0'
274 // and saves 'index.html?var=a%2Fb' (we save 'b')
275 // wget 'http://busybox.net?login=john@doe':
276 // request: 'GET /?login=john@doe HTTP/1.0'
277 // saves: 'index.html?login=john@doe' (we save '?login=john@doe')
278 // wget 'http://busybox.net#test/test':
279 // request: 'GET / HTTP/1.0'
280 // saves: 'index.html' (we save 'test')
282 // We also don't add unique .N suffix if file exists...
283 sp = strchr(h->host, '/');
284 p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
285 p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
288 } else if (*sp == '/') {
291 } else { // '#' or '?'
292 // http://busybox.net?login=john@doe is a valid URL
293 // memmove converts to:
294 // http:/busybox.nett?login=john@doe...
295 memmove(h->host - 1, h->host, sp - h->host);
301 // We used to set h->user to NULL here, but this interferes
302 // with handling of code 302 ("object was moved")
304 sp = strrchr(h->host, '@');
314 static char *gethdr(FILE *fp)
321 /* retrieve header line */
322 c = fgets_and_trim(fp);
324 /* end of the headers? */
325 if (G.wget_buf[0] == '\0')
328 /* convert the header name to lower case */
329 for (s = G.wget_buf; isalnum(*s) || *s == '-' || *s == '.'; ++s) {
330 /* tolower for "A-Z", no-op for "0-9a-z-." */
334 /* verify we are at the end of the header name */
336 bb_error_msg_and_die("bad header line: %s", sanitize_string(G.wget_buf));
338 /* locate the start of the header value */
340 hdrval = skip_whitespace(s);
343 /* Rats! The buffer isn't big enough to hold the entire header value */
344 while (c = getc(fp), c != EOF && c != '\n')
351 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
352 static char *URL_escape(const char *str)
354 /* URL encode, see RFC 2396 */
356 char *res = dst = xmalloc(strlen(str) * 3 + 1);
362 /* || strchr("!&'()*-.=_~", c) - more code */
374 || (c >= '0' && c <= '9')
375 || ((c|0x20) >= 'a' && (c|0x20) <= 'z')
382 *dst++ = bb_hexdigits_upcase[c >> 4];
383 *dst++ = bb_hexdigits_upcase[c & 0xf];
389 static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
396 target->user = xstrdup("anonymous:busybox@");
398 sfp = open_socket(lsa);
399 if (ftpcmd(NULL, NULL, sfp) != 220)
400 bb_error_msg_and_die("%s", sanitize_string(G.wget_buf + 4));
403 * Splitting username:password pair,
406 str = strchr(target->user, ':');
409 switch (ftpcmd("USER ", target->user, sfp)) {
413 if (ftpcmd("PASS ", str, sfp) == 230)
415 /* fall through (failed login) */
417 bb_error_msg_and_die("ftp login: %s", sanitize_string(G.wget_buf + 4));
420 ftpcmd("TYPE I", NULL, sfp);
425 if (ftpcmd("SIZE ", target->path, sfp) == 213) {
426 G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10);
427 if (G.content_len < 0 || errno) {
428 bb_error_msg_and_die("SIZE value is garbage");
434 * Entering passive mode
436 if (ftpcmd("PASV", NULL, sfp) != 227) {
438 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(G.wget_buf));
440 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
441 // Server's IP is N1.N2.N3.N4 (we ignore it)
442 // Server's port for data connection is P1*256+P2
443 str = strrchr(G.wget_buf, ')');
444 if (str) str[0] = '\0';
445 str = strrchr(G.wget_buf, ',');
446 if (!str) goto pasv_error;
447 port = xatou_range(str+1, 0, 255);
449 str = strrchr(G.wget_buf, ',');
450 if (!str) goto pasv_error;
451 port += xatou_range(str+1, 0, 255) * 256;
452 set_nport(lsa, htons(port));
454 *dfpp = open_socket(lsa);
457 sprintf(G.wget_buf, "REST %"OFF_FMT"u", G.beg_range);
458 if (ftpcmd(G.wget_buf, NULL, sfp) == 350)
459 G.content_len -= G.beg_range;
462 if (ftpcmd("RETR ", target->path, sfp) > 150)
463 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(G.wget_buf));
468 static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
470 #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
471 # if ENABLE_FEATURE_WGET_TIMEOUT
474 struct pollfd polldata;
476 polldata.fd = fileno(dfp);
477 polldata.events = POLLIN | POLLPRI;
479 progress_meter(PROGRESS_START);
484 /* Loops only if chunked */
487 #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
488 /* Must use nonblocking I/O, otherwise fread will loop
489 * and *block* until it reads full buffer,
490 * which messes up progress bar and/or timeout logic.
491 * Because of nonblocking I/O, we need to dance
492 * very carefully around EAGAIN. See explanation at
495 ndelay_on(polldata.fd);
501 rdsz = sizeof(G.wget_buf);
503 if (G.content_len < (off_t)sizeof(G.wget_buf)) {
504 if ((int)G.content_len <= 0)
506 rdsz = (unsigned)G.content_len;
510 #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
511 # if ENABLE_FEATURE_WGET_TIMEOUT
512 second_cnt = G.timeout_seconds;
515 if (safe_poll(&polldata, 1, 1000) != 0)
516 break; /* error, EOF, or data is available */
517 # if ENABLE_FEATURE_WGET_TIMEOUT
518 if (second_cnt != 0 && --second_cnt == 0) {
519 progress_meter(PROGRESS_END);
520 bb_error_msg_and_die("download timed out");
523 /* Needed for "stalled" indicator */
524 progress_meter(PROGRESS_BUMP);
527 /* fread internally uses read loop, which in our case
528 * is usually exited when we get EAGAIN.
529 * In this case, libc sets error marker on the stream.
530 * Need to clear it before next fread to avoid possible
531 * rare false positive ferror below. Rare because usually
532 * fread gets more than zero bytes, and we don't fall
533 * into if (n <= 0) ...
538 n = fread(G.wget_buf, 1, rdsz, dfp);
540 * If error occurs, or EOF is reached, the return value
541 * is a short item count (or zero).
542 * fread does not distinguish between EOF and error.
545 #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
546 if (errno == EAGAIN) /* poll lied, there is no data? */
550 bb_perror_msg_and_die(bb_msg_read_error);
551 break; /* EOF, not error */
554 xwrite(output_fd, G.wget_buf, n);
556 #if ENABLE_FEATURE_WGET_STATUSBAR
558 progress_meter(PROGRESS_BUMP);
562 if (G.content_len == 0)
566 #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
568 ndelay_off(polldata.fd); /* else fgets can get very unhappy */
573 fgets_and_trim(dfp); /* Eat empty line */
576 G.content_len = STRTOOFF(G.wget_buf, NULL, 16);
577 /* FIXME: error check? */
578 if (G.content_len == 0)
579 break; /* all done! */
583 /* Draw full bar and free its resources */
584 G.chunked = 0; /* makes it show 100% even for chunked download */
585 progress_meter(PROGRESS_END);
588 static int download_one_url(const char *url)
590 bool use_proxy; /* Use proxies if env vars are set */
593 len_and_sockaddr *lsa;
594 FILE *sfp; /* socket to web/ftp server */
595 FILE *dfp; /* socket to ftp server (data) */
597 char *fname_out_alloc;
598 struct host_info server;
599 struct host_info target;
601 server.allocated = NULL;
602 target.allocated = NULL;
606 parse_url(url, &target);
608 /* Use the proxy if necessary */
609 use_proxy = (strcmp(G.proxy_flag, "off") != 0);
611 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
612 if (proxy && proxy[0]) {
613 parse_url(proxy, &server);
619 server.port = target.port;
620 if (ENABLE_FEATURE_IPV6) {
621 //free(server.allocated); - can't be non-NULL
622 server.host = server.allocated = xstrdup(target.host);
624 server.host = target.host;
628 if (ENABLE_FEATURE_IPV6)
629 strip_ipv6_scope_id(target.host);
631 /* If there was no -O FILE, guess output filename */
633 fname_out_alloc = NULL;
635 G.fname_out = bb_get_last_path_component_nostrip(target.path);
636 /* handle "wget http://kernel.org//" */
637 if (G.fname_out[0] == '/' || !G.fname_out[0])
638 G.fname_out = (char*)"index.html";
639 /* -P DIR is considered only if there was no -O FILE */
641 G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out);
643 if (LONE_DASH(G.fname_out)) {
646 option_mask32 &= ~WGET_OPT_CONTINUE;
649 #if ENABLE_FEATURE_WGET_STATUSBAR
650 G.curfile = bb_get_last_path_component_nostrip(G.fname_out);
653 /* Determine where to start transfer */
654 if (option_mask32 & WGET_OPT_CONTINUE) {
655 output_fd = open(G.fname_out, O_WRONLY);
656 if (output_fd >= 0) {
657 G.beg_range = xlseek(output_fd, 0, SEEK_END);
659 /* File doesn't exist. We do not create file here yet.
660 * We are not sure it exists on remote side */
665 lsa = xhost2sockaddr(server.host, server.port);
666 if (!(option_mask32 & WGET_OPT_QUIET)) {
667 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
668 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
672 G.chunked = G.got_clen = 0;
673 if (use_proxy || !target.is_ftp) {
681 /* Open socket to http server */
682 sfp = open_socket(lsa);
684 /* Send HTTP request */
686 fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
687 target.is_ftp ? "f" : "ht", target.host,
690 if (option_mask32 & WGET_OPT_POST_DATA)
691 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
693 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
696 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
697 target.host, G.user_agent);
699 /* Ask server to close the connection as soon as we are done
700 * (IOW: we do not intend to send more requests)
702 fprintf(sfp, "Connection: close\r\n");
704 #if ENABLE_FEATURE_WGET_AUTHENTICATION
706 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
707 base64enc(target.user));
709 if (use_proxy && server.user) {
710 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
711 base64enc(server.user));
716 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
718 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
720 fputs(G.extra_headers, sfp);
722 if (option_mask32 & WGET_OPT_POST_DATA) {
723 char *estr = URL_escape(G.post_data);
725 "Content-Type: application/x-www-form-urlencoded\r\n"
726 "Content-Length: %u\r\n"
729 (int) strlen(estr), estr
735 fprintf(sfp, "\r\n");
741 * Retrieve HTTP response line and check for "200" status code.
747 str = skip_non_whitespace(str);
748 str = skip_whitespace(str);
749 // FIXME: no error check
750 // xatou wouldn't work: "200 OK"
755 while (gethdr(sfp) != NULL)
756 /* eat all remaining headers */;
760 Response 204 doesn't say "null file", it says "metadata
761 has changed but data didn't":
763 "10.2.5 204 No Content
764 The server has fulfilled the request but does not need to return
765 an entity-body, and might want to return updated metainformation.
766 The response MAY include new or updated metainformation in the form
767 of entity-headers, which if present SHOULD be associated with
768 the requested variant.
770 If the client is a user agent, it SHOULD NOT change its document
771 view from that which caused the request to be sent. This response
772 is primarily intended to allow input for actions to take place
773 without causing a change to the user agent's active document view,
774 although any new or updated metainformation SHOULD be applied
775 to the document currently in the user agent's active view.
777 The 204 response MUST NOT include a message-body, and thus
778 is always terminated by the first empty line after the header fields."
780 However, in real world it was observed that some web servers
781 (e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
785 case 300: /* redirection */
795 bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf));
799 * Retrieve HTTP headers.
801 while ((str = gethdr(sfp)) != NULL) {
802 static const char keywords[] ALIGN1 =
803 "content-length\0""transfer-encoding\0""location\0";
805 KEY_content_length = 1, KEY_transfer_encoding, KEY_location
809 /* gethdr converted "FOO:" string to lowercase */
811 /* strip trailing whitespace */
812 char *s = strchrnul(str, '\0') - 1;
813 while (s >= str && (*s == ' ' || *s == '\t')) {
817 key = index_in_strings(keywords, G.wget_buf) + 1;
818 if (key == KEY_content_length) {
819 G.content_len = BB_STRTOOFF(str, NULL, 10);
820 if (G.content_len < 0 || errno) {
821 bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
826 if (key == KEY_transfer_encoding) {
827 if (strcmp(str_tolower(str), "chunked") != 0)
828 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
831 if (key == KEY_location && status >= 300) {
832 if (--redir_limit == 0)
833 bb_error_msg_and_die("too many redirections");
836 free(target.allocated);
837 target.path = target.allocated = xstrdup(str+1);
838 /* lsa stays the same: it's on the same server */
840 parse_url(str, &target);
842 free(server.allocated);
843 server.host = target.host;
844 /* strip_ipv6_scope_id(target.host); - no! */
845 /* we assume remote never gives us IPv6 addr with scope id */
846 server.port = target.port;
849 } /* else: lsa stays the same: we use proxy */
851 goto establish_session;
854 // if (status >= 300)
855 // bb_error_msg_and_die("bad redirection (no Location: header from server)");
857 /* For HTTP, data is pumped over the same connection */
864 sfp = prepare_ftp_session(&dfp, &target, lsa);
868 free(server.allocated);
869 free(target.allocated);
871 if (option_mask32 & WGET_OPT_SPIDER) {
872 free(fname_out_alloc);
878 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
879 /* compat with wget: -O FILE can overwrite */
880 if (option_mask32 & WGET_OPT_OUTNAME)
881 o_flags = O_WRONLY | O_CREAT | O_TRUNC;
882 output_fd = xopen(G.fname_out, o_flags);
885 free(fname_out_alloc);
887 retrieve_file_data(dfp, output_fd);
891 /* It's ftp. Close data connection properly */
893 if (ftpcmd(NULL, NULL, sfp) != 226)
894 bb_error_msg_and_die("ftp error: %s", sanitize_string(G.wget_buf + 4));
895 /* ftpcmd("QUIT", NULL, sfp); - why bother? */
902 int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
903 int wget_main(int argc UNUSED_PARAM, char **argv)
905 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
906 static const char wget_longopts[] ALIGN1 =
907 /* name, has_arg, val */
908 "continue\0" No_argument "c"
909 //FIXME: -s isn't --spider, it's --save-headers!
910 "spider\0" No_argument "s"
911 "quiet\0" No_argument "q"
912 "output-document\0" Required_argument "O"
913 "directory-prefix\0" Required_argument "P"
914 "proxy\0" Required_argument "Y"
915 "user-agent\0" Required_argument "U"
916 #if ENABLE_FEATURE_WGET_TIMEOUT
917 "timeout\0" Required_argument "T"
920 // "tries\0" Required_argument "t"
921 /* Ignored (we always use PASV): */
922 "passive-ftp\0" No_argument "\xff"
923 "header\0" Required_argument "\xfe"
924 "post-data\0" Required_argument "\xfd"
925 /* Ignored (we don't do ssl) */
926 "no-check-certificate\0" No_argument "\xfc"
931 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
932 llist_t *headers_llist = NULL;
937 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;)
938 G.proxy_flag = "on"; /* use proxies if env vars are set */
939 G.user_agent = "Wget"; /* "User-Agent" header field */
941 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
942 applet_long_options = wget_longopts;
944 opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
945 getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
946 &G.fname_out, &G.dir_prefix,
947 &G.proxy_flag, &G.user_agent,
948 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
949 NULL /* -t RETRIES */
950 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
951 IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data)
955 #if ENABLE_FEATURE_WGET_LONG_OPTIONS
959 llist_t *ll = headers_llist;
961 size += strlen(ll->data) + 2;
964 G.extra_headers = cp = xmalloc(size);
965 while (headers_llist) {
966 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
973 exitcode |= download_one_url(*argv++);