#include "connect.h"
#include "netrc.h"
#ifdef HAVE_SSL
-# include "gen_sslfunc.h"
+# include "ssl.h"
#endif
#ifdef ENABLE_NTLM
# include "http-ntlm.h"
/* 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); \
/* Send the request to the server. */
- write_error = fd_write (fd, request_string, size - 1, -1);
+ write_error = fd_write (fd, request_string, size - 1, -1.0);
if (write_error < 0)
logprintf (LOG_VERBOSE, _("Failed writing HTTP request: %s.\n"),
strerror (errno));
if (length == 0)
break;
towrite = MIN (promised_size - written, length);
- write_error = fd_write (sock, chunk, towrite, -1);
+ write_error = fd_write (sock, chunk, towrite, -1.0);
if (write_error < 0)
{
fclose (fp);
while (contlen > 0)
{
- int ret = fd_read (fd, dlbuf, MIN (contlen, SKIP_SIZE), -1);
+ int ret = fd_read (fd, dlbuf, MIN (contlen, SKIP_SIZE), -1.0);
if (ret <= 0)
{
/* Don't normally report the error since this is an
/* 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;
{
/* Initialize the SSL context. After this has once been done,
it becomes a no-op. */
- switch (ssl_init ())
+ if (!ssl_init ())
{
- case SSLERRCTXCREATE:
- /* this is fatal */
- logprintf (LOG_NOTQUIET, _("Failed to set up an SSL context\n"));
- return SSLERRCTXCREATE;
- case SSLERRCERTFILE:
- /* try without certfile */
+ scheme_disable (SCHEME_HTTPS);
logprintf (LOG_NOTQUIET,
- _("Failed to load certificates from %s\n"),
- opt.sslcertfile);
- 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);
- logprintf (LOG_NOTQUIET,
- _("Trying without the specified certificate\n"));
- break;
- default:
- break;
+ _("Disabling SSL due to encountered errors.\n"));
+ return SSLINITFAILED;
}
}
#endif /* HAVE_SSL */
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. */
user = u->user;
passwd = u->passwd;
search_netrc (u->host, (const char **)&user, (const char **)&passwd, 0);
- user = user ? user : opt.http_user;
- passwd = passwd ? passwd : opt.http_passwd;
+ user = user ? user : (opt.http_user ? opt.http_user : opt.user);
+ passwd = passwd ? passwd : (opt.http_passwd ? opt.http_passwd : opt.passwd);
if (user && passwd)
{
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 (connreq);
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 (opt.post_data)
{
DEBUGP (("[POST data: %s]\n", opt.post_data));
- write_error = fd_write (sock, opt.post_data, post_data_size, -1);
+ write_error = fd_write (sock, opt.post_data, post_data_size, -1.0);
}
else if (opt.post_file_name && post_data_size != 0)
write_error = post_file (sock, opt.post_file_name, post_data_size);
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);
}
}
locf = opt.output_document;
}
continue;
- break;
case HOSTERR: case CONIMPOSSIBLE: case PROXERR: case AUTHFAILED:
- case SSLERRCTXCREATE: case CONTNOTSUPPORTED:
+ case SSLINITFAILED: case CONTNOTSUPPORTED:
/* Fatal errors just return from the function. */
free_hstat (&hstat);
xfree_null (dummy);
return err;
- break;
case FWRITEERR: case FOPENERR:
/* Another fatal error. */
logputs (LOG_VERBOSE, "\n");
free_hstat (&hstat);
xfree_null (dummy);
return err;
- break;
case CONSSLERR:
/* Another fatal error. */
logputs (LOG_VERBOSE, "\n");
free_hstat (&hstat);
xfree_null (dummy);
return err;
- break;
case NEWLOCATION:
/* Return the new location to the caller. */
if (!hstat.newloc)
free_hstat (&hstat);
xfree_null (dummy);
return NEWLOCATION;
- break;
case RETRUNNEEDED:
/* The file was already fully retrieved. */
free_hstat (&hstat);
xfree_null (dummy);
return RETROK;
- break;
case RETRFINISHED:
/* Deal with you later. */
break;
}
}
/* not reached */
- break;
}
while (!opt.ntry || (count < opt.ntry));
return TRYLIMEXC;