]> sjero.net Git - wget/blobdiff - src/http.c
[svn] Update FSF's address and copyright years.
[wget] / src / http.c
index 5eb256ba06ac7500069814b53b7b727442739a13..4883fb64d7ddfaa51a93a778577e43d85cd9dc08 100644 (file)
@@ -1,5 +1,5 @@
 /* HTTP support.
-   Copyright (C) 2005 Free Software Foundation, Inc.
+   Copyright (C) 1996-2005 Free Software Foundation, Inc.
 
 This file is part of GNU Wget.
 
@@ -14,8 +14,8 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+along with Wget; if not, write to the Free Software Foundation, Inc.,
+51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
 In addition, as a special exception, the Free Software Foundation
 gives permission to link the code of its release of Wget with the
@@ -38,8 +38,10 @@ so, delete this exception statement from your version.  */
 #include <assert.h>
 #include <errno.h>
 #include <time.h>
+#include <locale.h>
 
 #include "wget.h"
+#include "http.h"
 #include "utils.h"
 #include "url.h"
 #include "host.h"
@@ -59,10 +61,6 @@ so, delete this exception statement from your version.  */
 #include "convert.h"
 
 extern char *version_string;
-extern LARGE_INT total_downloaded_bytes;
-
-extern FILE *output_stream;
-extern bool output_stream_regular;
 
 #ifndef MIN
 # define MIN(x, y) ((x) > (y) ? (y) : (x))
@@ -418,40 +416,51 @@ post_file (int sock, const char *file_name, wgint promised_size)
   return 0;
 }
 \f
+/* Determine whether [START, PEEKED + PEEKLEN) contains an empty line.
+   If so, return the pointer to the position after the line, otherwise
+   return NULL.  This is used as callback to fd_read_hunk.  The data
+   between START and PEEKED has been read and cannot be "unread"; the
+   data after PEEKED has only been peeked.  */
+
 static const char *
-response_head_terminator (const char *hunk, int oldlen, int peeklen)
+response_head_terminator (const char *start, const char *peeked, int peeklen)
 {
-  const char *start, *end;
+  const char *p, *end;
 
   /* If at first peek, verify whether HUNK starts with "HTTP".  If
      not, this is a HTTP/0.9 request and we must bail out without
      reading anything.  */
-  if (oldlen == 0 && 0 != memcmp (hunk, "HTTP", MIN (peeklen, 4)))
-    return hunk;
-
-  if (oldlen < 4)
-    start = hunk;
-  else
-    start = hunk + oldlen - 4;
-  end = hunk + oldlen + peeklen;
-
-  for (; start < end - 1; start++)
-    if (*start == '\n')
+  if (start == peeked && 0 != memcmp (start, "HTTP", MIN (peeklen, 4)))
+    return start;
+
+  /* Look for "\n[\r]\n", and return the following position if found.
+     Start two chars before the current to cover the possibility that
+     part of the terminator (e.g. "\n\r") arrived in the previous
+     batch.  */
+  p = peeked - start < 2 ? start : peeked - 2;
+  end = peeked + peeklen;
+
+  /* Check for \n\r\n or \n\n anywhere in [p, end-2). */
+  for (; p < end - 2; p++)
+    if (*p == '\n')
       {
-       if (start < end - 2
-           && start[1] == '\r'
-           && start[2] == '\n')
-         return start + 3;
-       if (start[1] == '\n')
-         return start + 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')
+    return p + 2;
+
   return NULL;
 }
 
-/* The maximum size of a single HTTP response we care to read.  This
-   is not meant to impose an arbitrary limit, but to protect the user
-   from Wget slurping up available memory upon encountering malicious
-   or buggy server output.  Define it to 0 to remove the limit.  */
+/* The maximum size of a single HTTP response we care to read.  Rather
+   than being a limit of the reader implementation, this limit
+   prevents Wget from slurping all available memory upon encountering
+   malicious or buggy server output, thus protecting the user.  Define
+   it to 0 to remove the limit.  */
 
 #define HTTP_RESPONSE_MAX_SIZE 65536
 
@@ -1089,8 +1098,6 @@ static char *create_authorization_line (const char *, const char *,
 static char *basic_authentication_encode (const char *, const char *);
 static bool known_authentication_scheme_p (const char *, const char *);
 
-time_t http_atotm (const char *);
-
 #define BEGINS_WITH(line, string_constant)                             \
   (!strncasecmp (line, string_constant, sizeof (string_constant) - 1)  \
    && (ISSPACE (line[sizeof (string_constant) - 1])                    \
@@ -1312,20 +1319,25 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
        request_set_header (req, "Proxy-Authorization", proxyauth, rel_value);
     }
 
+  /* Generate the Host header, HOST:PORT.  Take into account that:
+
+     - Broken server-side software often doesn't recognize the PORT
+       argument, so we must generate "Host: www.server.com" instead of
+       "Host: www.server.com:80" (and likewise for https port).
+
+     - IPv6 addresses contain ":", so "Host: 3ffe:8100:200:2::2:1234"
+       becomes ambiguous and needs to be rewritten as "Host:
+       [3ffe:8100:200:2::2]:1234".  */
   {
-    /* Whether we need to print the host header with braces around
-       host, e.g. "Host: [3ffe:8100:200:2::2]:1234" instead of the
-       usual "Host: symbolic-name:1234". */
-    bool squares = strchr (u->host, ':') != NULL;
-    if (u->port == scheme_default_port (u->scheme))
-      request_set_header (req, "Host",
-                         aprintf (squares ? "[%s]" : "%s", u->host),
-                         rel_value);
-    else
-      request_set_header (req, "Host",
-                         aprintf (squares ? "[%s]:%d" : "%s:%d",
-                                  u->host, u->port),
-                         rel_value);
+    /* Formats arranged for hfmt[add_port][add_squares].  */
+    static const char *hfmt[][2] = {
+      { "%s", "[%s]" }, { "%s:%d", "[%s]:%d" }
+    };
+    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);
   }
 
   if (!inhibit_keep_alive)
@@ -1354,7 +1366,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
          post_data_size = file_size (opt.post_file_name);
          if (post_data_size == -1)
            {
-             logprintf (LOG_NOTQUIET, _("POST data file missing: %s (%s)\n"),
+             logprintf (LOG_NOTQUIET, _("POST data file `%s' missing: %s\n"),
                         opt.post_file_name, strerror (errno));
              post_data_size = 0;
            }
@@ -1706,7 +1718,6 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   /* Handle (possibly multiple instances of) the Set-Cookie header. */
   if (opt.cookies)
     {
-      char *pth = NULL;
       int scpos;
       const char *scbeg, *scend;
       /* The jar should have been created by now. */
@@ -1717,15 +1728,8 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
           ++scpos)
        {
          char *set_cookie; BOUNDED_TO_ALLOCA (scbeg, scend, set_cookie);
-         if (pth == NULL)
-           {
-             /* u->path doesn't begin with /, which cookies.c expects. */
-             pth = (char *) alloca (1 + strlen (u->path) + 1);
-             pth[0] = '/';
-             strcpy (pth + 1, u->path);
-           }
-         cookie_handle_set_cookie (wget_cookie_jar, u->host, u->port, pth,
-                                   set_cookie);
+         cookie_handle_set_cookie (wget_cookie_jar, u->host, u->port,
+                                   u->path, set_cookie);
        }
     }
 
@@ -1846,7 +1850,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
          logputs (LOG_VERBOSE, _("Length: "));
          if (contlen != -1)
            {
-             logputs (LOG_VERBOSE, with_thousand_seps (contlen + contrange));
+             logputs (LOG_VERBOSE, number_to_static_string (contlen + contrange));
              if (contlen + contrange >= 1024)
                logprintf (LOG_VERBOSE, " (%s)",
                           human_readable (contlen + contrange));
@@ -1854,11 +1858,11 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
                {
                  if (contlen >= 1024)
                    logprintf (LOG_VERBOSE, _(", %s (%s) remaining"),
-                              with_thousand_seps (contlen),
+                              number_to_static_string (contlen),
                               human_readable (contlen));
                  else
                    logprintf (LOG_VERBOSE, _(", %s remaining"),
-                              with_thousand_seps (contlen));
+                              number_to_static_string (contlen));
                }
            }
          else
@@ -1978,7 +1982,8 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer,
   bool use_ts, got_head = false;/* time-stamping info */
   char *filename_plus_orig_suffix;
   char *local_filename = NULL;
-  char *tms, *locf, *tmrate;
+  char *tms, *locf;
+  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 */
@@ -2004,7 +2009,7 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer,
   *newloc = NULL;
 
   /* Warn on (likely bogus) wildcard usage in HTTP.  */
-  if (has_wildcards_p (u->path))
+  if (opt.ftp_glob && has_wildcards_p (u->path))
     logputs (LOG_VERBOSE, _("Warning: wildcards not supported in HTTP.\n"));
 
   xzero (hstat);
@@ -2368,7 +2373,8 @@ The sizes do not match (local %s) -- retrieving.\n"),
          return RETROK;
        }
 
-      tmrate = retr_rate (hstat.rd_size, hstat.dltime, 0);
+      tmrate = retr_rate (hstat.rd_size, hstat.dltime);
+      total_download_time += hstat.dltime;
 
       if (hstat.len == hstat.contlen)
        {
@@ -2507,74 +2513,6 @@ The sizes do not match (local %s) -- retrieving.\n"),
   return TRYLIMEXC;
 }
 \f
-/* Converts struct tm to time_t, assuming the data in tm is UTC rather
-   than local timezone.
-
-   mktime is similar but assumes struct tm, also known as the
-   "broken-down" form of time, is in local time zone.  mktime_from_utc
-   uses mktime to make the conversion understanding that an offset
-   will be introduced by the local time assumption.
-
-   mktime_from_utc then measures the introduced offset by applying
-   gmtime to the initial result and applying mktime to the resulting
-   "broken-down" form.  The difference between the two mktime results
-   is the measured offset which is then subtracted from the initial
-   mktime result to yield a calendar time which is the value returned.
-
-   tm_isdst in struct tm is set to 0 to force mktime to introduce a
-   consistent offset (the non DST offset) since tm and tm+o might be
-   on opposite sides of a DST change.
-
-   Some implementations of mktime return -1 for the nonexistent
-   localtime hour at the beginning of DST.  In this event, use
-   mktime(tm - 1hr) + 3600.
-
-   Schematically
-     mktime(tm)   --> t+o
-     gmtime(t+o)  --> tm+o
-     mktime(tm+o) --> t+2o
-     t+o - (t+2o - t+o) = t
-
-   Note that glibc contains a function of the same purpose named
-   `timegm' (reverse of gmtime).  But obviously, it is not universally
-   available, and unfortunately it is not straightforwardly
-   extractable for use here.  Perhaps configure should detect timegm
-   and use it where available.
-
-   Contributed by Roger Beeman <beeman@cisco.com>, with the help of
-   Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.
-   Further improved by Roger with assistance from Edward J. Sabol
-   based on input by Jamie Zawinski.  */
-
-static time_t
-mktime_from_utc (struct tm *t)
-{
-  time_t tl, tb;
-  struct tm *tg;
-
-  tl = mktime (t);
-  if (tl == -1)
-    {
-      t->tm_hour--;
-      tl = mktime (t);
-      if (tl == -1)
-       return -1; /* can't deal with output from strptime */
-      tl += 3600;
-    }
-  tg = gmtime (&tl);
-  tg->tm_isdst = 0;
-  tb = mktime (tg);
-  if (tb == -1)
-    {
-      tg->tm_hour--;
-      tb = mktime (tg);
-      if (tb == -1)
-       return -1; /* can't deal with output from gmtime */
-      tb += 3600;
-    }
-  return (tl - (tb - tl));
-}
-
 /* Check whether the result of strptime() indicates success.
    strptime() returns the pointer to how far it got to in the string.
    The processing has been successful if the string is at `GMT' or
@@ -2642,7 +2580,15 @@ http_atotm (const char *time_string)
                                   (used in Set-Cookie, defined in the
                                   Netscape cookie specification.) */
   };
+  const char *oldlocale;
   int 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);
+  setlocale (LC_TIME, "C");
 
   for (i = 0; i < countof (time_formats); i++)
     {
@@ -2650,22 +2596,20 @@ http_atotm (const char *time_string)
 
       /* Some versions of strptime use the existing contents of struct
         tm to recalculate the date according to format.  Zero it out
-        to prevent garbage from the stack influencing strptime.  */
+        to prevent stack garbage from influencing strptime.  */
       xzero (t);
 
-      /* Solaris strptime fails to recognize English month names in
-        non-English locales, which we work around by not setting the
-        LC_TIME category.  Another way would be to temporarily set
-        locale to C before invoking strptime, but that's slow and
-        messy.  GNU strptime does not have this problem because it
-        recognizes English month names along with the local ones.  */
-
       if (check_end (strptime (time_string, time_formats[i], &t)))
-       return mktime_from_utc (&t);
+       {
+         ret = timegm (&t);
+         break;
+       }
     }
 
-  /* All formats have failed.  */
-  return -1;
+  /* Restore the previous locale. */
+  setlocale (LC_TIME, oldlocale);
+
+  return ret;
 }
 \f
 /* Authorization support: We support three authorization schemes:
@@ -2751,7 +2695,7 @@ extract_header_attr (const char *au, const char *attr_name, char **ret)
    buffer of 33 writable characters (32 for hex digits plus one for
    zero termination).  */
 static void
-dump_hash (unsigned char *buf, const unsigned char *hash)
+dump_hash (char *buf, const unsigned char *hash)
 {
   int i;
 
@@ -2840,8 +2784,8 @@ digest_authentication_encode (const char *au, const char *user,
   {
     ALLOCA_MD5_CONTEXT (ctx);
     unsigned char hash[MD5_HASHLEN];
-    unsigned char a1buf[MD5_HASHLEN * 2 + 1], a2buf[MD5_HASHLEN * 2 + 1];
-    unsigned char response_digest[MD5_HASHLEN * 2 + 1];
+    char a1buf[MD5_HASHLEN * 2 + 1], a2buf[MD5_HASHLEN * 2 + 1];
+    char response_digest[MD5_HASHLEN * 2 + 1];
 
     /* A1BUF = H(user ":" realm ":" password) */
     gen_md5_init (ctx);
@@ -2863,11 +2807,11 @@ digest_authentication_encode (const char *au, const char *user,
 
     /* RESPONSE_DIGEST = H(A1BUF ":" nonce ":" A2BUF) */
     gen_md5_init (ctx);
-    gen_md5_update (a1buf, MD5_HASHLEN * 2, ctx);
+    gen_md5_update ((unsigned char *)a1buf, MD5_HASHLEN * 2, ctx);
     gen_md5_update ((unsigned char *)":", 1, ctx);
     gen_md5_update ((unsigned char *)nonce, strlen (nonce), ctx);
     gen_md5_update ((unsigned char *)":", 1, ctx);
-    gen_md5_update (a2buf, MD5_HASHLEN * 2, ctx);
+    gen_md5_update ((unsigned char *)a2buf, MD5_HASHLEN * 2, ctx);
     gen_md5_finish (ctx, hash);
     dump_hash (response_digest, hash);