+ if (option_mask32 & WGET_OPT_POST_DATA)
+ fprintf(sfp, "POST /%s HTTP/1.1\r\n", 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, G.user_agent);
+
+ /* Ask server to close the connection as soon as we are done
+ * (IOW: we do not intend to send more requests)
+ */
+ fprintf(sfp, "Connection: close\r\n");
+
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
+ if (target.user) {
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
+ base64enc(target.user));
+ }
+ if (use_proxy && server.user) {
+ fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
+ base64enc(server.user));
+ }
+#endif
+
+ if (G.beg_range != 0)
+ fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
+
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+ if (G.extra_headers)
+ fputs(G.extra_headers, sfp);
+
+ if (option_mask32 & WGET_OPT_POST_DATA) {
+ fprintf(sfp,
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: %u\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(G.post_data), G.post_data
+ );
+ } else
+#endif
+ {
+ fprintf(sfp, "\r\n");
+ }
+
+ fflush(sfp);
+
+ /*
+ * Retrieve HTTP response line and check for "200" status code.
+ */
+ read_response:
+ fgets_and_trim(sfp);
+
+ str = G.wget_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(sfp) != NULL)
+ /* eat all remaining headers */;
+ goto read_response;
+ case 200:
+/*
+Response 204 doesn't say "null file", it says "metadata
+has changed but data didn't":
+
+"10.2.5 204 No Content
+The server has fulfilled the request but does not need to return
+an entity-body, and might want to return updated metainformation.
+The response MAY include new or updated metainformation in the form
+of entity-headers, which if present SHOULD be associated with
+the requested variant.
+
+If the client is a user agent, it SHOULD NOT change its document
+view from that which caused the request to be sent. This response
+is primarily intended to allow input for actions to take place
+without causing a change to the user agent's active document view,
+although any new or updated metainformation SHOULD be applied
+to the document currently in the user agent's active view.
+
+The 204 response MUST NOT include a message-body, and thus
+is always terminated by the first empty line after the header fields."
+
+However, in real world it was observed that some web servers
+(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
+*/
+ case 204:
+ if (G.beg_range != 0) {
+ /* "Range:..." was not honored by the server.
+ * Restart download from the beginning.
+ */
+ reset_beg_range_to_zero();
+ }
+ break;
+ case 300: /* redirection */
+ case 301:
+ case 302:
+ case 303:
+ break;
+ case 206: /* Partial Content */
+ if (G.beg_range != 0)
+ /* "Range:..." worked. Good. */
+ break;
+ /* Partial Content even though we did not ask for it??? */
+ /* fall through */
+ default:
+ bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf));
+ }
+
+ /*
+ * Retrieve HTTP headers.
+ */
+ while ((str = gethdr(sfp)) != NULL) {
+ static const char keywords[] ALIGN1 =
+ "content-length\0""transfer-encoding\0""location\0";
+ enum {
+ KEY_content_length = 1, KEY_transfer_encoding, KEY_location
+ };
+ smalluint key;
+
+ /* gethdr converted "FOO:" string to lowercase */
+
+ /* strip trailing whitespace */
+ char *s = strchrnul(str, '\0') - 1;
+ while (s >= str && (*s == ' ' || *s == '\t')) {
+ *s = '\0';
+ s--;
+ }
+ key = index_in_strings(keywords, G.wget_buf) + 1;
+ if (key == KEY_content_length) {
+ G.content_len = BB_STRTOOFF(str, NULL, 10);
+ if (G.content_len < 0 || errno) {
+ bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
+ }
+ G.got_clen = 1;
+ continue;
+ }
+ if (key == KEY_transfer_encoding) {
+ if (strcmp(str_tolower(str), "chunked") != 0)
+ bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
+ G.chunked = 1;
+ }
+ if (key == KEY_location && status >= 300) {
+ if (--redir_limit == 0)
+ bb_error_msg_and_die("too many redirections");
+ fclose(sfp);
+ if (str[0] == '/') {
+ free(redirected_path);
+ target.path = redirected_path = xstrdup(str+1);
+ /* lsa stays the same: it's on the same server */
+ } else {
+ parse_url(str, &target);
+ if (!use_proxy) {
+ free(server.allocated);
+ server.allocated = NULL;
+ server.host = target.host;
+ /* strip_ipv6_scope_id(target.host); - no! */
+ /* we assume remote never gives us IPv6 addr with scope id */
+ server.port = target.port;
+ free(lsa);
+ goto resolve_lsa;
+ } /* else: lsa stays the same: we use proxy */
+ }
+ goto establish_session;
+ }
+ }
+// if (status >= 300)
+// bb_error_msg_and_die("bad redirection (no Location: header from server)");
+
+ /* For HTTP, data is pumped over the same connection */
+ dfp = sfp;
+
+ } else {
+ /*
+ * FTP session
+ */
+ sfp = prepare_ftp_session(&dfp, &target, lsa);
+ }
+
+ free(lsa);
+
+ if (!(option_mask32 & WGET_OPT_SPIDER)) {
+ if (G.output_fd < 0)
+ G.output_fd = xopen(G.fname_out, G.o_flags);
+ retrieve_file_data(dfp);
+ if (!(option_mask32 & WGET_OPT_OUTNAME)) {
+ xclose(G.output_fd);
+ G.output_fd = -1;