X-Git-Url: http://sjero.net/git/?a=blobdiff_plain;f=src%2Fhttp.c;h=669f0fe3a80e5bc41d0ea97768b3e66385f5130e;hb=c9c0e4c6418350d913638d73e0a50bebdb5fd983;hp=02988d20185c1b7316b026d0b05b7049434ca6f8;hpb=4759cf099f7bc07e47075a0e962e4999b2ad429e;p=wget diff --git a/src/http.c b/src/http.c index 02988d20..669f0fe3 100644 --- a/src/http.c +++ b/src/http.c @@ -147,27 +147,20 @@ struct request { extern int numurls; -/* Create a new, empty request. At least request_set_method must be - called before the request can be used. */ +/* Create a new, empty request. Set the request's method and its + arguments. METHOD should be a literal string (or it should outlive + the request) because it will not be freed. ARG will be freed by + request_free. */ static struct request * -request_new (void) +request_new (const char *method, char *arg) { struct request *req = xnew0 (struct request); req->hcapacity = 8; req->headers = xnew_array (struct request_header, req->hcapacity); - return req; -} - -/* Set the request's method and its arguments. METH should be a - literal string (or it should outlive the request) because it will - not be freed. ARG will be freed by request_free. */ - -static void -request_set_method (struct request *req, const char *meth, char *arg) -{ - req->method = meth; + req->method = method; req->arg = arg; + return req; } /* Return the method string passed with the last call to @@ -459,14 +452,14 @@ register_basic_auth_host (const char *hostname) also be written to that file. */ static int -post_file (int sock, const char *file_name, wgint promised_size, FILE *warc_tmp) +body_file_send (int sock, const char *file_name, wgint promised_size, FILE *warc_tmp) { static char chunk[8192]; wgint written = 0; int write_error; FILE *fp; - DEBUGP (("[writing POST file %s ... ", file_name)); + DEBUGP (("[writing BODY file %s ... ", file_name)); fp = fopen (file_name, "rb"); if (!fp) @@ -1726,7 +1719,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy, !opt.http_keep_alive || opt.ignore_length; /* Headers sent when using POST. */ - wgint post_data_size = 0; + wgint body_data_size = 0; bool host_lookup_failed = false; @@ -1758,15 +1751,13 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy, conn = u; /* Prepare the request to send. */ - - req = request_new (); { char *meth_arg; const char *meth = "GET"; if (head_only) meth = "HEAD"; - else if (opt.post_file_name || opt.post_data) - meth = "POST"; + else if (opt.method) + meth = opt.method; /* Use the full path, i.e. one that includes the leading slash and the query string. E.g. if u->path is "foo/bar" and u->query is "param=value", full_path will be "/foo/bar?param=value". */ @@ -1781,7 +1772,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy, meth_arg = xstrdup (u->url); else meth_arg = url_full_path (u); - request_set_method (req, meth, meth_arg); + req = request_new (meth, meth_arg); } request_set_header (req, "Referer", (char *) hs->referer, rel_none); @@ -1852,25 +1843,30 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy, } } - if (opt.post_data || opt.post_file_name) + if (opt.method) { - request_set_header (req, "Content-Type", - "application/x-www-form-urlencoded", rel_none); - if (opt.post_data) - post_data_size = strlen (opt.post_data); - else + + if (opt.body_data || opt.body_file) { - post_data_size = file_size (opt.post_file_name); - if (post_data_size == -1) + request_set_header (req, "Content-Type", + "application/x-www-form-urlencoded", rel_none); + + if (opt.body_data) + body_data_size = strlen (opt.body_data); + else { - logprintf (LOG_NOTQUIET, _("POST data file %s missing: %s\n"), - quote (opt.post_file_name), strerror (errno)); - post_data_size = 0; + body_data_size = file_size (opt.body_file); + if (body_data_size == -1) + { + logprintf (LOG_NOTQUIET, _("BODY data file %s missing: %s\n"), + quote (opt.body_file), strerror (errno)); + return FILEBADFILE; + } } + request_set_header (req, "Content-Length", + xstrdup (number_to_static_string (body_data_size)), + rel_value); } - request_set_header (req, "Content-Length", - xstrdup (number_to_static_string (post_data_size)), - rel_value); } retry_with_auth: @@ -2009,8 +2005,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy, { /* When requesting SSL URLs through proxies, use the CONNECT method to request passthrough. */ - struct request *connreq = request_new (); - request_set_method (connreq, "CONNECT", + struct request *connreq = request_new ("CONNECT", aprintf ("%s:%d", u->host, u->port)); SET_USER_AGENT (connreq); if (proxyauth) @@ -2128,28 +2123,28 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy, if (write_error >= 0) { - if (opt.post_data) + if (opt.body_data) { - DEBUGP (("[POST data: %s]\n", opt.post_data)); - write_error = fd_write (sock, opt.post_data, post_data_size, -1); + DEBUGP (("[BODY data: %s]\n", opt.body_data)); + write_error = fd_write (sock, opt.body_data, body_data_size, -1); if (write_error >= 0 && warc_tmp != NULL) { /* Remember end of headers / start of payload. */ warc_payload_offset = ftello (warc_tmp); /* Write a copy of the data to the WARC record. */ - int warc_tmp_written = fwrite (opt.post_data, 1, post_data_size, warc_tmp); - if (warc_tmp_written != post_data_size) + int warc_tmp_written = fwrite (opt.body_data, 1, body_data_size, warc_tmp); + if (warc_tmp_written != body_data_size) write_error = -2; } - } - else if (opt.post_file_name && post_data_size != 0) + } + else if (opt.body_file && body_data_size != 0) { if (warc_tmp != NULL) - /* Remember end of headers / start of payload. */ + /* Remember end of headers / start of payload */ warc_payload_offset = ftello (warc_tmp); - write_error = post_file (sock, opt.post_file_name, post_data_size, warc_tmp); + write_error = body_file_send (sock, opt.body_file, body_data_size, warc_tmp); } } @@ -2646,12 +2641,35 @@ read_header: /* From RFC2616: The status codes 303 and 307 have been added for servers that wish to make unambiguously clear which kind of reaction is expected of the client. - + A 307 should be redirected using the same method, in other words, a POST should be preserved and not - converted to a GET in that case. */ - if (statcode == HTTP_STATUS_TEMPORARY_REDIRECT) - return NEWLOCATION_KEEP_POST; + converted to a GET in that case. + + With strict adherence to RFC2616, POST requests are not + converted to a GET request on 301 Permanent Redirect + or 302 Temporary Redirect. + + A switch may be provided later based on the HTTPbis draft + that allows clients to convert POST requests to GET + requests on 301 and 302 response codes. */ + switch (statcode) + { + case HTTP_STATUS_TEMPORARY_REDIRECT: + return NEWLOCATION_KEEP_POST; + break; + case HTTP_STATUS_MOVED_PERMANENTLY: + if (opt.method && strcasecmp (opt.method, "post") != 0) + return NEWLOCATION_KEEP_POST; + break; + case HTTP_STATUS_MOVED_TEMPORARILY: + if (opt.method && strcasecmp (opt.method, "post") != 0) + return NEWLOCATION_KEEP_POST; + break; + default: + return NEWLOCATION; + break; + } return NEWLOCATION; } } @@ -3004,6 +3022,11 @@ http_loop (struct url *u, struct url *original_url, char **newloc, if (!opt.spider) send_head_first = false; + /* Send preliminary HEAD request if --content-disposition and -c are used + together. */ + if (opt.content_disposition && opt.always_rest) + send_head_first = true; + /* Send preliminary HEAD request if -N is given and we have an existing * destination file. */ file_name = url_file_name (opt.trustservernames ? u : original_url, NULL); @@ -3114,6 +3137,7 @@ Spider mode enabled. Check if remote file exists.\n")); quote (hstat.local_file), strerror (errno)); case HOSTERR: case CONIMPOSSIBLE: case PROXERR: case AUTHFAILED: case SSLINITFAILED: case CONTNOTSUPPORTED: case VERIFCERTERR: + case FILEBADFILE: /* Fatal errors just return from the function. */ ret = err; goto exit; @@ -3657,7 +3681,7 @@ digest_authentication_encode (const char *au, const char *user, const char *passwd, const char *method, const char *path) { - static char *realm, *opaque, *nonce, *qop; + static char *realm, *opaque, *nonce, *qop, *algorithm; static struct { const char *name; char **variable; @@ -3665,15 +3689,17 @@ digest_authentication_encode (const char *au, const char *user, { "realm", &realm }, { "opaque", &opaque }, { "nonce", &nonce }, - { "qop", &qop } + { "qop", &qop }, + { "algorithm", &algorithm } }; char cnonce[16] = ""; char *res; + int res_len; size_t res_size; param_token name, value; - realm = opaque = nonce = qop = NULL; + realm = opaque = nonce = qop = algorithm = NULL; au += 6; /* skip over `Digest' */ while (extract_param (&au, &name, &value, ',')) @@ -3696,12 +3722,19 @@ digest_authentication_encode (const char *au, const char *user, user = NULL; /* force freeing mem and return */ } + if (algorithm != NULL && strcmp (algorithm,"MD5") && strcmp (algorithm,"MD5-sess")) + { + logprintf (LOG_NOTQUIET, _("Unsupported algorithm '%s'.\n"), algorithm); + user = NULL; /* force freeing mem and return */ + } + if (!realm || !nonce || !user || !passwd || !path || !method) { xfree_null (realm); xfree_null (opaque); xfree_null (nonce); xfree_null (qop); + xfree_null (algorithm); return NULL; } @@ -3720,8 +3753,26 @@ digest_authentication_encode (const char *au, const char *user, md5_process_bytes ((unsigned char *)":", 1, &ctx); md5_process_bytes ((unsigned char *)passwd, strlen (passwd), &ctx); md5_finish_ctx (&ctx, hash); + dump_hash (a1buf, hash); + if (! strcmp (algorithm, "MD5-sess")) + { + /* A1BUF = H( H(user ":" realm ":" password) ":" nonce ":" cnonce ) */ + snprintf (cnonce, sizeof (cnonce), "%08x", random_number(INT_MAX)); + + md5_init_ctx (&ctx); + // md5_process_bytes (hash, MD5_DIGEST_SIZE, &ctx); + md5_process_bytes (a1buf, MD5_DIGEST_SIZE * 2, &ctx); + md5_process_bytes ((unsigned char *)":", 1, &ctx); + md5_process_bytes ((unsigned char *)nonce, strlen (nonce), &ctx); + md5_process_bytes ((unsigned char *)":", 1, &ctx); + md5_process_bytes ((unsigned char *)cnonce, strlen (cnonce), &ctx); + md5_finish_ctx (&ctx, hash); + + dump_hash (a1buf, hash); + } + /* A2BUF = H(method ":" path) */ md5_init_ctx (&ctx); md5_process_bytes ((unsigned char *)method, strlen (method), &ctx); @@ -3730,11 +3781,12 @@ digest_authentication_encode (const char *au, const char *user, md5_finish_ctx (&ctx, hash); dump_hash (a2buf, hash); - if (!strcmp(qop,"auth")) + if (!strcmp(qop, "auth") || !strcmp (qop, "auth-int")) { /* RFC 2617 Digest Access Authentication */ /* generate random hex string */ - snprintf(cnonce, sizeof(cnonce), "%08x", random_number(INT_MAX)); + if (!*cnonce) + snprintf(cnonce, sizeof(cnonce), "%08x", random_number(INT_MAX)); /* RESPONSE_DIGEST = H(A1BUF ":" nonce ":" noncecount ":" clientnonce ":" qop ": " A2BUF) */ md5_init_ctx (&ctx); @@ -3767,20 +3819,21 @@ digest_authentication_encode (const char *au, const char *user, dump_hash (response_digest, hash); res_size = strlen (user) - + strlen (user) + strlen (realm) + strlen (nonce) + strlen (path) + 2 * MD5_DIGEST_SIZE /*strlen (response_digest)*/ + (opaque ? strlen (opaque) : 0) + + (algorithm ? strlen (algorithm) : 0) + (qop ? 128: 0) + + strlen (cnonce) + 128; res = xmalloc (res_size); if (!strcmp(qop,"auth")) { - snprintf (res, res_size, "Digest "\ + res_len = snprintf (res, res_size, "Digest "\ "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\""\ ", qop=auth, nc=00000001, cnonce=\"%s\"", user, realm, nonce, path, response_digest, cnonce); @@ -3788,17 +3841,19 @@ digest_authentication_encode (const char *au, const char *user, } else { - snprintf (res, res_size, "Digest "\ + res_len = snprintf (res, res_size, "Digest "\ "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", user, realm, nonce, path, response_digest); } if (opaque) { - char *p = res + strlen (res); - strcat (p, ", opaque=\""); - strcat (p, opaque); - strcat (p, "\""); + res_len += snprintf(res + res_len, res_size - res_len, ", opaque=\"%s\"", opaque); + } + + if (algorithm) + { + snprintf(res + res_len, res_size - res_len, ", algorithm=\"%s\"", algorithm); } } return res;