/* Set the request named NAME to VALUE. Specifically, this means that
a "NAME: VALUE\r\n" header line will be used in the request. If a
header with the same name previously existed in the request, its
- value will be replaced by this one.
+ value will be replaced by this one. A NULL value means do nothing.
RELEASE_POLICY determines whether NAME and VALUE should be released
(freed) with request_free. Allowed values are:
{
struct request_header *hdr;
int i;
+
if (!value)
{
/* A NULL value is a no-op; if freeing the name is requested,
xfree (name);
return;
}
+
for (i = 0; i < req->hcount; i++)
{
hdr = &req->headers[i];
if (req->hcount >= req->hcapacity)
{
req->hcapacity <<= 1;
- req->headers = xrealloc (req->headers,
- req->hcapacity * sizeof (struct request_header));
+ req->headers = xrealloc (req->headers, req->hcapacity * sizeof (*hdr));
}
hdr = &req->headers[req->hcount++];
hdr->name = name;
request_set_header (req, xstrdup (name), (char *) p, rel_name);
}
+/* Remove the header with specified name from REQ. Returns 1 if the
+ header was actually removed, 0 otherwise. */
+
+static int
+request_remove_header (struct request *req, char *name)
+{
+ int i;
+ for (i = 0; i < req->hcount; i++)
+ {
+ struct request_header *hdr = &req->headers[i];
+ if (0 == strcasecmp (name, hdr->name))
+ {
+ release_header (hdr);
+ /* Move the remaining headers by one. */
+ if (i < req->hcount - 1)
+ memmove (hdr, hdr + 1, (req->hcount - i - 1) * sizeof (*hdr));
+ --req->hcount;
+ return 1;
+ }
+ }
+ return 0;
+}
+
#define APPEND(p, str) do { \
int A_len = strlen (str); \
memcpy (p, str, A_len); \
/* Whether a ssl handshake has occoured on this connection. */
int ssl;
+ /* Whether the connection was authorized. This is only done by
+ NTLM, which authorizes *connections* rather than individual
+ requests. (That practice is peculiar for HTTP, but it is a
+ useful optimization.) */
+ int authorized;
+
#ifdef ENABLE_NTLM
/* NTLM data of the current connection. */
struct ntlmdata ntlm;
pconn.host = xstrdup (host);
pconn.port = port;
pconn.ssl = ssl;
+ pconn.authorized = 0;
DEBUGP (("Registered socket %d for persistent reuse.\n", fd));
}
&& (ISSPACE (line[sizeof (string_constant) - 1]) \
|| !line[sizeof (string_constant) - 1]))
+#define SET_USER_AGENT(req) \
+ if (opt.useragent) \
+ request_set_header (req, "User-Agent", opt.useragent, rel_none); \
+ else \
+ request_set_header (req, "User-Agent", \
+ aprintf ("Wget/%s", version_string), rel_value);
+
+
/* Retrieve a document through HTTP protocol. It recognizes status
code, and correctly handles redirections. It closes the network
socket. If it receives an error from the functions below it, it
not be tried again. */
int auth_finished = 0;
+ /* Whether NTLM authentication is used for this request. */
+ int ntlm_seen = 0;
+
/* Whether our connection to the remote host is through SSL. */
int using_ssl = 0;
/* try without certfile */
logprintf (LOG_NOTQUIET,
_("Failed to load certificates from %s\n"),
- opt.sslcertfile);
+ opt.cert_file);
logprintf (LOG_NOTQUIET,
_("Trying without the specified certificate\n"));
break;
case SSLERRCERTKEY:
logprintf (LOG_NOTQUIET,
_("Failed to get certificate key from %s\n"),
- opt.sslcertkey);
+ opt.cert_key);
logprintf (LOG_NOTQUIET,
_("Trying without the specified certificate\n"));
break;
aprintf ("bytes=%s-",
number_to_static_string (hs->restval)),
rel_value);
- if (opt.useragent)
- request_set_header (req, "User-Agent", opt.useragent, rel_none);
- else
- request_set_header (req, "User-Agent",
- aprintf ("Wget/%s", version_string), rel_value);
+ SET_USER_AGENT (req);
request_set_header (req, "Accept", "*/*", rel_none);
/* Find the username and password for authentication. */
logprintf (LOG_VERBOSE, _("Reusing existing connection to %s:%d.\n"),
escnonprint (pconn.host), pconn.port);
DEBUGP (("Reusing fd %d.\n", sock));
+ if (pconn.authorized)
+ /* If the connection is already authorized, the "Basic"
+ authorization added by code above is unnecessary and
+ only hurts us. */
+ request_remove_header (req, "Authorization");
}
}
struct request *connreq = request_new ();
request_set_method (connreq, "CONNECT",
aprintf ("%s:%d", u->host, u->port));
+ SET_USER_AGENT (req);
if (proxyauth)
{
request_set_header (connreq, "Proxy-Authorization",
the regular request below. */
proxyauth = NULL;
}
+ /* Examples in rfc2817 use the Host header in CONNECT
+ requests. I don't see how that gains anything, given
+ that the contents of Host would be exactly the same as
+ the contents of CONNECT. */
write_error = request_send (connreq, sock);
request_free (connreq);
if (statcode == HTTP_STATUS_UNAUTHORIZED)
{
/* Authorization is required. */
- if (skip_short_body (sock, contlen))
- CLOSE_FINISH (sock);
- else
- CLOSE_INVALIDATE (sock);
+ if (keep_alive)
+ {
+ if (skip_short_body (sock, contlen))
+ CLOSE_FINISH (sock);
+ else
+ CLOSE_INVALIDATE (sock);
+ }
+ pconn.authorized = 0;
if (auth_finished || !(user && passwd))
{
/* If we have tried it already, then there is not point
pth,
&auth_finished),
rel_value);
+ if (BEGINS_WITH (www_authenticate, "NTLM"))
+ ntlm_seen = 1;
xfree (pth);
xfree (www_authenticate);
goto retry_with_auth;
request_free (req);
return AUTHFAILED;
}
+ else /* statcode != HTTP_STATUS_UNAUTHORIZED */
+ {
+ /* Kludge: if NTLM is used, mark the TCP connection as authorized. */
+ if (ntlm_seen)
+ pconn.authorized = 1;
+ }
request_free (req);
hs->statcode = statcode;
/* Handle (possibly multiple instances of) the Set-Cookie header. */
{
+ char *pth = NULL;
int scpos;
const char *scbeg, *scend;
/* The jar should have been created by now. */
&scbeg, &scend)) != -1;
++scpos)
{
- char *set_cookie = strdupdelim (scbeg, scend);
- cookie_handle_set_cookie (wget_cookie_jar, u->host, u->port, u->path,
+ char *set_cookie; BOUNDED_TO_ALLOCA (scbeg, scend, set_cookie);
+ if (pth == NULL)
+ {
+ /* u->path doesn't begin with /, which cookies.c expects. */
+ pth = (char *) alloca (1 + strlen (u->path) + 1);
+ pth[0] = '/';
+ strcpy (pth + 1, u->path);
+ }
+ cookie_handle_set_cookie (wget_cookie_jar, u->host, u->port, pth,
set_cookie);
- xfree (set_cookie);
}
}