]> sjero.net Git - wget/blobdiff - src/http.c
Rename --html-extension to --adjust-extension.
[wget] / src / http.c
index 9b02e37d97879467af8d23c01f7f1c0151ceb71f..d24db5fc38f3393ef6937c32e1ba03e3e6eba24b 100644 (file)
@@ -1,11 +1,12 @@
 /* HTTP support.
-   Copyright (C) 1996-2005 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
 This file is part of GNU Wget.
 
 GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
@@ -14,20 +15,20 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with Wget; if not, write to the Free Software Foundation, Inc.,
-51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+along with Wget.  If not, see <http://www.gnu.org/licenses/>.
 
-In addition, as a special exception, the Free Software Foundation
-gives permission to link the code of its release of Wget with the
-OpenSSL project's "OpenSSL" library (or with modified versions of it
-that use the same license as the "OpenSSL" library), and distribute
-the linked executables.  You must obey the GNU General Public License
-in all respects for all of the code used other than "OpenSSL".  If you
-modify this file, you may extend this exception to your version of the
-file, but you are not obligated to do so.  If you do not wish to do
-so, delete this exception statement from your version.  */
+Additional permission under GNU GPL version 3 section 7
 
-#include <config.h>
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.  */
+
+#include "wget.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -40,7 +41,7 @@ so, delete this exception statement from your version.  */
 #include <time.h>
 #include <locale.h>
 
-#include "wget.h"
+#include "hash.h"
 #include "http.h"
 #include "utils.h"
 #include "url.h"
@@ -59,9 +60,28 @@ so, delete this exception statement from your version.  */
 # include "gen-md5.h"
 #endif
 #include "convert.h"
+#include "spider.h"
+
+#ifdef TESTING
+#include "test.h"
+#endif
+
+#ifdef __VMS
+# include "vms.h"
+#endif /* def __VMS */
 
 extern char *version_string;
 
+/* Forward decls. */
+struct http_stat;
+static char *create_authorization_line (const char *, const char *,
+                                        const char *, const char *,
+                                        const char *, bool *);
+static char *basic_authentication_encode (const char *, const char *);
+static bool known_authentication_scheme_p (const char *, const char *);
+static void ensure_extension (struct http_stat *, const char *, int *);
+static void load_cookies (void);
+
 #ifndef MIN
 # define MIN(x, y) ((x) > (y) ? (y) : (x))
 #endif
@@ -72,43 +92,44 @@ static struct cookie_jar *wget_cookie_jar;
 
 #define TEXTHTML_S "text/html"
 #define TEXTXHTML_S "application/xhtml+xml"
+#define TEXTCSS_S "text/css"
 
 /* Some status code validation macros: */
 #define H_20X(x)        (((x) >= 200) && ((x) < 300))
 #define H_PARTIAL(x)    ((x) == HTTP_STATUS_PARTIAL_CONTENTS)
-#define H_REDIRECTED(x) ((x) == HTTP_STATUS_MOVED_PERMANENTLY          \
-                         || (x) == HTTP_STATUS_MOVED_TEMPORARILY       \
-                        || (x) == HTTP_STATUS_SEE_OTHER                \
-                        || (x) == HTTP_STATUS_TEMPORARY_REDIRECT)
+#define H_REDIRECTED(x) ((x) == HTTP_STATUS_MOVED_PERMANENTLY          \
+                         || (x) == HTTP_STATUS_MOVED_TEMPORARILY       \
+                         || (x) == HTTP_STATUS_SEE_OTHER               \
+                         || (x) == HTTP_STATUS_TEMPORARY_REDIRECT)
 
 /* HTTP/1.0 status codes from RFC1945, provided for reference.  */
 /* Successful 2xx.  */
-#define HTTP_STATUS_OK                 200
-#define HTTP_STATUS_CREATED            201
-#define HTTP_STATUS_ACCEPTED           202
-#define HTTP_STATUS_NO_CONTENT         204
-#define HTTP_STATUS_PARTIAL_CONTENTS   206
+#define HTTP_STATUS_OK                    200
+#define HTTP_STATUS_CREATED               201
+#define HTTP_STATUS_ACCEPTED              202
+#define HTTP_STATUS_NO_CONTENT            204
+#define HTTP_STATUS_PARTIAL_CONTENTS      206
 
 /* Redirection 3xx.  */
-#define HTTP_STATUS_MULTIPLE_CHOICES   300
-#define HTTP_STATUS_MOVED_PERMANENTLY  301
-#define HTTP_STATUS_MOVED_TEMPORARILY  302
-#define HTTP_STATUS_SEE_OTHER           303 /* from HTTP/1.1 */
-#define HTTP_STATUS_NOT_MODIFIED       304
-#define HTTP_STATUS_TEMPORARY_REDIRECT  307 /* from HTTP/1.1 */
+#define HTTP_STATUS_MULTIPLE_CHOICES      300
+#define HTTP_STATUS_MOVED_PERMANENTLY     301
+#define HTTP_STATUS_MOVED_TEMPORARILY     302
+#define HTTP_STATUS_SEE_OTHER             303 /* from HTTP/1.1 */
+#define HTTP_STATUS_NOT_MODIFIED          304
+#define HTTP_STATUS_TEMPORARY_REDIRECT    307 /* from HTTP/1.1 */
 
 /* Client error 4xx.  */
-#define HTTP_STATUS_BAD_REQUEST                400
-#define HTTP_STATUS_UNAUTHORIZED       401
-#define HTTP_STATUS_FORBIDDEN          403
-#define HTTP_STATUS_NOT_FOUND          404
+#define HTTP_STATUS_BAD_REQUEST           400
+#define HTTP_STATUS_UNAUTHORIZED          401
+#define HTTP_STATUS_FORBIDDEN             403
+#define HTTP_STATUS_NOT_FOUND             404
 #define HTTP_STATUS_RANGE_NOT_SATISFIABLE 416
 
 /* Server errors 5xx.  */
-#define HTTP_STATUS_INTERNAL           500
-#define HTTP_STATUS_NOT_IMPLEMENTED    501
-#define HTTP_STATUS_BAD_GATEWAY                502
-#define HTTP_STATUS_UNAVAILABLE                503
+#define HTTP_STATUS_INTERNAL              500
+#define HTTP_STATUS_NOT_IMPLEMENTED       501
+#define HTTP_STATUS_BAD_GATEWAY           502
+#define HTTP_STATUS_UNAVAILABLE           503
 \f
 enum rp {
   rel_none, rel_name, rel_value, rel_both
@@ -125,6 +146,8 @@ struct request {
   int hcount, hcapacity;
 };
 
+extern int numurls;
+
 /* Create a new, empty request.  At least request_set_method must be
    called before the request can be used.  */
 
@@ -205,12 +228,12 @@ release_header (struct request_header *hdr)
      // Value freshly allocated, free it when done.
      request_set_header (req, "Range",
                          aprintf ("bytes=%s-", number_to_static_string (hs->restval)),
-                        rel_value);
+                         rel_value);
    */
 
 static void
 request_set_header (struct request *req, char *name, char *value,
-                   enum rp release_policy)
+                    enum rp release_policy)
 {
   struct request_header *hdr;
   int i;
@@ -218,9 +241,9 @@ request_set_header (struct request *req, char *name, char *value,
   if (!value)
     {
       /* A NULL value is a no-op; if freeing the name is requested,
-        free it now to avoid leaks.  */
+         free it now to avoid leaks.  */
       if (release_policy == rel_name || release_policy == rel_both)
-       xfree (name);
+        xfree (name);
       return;
     }
 
@@ -228,14 +251,14 @@ request_set_header (struct request *req, char *name, char *value,
     {
       hdr = &req->headers[i];
       if (0 == strcasecmp (name, hdr->name))
-       {
-         /* Replace existing header. */
-         release_header (hdr);
-         hdr->name = name;
-         hdr->value = value;
-         hdr->release_policy = release_policy;
-         return;
-       }
+        {
+          /* Replace existing header. */
+          release_header (hdr);
+          hdr->name = name;
+          hdr->value = value;
+          hdr->release_policy = release_policy;
+          return;
+        }
     }
 
   /* Install new header. */
@@ -265,7 +288,7 @@ request_set_user_header (struct request *req, const char *header)
     return;
   BOUNDED_TO_ALLOCA (header, p, name);
   ++p;
-  while (ISSPACE (*p))
+  while (c_isspace (*p))
     ++p;
   request_set_header (req, xstrdup (name), (char *) p, rel_name);
 }
@@ -281,22 +304,22 @@ request_remove_header (struct request *req, char *name)
     {
       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 true;
-       }
+        {
+          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 true;
+        }
     }
   return false;
 }
 
-#define APPEND(p, str) do {                    \
-  int A_len = strlen (str);                    \
-  memcpy (p, str, A_len);                      \
-  p += A_len;                                  \
+#define APPEND(p, str) do {                     \
+  int A_len = strlen (str);                     \
+  memcpy (p, str, A_len);                       \
+  p += A_len;                                   \
 } while (0)
 
 /* Construct the request and write it to FD using fd_write.  */
@@ -352,7 +375,7 @@ request_send (const struct request *req, int fd)
   write_error = fd_write (fd, request_string, size - 1, -1);
   if (write_error < 0)
     logprintf (LOG_VERBOSE, _("Failed writing HTTP request: %s.\n"),
-              fd_errstr (fd));
+               fd_errstr (fd));
   return write_error;
 }
 
@@ -369,6 +392,58 @@ request_free (struct request *req)
   xfree (req);
 }
 
+static struct hash_table *basic_authed_hosts;
+
+/* Find out if this host has issued a Basic challenge yet; if so, give
+ * it the username, password. A temporary measure until we can get
+ * proper authentication in place. */
+
+static bool
+maybe_send_basic_creds (const char *hostname, const char *user,
+                        const char *passwd, struct request *req)
+{
+  bool do_challenge = false;
+
+  if (opt.auth_without_challenge)
+    {
+      DEBUGP(("Auth-without-challenge set, sending Basic credentials.\n"));
+      do_challenge = true;
+    }
+  else if (basic_authed_hosts
+      && hash_table_contains(basic_authed_hosts, hostname))
+    {
+      DEBUGP(("Found %s in basic_authed_hosts.\n", quote (hostname)));
+      do_challenge = true;
+    }
+  else
+    {
+      DEBUGP(("Host %s has not issued a general basic challenge.\n",
+              quote (hostname)));
+    }
+  if (do_challenge)
+    {
+      request_set_header (req, "Authorization",
+                          basic_authentication_encode (user, passwd),
+                          rel_value);
+    }
+  return do_challenge;
+}
+
+static void
+register_basic_auth_host (const char *hostname)
+{
+  if (!basic_authed_hosts)
+    {
+      basic_authed_hosts = make_nocase_string_hash_table (1);
+    }
+  if (!hash_table_contains(basic_authed_hosts, hostname))
+    {
+      hash_table_put (basic_authed_hosts, xstrdup(hostname), NULL);
+      DEBUGP(("Inserted %s into basic_authed_hosts\n", quote (hostname)));
+    }
+}
+
+
 /* Send the contents of FILE_NAME to SOCK.  Make sure that exactly
    PROMISED_SIZE bytes are sent over the wire -- if the file is
    longer, read only that much; if the file is shorter, report an error.  */
@@ -391,14 +466,14 @@ post_file (int sock, const char *file_name, wgint promised_size)
       int towrite;
       int length = fread (chunk, 1, sizeof (chunk), fp);
       if (length == 0)
-       break;
+        break;
       towrite = MIN (promised_size - written, length);
       write_error = fd_write (sock, chunk, towrite, -1);
       if (write_error < 0)
-       {
-         fclose (fp);
-         return -1;
-       }
+        {
+          fclose (fp);
+          return -1;
+        }
       written += towrite;
     }
   fclose (fp);
@@ -444,10 +519,10 @@ response_head_terminator (const char *start, const char *peeked, int peeklen)
   for (; p < end - 2; p++)
     if (*p == '\n')
       {
-       if (p[1] == '\r' && p[2] == '\n')
-         return p + 3;
-       else if (p[1] == '\n')
-         return p + 2;
+        if (p[1] == '\r' && p[2] == '\n')
+          return p + 3;
+        else if (p[1] == '\n')
+          return p + 2;
       }
   /* p==end-2: check for \n\n directly preceding END. */
   if (p[0] == '\n' && p[1] == '\n')
@@ -476,7 +551,7 @@ static char *
 read_http_response_head (int fd)
 {
   return fd_read_hunk (fd, response_head_terminator, 512,
-                      HTTP_RESPONSE_MAX_SIZE);
+                       HTTP_RESPONSE_MAX_SIZE);
 }
 
 struct response {
@@ -521,8 +596,8 @@ resp_new (const char *head)
   if (*head == '\0')
     {
       /* Empty head means that we're dealing with a headerless
-        (HTTP/0.9) response.  In that case, don't set HEADERS at
-        all.  */
+         (HTTP/0.9) response.  In that case, don't set HEADERS at
+         all.  */
       return resp;
     }
 
@@ -538,17 +613,17 @@ resp_new (const char *head)
 
       /* Break upon encountering an empty line. */
       if (!hdr[0] || (hdr[0] == '\r' && hdr[1] == '\n') || hdr[0] == '\n')
-       break;
+        break;
 
       /* Find the end of HDR, including continuations. */
       do
-       {
-         const char *end = strchr (hdr, '\n');
-         if (end)
-           hdr = end + 1;
-         else
-           hdr += strlen (hdr);
-       }
+        {
+          const char *end = strchr (hdr, '\n');
+          if (end)
+            hdr = end + 1;
+          else
+            hdr += strlen (hdr);
+        }
       while (*hdr == ' ' || *hdr == '\t');
     }
   DO_REALLOC (resp->headers, size, count + 1, const char *);
@@ -571,7 +646,7 @@ resp_new (const char *head)
 
 static int
 resp_header_locate (const struct response *resp, const char *name, int start,
-                   const char **begptr, const char **endptr)
+                    const char **begptr, const char **endptr)
 {
   int i;
   const char **headers = resp->headers;
@@ -591,18 +666,18 @@ resp_header_locate (const struct response *resp, const char *name, int start,
       const char *b = headers[i];
       const char *e = headers[i + 1];
       if (e - b > name_len
-         && b[name_len] == ':'
-         && 0 == strncasecmp (b, name, name_len))
-       {
-         b += name_len + 1;
-         while (b < e && ISSPACE (*b))
-           ++b;
-         while (b < e && ISSPACE (e[-1]))
-           --e;
-         *begptr = b;
-         *endptr = e;
-         return i;
-       }
+          && b[name_len] == ':'
+          && 0 == strncasecmp (b, name, name_len))
+        {
+          b += name_len + 1;
+          while (b < e && c_isspace (*b))
+            ++b;
+          while (b < e && c_isspace (e[-1]))
+            --e;
+          *begptr = b;
+          *endptr = e;
+          return i;
+        }
     }
   return -1;
 }
@@ -616,7 +691,7 @@ resp_header_locate (const struct response *resp, const char *name, int start,
 
 static bool
 resp_header_get (const struct response *resp, const char *name,
-                const char **begptr, const char **endptr)
+                 const char **begptr, const char **endptr)
 {
   int pos = resp_header_locate (resp, name, 0, begptr, endptr);
   return pos != -1;
@@ -632,7 +707,7 @@ resp_header_get (const struct response *resp, const char *name,
 
 static bool
 resp_header_copy (const struct response *resp, const char *name,
-                 char *buf, int bufsize)
+                  char *buf, int bufsize)
 {
   const char *b, *e;
   if (!resp_header_get (resp, name, &b, &e))
@@ -676,7 +751,7 @@ resp_status (const struct response *resp, char **message)
     {
       /* For a HTTP/0.9 response, assume status 200. */
       if (message)
-       *message = xstrdup (_("No headers, assuming HTTP/0.9"));
+        *message = xstrdup (_("No headers, assuming HTTP/0.9"));
       return 200;
     }
 
@@ -696,17 +771,17 @@ resp_status (const struct response *resp, char **message)
   if (p < end && *p == '/')
     {
       ++p;
-      while (p < end && ISDIGIT (*p))
-       ++p;
+      while (p < end && c_isdigit (*p))
+        ++p;
       if (p < end && *p == '.')
-       ++p; 
-      while (p < end && ISDIGIT (*p))
-       ++p;
+        ++p; 
+      while (p < end && c_isdigit (*p))
+        ++p;
     }
 
-  while (p < end && ISSPACE (*p))
+  while (p < end && c_isspace (*p))
     ++p;
-  if (end - p < 3 || !ISDIGIT (p[0]) || !ISDIGIT (p[1]) || !ISDIGIT (p[2]))
+  if (end - p < 3 || !c_isdigit (p[0]) || !c_isdigit (p[1]) || !c_isdigit (p[2]))
     return -1;
 
   status = 100 * (p[0] - '0') + 10 * (p[1] - '0') + (p[2] - '0');
@@ -714,10 +789,10 @@ resp_status (const struct response *resp, char **message)
 
   if (message)
     {
-      while (p < end && ISSPACE (*p))
-       ++p;
-      while (p < end && ISSPACE (end[-1]))
-       --end;
+      while (p < end && c_isspace (*p))
+        ++p;
+      while (p < end && c_isspace (end[-1]))
+        --end;
       *message = strdupdelim (p, end);
     }
 
@@ -733,6 +808,21 @@ resp_free (struct response *resp)
   xfree (resp);
 }
 
+/* Print a single line of response, the characters [b, e).  We tried
+   getting away with
+      logprintf (LOG_VERBOSE, "%s%.*s\n", prefix, (int) (e - b), b);
+   but that failed to escape the non-printable characters and, in fact,
+   caused crashes in UTF-8 locales.  */
+
+static void
+print_response_line(const char *prefix, const char *b, const char *e)
+{
+  char *copy;
+  BOUNDED_TO_ALLOCA(b, e, copy);
+  logprintf (LOG_ALWAYS, "%s%s\n", prefix, 
+             quotearg_style (escape_quoting_style, copy));
+}
+
 /* Print the server response, line by line, omitting the trailing CRLF
    from individual header lines, and prefixed with PREFIX.  */
 
@@ -748,12 +838,10 @@ print_server_response (const struct response *resp, const char *prefix)
       const char *e = resp->headers[i + 1];
       /* Skip CRLF */
       if (b < e && e[-1] == '\n')
-       --e;
+        --e;
       if (b < e && e[-1] == '\r')
-       --e;
-      /* This is safe even on printfs with broken handling of "%.<n>s"
-        because resp->headers ends with \0.  */
-      logprintf (LOG_VERBOSE, "%s%.*s\n", prefix, e - b, b);
+        --e;
+      print_response_line(prefix, b, e);
     }
 }
 
@@ -761,7 +849,7 @@ print_server_response (const struct response *resp, const char *prefix)
    contains.  Returns true if successful, false otherwise.  */
 static bool
 parse_content_range (const char *hdr, wgint *first_byte_ptr,
-                    wgint *last_byte_ptr, wgint *entity_length_ptr)
+                     wgint *last_byte_ptr, wgint *entity_length_ptr)
 {
   wgint num;
 
@@ -772,30 +860,33 @@ parse_content_range (const char *hdr, wgint *first_byte_ptr,
     {
       hdr += 5;
       /* "JavaWebServer/1.1.1" sends "bytes: x-y/z", contrary to the
-        HTTP spec. */
+         HTTP spec. */
       if (*hdr == ':')
-       ++hdr;
-      while (ISSPACE (*hdr))
-       ++hdr;
+        ++hdr;
+      while (c_isspace (*hdr))
+        ++hdr;
       if (!*hdr)
-       return false;
+        return false;
     }
-  if (!ISDIGIT (*hdr))
+  if (!c_isdigit (*hdr))
     return false;
-  for (num = 0; ISDIGIT (*hdr); hdr++)
+  for (num = 0; c_isdigit (*hdr); hdr++)
     num = 10 * num + (*hdr - '0');
-  if (*hdr != '-' || !ISDIGIT (*(hdr + 1)))
+  if (*hdr != '-' || !c_isdigit (*(hdr + 1)))
     return false;
   *first_byte_ptr = num;
   ++hdr;
-  for (num = 0; ISDIGIT (*hdr); hdr++)
+  for (num = 0; c_isdigit (*hdr); hdr++)
     num = 10 * num + (*hdr - '0');
-  if (*hdr != '/' || !ISDIGIT (*(hdr + 1)))
+  if (*hdr != '/' || !c_isdigit (*(hdr + 1)))
     return false;
   *last_byte_ptr = num;
   ++hdr;
-  for (num = 0; ISDIGIT (*hdr); hdr++)
-    num = 10 * num + (*hdr - '0');
+  if (*hdr == '*')
+    num = -1;
+  else
+    for (num = 0; c_isdigit (*hdr); hdr++)
+      num = 10 * num + (*hdr - '0');
   *entity_length_ptr = num;
   return true;
 }
@@ -813,11 +904,11 @@ static bool
 skip_short_body (int fd, wgint contlen)
 {
   enum {
-    SKIP_SIZE = 512,           /* size of the download buffer */
-    SKIP_THRESHOLD = 4096      /* the largest size we read */
+    SKIP_SIZE = 512,                /* size of the download buffer */
+    SKIP_THRESHOLD = 4096        /* the largest size we read */
   };
   char dlbuf[SKIP_SIZE + 1];
-  dlbuf[SKIP_SIZE] = '\0';     /* so DEBUGP can safely print it */
+  dlbuf[SKIP_SIZE] = '\0';        /* so DEBUGP can safely print it */
 
   /* We shouldn't get here with unknown contlen.  (This will change
      with HTTP/1.1, which supports "chunked" transfer.)  */
@@ -834,22 +925,156 @@ skip_short_body (int fd, wgint contlen)
     {
       int ret = fd_read (fd, dlbuf, MIN (contlen, SKIP_SIZE), -1);
       if (ret <= 0)
-       {
-         /* Don't normally report the error since this is an
-            optimization that should be invisible to the user.  */
-         DEBUGP (("] aborting (%s).\n",
-                  ret < 0 ? fd_errstr (fd) : "EOF received"));
-         return false;
-       }
+        {
+          /* Don't normally report the error since this is an
+             optimization that should be invisible to the user.  */
+          DEBUGP (("] aborting (%s).\n",
+                   ret < 0 ? fd_errstr (fd) : "EOF received"));
+          return false;
+        }
       contlen -= ret;
       /* Safe even if %.*s bogusly expects terminating \0 because
-        we've zero-terminated dlbuf above.  */
+         we've zero-terminated dlbuf above.  */
       DEBUGP (("%.*s", ret, dlbuf));
     }
 
   DEBUGP (("] done.\n"));
   return true;
 }
+
+/* Extract a parameter from the string (typically an HTTP header) at
+   **SOURCE and advance SOURCE to the next parameter.  Return false
+   when there are no more parameters to extract.  The name of the
+   parameter is returned in NAME, and the value in VALUE.  If the
+   parameter has no value, the token's value is zeroed out.
+
+   For example, if *SOURCE points to the string "attachment;
+   filename=\"foo bar\"", the first call to this function will return
+   the token named "attachment" and no value, and the second call will
+   return the token named "filename" and value "foo bar".  The third
+   call will return false, indicating no more valid tokens.  */
+
+bool
+extract_param (const char **source, param_token *name, param_token *value,
+               char separator)
+{
+  const char *p = *source;
+
+  while (c_isspace (*p)) ++p;
+  if (!*p)
+    {
+      *source = p;
+      return false;             /* no error; nothing more to extract */
+    }
+
+  /* Extract name. */
+  name->b = p;
+  while (*p && !c_isspace (*p) && *p != '=' && *p != separator) ++p;
+  name->e = p;
+  if (name->b == name->e)
+    return false;               /* empty name: error */
+  while (c_isspace (*p)) ++p;
+  if (*p == separator || !*p)           /* no value */
+    {
+      xzero (*value);
+      if (*p == separator) ++p;
+      *source = p;
+      return true;
+    }
+  if (*p != '=')
+    return false;               /* error */
+
+  /* *p is '=', extract value */
+  ++p;
+  while (c_isspace (*p)) ++p;
+  if (*p == '"')                /* quoted */
+    {
+      value->b = ++p;
+      while (*p && *p != '"') ++p;
+      if (!*p)
+        return false;
+      value->e = p++;
+      /* Currently at closing quote; find the end of param. */
+      while (c_isspace (*p)) ++p;
+      while (*p && *p != separator) ++p;
+      if (*p == separator)
+        ++p;
+      else if (*p)
+        /* garbage after closed quote, e.g. foo="bar"baz */
+        return false;
+    }
+  else                          /* unquoted */
+    {
+      value->b = p;
+      while (*p && *p != separator) ++p;
+      value->e = p;
+      while (value->e != value->b && c_isspace (value->e[-1]))
+        --value->e;
+      if (*p == separator) ++p;
+    }
+  *source = p;
+  return true;
+}
+
+#undef MAX
+#define MAX(p, q) ((p) > (q) ? (p) : (q))
+
+/* Parse the contents of the `Content-Disposition' header, extracting
+   the information useful to Wget.  Content-Disposition is a header
+   borrowed from MIME; when used in HTTP, it typically serves for
+   specifying the desired file name of the resource.  For example:
+
+       Content-Disposition: attachment; filename="flora.jpg"
+
+   Wget will skip the tokens it doesn't care about, such as
+   "attachment" in the previous example; it will also skip other
+   unrecognized params.  If the header is syntactically correct and
+   contains a file name, a copy of the file name is stored in
+   *filename and true is returned.  Otherwise, the function returns
+   false.
+
+   The file name is stripped of directory components and must not be
+   empty.  */
+
+static bool
+parse_content_disposition (const char *hdr, char **filename)
+{
+  param_token name, value;
+  while (extract_param (&hdr, &name, &value, ';'))
+    if (BOUNDED_EQUAL_NO_CASE (name.b, name.e, "filename") && value.b != NULL)
+      {
+        /* Make the file name begin at the last slash or backslash. */
+        const char *last_slash = memrchr (value.b, '/', value.e - value.b);
+        const char *last_bs = memrchr (value.b, '\\', value.e - value.b);
+        if (last_slash && last_bs)
+          value.b = 1 + MAX (last_slash, last_bs);
+        else if (last_slash || last_bs)
+          value.b = 1 + (last_slash ? last_slash : last_bs);
+        if (value.b == value.e)
+          continue;
+        /* Start with the directory prefix, if specified. */
+        if (opt.dir_prefix)
+          {
+            int prefix_length = strlen (opt.dir_prefix);
+            bool add_slash = (opt.dir_prefix[prefix_length - 1] != '/');
+            int total_length;
+
+            if (add_slash) 
+              ++prefix_length;
+            total_length = prefix_length + (value.e - value.b);            
+            *filename = xmalloc (total_length + 1);
+            strcpy (*filename, opt.dir_prefix);
+            if (add_slash) 
+              (*filename)[prefix_length - 1] = '/';
+            memcpy (*filename + prefix_length, value.b, (value.e - value.b));
+            (*filename)[total_length] = '\0';
+          }
+        else
+          *filename = strdupdelim (value.b, value.e);
+        return true;
+      }
+  return false;
+}
 \f
 /* Persistent connections.  Currently, we cache the most recently used
    connection as persistent, provided that the HTTP server agrees to
@@ -911,19 +1136,19 @@ register_persistent (const char *host, int port, int fd, bool ssl)
   if (pconn_active)
     {
       if (pconn.socket == fd)
-       {
-         /* The connection FD is already registered. */
-         return;
-       }
+        {
+          /* The connection FD is already registered. */
+          return;
+        }
       else
-       {
-         /* The old persistent connection is still active; close it
-            first.  This situation arises whenever a persistent
-            connection exists, but we then connect to a different
-            host, and try to register a persistent connection to that
-            one.  */
-         invalidate_persistent ();
-       }
+        {
+          /* The old persistent connection is still active; close it
+             first.  This situation arises whenever a persistent
+             connection exists, but we then connect to a different
+             host, and try to register a persistent connection to that
+             one.  */
+          invalidate_persistent ();
+        }
     }
 
   pconn_active = true;
@@ -941,7 +1166,7 @@ register_persistent (const char *host, int port, int fd, bool ssl)
 
 static bool
 persistent_available_p (const char *host, int port, bool ssl,
-                       bool *host_lookup_failed)
+                        bool *host_lookup_failed)
 {
   /* First, check whether a persistent connection is active at all.  */
   if (!pconn_active)
@@ -962,50 +1187,50 @@ persistent_available_p (const char *host, int port, bool ssl,
   if (0 != strcasecmp (host, pconn.host))
     {
       /* Check if pconn.socket is talking to HOST under another name.
-        This happens often when both sites are virtual hosts
-        distinguished only by name and served by the same network
-        interface, and hence the same web server (possibly set up by
-        the ISP and serving many different web sites).  This
-        admittedly unconventional optimization does not contradict
-        HTTP and works well with popular server software.  */
+         This happens often when both sites are virtual hosts
+         distinguished only by name and served by the same network
+         interface, and hence the same web server (possibly set up by
+         the ISP and serving many different web sites).  This
+         admittedly unconventional optimization does not contradict
+         HTTP and works well with popular server software.  */
 
       bool found;
       ip_address ip;
       struct address_list *al;
 
       if (ssl)
-       /* Don't try to talk to two different SSL sites over the same
-          secure connection!  (Besides, it's not clear that
-          name-based virtual hosting is even possible with SSL.)  */
-       return false;
+        /* Don't try to talk to two different SSL sites over the same
+           secure connection!  (Besides, it's not clear that
+           name-based virtual hosting is even possible with SSL.)  */
+        return false;
 
       /* If pconn.socket's peer is one of the IP addresses HOST
-        resolves to, pconn.socket is for all intents and purposes
-        already talking to HOST.  */
+         resolves to, pconn.socket is for all intents and purposes
+         already talking to HOST.  */
 
       if (!socket_ip_address (pconn.socket, &ip, ENDPOINT_PEER))
-       {
-         /* Can't get the peer's address -- something must be very
-            wrong with the connection.  */
-         invalidate_persistent ();
-         return false;
-       }
+        {
+          /* Can't get the peer's address -- something must be very
+             wrong with the connection.  */
+          invalidate_persistent ();
+          return false;
+        }
       al = lookup_host (host, 0);
       if (!al)
-       {
-         *host_lookup_failed = true;
-         return false;
-       }
+        {
+          *host_lookup_failed = true;
+          return false;
+        }
 
       found = address_list_contains (al, &ip);
       address_list_release (al);
 
       if (!found)
-       return false;
+        return false;
 
       /* The persistent connection's peer address was found among the
-        addresses HOST resolved to; therefore, pconn.sock is in fact
-        already talking to HOST -- no need to reconnect.  */
+         addresses HOST resolved to; therefore, pconn.sock is in fact
+         already talking to HOST -- no need to reconnect.  */
     }
 
   /* Finally, check whether the connection is still open.  This is
@@ -1048,42 +1273,54 @@ persistent_available_p (const char *host, int port, bool ssl,
    `pc_active_p && (fd) == pc_last_fd' is "we're *now* using an
    active, registered connection".  */
 
-#define CLOSE_FINISH(fd) do {                  \
-  if (!keep_alive)                             \
-    {                                          \
-      if (pconn_active && (fd) == pconn.socket)        \
-       invalidate_persistent ();               \
-      else                                     \
-       {                                       \
-         fd_close (fd);                        \
-         fd = -1;                              \
-       }                                       \
-    }                                          \
+#define CLOSE_FINISH(fd) do {                   \
+  if (!keep_alive)                              \
+    {                                           \
+      if (pconn_active && (fd) == pconn.socket) \
+        invalidate_persistent ();               \
+      else                                      \
+        {                                       \
+          fd_close (fd);                        \
+          fd = -1;                              \
+        }                                       \
+    }                                           \
 } while (0)
 
-#define CLOSE_INVALIDATE(fd) do {              \
-  if (pconn_active && (fd) == pconn.socket)    \
-    invalidate_persistent ();                  \
-  else                                         \
-    fd_close (fd);                             \
-  fd = -1;                                     \
+#define CLOSE_INVALIDATE(fd) do {               \
+  if (pconn_active && (fd) == pconn.socket)     \
+    invalidate_persistent ();                   \
+  else                                          \
+    fd_close (fd);                              \
+  fd = -1;                                      \
 } while (0)
 \f
 struct http_stat
 {
-  wgint len;                   /* received length */
-  wgint contlen;               /* expected length */
-  wgint restval;               /* the restart value */
-  int res;                     /* the result of last read */
-  const char *errstr;          /* error message from read error */
-  char *newloc;                        /* new location (redirection) */
-  char *remote_time;           /* remote time-stamp string */
-  char *error;                 /* textual HTTP error */
-  int statcode;                        /* status code */
-  wgint rd_size;               /* amount of data read from socket */
-  double dltime;               /* time it took to download the data */
-  const char *referer;         /* value of the referer header. */
-  char **local_file;           /* local file. */
+  wgint len;                    /* received length */
+  wgint contlen;                /* expected length */
+  wgint restval;                /* the restart value */
+  int res;                      /* the result of last read */
+  char *rderrmsg;               /* error message from read error */
+  char *newloc;                 /* new location (redirection) */
+  char *remote_time;            /* remote time-stamp string */
+  char *error;                  /* textual HTTP error */
+  int statcode;                 /* status code */
+  char *message;                /* status message */
+  wgint rd_size;                /* amount of data read from socket */
+  double dltime;                /* time it took to download the data */
+  const char *referer;          /* value of the referer header. */
+  char *local_file;             /* local file name. */
+  bool existence_checked;       /* true if we already checked for a file's
+                                   existence after having begun to download
+                                   (needed in gethttp for when connection is
+                                   interrupted/restarted. */
+  bool timestamp_checked;       /* true if pre-download time-stamping checks 
+                                 * have already been performed */
+  char *orig_file_name;         /* name of file to compare for time-stamping
+                                 * (might be != local_file if -K is set) */
+  wgint orig_file_size;         /* size of file to compare for time-stamping */
+  time_t orig_file_tstamp;      /* time-stamp of file to compare for 
+                                 * time-stamping */
 };
 
 static void
@@ -1092,6 +1329,10 @@ free_hstat (struct http_stat *hs)
   xfree_null (hs->newloc);
   xfree_null (hs->remote_time);
   xfree_null (hs->error);
+  xfree_null (hs->rderrmsg);
+  xfree_null (hs->local_file);
+  xfree_null (hs->orig_file_name);
+  xfree_null (hs->message);
 
   /* Guard against being called twice. */
   hs->newloc = NULL;
@@ -1099,30 +1340,38 @@ free_hstat (struct http_stat *hs)
   hs->error = NULL;
 }
 
-static char *create_authorization_line (const char *, const char *,
-                                       const char *, const char *,
-                                       const char *, bool *);
-static char *basic_authentication_encode (const char *, const char *);
-static bool known_authentication_scheme_p (const char *, const char *);
-
-#define BEGINS_WITH(line, string_constant)                             \
-  (!strncasecmp (line, string_constant, sizeof (string_constant) - 1)  \
-   && (ISSPACE (line[sizeof (string_constant) - 1])                    \
+#define BEGINS_WITH(line, string_constant)                               \
+  (!strncasecmp (line, string_constant, sizeof (string_constant) - 1)    \
+   && (c_isspace (line[sizeof (string_constant) - 1])                      \
        || !line[sizeof (string_constant) - 1]))
 
-#define SET_USER_AGENT(req) do {                                       \
-  if (!opt.useragent)                                                  \
-    request_set_header (req, "User-Agent",                             \
-                       aprintf ("Wget/%s", version_string), rel_value); \
-  else if (*opt.useragent)                                             \
-    request_set_header (req, "User-Agent", opt.useragent, rel_none);   \
+#ifdef __VMS
+#define SET_USER_AGENT(req) do {                                         \
+  if (!opt.useragent)                                                    \
+    request_set_header (req, "User-Agent",                               \
+                        aprintf ("Wget/%s (VMS %s %s)",                  \
+                        version_string, vms_arch(), vms_vers()),         \
+                        rel_value);                                      \
+  else if (*opt.useragent)                                               \
+    request_set_header (req, "User-Agent", opt.useragent, rel_none);     \
 } while (0)
+#else /* def __VMS */
+#define SET_USER_AGENT(req) do {                                         \
+  if (!opt.useragent)                                                    \
+    request_set_header (req, "User-Agent",                               \
+                        aprintf ("Wget/%s (%s)",                         \
+                        version_string, OS_TYPE),                        \
+                        rel_value);                                      \
+  else if (*opt.useragent)                                               \
+    request_set_header (req, "User-Agent", opt.useragent, rel_none);     \
+} while (0)
+#endif /* def __VMS [else] */
 
 /* The flags that allow clobbering the file (opening with "wb").
    Defined here to avoid repetition later.  #### This will require
    rework.  */
 #define ALLOW_CLOBBER (opt.noclobber || opt.always_rest || opt.timestamping \
-                      || opt.dirstruct || opt.output_document)
+                       || opt.dirstruct || opt.output_document)
 
 /* Retrieve a document through HTTP protocol.  It recognizes status
    code, and correctly handles redirections.  It closes the network
@@ -1135,7 +1384,8 @@ static bool known_authentication_scheme_p (const char *, const char *);
    If PROXY is non-NULL, the connection will be made to the proxy
    server, and u->url will be requested.  */
 static uerr_t
-gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
+gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy,
+         struct iri *iri)
 {
   struct request *req;
 
@@ -1151,10 +1401,15 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   int sock = -1;
   int flags;
 
-  /* Set to 1 when the authorization has failed permanently and should
+  /* Set to 1 when the authorization has already been sent and should
      not be tried again. */
   bool auth_finished = false;
 
+  /* Set to 1 when just globally-set Basic authorization has been sent;
+   * should prevent further Basic negotiations, but not other
+   * mechanisms. */
+  bool basic_auth_finished = false;
+
   /* Whether NTLM authentication is used for this request. */
   bool ntlm_seen = false;
 
@@ -1193,30 +1448,26 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   if (u->scheme == SCHEME_HTTPS)
     {
       /* Initialize the SSL context.  After this has once been done,
-        it becomes a no-op.  */
+         it becomes a no-op.  */
       if (!ssl_init ())
-       {
-         scheme_disable (SCHEME_HTTPS);
-         logprintf (LOG_NOTQUIET,
-                    _("Disabling SSL due to encountered errors.\n"));
-         return SSLINITFAILED;
-       }
+        {
+          scheme_disable (SCHEME_HTTPS);
+          logprintf (LOG_NOTQUIET,
+                     _("Disabling SSL due to encountered errors.\n"));
+          return SSLINITFAILED;
+        }
     }
 #endif /* HAVE_SSL */
 
-  if (!head_only)
-    /* If we're doing a GET on the URL, as opposed to just a HEAD, we need to
-       know the local filename so we can save to it. */
-    assert (*hs->local_file != NULL);
-
   /* Initialize certain elements of struct http_stat.  */
   hs->len = 0;
   hs->contlen = -1;
   hs->res = -1;
-  hs->errstr = "";
+  hs->rderrmsg = NULL;
   hs->newloc = NULL;
   hs->remote_time = NULL;
   hs->error = NULL;
+  hs->message = NULL;
 
   conn = u;
 
@@ -1235,12 +1486,12 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
        "param=value", full_path will be "/foo/bar?param=value".  */
     if (proxy
 #ifdef HAVE_SSL
-       /* When using SSL over proxy, CONNECT establishes a direct
-          connection to the HTTPS server.  Therefore use the same
-          argument as when talking to the server directly. */
-       && u->scheme != SCHEME_HTTPS
+        /* When using SSL over proxy, CONNECT establishes a direct
+           connection to the HTTPS server.  Therefore use the same
+           argument as when talking to the server directly. */
+        && u->scheme != SCHEME_HTTPS
 #endif
-       )
+        )
       meth_arg = xstrdup (u->url);
     else
       meth_arg = url_full_path (u);
@@ -1252,9 +1503,9 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
     request_set_header (req, "Pragma", "no-cache", rel_none);
   if (hs->restval)
     request_set_header (req, "Range",
-                       aprintf ("bytes=%s-",
-                                number_to_static_string (hs->restval)),
-                       rel_value);
+                        aprintf ("bytes=%s-",
+                                 number_to_static_string (hs->restval)),
+                        rel_value);
   SET_USER_AGENT (req);
   request_set_header (req, "Accept", "*/*", rel_none);
 
@@ -1265,66 +1516,14 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   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)
-    {
-      /* We have the username and the password, but haven't tried
-        any authorization yet.  Let's see if the "Basic" method
-        works.  If not, we'll come back here and construct a
-        proper authorization method with the right challenges.
-
-        If we didn't employ this kind of logic, every URL that
-        requires authorization would have to be processed twice,
-        which is very suboptimal and generates a bunch of false
-        "unauthorized" errors in the server log.
-
-        #### But this logic also has a serious problem when used
-        with stronger authentications: we *first* transmit the
-        username and the password in clear text, and *then* attempt a
-        stronger authentication scheme.  That cannot be right!  We
-        are only fortunate that almost everyone still uses the
-        `Basic' scheme anyway.
-
-        There should be an option to prevent this from happening, for
-        those who use strong authentication schemes and value their
-        passwords.  */
-      request_set_header (req, "Authorization",
-                         basic_authentication_encode (user, passwd),
-                         rel_value);
-    }
-
-  proxyauth = NULL;
-  if (proxy)
+  /* We only do "site-wide" authentication with "global" user/password
+   * values unless --auth-no-challange has been requested; URL user/password
+   * info overrides. */
+  if (user && passwd && (!u->user || opt.auth_without_challenge))
     {
-      char *proxy_user, *proxy_passwd;
-      /* For normal username and password, URL components override
-        command-line/wgetrc parameters.  With proxy
-        authentication, it's the reverse, because proxy URLs are
-        normally the "permanent" ones, so command-line args
-        should take precedence.  */
-      if (opt.proxy_user && opt.proxy_passwd)
-       {
-         proxy_user = opt.proxy_user;
-         proxy_passwd = opt.proxy_passwd;
-       }
-      else
-       {
-         proxy_user = proxy->user;
-         proxy_passwd = proxy->passwd;
-       }
-      /* #### This does not appear right.  Can't the proxy request,
-        say, `Digest' authentication?  */
-      if (proxy_user && proxy_passwd)
-       proxyauth = basic_authentication_encode (proxy_user, proxy_passwd);
-
-      /* If we're using a proxy, we will be connecting to the proxy
-        server.  */
-      conn = proxy;
-
-      /* Proxy authorization over SSL is handled below. */
-#ifdef HAVE_SSL
-      if (u->scheme != SCHEME_HTTPS)
-#endif
-       request_set_header (req, "Proxy-Authorization", proxyauth, rel_value);
+      /* If this is a host for which we've already received a Basic
+       * challenge, we'll go ahead and send Basic authentication creds. */
+      basic_auth_finished = maybe_send_basic_creds(u->host, user, passwd, req);
     }
 
   /* Generate the Host header, HOST:PORT.  Take into account that:
@@ -1344,8 +1543,8 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
     int add_port = u->port != scheme_default_port (u->scheme);
     int add_squares = strchr (u->host, ':') != NULL;
     request_set_header (req, "Host",
-                       aprintf (hfmt[add_port][add_squares], u->host, u->port),
-                       rel_value);
+                        aprintf (hfmt[add_port][add_squares], u->host, u->port),
+                        rel_value);
   }
 
   if (!inhibit_keep_alive)
@@ -1353,35 +1552,35 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
 
   if (opt.cookies)
     request_set_header (req, "Cookie",
-                       cookie_header (wget_cookie_jar,
-                                      u->host, u->port, u->path,
+                        cookie_header (wget_cookie_jar,
+                                       u->host, u->port, u->path,
 #ifdef HAVE_SSL
-                                      u->scheme == SCHEME_HTTPS
+                                       u->scheme == SCHEME_HTTPS
 #else
-                                      0
+                                       0
 #endif
-                                      ),
-                       rel_value);
+                                       ),
+                        rel_value);
 
   if (opt.post_data || opt.post_file_name)
     {
       request_set_header (req, "Content-Type",
-                         "application/x-www-form-urlencoded", rel_none);
+                          "application/x-www-form-urlencoded", rel_none);
       if (opt.post_data)
-       post_data_size = strlen (opt.post_data);
+        post_data_size = strlen (opt.post_data);
       else
-       {
-         post_data_size = file_size (opt.post_file_name);
-         if (post_data_size == -1)
-           {
-             logprintf (LOG_NOTQUIET, _("POST data file `%s' missing: %s\n"),
-                        opt.post_file_name, strerror (errno));
-             post_data_size = 0;
-           }
-       }
+        {
+          post_data_size = file_size (opt.post_file_name);
+          if (post_data_size == -1)
+            {
+              logprintf (LOG_NOTQUIET, _("POST data file %s missing: %s\n"),
+                         quote (opt.post_file_name), strerror (errno));
+              post_data_size = 0;
+            }
+        }
       request_set_header (req, "Content-Length",
-                         xstrdup (number_to_static_string (post_data_size)),
-                         rel_value);
+                          xstrdup (number_to_static_string (post_data_size)),
+                          rel_value);
     }
 
   /* Add the user headers. */
@@ -1389,7 +1588,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
     {
       int i;
       for (i = 0; opt.user_headers[i]; i++)
-       request_set_user_header (req, opt.user_headers[i]);
+        request_set_user_header (req, opt.user_headers[i]);
     }
 
  retry_with_auth:
@@ -1397,6 +1596,41 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
      without authorization header fails.  (Expected to happen at least
      for the Digest authorization scheme.)  */
 
+  proxyauth = NULL;
+  if (proxy)
+    {
+      char *proxy_user, *proxy_passwd;
+      /* For normal username and password, URL components override
+         command-line/wgetrc parameters.  With proxy
+         authentication, it's the reverse, because proxy URLs are
+         normally the "permanent" ones, so command-line args
+         should take precedence.  */
+      if (opt.proxy_user && opt.proxy_passwd)
+        {
+          proxy_user = opt.proxy_user;
+          proxy_passwd = opt.proxy_passwd;
+        }
+      else
+        {
+          proxy_user = proxy->user;
+          proxy_passwd = proxy->passwd;
+        }
+      /* #### This does not appear right.  Can't the proxy request,
+         say, `Digest' authentication?  */
+      if (proxy_user && proxy_passwd)
+        proxyauth = basic_authentication_encode (proxy_user, proxy_passwd);
+
+      /* If we're using a proxy, we will be connecting to the proxy
+         server.  */
+      conn = proxy;
+
+      /* Proxy authorization over SSL is handled below. */
+#ifdef HAVE_SSL
+      if (u->scheme != SCHEME_HTTPS)
+#endif
+        request_set_header (req, "Proxy-Authorization", proxyauth, rel_value);
+    }
+
   keep_alive = false;
 
   /* Establish the connection.  */
@@ -1404,138 +1638,137 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   if (!inhibit_keep_alive)
     {
       /* Look for a persistent connection to target host, unless a
-        proxy is used.  The exception is when SSL is in use, in which
-        case the proxy is nothing but a passthrough to the target
-        host, registered as a connection to the latter.  */
+         proxy is used.  The exception is when SSL is in use, in which
+         case the proxy is nothing but a passthrough to the target
+         host, registered as a connection to the latter.  */
       struct url *relevant = conn;
 #ifdef HAVE_SSL
       if (u->scheme == SCHEME_HTTPS)
-       relevant = u;
+        relevant = u;
 #endif
 
       if (persistent_available_p (relevant->host, relevant->port,
 #ifdef HAVE_SSL
-                                 relevant->scheme == SCHEME_HTTPS,
+                                  relevant->scheme == SCHEME_HTTPS,
 #else
-                                 0,
+                                  0,
 #endif
-                                 &host_lookup_failed))
-       {
-         sock = pconn.socket;
-         using_ssl = pconn.ssl;
-         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");
-       }
+                                  &host_lookup_failed))
+        {
+          sock = pconn.socket;
+          using_ssl = pconn.ssl;
+          logprintf (LOG_VERBOSE, _("Reusing existing connection to %s:%d.\n"),
+                     quotearg_style (escape_quoting_style, 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");
+        }
+      else if (host_lookup_failed)
+        {
+          request_free (req);
+          logprintf(LOG_NOTQUIET,
+                    _("%s: unable to resolve host address %s\n"),
+                    exec_name, quote (relevant->host));
+          return HOSTERR;
+        }
     }
 
   if (sock < 0)
     {
-      /* In its current implementation, persistent_available_p will
-        look up conn->host in some cases.  If that lookup failed, we
-        don't need to bother with connect_to_host.  */
-      if (host_lookup_failed)
-       {
-         request_free (req);
-         return HOSTERR;
-       }
-
       sock = connect_to_host (conn->host, conn->port);
       if (sock == E_HOST)
-       {
-         request_free (req);
-         return HOSTERR;
-       }
+        {
+          request_free (req);
+          return HOSTERR;
+        }
       else if (sock < 0)
-       {
-         request_free (req);
-         return (retryable_socket_connect_error (errno)
-                 ? CONERROR : CONIMPOSSIBLE);
-       }
+        {
+          request_free (req);
+          return (retryable_socket_connect_error (errno)
+                  ? CONERROR : CONIMPOSSIBLE);
+        }
 
 #ifdef HAVE_SSL
       if (proxy && u->scheme == SCHEME_HTTPS)
-       {
-         /* When requesting SSL URLs through proxies, use the
-            CONNECT method to request passthrough.  */
-         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",
-                                 proxyauth, rel_value);
-             /* Now that PROXYAUTH is part of the CONNECT request,
-                zero it out so we don't send proxy authorization with
-                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 (write_error < 0)
-           {
-             logprintf (LOG_VERBOSE, _("Failed writing to proxy: %s.\n"),
-                        fd_errstr (sock));
-             CLOSE_INVALIDATE (sock);
-             return WRITEFAILED;
-           }
-
-         head = read_http_response_head (sock);
-         if (!head)
-           {
-             logprintf (LOG_VERBOSE, _("Failed reading proxy response: %s\n"),
-                        fd_errstr (sock));
-             CLOSE_INVALIDATE (sock);
-             return HERR;
-           }
-         message = NULL;
-         if (!*head)
-           {
-             xfree (head);
-             goto failed_tunnel;
-           }
-         DEBUGP (("proxy responded with: [%s]\n", head));
-
-         resp = resp_new (head);
-         statcode = resp_status (resp, &message);
-         resp_free (resp);
-         xfree (head);
-         if (statcode != 200)
-           {
-           failed_tunnel:
-             logprintf (LOG_NOTQUIET, _("Proxy tunneling failed: %s"),
-                        message ? escnonprint (message) : "?");
-             xfree_null (message);
-             return CONSSLERR;
-           }
-         xfree_null (message);
-
-         /* SOCK is now *really* connected to u->host, so update CONN
-            to reflect this.  That way register_persistent will
-            register SOCK as being connected to u->host:u->port.  */
-         conn = u;
-       }
+        {
+          /* When requesting SSL URLs through proxies, use the
+             CONNECT method to request passthrough.  */
+          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",
+                                  proxyauth, rel_value);
+              /* Now that PROXYAUTH is part of the CONNECT request,
+                 zero it out so we don't send proxy authorization with
+                 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 (write_error < 0)
+            {
+              CLOSE_INVALIDATE (sock);
+              return WRITEFAILED;
+            }
+
+          head = read_http_response_head (sock);
+          if (!head)
+            {
+              logprintf (LOG_VERBOSE, _("Failed reading proxy response: %s\n"),
+                         fd_errstr (sock));
+              CLOSE_INVALIDATE (sock);
+              return HERR;
+            }
+          message = NULL;
+          if (!*head)
+            {
+              xfree (head);
+              goto failed_tunnel;
+            }
+          DEBUGP (("proxy responded with: [%s]\n", head));
+
+          resp = resp_new (head);
+          statcode = resp_status (resp, &message);
+          hs->message = xstrdup (message);
+          resp_free (resp);
+          xfree (head);
+          if (statcode != 200)
+            {
+            failed_tunnel:
+              logprintf (LOG_NOTQUIET, _("Proxy tunneling failed: %s"),
+                         message ? quotearg_style (escape_quoting_style, message) : "?");
+              xfree_null (message);
+              return CONSSLERR;
+            }
+          xfree_null (message);
+
+          /* SOCK is now *really* connected to u->host, so update CONN
+             to reflect this.  That way register_persistent will
+             register SOCK as being connected to u->host:u->port.  */
+          conn = u;
+        }
 
       if (conn->scheme == SCHEME_HTTPS)
-       {
-         if (!ssl_connect (sock) || !ssl_check_certificate (sock, u->host))
-           {
-             fd_close (sock);
-             return CONSSLERR;
-           }
-         using_ssl = true;
-       }
+        {
+          if (!ssl_connect_wget (sock) || !ssl_check_certificate (sock, u->host))
+            {
+              fd_close (sock);
+              return CONSSLERR;
+            }
+          using_ssl = true;
+        }
 #endif /* HAVE_SSL */
     }
 
@@ -1545,24 +1778,22 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   if (write_error >= 0)
     {
       if (opt.post_data)
-       {
-         DEBUGP (("[POST data: %s]\n", opt.post_data));
-         write_error = fd_write (sock, opt.post_data, post_data_size, -1);
-       }
+        {
+          DEBUGP (("[POST data: %s]\n", opt.post_data));
+          write_error = fd_write (sock, opt.post_data, post_data_size, -1);
+        }
       else if (opt.post_file_name && post_data_size != 0)
-       write_error = post_file (sock, opt.post_file_name, post_data_size);
+        write_error = post_file (sock, opt.post_file_name, post_data_size);
     }
 
   if (write_error < 0)
     {
-      logprintf (LOG_VERBOSE, _("Failed writing HTTP request: %s.\n"),
-                fd_errstr (sock));
       CLOSE_INVALIDATE (sock);
       request_free (req);
       return WRITEFAILED;
     }
   logprintf (LOG_VERBOSE, _("%s request sent, awaiting response... "),
-            proxy ? "Proxy" : "HTTP");
+             proxy ? "Proxy" : "HTTP");
   contlen = -1;
   contrange = 0;
   *dt &= ~RETROKF;
@@ -1571,20 +1802,20 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   if (!head)
     {
       if (errno == 0)
-       {
-         logputs (LOG_NOTQUIET, _("No data received.\n"));
-         CLOSE_INVALIDATE (sock);
-         request_free (req);
-         return HEOF;
-       }
+        {
+          logputs (LOG_NOTQUIET, _("No data received.\n"));
+          CLOSE_INVALIDATE (sock);
+          request_free (req);
+          return HEOF;
+        }
       else
-       {
-         logprintf (LOG_NOTQUIET, _("Read error (%s) in headers.\n"),
-                    fd_errstr (sock));
-         CLOSE_INVALIDATE (sock);
-         request_free (req);
-         return HERR;
-       }
+        {
+          logprintf (LOG_NOTQUIET, _("Read error (%s) in headers.\n"),
+                     fd_errstr (sock));
+          CLOSE_INVALIDATE (sock);
+          request_free (req);
+          return HERR;
+        }
     }
   DEBUGP (("\n---response begin---\n%s---response end---\n", head));
 
@@ -1593,43 +1824,28 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   /* Check for status line.  */
   message = NULL;
   statcode = resp_status (resp, &message);
+  hs->message = xstrdup (message);
   if (!opt.server_response)
     logprintf (LOG_VERBOSE, "%2d %s\n", statcode,
-              message ? escnonprint (message) : "");
+               message ? quotearg_style (escape_quoting_style, message) : "");
   else
     {
       logprintf (LOG_VERBOSE, "\n");
       print_server_response (resp, "  ");
     }
 
-  if (!opt.ignore_length
-      && resp_header_copy (resp, "Content-Length", hdrval, sizeof (hdrval)))
-    {
-      wgint parsed;
-      errno = 0;
-      parsed = str_to_wgint (hdrval, NULL, 10);
-      if (parsed == WGINT_MAX && errno == ERANGE)
-       /* Out of range.
-          #### If Content-Length is out of range, it most likely
-          means that the file is larger than 2G and that we're
-          compiled without LFS.  In that case we should probably
-          refuse to even attempt to download the file.  */
-       contlen = -1;
-      else
-       contlen = parsed;
-    }
-
   /* Check for keep-alive related responses. */
   if (!inhibit_keep_alive && contlen != -1)
     {
       if (resp_header_copy (resp, "Keep-Alive", NULL, 0))
-       keep_alive = true;
+        keep_alive = true;
       else if (resp_header_copy (resp, "Connection", hdrval, sizeof (hdrval)))
-       {
-         if (0 == strcasecmp (hdrval, "Keep-Alive"))
-           keep_alive = true;
-       }
+        {
+          if (0 == strcasecmp (hdrval, "Keep-Alive"))
+            keep_alive = true;
+        }
     }
+
   if (keep_alive)
     /* The server has promised that it will not close the connection
        when we're done.  This means that we can register it.  */
@@ -1639,65 +1855,214 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
     {
       /* Authorization is required.  */
       if (keep_alive && !head_only && skip_short_body (sock, contlen))
-       CLOSE_FINISH (sock);
+        CLOSE_FINISH (sock);
       else
-       CLOSE_INVALIDATE (sock);
+        CLOSE_INVALIDATE (sock);
       pconn.authorized = false;
       if (!auth_finished && (user && passwd))
-       {
-         /* IIS sends multiple copies of WWW-Authenticate, one with
-            the value "negotiate", and other(s) with data.  Loop over
-            all the occurrences and pick the one we recognize.  */
-         int wapos;
-         const char *wabeg, *waend;
-         char *www_authenticate = NULL;
-         for (wapos = 0;
-              (wapos = resp_header_locate (resp, "WWW-Authenticate", wapos,
-                                           &wabeg, &waend)) != -1;
-              ++wapos)
-           if (known_authentication_scheme_p (wabeg, waend))
-             {
-               BOUNDED_TO_ALLOCA (wabeg, waend, www_authenticate);
-               break;
-             }
-
-         if (!www_authenticate)
-           /* If the authentication header is missing or
-              unrecognized, there's no sense in retrying.  */
-           logputs (LOG_NOTQUIET, _("Unknown authentication scheme.\n"));
-         else if (BEGINS_WITH (www_authenticate, "Basic"))
-           /* If the authentication scheme is "Basic", which we send
-              by default, there's no sense in retrying either.  (This
-              should be changed when we stop sending "Basic" data by
-              default.)  */
-           ;
-         else
-           {
-             char *pth;
-             pth = url_full_path (u);
-             request_set_header (req, "Authorization",
-                                 create_authorization_line (www_authenticate,
-                                                            user, passwd,
-                                                            request_method (req),
-                                                            pth,
-                                                            &auth_finished),
-                                 rel_value);
-             if (BEGINS_WITH (www_authenticate, "NTLM"))
-               ntlm_seen = true;
-             xfree (pth);
-             goto retry_with_auth;
-           }
-       }
+        {
+          /* IIS sends multiple copies of WWW-Authenticate, one with
+             the value "negotiate", and other(s) with data.  Loop over
+             all the occurrences and pick the one we recognize.  */
+          int wapos;
+          const char *wabeg, *waend;
+          char *www_authenticate = NULL;
+          for (wapos = 0;
+               (wapos = resp_header_locate (resp, "WWW-Authenticate", wapos,
+                                            &wabeg, &waend)) != -1;
+               ++wapos)
+            if (known_authentication_scheme_p (wabeg, waend))
+              {
+                BOUNDED_TO_ALLOCA (wabeg, waend, www_authenticate);
+                break;
+              }
+
+          if (!www_authenticate)
+            {
+              /* If the authentication header is missing or
+                 unrecognized, there's no sense in retrying.  */
+              logputs (LOG_NOTQUIET, _("Unknown authentication scheme.\n"));
+            }
+          else if (!basic_auth_finished
+                   || !BEGINS_WITH (www_authenticate, "Basic"))
+            {
+              char *pth;
+              pth = url_full_path (u);
+              request_set_header (req, "Authorization",
+                                  create_authorization_line (www_authenticate,
+                                                             user, passwd,
+                                                             request_method (req),
+                                                             pth,
+                                                             &auth_finished),
+                                  rel_value);
+              if (BEGINS_WITH (www_authenticate, "NTLM"))
+                ntlm_seen = true;
+              else if (!u->user && BEGINS_WITH (www_authenticate, "Basic"))
+                {
+                  /* Need to register this host as using basic auth,
+                   * so we automatically send creds next time. */
+                  register_basic_auth_host (u->host);
+                }
+              xfree (pth);
+              xfree_null (message);
+              resp_free (resp);
+              xfree (head);
+              goto retry_with_auth;
+            }
+          else
+            {
+              /* We already did Basic auth, and it failed. Gotta
+               * give up. */
+            }
+        }
       logputs (LOG_NOTQUIET, _("Authorization failed.\n"));
       request_free (req);
+      xfree_null (message);
+      resp_free (resp);
+      xfree (head);
       return AUTHFAILED;
     }
   else /* statcode != HTTP_STATUS_UNAUTHORIZED */
     {
       /* Kludge: if NTLM is used, mark the TCP connection as authorized. */
       if (ntlm_seen)
-       pconn.authorized = true;
+        pconn.authorized = true;
+    }
+
+  /* Determine the local filename if needed. Notice that if -O is used 
+   * hstat.local_file is set by http_loop to the argument of -O. */
+  if (!hs->local_file)
+    {
+      /* Honor Content-Disposition whether possible. */
+      if (!opt.content_disposition
+          || !resp_header_copy (resp, "Content-Disposition", 
+                                hdrval, sizeof (hdrval))
+          || !parse_content_disposition (hdrval, &hs->local_file))
+        {
+          /* The Content-Disposition header is missing or broken. 
+           * Choose unique file name according to given URL. */
+          hs->local_file = url_file_name (u);
+        }
+    }
+
+  /* TODO: perform this check only once. */
+  if (!hs->existence_checked && file_exists_p (hs->local_file))
+    {
+      if (opt.noclobber && !opt.output_document)
+        {
+          /* If opt.noclobber is turned on and file already exists, do not
+             retrieve the file. But if the output_document was given, then this
+             test was already done and the file didn't exist. Hence the !opt.output_document */
+          logprintf (LOG_VERBOSE, _("\
+File %s already there; not retrieving.\n\n"), quote (hs->local_file));
+          /* If the file is there, we suppose it's retrieved OK.  */
+          *dt |= RETROKF;
+
+          /* #### Bogusness alert.  */
+          /* If its suffix is "html" or "htm" or similar, assume text/html.  */
+          if (has_html_suffix_p (hs->local_file))
+            *dt |= TEXTHTML;
+
+          xfree (head);
+          xfree_null (message);
+          return RETRUNNEEDED;
+        }
+      else if (!ALLOW_CLOBBER)
+        {
+          char *unique = unique_name (hs->local_file, true);
+          if (unique != hs->local_file)
+            xfree (hs->local_file);
+          hs->local_file = unique;
+        }
+    }
+  hs->existence_checked = true;
+
+  /* Support timestamping */
+  /* TODO: move this code out of gethttp. */
+  if (opt.timestamping && !hs->timestamp_checked)
+    {
+      size_t filename_len = strlen (hs->local_file);
+      char *filename_plus_orig_suffix = alloca (filename_len + sizeof (ORIG_SFX));
+      bool local_dot_orig_file_exists = false;
+      char *local_filename = NULL;
+      struct_stat st;
+
+      if (opt.backup_converted)
+        /* If -K is specified, we'll act on the assumption that it was specified
+           last time these files were downloaded as well, and instead of just
+           comparing local file X against server file X, we'll compare local
+           file X.orig (if extant, else X) against server file X.  If -K
+           _wasn't_ specified last time, or the server contains files called
+           *.orig, -N will be back to not operating correctly with -k. */
+        {
+          /* Would a single s[n]printf() call be faster?  --dan
+
+             Definitely not.  sprintf() is horribly slow.  It's a
+             different question whether the difference between the two
+             affects a program.  Usually I'd say "no", but at one
+             point I profiled Wget, and found that a measurable and
+             non-negligible amount of time was lost calling sprintf()
+             in url.c.  Replacing sprintf with inline calls to
+             strcpy() and number_to_string() made a difference.
+             --hniksic */
+          memcpy (filename_plus_orig_suffix, hs->local_file, filename_len);
+          memcpy (filename_plus_orig_suffix + filename_len,
+                  ORIG_SFX, sizeof (ORIG_SFX));
+
+          /* Try to stat() the .orig file. */
+          if (stat (filename_plus_orig_suffix, &st) == 0)
+            {
+              local_dot_orig_file_exists = true;
+              local_filename = filename_plus_orig_suffix;
+            }
+        }
+
+      if (!local_dot_orig_file_exists)
+        /* Couldn't stat() <file>.orig, so try to stat() <file>. */
+        if (stat (hs->local_file, &st) == 0)
+          local_filename = hs->local_file;
+
+      if (local_filename != NULL)
+        /* There was a local file, so we'll check later to see if the version
+           the server has is the same version we already have, allowing us to
+           skip a download. */
+        {
+          hs->orig_file_name = xstrdup (local_filename);
+          hs->orig_file_size = st.st_size;
+          hs->orig_file_tstamp = st.st_mtime;
+#ifdef WINDOWS
+          /* Modification time granularity is 2 seconds for Windows, so
+             increase local time by 1 second for later comparison. */
+          ++hs->orig_file_tstamp;
+#endif
+        }
+    }
+
+  if (!opt.ignore_length
+      && resp_header_copy (resp, "Content-Length", hdrval, sizeof (hdrval)))
+    {
+      wgint parsed;
+      errno = 0;
+      parsed = str_to_wgint (hdrval, NULL, 10);
+      if (parsed == WGINT_MAX && errno == ERANGE)
+        {
+          /* Out of range.
+             #### If Content-Length is out of range, it most likely
+             means that the file is larger than 2G and that we're
+             compiled without LFS.  In that case we should probably
+             refuse to even attempt to download the file.  */
+          contlen = -1;
+        }
+      else if (parsed < 0)
+        {
+          /* Negative Content-Length; nonsensical, so we can't
+             assume any information about the content to receive. */
+          contlen = -1;
+        }
+      else
+        contlen = parsed;
     }
+
   request_free (req);
 
   hs->statcode = statcode;
@@ -1707,18 +2072,29 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
     hs->error = xstrdup (_("(no description)"));
   else
     hs->error = xstrdup (message);
-  xfree (message);
+  xfree_null (message);
 
   type = resp_header_strdup (resp, "Content-Type");
   if (type)
     {
       char *tmp = strchr (type, ';');
       if (tmp)
-       {
-         while (tmp > type && ISSPACE (tmp[-1]))
-           --tmp;
-         *tmp = '\0';
-       }
+        {
+          /* sXXXav: only needed if IRI support is enabled */
+          char *tmp2 = tmp + 1;
+
+          while (tmp > type && c_isspace (tmp[-1]))
+            --tmp;
+          *tmp = '\0';
+
+          /* Try to get remote encoding if needed */
+          if (opt.enable_iri && !opt.encoding_remote)
+            {
+              tmp = parse_charset (tmp2);
+              if (tmp)
+                set_content_encoding (iri, tmp);
+            }
+        }
     }
   hs->newloc = resp_header_strdup (resp, "Location");
   hs->remote_time = resp_header_strdup (resp, "Last-Modified");
@@ -1731,22 +2107,25 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
       /* The jar should have been created by now. */
       assert (wget_cookie_jar != NULL);
       for (scpos = 0;
-          (scpos = resp_header_locate (resp, "Set-Cookie", scpos,
-                                       &scbeg, &scend)) != -1;
-          ++scpos)
-       {
-         char *set_cookie; BOUNDED_TO_ALLOCA (scbeg, scend, set_cookie);
-         cookie_handle_set_cookie (wget_cookie_jar, u->host, u->port,
-                                   u->path, set_cookie);
-       }
+           (scpos = resp_header_locate (resp, "Set-Cookie", scpos,
+                                        &scbeg, &scend)) != -1;
+           ++scpos)
+        {
+          char *set_cookie; BOUNDED_TO_ALLOCA (scbeg, scend, set_cookie);
+          cookie_handle_set_cookie (wget_cookie_jar, u->host, u->port,
+                                    u->path, set_cookie);
+        }
     }
 
   if (resp_header_copy (resp, "Content-Range", hdrval, sizeof (hdrval)))
     {
       wgint first_byte_pos, last_byte_pos, entity_length;
       if (parse_content_range (hdrval, &first_byte_pos, &last_byte_pos,
-                              &entity_length))
-       contrange = first_byte_pos;
+                               &entity_length))
+        {
+          contrange = first_byte_pos;
+          contlen = last_byte_pos - first_byte_pos + 1;
+        }
     }
   resp_free (resp);
 
@@ -1758,25 +2137,26 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   if (H_REDIRECTED (statcode) || statcode == HTTP_STATUS_MULTIPLE_CHOICES)
     {
       /* RFC2068 says that in case of the 300 (multiple choices)
-        response, the server can output a preferred URL through
-        `Location' header; otherwise, the request should be treated
-        like GET.  So, if the location is set, it will be a
-        redirection; otherwise, just proceed normally.  */
+         response, the server can output a preferred URL through
+         `Location' header; otherwise, the request should be treated
+         like GET.  So, if the location is set, it will be a
+         redirection; otherwise, just proceed normally.  */
       if (statcode == HTTP_STATUS_MULTIPLE_CHOICES && !hs->newloc)
-       *dt |= RETROKF;
+        *dt |= RETROKF;
       else
-       {
-         logprintf (LOG_VERBOSE,
-                    _("Location: %s%s\n"),
-                    hs->newloc ? escnonprint_uri (hs->newloc) : _("unspecified"),
-                    hs->newloc ? _(" [following]") : "");
-         if (keep_alive && !head_only && skip_short_body (sock, contlen))
-           CLOSE_FINISH (sock);
-         else
-           CLOSE_INVALIDATE (sock);
-         xfree_null (type);
-         return NEWLOCATION;
-       }
+        {
+          logprintf (LOG_VERBOSE,
+                     _("Location: %s%s\n"),
+                     hs->newloc ? escnonprint_uri (hs->newloc) : _("unspecified"),
+                     hs->newloc ? _(" [following]") : "");
+          if (keep_alive && !head_only && skip_short_body (sock, contlen))
+            CLOSE_FINISH (sock);
+          else
+            CLOSE_INVALIDATE (sock);
+          xfree_null (type);
+          xfree (head);
+          return NEWLOCATION;
+        }
     }
 
   /* If content-type is not given, assume text/html.  This is because
@@ -1784,47 +2164,42 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
      content-type.  */
   if (!type ||
         0 == strncasecmp (type, TEXTHTML_S, strlen (TEXTHTML_S)) ||
-        0 == strncasecmp (type, TEXTXHTML_S, strlen (TEXTXHTML_S)))
+        0 == strncasecmp (type, TEXTXHTML_S, strlen (TEXTXHTML_S)))    
     *dt |= TEXTHTML;
   else
     *dt &= ~TEXTHTML;
 
-  if (opt.html_extension && (*dt & TEXTHTML))
-    /* -E / --html-extension / html_extension = on was specified, and this is a
-       text/html file.  If some case-insensitive variation on ".htm[l]" isn't
-       already the file's suffix, tack on ".html". */
-    {
-      char *last_period_in_local_filename = strrchr (*hs->local_file, '.');
-
-      if (last_period_in_local_filename == NULL
-         || !(0 == strcasecmp (last_period_in_local_filename, ".htm")
-              || 0 == strcasecmp (last_period_in_local_filename, ".html")))
-       {
-         int local_filename_len = strlen (*hs->local_file);
-         /* Resize the local file, allowing for ".html" preceded by
-            optional ".NUMBER".  */
-         *hs->local_file = xrealloc (*hs->local_file,
-                                     local_filename_len + 24 + sizeof (".html"));
-         strcpy(*hs->local_file + local_filename_len, ".html");
-         /* If clobbering is not allowed and the file, as named,
-            exists, tack on ".NUMBER.html" instead. */
-         if (!ALLOW_CLOBBER)
-           {
-             int ext_num = 1;
-             do
-               sprintf (*hs->local_file + local_filename_len,
-                        ".%d.html", ext_num++);
-             while (file_exists_p (*hs->local_file));
-           }
-         *dt |= ADDED_HTML_EXTENSION;
-       }
-    }
-
-  if (statcode == HTTP_STATUS_RANGE_NOT_SATISFIABLE)
+  if (type &&
+      0 == strncasecmp (type, TEXTCSS_S, strlen (TEXTCSS_S)))
+    *dt |= TEXTCSS;
+  else
+    *dt &= ~TEXTCSS;
+
+  if (opt.adjust_extension)
+    {
+      if (*dt & TEXTHTML)
+        /* -E / --adjust-extension / adjust_extension = on was specified,
+           and this is a text/html file.  If some case-insensitive
+           variation on ".htm[l]" isn't already the file's suffix,
+           tack on ".html". */
+        {
+          ensure_extension (hs, ".html", dt);
+        }
+      else if (*dt & TEXTCSS)
+        {
+          ensure_extension (hs, ".css", dt);
+        }
+    }
+
+  if (statcode == HTTP_STATUS_RANGE_NOT_SATISFIABLE
+      || (hs->restval > 0 && statcode == HTTP_STATUS_OK
+          && contrange == 0 && hs->restval >= contlen)
+     )
     {
       /* If `-c' is in use and the file has been fully downloaded (or
-        the remote file has shrunk), Wget effectively requests bytes
-        after the end of file and the server response with 416.  */
+         the remote file has shrunk), Wget effectively requests bytes
+         after the end of file and the server response with 416
+         (or 200 with a <= Content-Length.  */
       logputs (LOG_VERBOSE, _("\
 \n    The file is already fully retrieved; nothing to do.\n\n"));
       /* In case the caller inspects. */
@@ -1833,57 +2208,62 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
       /* Mark as successfully retrieved. */
       *dt |= RETROKF;
       xfree_null (type);
-      CLOSE_INVALIDATE (sock); /* would be CLOSE_FINISH, but there
-                                  might be more bytes in the body. */
+      CLOSE_INVALIDATE (sock);        /* would be CLOSE_FINISH, but there
+                                   might be more bytes in the body. */
+      xfree (head);
       return RETRUNNEEDED;
     }
   if ((contrange != 0 && contrange != hs->restval)
       || (H_PARTIAL (statcode) && !contrange))
     {
       /* The Range request was somehow misunderstood by the server.
-        Bail out.  */
+         Bail out.  */
       xfree_null (type);
       CLOSE_INVALIDATE (sock);
+      xfree (head);
       return RANGEERR;
     }
-  hs->contlen = contlen + contrange;
+  if (contlen == -1)
+    hs->contlen = -1;
+  else
+    hs->contlen = contlen + contrange;
 
   if (opt.verbose)
     {
       if (*dt & RETROKF)
-       {
-         /* No need to print this output if the body won't be
-            downloaded at all, or if the original server response is
-            printed.  */
-         logputs (LOG_VERBOSE, _("Length: "));
-         if (contlen != -1)
-           {
-             logputs (LOG_VERBOSE, number_to_static_string (contlen + contrange));
-             if (contlen + contrange >= 1024)
-               logprintf (LOG_VERBOSE, " (%s)",
-                          human_readable (contlen + contrange));
-             if (contrange)
-               {
-                 if (contlen >= 1024)
-                   logprintf (LOG_VERBOSE, _(", %s (%s) remaining"),
-                              number_to_static_string (contlen),
-                              human_readable (contlen));
-                 else
-                   logprintf (LOG_VERBOSE, _(", %s remaining"),
-                              number_to_static_string (contlen));
-               }
-           }
-         else
-           logputs (LOG_VERBOSE,
-                    opt.ignore_length ? _("ignored") : _("unspecified"));
-         if (type)
-           logprintf (LOG_VERBOSE, " [%s]\n", escnonprint (type));
-         else
-           logputs (LOG_VERBOSE, "\n");
-       }
+        {
+          /* No need to print this output if the body won't be
+             downloaded at all, or if the original server response is
+             printed.  */
+          logputs (LOG_VERBOSE, _("Length: "));
+          if (contlen != -1)
+            {
+              logputs (LOG_VERBOSE, number_to_static_string (contlen + contrange));
+              if (contlen + contrange >= 1024)
+                logprintf (LOG_VERBOSE, " (%s)",
+                           human_readable (contlen + contrange));
+              if (contrange)
+                {
+                  if (contlen >= 1024)
+                    logprintf (LOG_VERBOSE, _(", %s (%s) remaining"),
+                               number_to_static_string (contlen),
+                               human_readable (contlen));
+                  else
+                    logprintf (LOG_VERBOSE, _(", %s remaining"),
+                               number_to_static_string (contlen));
+                }
+            }
+          else
+            logputs (LOG_VERBOSE,
+                     opt.ignore_length ? _("ignored") : _("unspecified"));
+          if (type)
+            logprintf (LOG_VERBOSE, " [%s]\n", quotearg_style (escape_quoting_style, type));
+          else
+            logputs (LOG_VERBOSE, "\n");
+        }
     }
   xfree_null (type);
-  type = NULL;                 /* We don't need it any more.  */
+  type = NULL;                        /* We don't need it any more.  */
 
   /* Return if we have no intention of further downloading.  */
   if (!(*dt & RETROKF) || head_only)
@@ -1893,59 +2273,97 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
       hs->res = 0;
       xfree_null (type);
       if (head_only)
-       /* Pre-1.10 Wget used CLOSE_INVALIDATE here.  Now we trust the
-          servers not to send body in response to a HEAD request, and
-          those that do will likely be caught by test_socket_open.
-          If not, they can be worked around using
-          `--no-http-keep-alive'.  */
-       CLOSE_FINISH (sock);
+        /* Pre-1.10 Wget used CLOSE_INVALIDATE here.  Now we trust the
+           servers not to send body in response to a HEAD request, and
+           those that do will likely be caught by test_socket_open.
+           If not, they can be worked around using
+           `--no-http-keep-alive'.  */
+        CLOSE_FINISH (sock);
       else if (keep_alive && skip_short_body (sock, contlen))
-       /* Successfully skipped the body; also keep using the socket. */
-       CLOSE_FINISH (sock);
+        /* Successfully skipped the body; also keep using the socket. */
+        CLOSE_FINISH (sock);
       else
-       CLOSE_INVALIDATE (sock);
+        CLOSE_INVALIDATE (sock);
+      xfree (head);
       return RETRFINISHED;
     }
 
+/* 2005-06-17 SMS.
+   For VMS, define common fopen() optional arguments.
+*/
+#ifdef __VMS
+# define FOPEN_OPT_ARGS "fop=sqo", "acc", acc_cb, &open_id
+# define FOPEN_BIN_FLAG 3
+#else /* def __VMS */
+# define FOPEN_BIN_FLAG 1
+#endif /* def __VMS [else] */
+
   /* Open the local file.  */
   if (!output_stream)
     {
-      mkalldirs (*hs->local_file);
+      mkalldirs (hs->local_file);
       if (opt.backups)
-       rotate_backups (*hs->local_file);
+        rotate_backups (hs->local_file);
       if (hs->restval)
-       fp = fopen (*hs->local_file, "ab");
+        {
+#ifdef __VMS
+          int open_id;
+
+          open_id = 21;
+          fp = fopen (hs->local_file, "ab", FOPEN_OPT_ARGS);
+#else /* def __VMS */
+          fp = fopen (hs->local_file, "ab");
+#endif /* def __VMS [else] */
+        }
       else if (ALLOW_CLOBBER)
-       fp = fopen (*hs->local_file, "wb");
+        {
+#ifdef __VMS
+          int open_id;
+
+          open_id = 22;
+          fp = fopen (hs->local_file, "wb", FOPEN_OPT_ARGS);
+#else /* def __VMS */
+          fp = fopen (hs->local_file, "wb");
+#endif /* def __VMS [else] */
+        }
       else
-       {
-         fp = fopen_excl (*hs->local_file, true);
-         if (!fp && errno == EEXIST)
-           {
-             /* We cannot just invent a new name and use it (which is
-                what functions like unique_create typically do)
-                because we told the user we'd use this name.
-                Instead, return and retry the download.  */
-             logprintf (LOG_NOTQUIET,
-                        _("%s has sprung into existence.\n"),
-                        *hs->local_file);
-             CLOSE_INVALIDATE (sock);
-             return FOPEN_EXCL_ERR;
-           }
-       }
+        {
+          fp = fopen_excl (hs->local_file, true);
+          if (!fp && errno == EEXIST)
+            {
+              /* We cannot just invent a new name and use it (which is
+                 what functions like unique_create typically do)
+                 because we told the user we'd use this name.
+                 Instead, return and retry the download.  */
+              logprintf (LOG_NOTQUIET,
+                         _("%s has sprung into existence.\n"),
+                         hs->local_file);
+              CLOSE_INVALIDATE (sock);
+              xfree (head);
+              return FOPEN_EXCL_ERR;
+            }
+        }
       if (!fp)
-       {
-         logprintf (LOG_NOTQUIET, "%s: %s\n", *hs->local_file, strerror (errno));
-         CLOSE_INVALIDATE (sock);
-         return FOPENERR;
-       }
+        {
+          logprintf (LOG_NOTQUIET, "%s: %s\n", hs->local_file, strerror (errno));
+          CLOSE_INVALIDATE (sock);
+          xfree (head);
+          return FOPENERR;
+        }
     }
   else
     fp = output_stream;
 
-  /* #### This confuses the timestamping code that checks for file
-     size.  Maybe we should save some additional information?  */
-  if (opt.save_headers)
+  /* Print fetch message, if opt.verbose.  */
+  if (opt.verbose)
+    {
+      logprintf (LOG_NOTQUIET, _("Saving to: %s\n"), 
+                 HYPHENP (hs->local_file) ? quote ("STDOUT") : quote (hs->local_file));
+    }
+    
+  /* This confuses the timestamping code that checks for file size.
+     #### The timestamping code should be smarter about file size.  */
+  if (opt.save_headers && hs->restval == 0)
     fwrite (head, 1, strlen (head), fp);
 
   /* Now we no longer need to store the response header. */
@@ -1953,7 +2371,10 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
 
   /* Download the request body.  */
   flags = 0;
-  if (keep_alive)
+  if (contlen != -1)
+    /* If content-length is present, read that much; otherwise, read
+       until EOF.  The HTTP spec doesn't require the server to
+       actually close the connection when it's done sending data. */
     flags |= rb_read_exactly;
   if (hs->restval > 0 && contrange == 0)
     /* If the server ignored our range request, instruct fd_read_body
@@ -1962,15 +2383,15 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   hs->len = hs->restval;
   hs->rd_size = 0;
   hs->res = fd_read_body (sock, fp, contlen != -1 ? contlen : 0,
-                         hs->restval, &hs->rd_size, &hs->len, &hs->dltime,
-                         flags);
+                          hs->restval, &hs->rd_size, &hs->len, &hs->dltime,
+                          flags);
 
   if (hs->res >= 0)
     CLOSE_FINISH (sock);
   else
     {
       if (hs->res < 0)
-       hs->errstr = fd_errstr (sock);
+        hs->rderrmsg = xstrdup (fd_errstr (sock));
       CLOSE_INVALIDATE (sock);
     }
 
@@ -1985,541 +2406,542 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
    retried, and retried, and retried, and...  */
 uerr_t
 http_loop (struct url *u, char **newloc, char **local_file, const char *referer,
-          int *dt, struct url *proxy)
+           int *dt, struct url *proxy, struct iri *iri)
 {
   int count;
-  bool use_ts, got_head = false;/* time-stamping info */
-  char *filename_plus_orig_suffix;
-  char *local_filename = NULL;
-  char *tms, *locf;
+  bool got_head = false;         /* used for time-stamping and filename detection */
+  bool time_came_from_head = false;
+  bool got_name = false;
+  char *tms;
   const char *tmrate;
-  uerr_t err;
-  time_t tml = -1, tmr = -1;   /* local and remote time-stamps */
-  wgint local_size = 0;                /* the size of the local file */
-  size_t filename_len;
-  struct http_stat hstat;      /* HTTP status */
+  uerr_t err, ret = TRYLIMEXC;
+  time_t tmr = -1;               /* remote time-stamp */
+  struct http_stat hstat;        /* HTTP status */
   struct_stat st;
-  char *dummy = NULL;
+  bool send_head_first = true;
+  char *file_name;
+
+  /* Assert that no value for *LOCAL_FILE was passed. */
+  assert (local_file == NULL || *local_file == NULL);
+
+  /* Set LOCAL_FILE parameter. */
+  if (local_file && opt.output_document)
+    *local_file = HYPHENP (opt.output_document) ? NULL : xstrdup (opt.output_document);
+
+  /* Reset NEWLOC parameter. */
+  *newloc = NULL;
 
   /* This used to be done in main(), but it's a better idea to do it
      here so that we don't go through the hoops if we're just using
      FTP or whatever. */
   if (opt.cookies)
-    {
-      if (!wget_cookie_jar)
-       wget_cookie_jar = cookie_jar_new ();
-      if (opt.cookies_input && !cookies_loaded_p)
-       {
-         cookie_jar_load (wget_cookie_jar, opt.cookies_input);
-         cookies_loaded_p = true;
-       }
-    }
-
-  *newloc = NULL;
+    load_cookies();
 
-  /* Warn on (likely bogus) wildcard usage in HTTP.  */
+  /* Warn on (likely bogus) wildcard usage in HTTP. */
   if (opt.ftp_glob && has_wildcards_p (u->path))
     logputs (LOG_VERBOSE, _("Warning: wildcards not supported in HTTP.\n"));
 
+  /* Setup hstat struct. */
   xzero (hstat);
+  hstat.referer = referer;
 
-  /* Determine the local filename.  */
-  if (local_file && *local_file)
-    hstat.local_file = local_file;
-  else if (local_file && !opt.output_document)
+  if (opt.output_document)
     {
-      *local_file = url_file_name (u);
-      hstat.local_file = local_file;
+      hstat.local_file = xstrdup (opt.output_document);
+      got_name = true;
     }
-  else
+  else if (!opt.content_disposition)
     {
-      dummy = url_file_name (u);
-      hstat.local_file = &dummy;
-      /* be honest about where we will save the file */
-      if (local_file && opt.output_document)
-        *local_file = HYPHENP (opt.output_document) ? NULL : xstrdup (opt.output_document);
+      hstat.local_file = url_file_name (u);
+      got_name = true;
     }
 
-  if (!opt.output_document)
-    locf = *hstat.local_file;
-  else
-    locf = opt.output_document;
-
-  hstat.referer = referer;
-
-  filename_len = strlen (*hstat.local_file);
-  filename_plus_orig_suffix = alloca (filename_len + sizeof (".orig"));
-
-  if (opt.noclobber && file_exists_p (*hstat.local_file))
+  /* TODO: Ick! This code is now in both gethttp and http_loop, and is
+   * screaming for some refactoring. */
+  if (got_name && file_exists_p (hstat.local_file) && opt.noclobber && !opt.output_document)
     {
       /* If opt.noclobber is turned on and file already exists, do not
-        retrieve the file */
+         retrieve the file. But if the output_document was given, then this
+         test was already done and the file didn't exist. Hence the !opt.output_document */
       logprintf (LOG_VERBOSE, _("\
-File `%s' already there; not retrieving.\n\n"), *hstat.local_file);
+File %s already there; not retrieving.\n\n"),
+                 quote (hstat.local_file));
       /* If the file is there, we suppose it's retrieved OK.  */
       *dt |= RETROKF;
 
       /* #### Bogusness alert.  */
       /* If its suffix is "html" or "htm" or similar, assume text/html.  */
-      if (has_html_suffix_p (*hstat.local_file))
-       *dt |= TEXTHTML;
+      if (has_html_suffix_p (hstat.local_file))
+        *dt |= TEXTHTML;
 
-      xfree_null (dummy);
-      return RETROK;
+      ret = RETROK;
+      goto exit;
     }
 
-  use_ts = false;
-  if (opt.timestamping)
-    {
-      bool local_dot_orig_file_exists = false;
-
-      if (opt.backup_converted)
-       /* If -K is specified, we'll act on the assumption that it was specified
-          last time these files were downloaded as well, and instead of just
-          comparing local file X against server file X, we'll compare local
-          file X.orig (if extant, else X) against server file X.  If -K
-          _wasn't_ specified last time, or the server contains files called
-          *.orig, -N will be back to not operating correctly with -k. */
-       {
-         /* Would a single s[n]printf() call be faster?  --dan
-
-            Definitely not.  sprintf() is horribly slow.  It's a
-            different question whether the difference between the two
-            affects a program.  Usually I'd say "no", but at one
-            point I profiled Wget, and found that a measurable and
-            non-negligible amount of time was lost calling sprintf()
-            in url.c.  Replacing sprintf with inline calls to
-            strcpy() and number_to_string() made a difference.
-            --hniksic */
-         memcpy (filename_plus_orig_suffix, *hstat.local_file, filename_len);
-         memcpy (filename_plus_orig_suffix + filename_len,
-                 ".orig", sizeof (".orig"));
-
-         /* Try to stat() the .orig file. */
-         if (stat (filename_plus_orig_suffix, &st) == 0)
-           {
-             local_dot_orig_file_exists = 1;
-             local_filename = filename_plus_orig_suffix;
-           }
-       }      
-
-      if (!local_dot_orig_file_exists)
-       /* Couldn't stat() <file>.orig, so try to stat() <file>. */
-       if (stat (*hstat.local_file, &st) == 0)
-         local_filename = *hstat.local_file;
-
-      if (local_filename != NULL)
-       /* There was a local file, so we'll check later to see if the version
-          the server has is the same version we already have, allowing us to
-          skip a download. */
-       {
-         use_ts = true;
-         tml = st.st_mtime;
-#ifdef WINDOWS
-         /* Modification time granularity is 2 seconds for Windows, so
-            increase local time by 1 second for later comparison. */
-         tml++;
-#endif
-         local_size = st.st_size;
-         got_head = false;
-       }
-    }
-  /* Reset the counter.  */
+  /* Reset the counter. */
   count = 0;
+
+  /* Reset the document type. */
   *dt = 0;
+
+  /* Skip preliminary HEAD request if we're not in spider mode AND
+   * if -O was given or HTTP Content-Disposition support is disabled. */
+  if (!opt.spider
+      && (got_name || !opt.content_disposition))
+    send_head_first = false;
+
+  /* Send preliminary HEAD request if -N is given and we have an existing 
+   * destination file. */
+  file_name = url_file_name (u);
+  if (opt.timestamping
+      && !opt.content_disposition
+      && file_exists_p (file_name))
+    send_head_first = true;
+  xfree (file_name);
+  
   /* THE loop */
   do
     {
       /* Increment the pass counter.  */
       ++count;
       sleep_between_retrievals (count);
+
       /* Get the current time string.  */
-      tms = time_str (NULL);
+      tms = datetime_str (time (NULL));
+
+      if (opt.spider && !got_head)
+        logprintf (LOG_VERBOSE, _("\
+Spider mode enabled. Check if remote file exists.\n"));
+
       /* Print fetch message, if opt.verbose.  */
       if (opt.verbose)
-       {
-         char *hurl = url_string (u, true);
-         char tmp[256];
-         strcpy (tmp, "        ");
-         if (count > 1)
-           sprintf (tmp, _("(try:%2d)"), count);
-         logprintf (LOG_VERBOSE, "--%s--  %s\n  %s => `%s'\n",
-                    tms, hurl, tmp, locf);
+        {
+          char *hurl = url_string (u, URL_AUTH_HIDE_PASSWD);
+
+          if (count > 1)
+            {
+              char tmp[256];
+              sprintf (tmp, _("(try:%2d)"), count);
+              logprintf (LOG_NOTQUIET, "--%s--  %s  %s\n",
+                         tms, tmp, hurl);
+            }
+          else
+            {
+              logprintf (LOG_NOTQUIET, "--%s--  %s\n",
+                         tms, hurl);
+            }
+
 #ifdef WINDOWS
-         ws_changetitle (hurl);
+          ws_changetitle (hurl);
 #endif
-         xfree (hurl);
-       }
+          xfree (hurl);
+        }
 
       /* Default document type is empty.  However, if spider mode is
-        on or time-stamping is employed, HEAD_ONLY commands is
-        encoded within *dt.  */
-      if (opt.spider || (use_ts && !got_head))
-       *dt |= HEAD_ONLY;
+         on or time-stamping is employed, HEAD_ONLY commands is
+         encoded within *dt.  */
+      if (send_head_first && !got_head)
+        *dt |= HEAD_ONLY;
       else
-       *dt &= ~HEAD_ONLY;
+        *dt &= ~HEAD_ONLY;
 
       /* Decide whether or not to restart.  */
       if (opt.always_rest
-         && stat (locf, &st) == 0
-         && S_ISREG (st.st_mode))
-       /* When -c is used, continue from on-disk size.  (Can't use
-          hstat.len even if count>1 because we don't want a failed
-          first attempt to clobber existing data.)  */
-       hstat.restval = st.st_size;
+          && got_name
+          && stat (hstat.local_file, &st) == 0
+          && S_ISREG (st.st_mode))
+        /* When -c is used, continue from on-disk size.  (Can't use
+           hstat.len even if count>1 because we don't want a failed
+           first attempt to clobber existing data.)  */
+        hstat.restval = st.st_size;
       else if (count > 1)
-       /* otherwise, continue where the previous try left off */
-       hstat.restval = hstat.len;
+        /* otherwise, continue where the previous try left off */
+        hstat.restval = hstat.len;
       else
-       hstat.restval = 0;
+        hstat.restval = 0;
 
       /* Decide whether to send the no-cache directive.  We send it in
-        two cases:
-          a) we're using a proxy, and we're past our first retrieval.
-             Some proxies are notorious for caching incomplete data, so
-             we require a fresh get.
-          b) caching is explicitly inhibited. */
-      if ((proxy && count > 1) /* a */
-         || !opt.allow_cache   /* b */
-         )
-       *dt |= SEND_NOCACHE;
+         two cases:
+           a) we're using a proxy, and we're past our first retrieval.
+              Some proxies are notorious for caching incomplete data, so
+              we require a fresh get.
+           b) caching is explicitly inhibited. */
+      if ((proxy && count > 1)        /* a */
+          || !opt.allow_cache)        /* b */
+        *dt |= SEND_NOCACHE;
       else
-       *dt &= ~SEND_NOCACHE;
+        *dt &= ~SEND_NOCACHE;
 
       /* Try fetching the document, or at least its head.  */
-      err = gethttp (u, &hstat, dt, proxy);
-
-      /* It's unfortunate that wget determines the local filename before finding
-        out the Content-Type of the file.  Barring a major restructuring of the
-        code, we need to re-set locf here, since gethttp() may have xrealloc()d
-        *hstat.local_file to tack on ".html". */
-      if (!opt.output_document)
-       locf = *hstat.local_file;
+      err = gethttp (u, &hstat, dt, proxy, iri);
 
       /* Time?  */
-      tms = time_str (NULL);
+      tms = datetime_str (time (NULL));
+
       /* Get the new location (with or without the redirection).  */
       if (hstat.newloc)
-       *newloc = xstrdup (hstat.newloc);
+        *newloc = xstrdup (hstat.newloc);
+
       switch (err)
-       {
-       case HERR: case HEOF: case CONSOCKERR: case CONCLOSED:
-       case CONERROR: case READERR: case WRITEFAILED:
-       case RANGEERR: case FOPEN_EXCL_ERR:
-         /* Non-fatal errors continue executing the loop, which will
-            bring them to "while" statement at the end, to judge
-            whether the number of tries was exceeded.  */
-         free_hstat (&hstat);
-         printwhat (count, opt.ntry);
-         if (err == FOPEN_EXCL_ERR)
-           {
-             /* Re-determine the file name. */
-             if (local_file && *local_file)
-               {
-                 xfree (*local_file);
-                 *local_file = url_file_name (u);
-                 hstat.local_file = local_file;
-               }
-             else
-               {
-                 xfree (dummy);
-                 dummy = url_file_name (u);
-                 hstat.local_file = &dummy;
-               }
-             /* be honest about where we will save the file */
-             if (local_file && opt.output_document)
-               *local_file = HYPHENP (opt.output_document) ? NULL : xstrdup (opt.output_document);
-             if (!opt.output_document)
-               locf = *hstat.local_file;
-             else
-               locf = opt.output_document;
-           }
-         continue;
-       case HOSTERR: case CONIMPOSSIBLE: case PROXERR: case AUTHFAILED: 
-       case SSLINITFAILED: case CONTNOTSUPPORTED:
-         /* Fatal errors just return from the function.  */
-         free_hstat (&hstat);
-         xfree_null (dummy);
-         return err;
-       case FWRITEERR: case FOPENERR:
-         /* Another fatal error.  */
-         logputs (LOG_VERBOSE, "\n");
-         logprintf (LOG_NOTQUIET, _("Cannot write to `%s' (%s).\n"),
-                    *hstat.local_file, strerror (errno));
-         free_hstat (&hstat);
-         xfree_null (dummy);
-         return err;
-       case CONSSLERR:
-         /* Another fatal error.  */
-         logprintf (LOG_NOTQUIET, _("Unable to establish SSL connection.\n"));
-         free_hstat (&hstat);
-         xfree_null (dummy);
-         return err;
-       case NEWLOCATION:
-         /* Return the new location to the caller.  */
-         if (!hstat.newloc)
-           {
-             logprintf (LOG_NOTQUIET,
-                        _("ERROR: Redirection (%d) without location.\n"),
-                        hstat.statcode);
-             free_hstat (&hstat);
-             xfree_null (dummy);
-             return WRONGCODE;
-           }
-         free_hstat (&hstat);
-         xfree_null (dummy);
-         return NEWLOCATION;
-       case RETRUNNEEDED:
-         /* The file was already fully retrieved. */
-         free_hstat (&hstat);
-         xfree_null (dummy);
-         return RETROK;
-       case RETRFINISHED:
-         /* Deal with you later.  */
-         break;
-       default:
-         /* All possibilities should have been exhausted.  */
-         abort ();
-       }
+        {
+        case HERR: case HEOF: case CONSOCKERR: case CONCLOSED:
+        case CONERROR: case READERR: case WRITEFAILED:
+        case RANGEERR: case FOPEN_EXCL_ERR:
+          /* Non-fatal errors continue executing the loop, which will
+             bring them to "while" statement at the end, to judge
+             whether the number of tries was exceeded.  */
+          printwhat (count, opt.ntry);
+          continue;
+        case FWRITEERR: case FOPENERR:
+          /* Another fatal error.  */
+          logputs (LOG_VERBOSE, "\n");
+          logprintf (LOG_NOTQUIET, _("Cannot write to %s (%s).\n"),
+                     quote (hstat.local_file), strerror (errno));
+        case HOSTERR: case CONIMPOSSIBLE: case PROXERR: case AUTHFAILED: 
+        case SSLINITFAILED: case CONTNOTSUPPORTED:
+          /* Fatal errors just return from the function.  */
+          ret = err;
+          goto exit;
+        case CONSSLERR:
+          /* Another fatal error.  */
+          logprintf (LOG_NOTQUIET, _("Unable to establish SSL connection.\n"));
+          ret = err;
+          goto exit;
+        case NEWLOCATION:
+          /* Return the new location to the caller.  */
+          if (!*newloc)
+            {
+              logprintf (LOG_NOTQUIET,
+                         _("ERROR: Redirection (%d) without location.\n"),
+                         hstat.statcode);
+              ret = WRONGCODE;
+            }
+          else
+            {
+              ret = NEWLOCATION;
+            }
+          goto exit;
+        case RETRUNNEEDED:
+          /* The file was already fully retrieved. */
+          ret = RETROK;
+          goto exit;
+        case RETRFINISHED:
+          /* Deal with you later.  */
+          break;
+        default:
+          /* All possibilities should have been exhausted.  */
+          abort ();
+        }
+
       if (!(*dt & RETROKF))
-       {
-         if (!opt.verbose)
-           {
-             /* #### Ugly ugly ugly! */
-             char *hurl = url_string (u, true);
-             logprintf (LOG_NONVERBOSE, "%s:\n", hurl);
-             xfree (hurl);
-           }
-         logprintf (LOG_NOTQUIET, _("%s ERROR %d: %s.\n"),
-                    tms, hstat.statcode, escnonprint (hstat.error));
-         logputs (LOG_VERBOSE, "\n");
-         free_hstat (&hstat);
-         xfree_null (dummy);
-         return WRONGCODE;
-       }
-
-      /* Did we get the time-stamp?  */
+        {
+          char *hurl = NULL;
+          if (!opt.verbose)
+            {
+              /* #### Ugly ugly ugly! */
+              hurl = url_string (u, URL_AUTH_HIDE_PASSWD);
+              logprintf (LOG_NONVERBOSE, "%s:\n", hurl);
+            }
+
+          /* Fall back to GET if HEAD fails with a 500 or 501 error code. */
+          if (*dt & HEAD_ONLY
+              && (hstat.statcode == 500 || hstat.statcode == 501))
+            {
+              got_head = true;
+              continue;
+            }
+          /* Maybe we should always keep track of broken links, not just in
+           * spider mode.
+           * Don't log error if it was UTF-8 encoded because we will try
+           * once unencoded. */
+          else if (opt.spider && !iri->utf8_encode)
+            {
+              /* #### Again: ugly ugly ugly! */
+              if (!hurl)
+                hurl = url_string (u, URL_AUTH_HIDE_PASSWD);
+              nonexisting_url (hurl);
+              logprintf (LOG_NOTQUIET, _("\
+Remote file does not exist -- broken link!!!\n"));
+            }
+          else
+            {
+              logprintf (LOG_NOTQUIET, _("%s ERROR %d: %s.\n"),
+                         tms, hstat.statcode,
+                         quotearg_style (escape_quoting_style, hstat.error));
+            }
+          logputs (LOG_VERBOSE, "\n");
+          ret = WRONGCODE;
+          xfree_null (hurl);
+          goto exit;
+        }
+
+      /* Did we get the time-stamp? */
       if (!got_head)
-       {
-         if (opt.timestamping && !hstat.remote_time)
-           {
-             logputs (LOG_NOTQUIET, _("\
+        {
+          got_head = true;    /* no more time-stamping */
+
+          if (opt.timestamping && !hstat.remote_time)
+            {
+              logputs (LOG_NOTQUIET, _("\
 Last-modified header missing -- time-stamps turned off.\n"));
-           }
-         else if (hstat.remote_time)
-           {
-             /* Convert the date-string into struct tm.  */
-             tmr = http_atotm (hstat.remote_time);
-             if (tmr == (time_t) (-1))
-               logputs (LOG_VERBOSE, _("\
+            }
+          else if (hstat.remote_time)
+            {
+              /* Convert the date-string into struct tm.  */
+              tmr = http_atotm (hstat.remote_time);
+              if (tmr == (time_t) (-1))
+                logputs (LOG_VERBOSE, _("\
 Last-modified header invalid -- time-stamp ignored.\n"));
-           }
-       }
-
-      /* The time-stamping section.  */
-      if (use_ts)
-       {
-         got_head = true;
-         *dt &= ~HEAD_ONLY;
-         use_ts = false;               /* no more time-stamping */
-         count = 0;            /* the retrieve count for HEAD is
-                                  reset */
-         if (hstat.remote_time && tmr != (time_t) (-1))
-           {
-             /* Now time-stamping can be used validly.  Time-stamping
-                means that if the sizes of the local and remote file
-                match, and local file is newer than the remote file,
-                it will not be retrieved.  Otherwise, the normal
-                download procedure is resumed.  */
-             if (tml >= tmr &&
-                 (hstat.contlen == -1 || local_size == hstat.contlen))
-               {
-                 logprintf (LOG_VERBOSE, _("\
-Server file no newer than local file `%s' -- not retrieving.\n\n"),
-                            local_filename);
-                 free_hstat (&hstat);
-                 xfree_null (dummy);
-                 return RETROK;
-               }
-             else if (tml >= tmr)
-               logprintf (LOG_VERBOSE, _("\
+              if (*dt & HEAD_ONLY)
+                time_came_from_head = true;
+            }
+      
+          if (send_head_first)
+            {
+              /* The time-stamping section.  */
+              if (opt.timestamping)
+                {
+                  if (hstat.orig_file_name) /* Perform the following
+                                               checks only if the file
+                                               we're supposed to
+                                               download already exists.  */
+                    {
+                      if (hstat.remote_time && 
+                          tmr != (time_t) (-1))
+                        {
+                          /* Now time-stamping can be used validly.
+                             Time-stamping means that if the sizes of
+                             the local and remote file match, and local
+                             file is newer than the remote file, it will
+                             not be retrieved.  Otherwise, the normal
+                             download procedure is resumed.  */
+                          if (hstat.orig_file_tstamp >= tmr)
+                            {
+                              if (hstat.contlen == -1 
+                                  || hstat.orig_file_size == hstat.contlen)
+                                {
+                                  logprintf (LOG_VERBOSE, _("\
+Server file no newer than local file %s -- not retrieving.\n\n"),
+                                             quote (hstat.orig_file_name));
+                                  ret = RETROK;
+                                  goto exit;
+                                }
+                              else
+                                {
+                                  logprintf (LOG_VERBOSE, _("\
 The sizes do not match (local %s) -- retrieving.\n"),
-                          number_to_static_string (local_size));
-             else
-               logputs (LOG_VERBOSE,
-                        _("Remote file is newer, retrieving.\n"));
-           }
-         free_hstat (&hstat);
-         continue;
-       }
+                                             number_to_static_string (hstat.orig_file_size));
+                                }
+                            }
+                          else
+                            logputs (LOG_VERBOSE,
+                                     _("Remote file is newer, retrieving.\n"));
+
+                          logputs (LOG_VERBOSE, "\n");
+                        }
+                    }
+                  
+                  /* free_hstat (&hstat); */
+                  hstat.timestamp_checked = true;
+                }
+              
+              if (opt.spider)
+                {
+                  bool finished = true;
+                  if (opt.recursive)
+                    {
+                      if (*dt & TEXTHTML)
+                        {
+                          logputs (LOG_VERBOSE, _("\
+Remote file exists and could contain links to other resources -- retrieving.\n\n"));
+                          finished = false;
+                        }
+                      else 
+                        {
+                          logprintf (LOG_VERBOSE, _("\
+Remote file exists but does not contain any link -- not retrieving.\n\n"));
+                          ret = RETROK; /* RETRUNNEEDED is not for caller. */
+                        }
+                    }
+                  else
+                    {
+                      if (*dt & TEXTHTML)
+                        {
+                          logprintf (LOG_VERBOSE, _("\
+Remote file exists and could contain further links,\n\
+but recursion is disabled -- not retrieving.\n\n"));
+                        }
+                      else 
+                        {
+                          logprintf (LOG_VERBOSE, _("\
+Remote file exists.\n\n"));
+                        }
+                      ret = RETROK; /* RETRUNNEEDED is not for caller. */
+                    }
+                  
+                  if (finished)
+                    {
+                      logprintf (LOG_NONVERBOSE, 
+                                 _("%s URL: %s %2d %s\n"), 
+                                 tms, u->url, hstat.statcode,
+                                 hstat.message ? quotearg_style (escape_quoting_style, hstat.message) : "");
+                      goto exit;
+                    }
+                }
+
+              got_name = true;
+              *dt &= ~HEAD_ONLY;
+              count = 0;          /* the retrieve count for HEAD is reset */
+              continue;
+            } /* send_head_first */
+        } /* !got_head */
+          
       if ((tmr != (time_t) (-1))
-         && !opt.spider
-         && ((hstat.len == hstat.contlen) ||
-             ((hstat.res == 0) &&
-              ((hstat.contlen == -1) ||
-               (hstat.len >= hstat.contlen && !opt.kill_longer)))))
-       {
-         /* #### This code repeats in http.c and ftp.c.  Move it to a
-             function!  */
-         const char *fl = NULL;
-         if (opt.output_document)
-           {
-             if (output_stream_regular)
-               fl = opt.output_document;
-           }
-         else
-           fl = *hstat.local_file;
-         if (fl)
-           touch (fl, tmr);
-       }
-      /* End of time-stamping section.  */
-
-      if (opt.spider)
-       {
-         logprintf (LOG_NOTQUIET, "%d %s\n\n", hstat.statcode,
-                    escnonprint (hstat.error));
-         xfree_null (dummy);
-         return RETROK;
-       }
+          && ((hstat.len == hstat.contlen) ||
+              ((hstat.res == 0) && (hstat.contlen == -1))))
+        {
+          const char *fl = NULL;
+          set_local_file (&fl, hstat.local_file);
+          if (fl)
+            {
+              time_t newtmr = -1;
+              /* Reparse time header, in case it's changed. */
+              if (time_came_from_head
+                  && hstat.remote_time && hstat.remote_time[0])
+                {
+                  newtmr = http_atotm (hstat.remote_time);
+                  if (newtmr != (time_t)-1)
+                    tmr = newtmr;
+                }
+              touch (fl, tmr);
+            }
+        }
+      /* End of time-stamping section. */
 
       tmrate = retr_rate (hstat.rd_size, hstat.dltime);
       total_download_time += hstat.dltime;
 
       if (hstat.len == hstat.contlen)
-       {
-         if (*dt & RETROKF)
-           {
-             logprintf (LOG_VERBOSE,
-                        _("%s (%s) - `%s' saved [%s/%s]\n\n"),
-                        tms, tmrate, locf,
-                        number_to_static_string (hstat.len),
-                        number_to_static_string (hstat.contlen));
-             logprintf (LOG_NONVERBOSE,
-                        "%s URL:%s [%s/%s] -> \"%s\" [%d]\n",
-                        tms, u->url,
-                        number_to_static_string (hstat.len),
-                        number_to_static_string (hstat.contlen),
-                        locf, count);
-           }
-         ++opt.numurls;
-         total_downloaded_bytes += hstat.len;
-
-         /* Remember that we downloaded the file for later ".orig" code. */
-         if (*dt & ADDED_HTML_EXTENSION)
-           downloaded_file(FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED, locf);
-         else
-           downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
-
-         free_hstat (&hstat);
-         xfree_null (dummy);
-         return RETROK;
-       }
+        {
+          if (*dt & RETROKF)
+            {
+              bool write_to_stdout = (opt.output_document && HYPHENP (opt.output_document));
+
+              logprintf (LOG_VERBOSE,
+                         write_to_stdout 
+                         ? _("%s (%s) - written to stdout %s[%s/%s]\n\n")
+                         : _("%s (%s) - %s saved [%s/%s]\n\n"),
+                         tms, tmrate,
+                         write_to_stdout ? "" : quote (hstat.local_file),
+                         number_to_static_string (hstat.len),
+                         number_to_static_string (hstat.contlen));
+              logprintf (LOG_NONVERBOSE,
+                         "%s URL:%s [%s/%s] -> \"%s\" [%d]\n",
+                         tms, u->url,
+                         number_to_static_string (hstat.len),
+                         number_to_static_string (hstat.contlen),
+                         hstat.local_file, count);
+            }
+          ++numurls;
+          total_downloaded_bytes += hstat.len;
+
+          /* Remember that we downloaded the file for later ".orig" code. */
+          if (*dt & ADDED_HTML_EXTENSION)
+            downloaded_file(FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED, hstat.local_file);
+          else
+            downloaded_file(FILE_DOWNLOADED_NORMALLY, hstat.local_file);
+
+          ret = RETROK;
+          goto exit;
+        }
       else if (hstat.res == 0) /* No read error */
-       {
-         if (hstat.contlen == -1)  /* We don't know how much we were supposed
-                                      to get, so assume we succeeded. */ 
-           {
-             if (*dt & RETROKF)
-               {
-                 logprintf (LOG_VERBOSE,
-                            _("%s (%s) - `%s' saved [%s]\n\n"),
-                            tms, tmrate, locf,
-                            number_to_static_string (hstat.len));
-                 logprintf (LOG_NONVERBOSE,
-                            "%s URL:%s [%s] -> \"%s\" [%d]\n",
-                            tms, u->url, number_to_static_string (hstat.len),
-                            locf, count);
-               }
-             ++opt.numurls;
-             total_downloaded_bytes += hstat.len;
-
-             /* Remember that we downloaded the file for later ".orig" code. */
-             if (*dt & ADDED_HTML_EXTENSION)
-               downloaded_file(FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED, locf);
-             else
-               downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
-             
-             free_hstat (&hstat);
-             xfree_null (dummy);
-             return RETROK;
-           }
-         else if (hstat.len < hstat.contlen) /* meaning we lost the
-                                                connection too soon */
-           {
-             logprintf (LOG_VERBOSE,
-                        _("%s (%s) - Connection closed at byte %s. "),
-                        tms, tmrate, number_to_static_string (hstat.len));
-             printwhat (count, opt.ntry);
-             free_hstat (&hstat);
-             continue;
-           }
-         else if (!opt.kill_longer) /* meaning we got more than expected */
-           {
-             logprintf (LOG_VERBOSE,
-                        _("%s (%s) - `%s' saved [%s/%s]\n\n"),
-                        tms, tmrate, locf,
-                        number_to_static_string (hstat.len),
-                        number_to_static_string (hstat.contlen));
-             logprintf (LOG_NONVERBOSE,
-                        "%s URL:%s [%s/%s] -> \"%s\" [%d]\n",
-                        tms, u->url,
-                        number_to_static_string (hstat.len),
-                        number_to_static_string (hstat.contlen),
-                        locf, count);
-             ++opt.numurls;
-             total_downloaded_bytes += hstat.len;
-
-             /* Remember that we downloaded the file for later ".orig" code. */
-             if (*dt & ADDED_HTML_EXTENSION)
-               downloaded_file(FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED, locf);
-             else
-               downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
-             
-             free_hstat (&hstat);
-             xfree_null (dummy);
-             return RETROK;
-           }
-         else                  /* the same, but not accepted */
-           {
-             logprintf (LOG_VERBOSE,
-                        _("%s (%s) - Connection closed at byte %s/%s. "),
-                        tms, tmrate,
-                        number_to_static_string (hstat.len),
-                        number_to_static_string (hstat.contlen));
-             printwhat (count, opt.ntry);
-             free_hstat (&hstat);
-             continue;
-           }
-       }
-      else                     /* now hstat.res can only be -1 */
-       {
-         if (hstat.contlen == -1)
-           {
-             logprintf (LOG_VERBOSE,
-                        _("%s (%s) - Read error at byte %s (%s)."),
-                        tms, tmrate, number_to_static_string (hstat.len),
-                        hstat.errstr);
-             printwhat (count, opt.ntry);
-             free_hstat (&hstat);
-             continue;
-           }
-         else                  /* hstat.res == -1 and contlen is given */
-           {
-             logprintf (LOG_VERBOSE,
-                        _("%s (%s) - Read error at byte %s/%s (%s). "),
-                        tms, tmrate,
-                        number_to_static_string (hstat.len),
-                        number_to_static_string (hstat.contlen),
-                        hstat.errstr);
-             printwhat (count, opt.ntry);
-             free_hstat (&hstat);
-             continue;
-           }
-       }
+        {
+          if (hstat.contlen == -1)  /* We don't know how much we were supposed
+                                       to get, so assume we succeeded. */ 
+            {
+              if (*dt & RETROKF)
+                {
+                  bool write_to_stdout = (opt.output_document && HYPHENP (opt.output_document));
+
+                  logprintf (LOG_VERBOSE,
+                             write_to_stdout
+                             ? _("%s (%s) - written to stdout %s[%s]\n\n")
+                             : _("%s (%s) - %s saved [%s]\n\n"),
+                             tms, tmrate, 
+                             write_to_stdout ? "" : quote (hstat.local_file),
+                             number_to_static_string (hstat.len));
+                  logprintf (LOG_NONVERBOSE,
+                             "%s URL:%s [%s] -> \"%s\" [%d]\n",
+                             tms, u->url, number_to_static_string (hstat.len),
+                             hstat.local_file, count);
+                }
+              ++numurls;
+              total_downloaded_bytes += hstat.len;
+
+              /* Remember that we downloaded the file for later ".orig" code. */
+              if (*dt & ADDED_HTML_EXTENSION)
+                downloaded_file(FILE_DOWNLOADED_AND_HTML_EXTENSION_ADDED, hstat.local_file);
+              else
+                downloaded_file(FILE_DOWNLOADED_NORMALLY, hstat.local_file);
+              
+              ret = RETROK;
+              goto exit;
+            }
+          else if (hstat.len < hstat.contlen) /* meaning we lost the
+                                                 connection too soon */
+            {
+              logprintf (LOG_VERBOSE,
+                         _("%s (%s) - Connection closed at byte %s. "),
+                         tms, tmrate, number_to_static_string (hstat.len));
+              printwhat (count, opt.ntry);
+              continue;
+            }
+          else if (hstat.len != hstat.restval)
+            /* Getting here would mean reading more data than
+               requested with content-length, which we never do.  */
+            abort ();
+          else
+            {
+              /* Getting here probably means that the content-length was
+               * _less_ than the original, local size. We should probably
+               * truncate or re-read, or something. FIXME */
+              ret = RETROK;
+              goto exit;
+            }
+        }
+      else /* from now on hstat.res can only be -1 */
+        {
+          if (hstat.contlen == -1)
+            {
+              logprintf (LOG_VERBOSE,
+                         _("%s (%s) - Read error at byte %s (%s)."),
+                         tms, tmrate, number_to_static_string (hstat.len),
+                         hstat.rderrmsg);
+              printwhat (count, opt.ntry);
+              continue;
+            }
+          else /* hstat.res == -1 and contlen is given */
+            {
+              logprintf (LOG_VERBOSE,
+                         _("%s (%s) - Read error at byte %s/%s (%s). "),
+                         tms, tmrate,
+                         number_to_static_string (hstat.len),
+                         number_to_static_string (hstat.contlen),
+                         hstat.rderrmsg);
+              printwhat (count, opt.ntry);
+              continue;
+            }
+        }
       /* not reached */
     }
   while (!opt.ntry || (count < opt.ntry));
-  return TRYLIMEXC;
+
+exit:
+  if (ret == RETROK) 
+    *local_file = xstrdup (hstat.local_file);
+  free_hstat (&hstat);
+  
+  return ret;
 }
 \f
 /* Check whether the result of strptime() indicates success.
@@ -2535,11 +2957,11 @@ check_end (const char *p)
 {
   if (!p)
     return false;
-  while (ISSPACE (*p))
+  while (c_isspace (*p))
     ++p;
   if (!*p
       || (p[0] == 'G' && p[1] == 'M' && p[2] == 'T')
-      || ((p[0] == '+' || p[0] == '-') && ISDIGIT (p[1])))
+      || ((p[0] == '+' || p[0] == '-') && c_isdigit (p[1])))
     return true;
   else
     return false;
@@ -2582,21 +3004,32 @@ http_atotm (const char *time_string)
      implementations I've tested.  */
 
   static const char *time_formats[] = {
-    "%a, %d %b %Y %T",         /* rfc1123: Thu, 29 Jan 1998 22:12:57 */
-    "%A, %d-%b-%y %T",         /* rfc850:  Thursday, 29-Jan-98 22:12:57 */
-    "%a %b %d %T %Y",          /* asctime: Thu Jan 29 22:12:57 1998 */
-    "%a, %d-%b-%Y %T"          /* cookies: Thu, 29-Jan-1998 22:12:57
-                                  (used in Set-Cookie, defined in the
-                                  Netscape cookie specification.) */
+    "%a, %d %b %Y %T",          /* rfc1123: Thu, 29 Jan 1998 22:12:57 */
+    "%A, %d-%b-%y %T",          /* rfc850:  Thursday, 29-Jan-98 22:12:57 */
+    "%a %b %d %T %Y",           /* asctime: Thu Jan 29 22:12:57 1998 */
+    "%a, %d-%b-%Y %T"           /* cookies: Thu, 29-Jan-1998 22:12:57
+                                   (used in Set-Cookie, defined in the
+                                   Netscape cookie specification.) */
   };
   const char *oldlocale;
-  int i;
+  char savedlocale[256];
+  size_t i;
   time_t ret = (time_t) -1;
 
   /* Solaris strptime fails to recognize English month names in
      non-English locales, which we work around by temporarily setting
      locale to C before invoking strptime.  */
   oldlocale = setlocale (LC_TIME, NULL);
+  if (oldlocale)
+    {
+      size_t l = strlen (oldlocale);
+      if (l >= sizeof savedlocale)
+        savedlocale[0] = '\0';
+      else
+        memcpy (savedlocale, oldlocale, l);
+    }
+  else savedlocale[0] = '\0';
+
   setlocale (LC_TIME, "C");
 
   for (i = 0; i < countof (time_formats); i++)
@@ -2604,19 +3037,20 @@ http_atotm (const char *time_string)
       struct tm t;
 
       /* Some versions of strptime use the existing contents of struct
-        tm to recalculate the date according to format.  Zero it out
-        to prevent stack garbage from influencing strptime.  */
+         tm to recalculate the date according to format.  Zero it out
+         to prevent stack garbage from influencing strptime.  */
       xzero (t);
 
       if (check_end (strptime (time_string, time_formats[i], &t)))
-       {
-         ret = timegm (&t);
-         break;
-       }
+        {
+          ret = timegm (&t);
+          break;
+        }
     }
 
   /* Restore the previous locale. */
-  setlocale (LC_TIME, oldlocale);
+  if (savedlocale[0])
+    setlocale (LC_TIME, savedlocale);
 
   return ret;
 }
@@ -2654,51 +3088,12 @@ basic_authentication_encode (const char *user, const char *passwd)
   return concat_strings ("Basic ", t2, (char *) 0);
 }
 
-#define SKIP_WS(x) do {                                \
-  while (ISSPACE (*(x)))                       \
-    ++(x);                                     \
+#define SKIP_WS(x) do {                         \
+  while (c_isspace (*(x)))                        \
+    ++(x);                                      \
 } while (0)
 
 #ifdef ENABLE_DIGEST
-/* Parse HTTP `WWW-Authenticate:' header.  AU points to the beginning
-   of a field in such a header.  If the field is the one specified by
-   ATTR_NAME ("realm", "opaque", and "nonce" are used by the current
-   digest authorization code), extract its value in the (char*)
-   variable pointed by RET.  Returns negative on a malformed header,
-   or number of bytes that have been parsed by this call.  */
-static int
-extract_header_attr (const char *au, const char *attr_name, char **ret)
-{
-  const char *ep;
-  const char *cp = au;
-
-  if (strncmp (cp, attr_name, strlen (attr_name)) == 0)
-    {
-      cp += strlen (attr_name);
-      if (!*cp)
-       return -1;
-      SKIP_WS (cp);
-      if (*cp != '=')
-       return -1;
-      if (!*++cp)
-       return -1;
-      SKIP_WS (cp);
-      if (*cp != '\"')
-       return -1;
-      if (!*++cp)
-       return -1;
-      for (ep = cp; *ep && *ep != '\"'; ep++)
-       ;
-      if (!*ep)
-       return -1;
-      xfree_null (*ret);
-      *ret = strdupdelim (cp, ep);
-      return ep - au + 1;
-    }
-  else
-    return 0;
-}
-
 /* Dump the hexadecimal representation of HASH to BUF.  HASH should be
    an array of 16 bytes containing the hash keys, and BUF should be a
    buffer of 33 writable characters (32 for hex digits plus one for
@@ -2720,8 +3115,8 @@ dump_hash (char *buf, const unsigned char *hash)
    authorization header.  See RFC2069 section 2.1.2.  */
 static char *
 digest_authentication_encode (const char *au, const char *user,
-                             const char *passwd, const char *method,
-                             const char *path)
+                              const char *passwd, const char *method,
+                              const char *path)
 {
   static char *realm, *opaque, *nonce;
   static struct {
@@ -2733,53 +3128,23 @@ digest_authentication_encode (const char *au, const char *user,
     { "nonce", &nonce }
   };
   char *res;
+  param_token name, value;
 
   realm = opaque = nonce = NULL;
 
-  au += 6;                     /* skip over `Digest' */
-  while (*au)
+  au += 6;                      /* skip over `Digest' */
+  while (extract_param (&au, &name, &value, ','))
     {
-      int i;
-
-      SKIP_WS (au);
+      size_t i;
+      size_t namelen = name.e - name.b;
       for (i = 0; i < countof (options); i++)
-       {
-         int skip = extract_header_attr (au, options[i].name,
-                                         options[i].variable);
-         if (skip < 0)
-           {
-             xfree_null (realm);
-             xfree_null (opaque);
-             xfree_null (nonce);
-             return NULL;
-           }
-         else if (skip)
-           {
-             au += skip;
-             break;
-           }
-       }
-      if (i == countof (options))
-       {
-         while (*au && *au != '=')
-           au++;
-         if (*au && *++au)
-           {
-             SKIP_WS (au);
-             if (*au == '\"')
-               {
-                 au++;
-                 while (*au && *au != '\"')
-                   au++;
-                 if (*au)
-                   au++;
-               }
-           }
-       }
-      while (*au && *au != ',')
-       au++;
-      if (*au)
-       au++;
+        if (namelen == strlen (options[i].name)
+            && 0 == strncmp (name.b, options[i].name,
+                             namelen))
+          {
+            *options[i].variable = strdupdelim (value.b, value.e);
+            break;
+          }
     }
   if (!realm || !nonce || !user || !passwd || !path || !method)
     {
@@ -2825,22 +3190,22 @@ digest_authentication_encode (const char *au, const char *user,
     dump_hash (response_digest, hash);
 
     res = xmalloc (strlen (user)
-                  + strlen (user)
-                  + strlen (realm)
-                  + strlen (nonce)
-                  + strlen (path)
-                  + 2 * MD5_HASHLEN /*strlen (response_digest)*/
-                  + (opaque ? strlen (opaque) : 0)
-                  + 128);
+                   + strlen (user)
+                   + strlen (realm)
+                   + strlen (nonce)
+                   + strlen (path)
+                   + 2 * MD5_HASHLEN /*strlen (response_digest)*/
+                   + (opaque ? strlen (opaque) : 0)
+                   + 128);
     sprintf (res, "Digest \
 username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
-            user, realm, nonce, path, response_digest);
+             user, realm, nonce, path, response_digest);
     if (opaque)
       {
-       char *p = res + strlen (res);
-       strcat (p, ", opaque=\"");
-       strcat (p, opaque);
-       strcat (p, "\"");
+        char *p = res + strlen (res);
+        strcat (p, ", opaque=\"");
+        strcat (p, opaque);
+        strcat (p, "\"");
       }
   }
   return res;
@@ -2854,11 +3219,12 @@ username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
 /* Whether chars in [b, e) begin with the literal string provided as
    first argument and are followed by whitespace or terminating \0.
    The comparison is case-insensitive.  */
-#define STARTS(literal, b, e)                          \
-  ((e) - (b) >= STRSIZE (literal)                      \
-   && 0 == strncasecmp (b, literal, STRSIZE (literal)) \
-   && ((e) - (b) == STRSIZE (literal)                  \
-       || ISSPACE (b[STRSIZE (literal)])))
+#define STARTS(literal, b, e)                           \
+  ((e > b) \
+   && ((size_t) ((e) - (b))) >= STRSIZE (literal)   \
+   && 0 == strncasecmp (b, literal, STRSIZE (literal))  \
+   && ((size_t) ((e) - (b)) == STRSIZE (literal)          \
+       || c_isspace (b[STRSIZE (literal)])))
 
 static bool
 known_authentication_scheme_p (const char *hdrbeg, const char *hdrend)
@@ -2882,37 +3248,49 @@ known_authentication_scheme_p (const char *hdrbeg, const char *hdrend)
    appropriate HTTP authorization request header.  */
 static char *
 create_authorization_line (const char *au, const char *user,
-                          const char *passwd, const char *method,
-                          const char *path, bool *finished)
+                           const char *passwd, const char *method,
+                           const char *path, bool *finished)
 {
   /* We are called only with known schemes, so we can dispatch on the
      first letter. */
-  switch (TOUPPER (*au))
+  switch (c_toupper (*au))
     {
-    case 'B':                  /* Basic */
+    case 'B':                   /* Basic */
       *finished = true;
       return basic_authentication_encode (user, passwd);
 #ifdef ENABLE_DIGEST
-    case 'D':                  /* Digest */
+    case 'D':                   /* Digest */
       *finished = true;
       return digest_authentication_encode (au, user, passwd, method, path);
 #endif
 #ifdef ENABLE_NTLM
-    case 'N':                  /* NTLM */
+    case 'N':                   /* NTLM */
       if (!ntlm_input (&pconn.ntlm, au))
-       {
-         *finished = true;
-         return NULL;
-       }
+        {
+          *finished = true;
+          return NULL;
+        }
       return ntlm_output (&pconn.ntlm, user, passwd, finished);
 #endif
     default:
       /* We shouldn't get here -- this function should be only called
-        with values approved by known_authentication_scheme_p.  */
+         with values approved by known_authentication_scheme_p.  */
       abort ();
     }
 }
 \f
+static void
+load_cookies (void)
+{
+  if (!wget_cookie_jar)
+    wget_cookie_jar = cookie_jar_new ();
+  if (opt.cookies_input && !cookies_loaded_p)
+    {
+      cookie_jar_load (wget_cookie_jar, opt.cookies_input);
+      cookies_loaded_p = true;
+    }
+}
+
 void
 save_cookies (void)
 {
@@ -2927,3 +3305,86 @@ http_cleanup (void)
   if (wget_cookie_jar)
     cookie_jar_delete (wget_cookie_jar);
 }
+
+void
+ensure_extension (struct http_stat *hs, const char *ext, int *dt)
+{
+  char *last_period_in_local_filename = strrchr (hs->local_file, '.');
+  char shortext[8];
+  int len = strlen (ext);
+  if (len == 5)
+    {
+      strncpy (shortext, ext, len - 1);
+      shortext[len - 2] = '\0';
+    }
+
+  if (last_period_in_local_filename == NULL
+      || !(0 == strcasecmp (last_period_in_local_filename, shortext)
+           || 0 == strcasecmp (last_period_in_local_filename, ext)))
+    {
+      int local_filename_len = strlen (hs->local_file);
+      /* Resize the local file, allowing for ".html" preceded by
+         optional ".NUMBER".  */
+      hs->local_file = xrealloc (hs->local_file,
+                                 local_filename_len + 24 + len);
+      strcpy (hs->local_file + local_filename_len, ext);
+      /* If clobbering is not allowed and the file, as named,
+         exists, tack on ".NUMBER.html" instead. */
+      if (!ALLOW_CLOBBER && file_exists_p (hs->local_file))
+        {
+          int ext_num = 1;
+          do
+            sprintf (hs->local_file + local_filename_len,
+                     ".%d%s", ext_num++, ext);
+          while (file_exists_p (hs->local_file));
+        }
+      *dt |= ADDED_HTML_EXTENSION;
+    }
+}
+
+
+#ifdef TESTING
+
+const char *
+test_parse_content_disposition()
+{
+  int i;
+  struct {
+    char *hdrval;    
+    char *opt_dir_prefix;
+    char *filename;
+    bool result;
+  } test_array[] = {
+    { "filename=\"file.ext\"", NULL, "file.ext", true },
+    { "filename=\"file.ext\"", "somedir", "somedir/file.ext", true },
+    { "attachment; filename=\"file.ext\"", NULL, "file.ext", true },
+    { "attachment; filename=\"file.ext\"", "somedir", "somedir/file.ext", true },
+    { "attachment; filename=\"file.ext\"; dummy", NULL, "file.ext", true },
+    { "attachment; filename=\"file.ext\"; dummy", "somedir", "somedir/file.ext", true },
+    { "attachment", NULL, NULL, false },
+    { "attachment", "somedir", NULL, false },
+  };
+  
+  for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i) 
+    {
+      char *filename;
+      bool res;
+
+      opt.dir_prefix = test_array[i].opt_dir_prefix;
+      res = parse_content_disposition (test_array[i].hdrval, &filename);
+
+      mu_assert ("test_parse_content_disposition: wrong result", 
+                 res == test_array[i].result
+                 && (res == false 
+                     || 0 == strcmp (test_array[i].filename, filename)));
+    }
+
+  return NULL;
+}
+
+#endif /* TESTING */
+
+/*
+ * vim: et sts=2 sw=2 cino+={s
+ */
+