- char buf[512];
- struct host_info server, target;
- len_and_sockaddr *lsa;
- int n, status;
- int port;
- int try = 5;
- unsigned opt;
- char *str;
- char *proxy = 0;
- char *dir_prefix = NULL;
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
- char *extra_headers = NULL;
- llist_t *headers_llist = NULL;
-#endif
-
- FILE *sfp = NULL; /* socket to web/ftp server */
- FILE *dfp = NULL; /* socket to ftp server (data) */
- char *fname_out = NULL; /* where to direct output (-O) */
- bool got_clen = 0; /* got content-length: from server */
- int output_fd = -1;
- bool use_proxy = 1; /* Use proxies if env vars are set */
- const char *proxy_flag = "on"; /* Use proxies if env vars are set */
- const char *user_agent = "Wget";/* "User-Agent" header field */
- static const char * const keywords[] = {
- "content-length", "transfer-encoding", "chunked", "location", NULL
- };
- enum {
- KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
- };
- enum {
- WGET_OPT_CONTINUE = 0x1,
- WGET_OPT_SPIDER = 0x2,
- WGET_OPT_QUIET = 0x4,
- WGET_OPT_OUTNAME = 0x8,
- WGET_OPT_PREFIX = 0x10,
- WGET_OPT_PROXY = 0x20,
- WGET_OPT_USER_AGENT = 0x40,
- WGET_OPT_PASSIVE = 0x80,
- WGET_OPT_HEADER = 0x100,
- };
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
- static const char wget_longopts[] =
- /* name, has_arg, val */
- "continue\0" No_argument "c"
- "spider\0" No_argument "s"
- "quiet\0" No_argument "q"
- "output-document\0" Required_argument "O"
- "directory-prefix\0" Required_argument "P"
- "proxy\0" Required_argument "Y"
- "user-agent\0" Required_argument "U"
- "passive-ftp\0" No_argument "\xff"
- "header\0" Required_argument "\xfe"
- "\0";
- applet_long_options = wget_longopts;
-#endif
- /* server.allocated = target.allocated = NULL; */
- opt_complementary = "-1" USE_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
- opt = getopt32(argc, argv, "csqO:P:Y:U:",
- &fname_out, &dir_prefix,
- &proxy_flag, &user_agent
- USE_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
- );
- if (strcmp(proxy_flag, "off") == 0) {
- /* Use the proxy if necessary */
- use_proxy = 0;
- }
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
- if (headers_llist) {
- int size = 1;
- char *cp;
- llist_t *ll = headers_llist;
- while (ll) {
- size += strlen(ll->data) + 2;
- ll = ll->link;
- }
- extra_headers = cp = xmalloc(size);
- while (headers_llist) {
- cp += sprintf(cp, "%s\r\n", headers_llist->data);
- headers_llist = headers_llist->link;
- }
- }
-#endif
-
- parse_url(argv[optind], &target);
- server.host = target.host;
- server.port = target.port;
-
- /* Use the proxy if necessary */
- if (use_proxy) {
- proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
- if (proxy && *proxy) {
- parse_url(proxy, &server);
- } else {
- use_proxy = 0;
- }
- }
-
- /* Guess an output filename */
- if (!fname_out) {
- // Dirty hack. Needed because bb_get_last_path_component
- // will destroy trailing / by storing '\0' in last byte!
- if (!last_char_is(target.path, '/')) {
- fname_out = bb_get_last_path_component(target.path);
-#if ENABLE_FEATURE_WGET_STATUSBAR
- curfile = fname_out;
-#endif
- }
- if (!fname_out || !fname_out[0]) {
- /* bb_get_last_path_component writes
- * to last '/' only. We don't have one here... */
- fname_out = (char*)"index.html";
-#if ENABLE_FEATURE_WGET_STATUSBAR
- curfile = fname_out;
-#endif
- }
- if (dir_prefix != NULL)
- fname_out = concat_path_file(dir_prefix, fname_out);
-#if ENABLE_FEATURE_WGET_STATUSBAR
- } else {
- curfile = bb_get_last_path_component(fname_out);
-#endif
- }
- /* Impossible?
- if ((opt & WGET_OPT_CONTINUE) && !fname_out)
- bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)"); */
-
- /* Determine where to start transfer */
- if (LONE_DASH(fname_out)) {
- output_fd = 1;
- opt &= ~WGET_OPT_CONTINUE;
- }
- if (opt & WGET_OPT_CONTINUE) {
- output_fd = open(fname_out, O_WRONLY);
- if (output_fd >= 0) {
- beg_range = xlseek(output_fd, 0, SEEK_END);
- }
- /* File doesn't exist. We do not create file here yet.
- We are not sure it exists on remove side */
- }
-
- /* We want to do exactly _one_ DNS lookup, since some
- * sites (i.e. ftp.us.debian.org) use round-robin DNS
- * and we want to connect to only one IP... */
- lsa = xhost2sockaddr(server.host, server.port);
- if (!(opt & WGET_OPT_QUIET)) {
- fprintf(stderr, "Connecting to %s (%s)\n", server.host,
- xmalloc_sockaddr2dotted(&lsa->sa, lsa->len));
- /* We leak result of xmalloc_sockaddr2dotted */
- }
-
- if (use_proxy || !target.is_ftp) {
- /*
- * HTTP session
- */
- do {
- got_clen = chunked = 0;
-
- if (!--try)
- bb_error_msg_and_die("too many redirections");
-
- /* Open socket to http server */
- if (sfp) fclose(sfp);
- sfp = open_socket(lsa);
-
- /* Send HTTP request. */
- if (use_proxy) {
- fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
- target.is_ftp ? "f" : "ht", target.host,
- target.path);
- } else {
- fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
- }
-
- fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
- target.host, user_agent);
-
-#if ENABLE_FEATURE_WGET_AUTHENTICATION
- if (target.user) {
- fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
- base64enc_512(buf, target.user));
- }
- if (use_proxy && server.user) {
- fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
- base64enc_512(buf, server.user));
- }
-#endif
-
- if (beg_range)
- fprintf(sfp, "Range: bytes=%"OFF_FMT"d-\r\n", beg_range);
-#if ENABLE_FEATURE_WGET_LONG_OPTIONS
- if (extra_headers)
- fputs(extra_headers, sfp);
-#endif
- fprintf(sfp, "Connection: close\r\n\r\n");
-
- /*
- * Retrieve HTTP response line and check for "200" status code.
- */
- read_response:
- if (fgets(buf, sizeof(buf), sfp) == NULL)
- bb_error_msg_and_die("no response from server");
-
- str = buf;
- str = skip_non_whitespace(str);
- str = skip_whitespace(str);
- // FIXME: no error check
- // xatou wouldn't work: "200 OK"
- status = atoi(str);
- switch (status) {
- case 0:
- case 100:
- while (gethdr(buf, sizeof(buf), sfp, &n) != NULL)
- /* eat all remaining headers */;
- goto read_response;
- case 200:
- break;
- case 300: /* redirection */
- case 301:
- case 302:
- case 303:
- break;
- case 206:
- if (beg_range)
- break;
- /*FALLTHRU*/
- default:
- /* Show first line only and kill any ESC tricks */
- buf[strcspn(buf, "\n\r\x1b")] = '\0';
- bb_error_msg_and_die("server returned error: %s", buf);
- }
-
- /*
- * Retrieve HTTP headers.
- */
- while ((str = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {
- /* gethdr did already convert the "FOO:" string to lowercase */
- smalluint key = index_in_str_array(keywords, *&buf) + 1;
- if (key == KEY_content_length) {
- content_len = BB_STRTOOFF(str, NULL, 10);
- if (errno || content_len < 0) {
- bb_error_msg_and_die("content-length %s is garbage", str);
- }
- got_clen = 1;
- continue;
- }
- if (key == KEY_transfer_encoding) {
- if (index_in_str_array(keywords, str_tolower(str)) + 1 != KEY_chunked)
- bb_error_msg_and_die("server wants to do %s transfer encoding", str);
- chunked = got_clen = 1;
- }
- if (key == KEY_location) {
- if (str[0] == '/')
- /* free(target.allocated); */
- target.path = /* target.allocated = */ xstrdup(str+1);
- else {
- parse_url(str, &target);
- if (use_proxy == 0) {
- server.host = target.host;
- server.port = target.port;
- }
- free(lsa);
- lsa = xhost2sockaddr(server.host, server.port);
- break;
- }
- }
- }
- } while (status >= 300);
-
- dfp = sfp;