]> sjero.net Git - wget/blobdiff - src/http.c
[svn] New option --no-http-keep-alive.
[wget] / src / http.c
index b25e01460719aa39ae02a43a4956b624e65109fc..f75980317a8ac001d48f9710a19ce4f86101540f 100644 (file)
@@ -1,5 +1,5 @@
 /* HTTP support.
-   Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
 
 This file is part of Wget.
 
@@ -241,7 +241,6 @@ http_process_type (const char *hdr, void *arg)
   char **result = (char **)arg;
   char *p;
 
-  *result = xstrdup (hdr);
   p = strrchr (hdr, ';');
   if (p)
     {
@@ -255,6 +254,142 @@ http_process_type (const char *hdr, void *arg)
   return 1;
 }
 
+/* Check whether the `Connection' header is set to "keep-alive". */
+static int
+http_process_connection (const char *hdr, void *arg)
+{
+  int *flag = (int *)arg;
+  if (!strcasecmp (hdr, "Keep-Alive"))
+    *flag = 1;
+  return 1;
+}
+\f
+/* Persistent connections (pc).  Currently, we cache the most recently
+   used connection as persistent, provided that the HTTP server agrees
+   to make it such.  The persistence data is stored in the variables
+   below.  Ideally, it would be in a structure, and it should be
+   possible to cache an arbitrary fixed number of these connections.
+
+   I think the code is quite easy to extend in that direction.  */
+
+/* Whether the persistent connection is active. */
+static int pc_active_p;
+
+/* Host and port of the last persistent connection. */
+static unsigned char pc_last_host[4];
+static unsigned short pc_last_port;
+
+/* File descriptor of the last persistent connection. */
+static int pc_last_fd;
+
+/* Mark the persistent connection as invalid.  This is used by the
+   CLOSE_* macros after they forcefully close a registered persistent
+   connection.  */
+
+static void
+invalidate_persistent (void)
+{
+  pc_active_p = 0;
+  DEBUGP (("Invalidating fd %d from further reuse.\n", pc_last_fd));
+}
+
+/* Register FD, which should be a TCP/IP connection to HOST:PORT, as
+   persistent.  This will enable someone to use the same connection
+   later.  In the context of HTTP, this must be called only AFTER the
+   response has been received and the server has promised that the
+   connection will remain alive.
+
+   If a previous connection was persistent, it is closed. */
+
+static void
+register_persistent (const char *host, unsigned short port, int fd)
+{
+  int success;
+
+  if (pc_active_p)
+    {
+      if (pc_last_fd == fd)
+       {
+         /* The connection FD is already registered.  Nothing to
+            do. */
+         return;
+       }
+      else
+       {
+         /* The old persistent connection is still active; let's
+            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.  */
+         CLOSE (pc_last_fd);
+         invalidate_persistent ();
+       }
+    }
+
+  /* This store_hostaddress may not fail, because it has the results
+     in the cache.  */
+  success = store_hostaddress (pc_last_host, host);
+  assert (success);
+  pc_last_port = port;
+  pc_last_fd = fd;
+  pc_active_p = 1;
+  DEBUGP (("Registered fd %d for persistent reuse.\n", fd));
+}
+
+/* Return non-zero if a persistent connection is available for
+   connecting to HOST:PORT.  */
+
+static int
+persistent_available_p (const char *host, unsigned short port)
+{
+  unsigned char this_host[4];
+  if (!pc_active_p)
+    return 0;
+  if (port != pc_last_port)
+    return 0;
+  if (!store_hostaddress (this_host, host))
+    return 0;
+  if (memcmp (pc_last_host, this_host, 4))
+    return 0;
+  if (!test_socket_open (pc_last_fd))
+    {
+      CLOSE (pc_last_fd);
+      invalidate_persistent ();
+      return 0;
+    }
+  return 1;
+}
+
+/* The idea behind these two CLOSE macros is to distinguish between
+   two cases: one when the job we've been doing is finished, and we
+   want to close the connection and leave, and two when something is
+   seriously wrong and we're closing the connection as part of
+   cleanup.
+
+   In case of keep_alive, CLOSE_FINISH should leave the connection
+   open, while CLOSE_INVALIDATE should still close it.
+
+   Note that the semantics of the flag `keep_alive' is "this
+   connection *will* be reused (the server has promised not to close
+   the connection once we're done)", while the semantics of
+   `pc_active_p && (fd) == pc_last_fd' is "we're *now* using an
+   active, registered connection".  */
+
+#define CLOSE_FINISH(fd) do {                  \
+  if (!keep_alive)                             \
+    {                                          \
+      CLOSE (fd);                              \
+      if (pc_active_p && (fd) == pc_last_fd)   \
+       invalidate_persistent ();               \
+    }                                          \
+} while (0)
+
+#define CLOSE_INVALIDATE(fd) do {              \
+  CLOSE (fd);                                  \
+  if (pc_active_p && (fd) == pc_last_fd)       \
+    invalidate_persistent ();                  \
+} while (0)
+
 \f
 struct http_stat
 {
@@ -309,8 +444,8 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   char *authenticate_h;
   char *proxyauth;
   char *all_headers;
-  char *host_port;
-  int host_port_len;
+  char *port_maybe;
+  char *request_keep_alive;
   int sock, hcount, num_written, all_length, remport, statcode;
   long contlen, contrange;
   struct urlinfo *ou;
@@ -319,18 +454,34 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   int auth_tried_already;
   struct rbuf rbuf;
 
-  /* Let the others worry about local filename...  */
+  /* Whether this connection will be kept alive after the HTTP request
+     is done. */
+  int keep_alive;
+
+  /* Flags that detect the two ways of specifying HTTP keep-alive
+     response.  */
+  int http_keep_alive_1, http_keep_alive_2;
+
+  /* Whether keep-alive should be inhibited. */
+  int inhibit_keep_alive;
+
   if (!(*dt & 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 (u->local != NULL);
 
   authenticate_h = 0;
   auth_tried_already = 0;
 
+  inhibit_keep_alive = (!opt.http_keep_alive || u->proxy != NULL);
+
  again:
   /* We need to come back here when the initial attempt to retrieve
      without authorization header fails.  */
+  keep_alive = 0;
+  http_keep_alive_1 = http_keep_alive_2 = 0;
 
-  /* Initialize certain elements of struct hstat.  */
+  /* Initialize certain elements of struct http_stat.  */
   hs->len = 0L;
   hs->contlen = -1;
   hs->res = -1;
@@ -345,45 +496,58 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
     ou = u;
 
   /* First: establish the connection.  */
-  logprintf (LOG_VERBOSE, _("Connecting to %s:%hu... "), u->host, u->port);
-  err = make_connection (&sock, u->host, u->port);
-  switch (err)
+  if (inhibit_keep_alive
+      || !persistent_available_p (u->host, u->port))
     {
-    case HOSTERR:
-      logputs (LOG_VERBOSE, "\n");
-      logprintf (LOG_NOTQUIET, "%s: %s.\n", u->host, herrmsg (h_errno));
-      return HOSTERR;
-      break;
-    case CONSOCKERR:
-      logputs (LOG_VERBOSE, "\n");
-      logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno));
-      return CONSOCKERR;
-      break;
-    case CONREFUSED:
-      logputs (LOG_VERBOSE, "\n");
-      logprintf (LOG_NOTQUIET,
-                _("Connection to %s:%hu refused.\n"), u->host, u->port);
-      CLOSE (sock);
-      return CONREFUSED;
-    case CONERROR:
-      logputs (LOG_VERBOSE, "\n");
-      logprintf (LOG_NOTQUIET, "connect: %s\n", strerror (errno));
-      CLOSE (sock);
-      return CONERROR;
-      break;
-    case NOCONERROR:
-      /* Everything is fine!  */
-      logputs (LOG_VERBOSE, _("connected!\n"));
-      break;
-    default:
-      abort ();
-      break;
-    } /* switch */
+      logprintf (LOG_VERBOSE, _("Connecting to %s:%hu... "), u->host, u->port);
+      err = make_connection (&sock, u->host, u->port);
+      switch (err)
+       {
+       case HOSTERR:
+         logputs (LOG_VERBOSE, "\n");
+         logprintf (LOG_NOTQUIET, "%s: %s.\n", u->host, herrmsg (h_errno));
+         return HOSTERR;
+         break;
+       case CONSOCKERR:
+         logputs (LOG_VERBOSE, "\n");
+         logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno));
+         return CONSOCKERR;
+         break;
+       case CONREFUSED:
+         logputs (LOG_VERBOSE, "\n");
+         logprintf (LOG_NOTQUIET,
+                    _("Connection to %s:%hu refused.\n"), u->host, u->port);
+         CLOSE (sock);
+         return CONREFUSED;
+       case CONERROR:
+         logputs (LOG_VERBOSE, "\n");
+         logprintf (LOG_NOTQUIET, "connect: %s\n", strerror (errno));
+         CLOSE (sock);
+         return CONERROR;
+         break;
+       case NOCONERROR:
+         /* Everything is fine!  */
+         logputs (LOG_VERBOSE, _("connected!\n"));
+         break;
+       default:
+         abort ();
+         break;
+       }
+    }
+  else
+    {
+      logprintf (LOG_VERBOSE, _("Reusing connection to %s:%hu.\n"), u->host, u->port);
+      /* #### pc_last_fd should be accessed through an accessor
+         function.  */
+      sock = pc_last_fd;
+      DEBUGP (("Reusing fd %d.\n", sock));
+    }
 
   if (u->proxy)
     path = u->proxy->url;
   else
     path = u->path;
+  
   command = (*dt & HEAD_ONLY) ? "HEAD" : "GET";
   referer = NULL;
   if (ou->referer)
@@ -398,12 +562,13 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   if (hs->restval)
     {
       range = (char *)alloca (13 + numdigit (hs->restval) + 4);
-      /* #### Gag me!  Some servers (e.g. WebSitePro) have been known
-         to misinterpret the following `Range' format, and return the
-         document as multipart/x-byte-ranges MIME type!
-
-        #### TODO: Interpret MIME types, recognize bullshits similar
-        the one described above, and deal with them!  */
+      /* Gag me!  Some servers (e.g. WebSitePro) have been known to
+         respond to the following `Range' format by generating a
+         multipart/x-byte-ranges MIME document!  This MIME type was
+         present in an old draft of the byteranges specification.
+         HTTP/1.1 specifies a multipart/byte-ranges MIME type, but
+         only if multiple non-overlapping ranges are requested --
+         which Wget never does.  */
       sprintf (range, "Range: bytes=%ld-\r\n", hs->restval);
     }
   else
@@ -457,19 +622,27 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   remhost = ou->host;
   remport = ou->port;
 
-  if (remport == 80) {
-      host_port = NULL; host_port_len = 0;
-  }
-  else {
-      host_port = (char *)alloca (numdigit (remport) + 2);
-      host_port_len = sprintf (host_port, ":%d", remport);
-  }
+  /* String of the form :PORT.  Used only for non-standard ports. */
+  port_maybe = NULL;
+  if (remport != 80)
+    {
+      port_maybe = (char *)alloca (numdigit (remport) + 2);
+      sprintf (port_maybe, ":%d", remport);
+    }
+
+  if (!inhibit_keep_alive)
+    request_keep_alive = "Connection: Keep-Alive\r\n";
+  else
+    request_keep_alive = NULL;
 
   /* Allocate the memory for the request.  */
   request = (char *)alloca (strlen (command) + strlen (path)
                            + strlen (useragent)
-                           + strlen (remhost) + host_port_len
+                           + strlen (remhost)
+                           + (port_maybe ? strlen (port_maybe) : 0)
                            + strlen (HTTP_ACCEPT)
+                           + (request_keep_alive
+                              ? strlen (request_keep_alive) : 0)
                            + (referer ? strlen (referer) : 0)
                            + (wwwauth ? strlen (wwwauth) : 0)
                            + (proxyauth ? strlen (proxyauth) : 0)
@@ -483,14 +656,17 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
 User-Agent: %s\r\n\
 Host: %s%s\r\n\
 Accept: %s\r\n\
-%s%s%s%s%s%s\r\n",
-         command, path, useragent, remhost, host_port ? host_port : "",
-         HTTP_ACCEPT, referer ? referer : "",
-         wwwauth ? wwwauth : "", 
-         proxyauth ? proxyauth : "", 
-         range ? range : "",
-         pragma_h, 
-         opt.user_header ? opt.user_header : "");
+%s%s%s%s%s%s%s\r\n",
+          command, path, useragent, remhost,
+          port_maybe ? port_maybe : "",
+          HTTP_ACCEPT,
+          request_keep_alive ? request_keep_alive : "",
+          referer ? referer : "",
+          wwwauth ? wwwauth : "", 
+          proxyauth ? proxyauth : "", 
+          range ? range : "",
+          pragma_h, 
+          opt.user_header ? opt.user_header : "");
   DEBUGP (("---request begin---\n%s---request end---\n", request));
    /* Free the temporary memory.  */
   FREE_MAYBE (wwwauth);
@@ -500,9 +676,9 @@ Accept: %s\r\n\
   num_written = iwrite (sock, request, strlen (request));
   if (num_written < 0)
     {
-      logputs (LOG_VERBOSE, _("Failed writing HTTP request.\n"));
-      free (request);
-      CLOSE (sock);
+      logprintf (LOG_VERBOSE, _("Failed writing HTTP request: %s.\n"),
+                strerror (errno));
+      CLOSE_INVALIDATE (sock);
       return WRITEFAILED;
     }
   logprintf (LOG_VERBOSE, _("%s request sent, awaiting response... "),
@@ -549,7 +725,7 @@ Accept: %s\r\n\
          FREE_MAYBE (type);
          FREE_MAYBE (hs->newloc);
          FREE_MAYBE (all_headers);
-         CLOSE (sock);
+         CLOSE_INVALIDATE (sock);
          return HEOF;
        }
       else if (status == HG_ERROR)
@@ -561,7 +737,7 @@ Accept: %s\r\n\
          FREE_MAYBE (type);
          FREE_MAYBE (hs->newloc);
          FREE_MAYBE (all_headers);
-         CLOSE (sock);
+         CLOSE_INVALIDATE (sock);
          return HERR;
        }
 
@@ -668,12 +844,41 @@ Accept: %s\r\n\
              goto done_header;
            }
        }
+      /* Check for keep-alive related responses. */
+      if (!inhibit_keep_alive)
+       {
+         /* Check for the `Keep-Alive' header. */
+         if (!http_keep_alive_1)
+           {
+             if (header_process (hdr, "Keep-Alive", header_exists,
+                                 &http_keep_alive_1))
+               goto done_header;
+           }
+         /* Check for `Connection: Keep-Alive'. */
+         if (!http_keep_alive_2)
+           {
+             if (header_process (hdr, "Connection", http_process_connection,
+                                 &http_keep_alive_2))
+               goto done_header;
+           }
+       }
     done_header:
       free (hdr);
     }
 
   logputs (LOG_VERBOSE, "\n");
 
+  if (contlen != -1
+      && (http_keep_alive_1 || http_keep_alive_2))
+    {
+      assert (inhibit_keep_alive == 0);
+      keep_alive = 1;
+    }
+  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.  */
+    register_persistent (u->host, u->port, sock);
+
   if ((statcode == HTTP_STATUS_UNAUTHORIZED)
       && authenticate_h)
     {
@@ -681,7 +886,7 @@ Accept: %s\r\n\
       FREE_MAYBE (type);
       type = NULL;
       FREEHSTAT (*hs);
-      CLOSE (sock);
+      CLOSE_FINISH (sock);
       if (auth_tried_already)
        {
          /* If we have tried it already, then there is not point
@@ -719,6 +924,26 @@ Accept: %s\r\n\
     /* We don't assume text/html by default.  */
     *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(u->local, '.');
+
+      if (last_period_in_local_filename == NULL ||
+         !(strcasecmp(last_period_in_local_filename, ".htm") == EQ ||
+           strcasecmp(last_period_in_local_filename, ".html") == EQ))
+       {
+         size_t  local_filename_len = strlen(u->local);
+         
+         u->local = xrealloc(u->local, local_filename_len + sizeof(".html"));
+         strcpy(u->local + local_filename_len, ".html");
+
+         *dt |= ADDED_HTML_EXTENSION;
+       }
+    }
+
   if (contrange == -1)
     hs->restval = 0;
   else if (contrange != hs->restval ||
@@ -729,7 +954,7 @@ Accept: %s\r\n\
       FREE_MAYBE (type);
       FREE_MAYBE (hs->newloc);
       FREE_MAYBE (all_headers);
-      CLOSE (sock);
+      CLOSE_INVALIDATE (sock);
       return RANGEERR;
     }
 
@@ -759,7 +984,7 @@ Accept: %s\r\n\
                     _("Location: %s%s\n"),
                     hs->newloc ? hs->newloc : _("unspecified"),
                     hs->newloc ? _(" [following]") : "");
-         CLOSE (sock);
+         CLOSE_FINISH (sock);
          FREE_MAYBE (type);
          FREE_MAYBE (all_headers);
          return NEWLOCATION;
@@ -800,7 +1025,7 @@ Accept: %s\r\n\
       hs->res = 0;
       FREE_MAYBE (type);
       FREE_MAYBE (all_headers);
-      CLOSE (sock);
+      CLOSE_FINISH (sock);
       return RETRFINISHED;
     }
 
@@ -814,13 +1039,22 @@ Accept: %s\r\n\
       if (!fp)
        {
          logprintf (LOG_NOTQUIET, "%s: %s\n", u->local, strerror (errno));
-         CLOSE (sock);
+         CLOSE_FINISH (sock);
          FREE_MAYBE (all_headers);
          return FOPENERR;
        }
     }
-  else                      /* opt.dfp */
-    fp = opt.dfp;
+  else                         /* opt.dfp */
+    {
+      fp = opt.dfp;
+      if (!hs->restval)
+       {
+         /* This will silently fail for streams that don't correspond
+            to regular files, but that's OK.  */
+         rewind (fp);
+         clearerr (fp);
+       }
+    }
 
   /* #### This confuses the code that checks for file size.  There
      should be some overhead information.  */
@@ -830,14 +1064,22 @@ Accept: %s\r\n\
   /* Get the contents of the document.  */
   hs->res = get_contents (sock, fp, &hs->len, hs->restval,
                          (contlen != -1 ? contlen : 0),
-                         &rbuf);
+                         &rbuf, keep_alive);
   hs->dltime = elapsed_time ();
-  if (!opt.dfp)
-    fclose (fp);
-  else
-    fflush (fp);
+  {
+    /* Close or flush the file.  We have to be careful to check for
+       error here.  Checking the result of fwrite() is not enough --
+       errors could go unnoticed!  */
+    int flush_res;
+    if (!opt.dfp)
+      flush_res = fclose (fp);
+    else
+      flush_res = fflush (fp);
+    if (flush_res == EOF)
+      hs->res = -2;
+  }
   FREE_MAYBE (all_headers);
-  CLOSE (sock);
+  CLOSE_FINISH (sock);
   if (hs->res == -2)
     return FWRITEERR;
   return RETRFINISHED;
@@ -851,12 +1093,14 @@ http_loop (struct urlinfo *u, char **newloc, int *dt)
   static int first_retrieval = 1;
 
   int count;
-  int local_dot_orig_file_exists = FALSE;
   int use_ts, got_head = 0;    /* time-stamping info */
+  char *filename_plus_orig_suffix;
+  char *local_filename = NULL;
   char *tms, *suf, *locf, *tmrate;
   uerr_t err;
   time_t tml = -1, tmr = -1;   /* local and remote time-stamps */
   long local_size = 0;         /* the size of the local file */
+  size_t filename_len;
   struct http_stat hstat;      /* HTTP status */
   struct stat st;
 
@@ -877,6 +1121,12 @@ http_loop (struct urlinfo *u, char **newloc, int *dt)
   else
     locf = opt.output_document;
 
+  /* Yuck.  Multiple returns suck.  We need to remember to free() the space we
+     xmalloc() here before EACH return.  This is one reason it's better to set
+     flags that influence flow control and then return once at the end. */
+  filename_len = strlen(u->local);
+  filename_plus_orig_suffix = xmalloc(filename_len + sizeof(".orig"));
+
   if (opt.noclobber && file_exists_p (u->local))
     {
       /* If opt.noclobber is turned on and file already exists, do not
@@ -893,6 +1143,7 @@ File `%s' already there, will not retrieve.\n"), u->local);
          && (!strcmp (suf, "html") || !strcmp (suf, "htm")))
        *dt |= TEXTHTML;
       free (suf);
+      free(filename_plus_orig_suffix);  /* must precede every return! */
       /* Another harmless lie: */
       return RETROK;
     }
@@ -900,7 +1151,7 @@ File `%s' already there, will not retrieve.\n"), u->local);
   use_ts = 0;
   if (opt.timestamping)
     {
-      boolean  local_file_exists = FALSE;
+      boolean  local_dot_orig_file_exists = FALSE;
 
       if (opt.backup_converted)
        /* If -K is specified, we'll act on the assumption that it was specified
@@ -910,30 +1161,31 @@ File `%s' already there, will not retrieve.\n"), u->local);
           _wasn't_ specified last time, or the server contains files called
           *.orig, -N will be back to not operating correctly with -k. */
        {
-         size_t filename_len = strlen(u->local);
-         char*  filename_plus_orig_suffix = malloc(filename_len +
-                                                   sizeof(".orig"));
-
-         /* Would a single s[n]printf() call be faster? */
+         /* Would a single s[n]printf() call be faster?  --dan
+
+            It wouldn't.  sprintf() is horribly slow.  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 long_to_string() made a difference.
+            --hniksic */
          strcpy(filename_plus_orig_suffix, u->local);
          strcpy(filename_plus_orig_suffix + filename_len, ".orig");
 
          /* Try to stat() the .orig file. */
          if (stat(filename_plus_orig_suffix, &st) == 0)
            {
-             local_file_exists = TRUE;
              local_dot_orig_file_exists = TRUE;
+             local_filename = filename_plus_orig_suffix;
            }
-
-         free(filename_plus_orig_suffix);
        }      
 
       if (!local_dot_orig_file_exists)
        /* Couldn't stat() <file>.orig, so try to stat() <file>. */
        if (stat (u->local, &st) == 0)
-         local_file_exists = TRUE;
+         local_filename = u->local;
 
-      if (local_file_exists)
+      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. */
@@ -953,9 +1205,20 @@ File `%s' already there, will not retrieve.\n"), u->local);
       /* Increment the pass counter.  */
       ++count;
       /* Wait before the retrieval (unless this is the very first
-        retrieval).  */
-      if (!first_retrieval && opt.wait)
-       sleep (opt.wait);
+        retrieval).
+        Check if we are retrying or not, wait accordingly - HEH */
+      if (!first_retrieval && (opt.wait || (count && opt.waitretry)))
+       {
+         if (count)
+           {
+             if (count<opt.waitretry)
+               sleep(count);
+             else
+               sleep(opt.waitretry);
+           }
+         else
+           sleep (opt.wait);
+       }
       if (first_retrieval)
        first_retrieval = 0;
       /* Get the current time string.  */
@@ -998,6 +1261,16 @@ File `%s' already there, will not retrieve.\n"), u->local);
 
       /* Try fetching the document, or at least its head.  :-) */
       err = gethttp (u, &hstat, dt);
+
+      /* 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
+        u->local to tack on ".html". */
+      if (!opt.output_document)
+       locf = u->local;
+      else
+       locf = opt.output_document;
+
       /* Time?  */
       tms = time_str (NULL);
       /* Get the new location (with or without the redirection).  */
@@ -1018,6 +1291,7 @@ File `%s' already there, will not retrieve.\n"), u->local);
        case HOSTERR: case CONREFUSED: case PROXERR: case AUTHFAILED:
          /* Fatal errors just return from the function.  */
          FREEHSTAT (hstat);
+         free(filename_plus_orig_suffix);  /* must precede every return! */
          return err;
          break;
        case FWRITEERR: case FOPENERR:
@@ -1026,6 +1300,7 @@ File `%s' already there, will not retrieve.\n"), u->local);
          logprintf (LOG_NOTQUIET, _("Cannot write to `%s' (%s).\n"),
                     u->local, strerror (errno));
          FREEHSTAT (hstat);
+         free(filename_plus_orig_suffix);  /* must precede every return! */
          return err;
          break;
        case NEWLOCATION:
@@ -1035,9 +1310,11 @@ File `%s' already there, will not retrieve.\n"), u->local);
              logprintf (LOG_NOTQUIET,
                         _("ERROR: Redirection (%d) without location.\n"),
                         hstat.statcode);
+             free(filename_plus_orig_suffix);  /* must precede every return! */
              return WRONGCODE;
            }
          FREEHSTAT (hstat);
+         free(filename_plus_orig_suffix);  /* must precede every return! */
          return NEWLOCATION;
          break;
        case RETRFINISHED:
@@ -1060,6 +1337,7 @@ File `%s' already there, will not retrieve.\n"), u->local);
                     tms, hstat.statcode, hstat.error);
          logputs (LOG_VERBOSE, "\n");
          FREEHSTAT (hstat);
+         free(filename_plus_orig_suffix);  /* must precede every return! */
          return WRONGCODE;
        }
 
@@ -1099,21 +1377,11 @@ Last-modified header invalid -- time-stamp ignored.\n"));
              if (tml >= tmr &&
                  (hstat.contlen == -1 || local_size == hstat.contlen))
                {
-                 if (local_dot_orig_file_exists)
-                   /* We can't collapse this down into just one logprintf()
-                      call with a variable set to u->local or the .orig
-                      filename because we have to malloc() space for the
-                      latter, and because there are multiple returns above (a
-                      coding style no-no by many measures, for reasons such as
-                      this) we'd have to remember to free() the string at each
-                      one to avoid a memory leak. */
-                   logprintf (LOG_VERBOSE, _("\
-Server file no newer than local file `%s.orig' -- not retrieving.\n\n"),
-                              u->local);
-                 else
-                   logprintf (LOG_VERBOSE, _("\
-Server file no newer than local file `%s' -- not retrieving.\n\n"), u->local);
+                 logprintf (LOG_VERBOSE, _("\
+Server file no newer than local file `%s' -- not retrieving.\n\n"),
+                            local_filename);
                  FREEHSTAT (hstat);
+                 free(filename_plus_orig_suffix);/*must precede every return!*/
                  return RETROK;
                }
              else if (tml >= tmr)
@@ -1141,6 +1409,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
       if (opt.spider)
        {
          logprintf (LOG_NOTQUIET, "%d %s\n\n", hstat.statcode, hstat.error);
+         free(filename_plus_orig_suffix);  /* must precede every return! */
          return RETROK;
        }
 
@@ -1162,8 +1431,15 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
                         tms, u->url, hstat.len, hstat.contlen, locf, count);
            }
          ++opt.numurls;
-         opt.downloaded += hstat.len;
-         downloaded_file(ADD_FILE, locf);
+         downloaded_increase (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(filename_plus_orig_suffix);  /* must precede every return! */
          return RETROK;
        }
       else if (hstat.res == 0) /* No read error */
@@ -1181,8 +1457,15 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
                             tms, u->url, hstat.len, locf, count);
                }
              ++opt.numurls;
-             opt.downloaded += hstat.len;
-             downloaded_file(ADD_FILE, locf);
+             downloaded_increase (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(filename_plus_orig_suffix);  /* must precede every return! */
              return RETROK;
            }
          else if (hstat.len < hstat.contlen) /* meaning we lost the
@@ -1203,8 +1486,15 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
                         "%s URL:%s [%ld/%ld] -> \"%s\" [%d]\n",
                         tms, u->url, hstat.len, hstat.contlen, locf, count);
              ++opt.numurls;
-             opt.downloaded += hstat.len;
-             downloaded_file(ADD_FILE, locf);
+             downloaded_increase (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(filename_plus_orig_suffix);  /* must precede every return! */
              return RETROK;
            }
          else                  /* the same, but not accepted */
@@ -1240,6 +1530,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
       break;
     }
   while (!opt.ntry || (count < opt.ntry));
+  free(filename_plus_orig_suffix);  /* must precede every return! */
   return TRYLIMEXC;
 }
 \f
@@ -1625,7 +1916,9 @@ username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
 static int
 known_authentication_scheme_p (const char *au)
 {
-  return HACK_O_MATIC (au, "Basic") || HACK_O_MATIC (au, "Digest");
+  return HACK_O_MATIC (au, "Basic")
+    || HACK_O_MATIC (au, "Digest")
+    || HACK_O_MATIC (au, "NTLM");
 }
 
 #undef HACK_O_MATIC
@@ -1644,6 +1937,8 @@ create_authorization_line (const char *au, const char *user,
 
   if (!strncasecmp (au, "Basic", 5))
     wwwauth = basic_authentication_encode (user, passwd, "Authorization");
+  if (!strncasecmp (au, "NTLM", 4))
+    wwwauth = basic_authentication_encode (user, passwd, "Authorization");
 #ifdef USE_DIGEST
   else if (!strncasecmp (au, "Digest", 6))
     wwwauth = digest_authentication_encode (au, user, passwd, method, path);