]> sjero.net Git - wget/blobdiff - src/http.c
[svn] Send the no-cache directive when required regardless of whether we're
[wget] / src / http.c
index 589657f37ee4bb171b69314758a98b6d3b41b56e..ecf29384c68708116e3a28422c97975775cf7993 100644 (file)
@@ -65,6 +65,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifdef HAVE_SSL
 # include "gen_sslfunc.h"
 #endif /* HAVE_SSL */
+#include "cookies.h"
 
 extern char *version_string;
 
@@ -77,6 +78,7 @@ extern int h_errno;
 # endif
 #endif
 \f
+static int cookies_loaded_p;
 
 #define TEXTHTML_S "text/html"
 #define HTTP_ACCEPT "*/*"
@@ -482,7 +484,7 @@ static char *basic_authentication_encode PARAMS ((const char *, const char *,
                                                  const char *));
 static int known_authentication_scheme_p PARAMS ((const char *));
 
-static time_t http_atotm PARAMS ((char *));
+time_t http_atotm PARAMS ((char *));
 
 #define BEGINS_WITH(line, string_constant)                             \
   (!strncasecmp (line, string_constant, sizeof (string_constant) - 1)  \
@@ -524,6 +526,8 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   static SSL_CTX *ssl_ctx = NULL;
   SSL *ssl = NULL;
 #endif /* HAVE_SSL */
+  struct wget_timer *timer;
+  char *cookies = NULL;
 
   /* Whether this connection will be kept alive after the HTTP request
      is done. */
@@ -592,6 +596,10 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   keep_alive = 0;
   http_keep_alive_1 = http_keep_alive_2 = 0;
 
+  if (opt.cookies)
+    cookies = build_cookies_request (u->host, u->port, u->path,
+                                    u->proto == URLHTTPS);
+
   /* Initialize certain elements of struct http_stat.  */
   hs->len = 0L;
   hs->contlen = -1;
@@ -781,11 +789,14 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
 
   /* String of the form :PORT.  Used only for non-standard ports. */
   port_maybe = NULL;
+  if (1
 #ifdef HAVE_SSL
-  if (remport != (u->proto == URLHTTPS ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT) )
+      && remport != (u->proto == URLHTTPS
+                    ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT)
 #else
-  if (remport != DEFAULT_HTTP_PORT)
+      && remport != DEFAULT_HTTP_PORT
 #endif
+      )
     {
       port_maybe = (char *)alloca (numdigit (remport) + 2);
       sprintf (port_maybe, ":%d", remport);
@@ -805,6 +816,7 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
                            + (request_keep_alive
                               ? strlen (request_keep_alive) : 0)
                            + (referer ? strlen (referer) : 0)
+                           + (cookies ? strlen (cookies) : 0)
                            + (wwwauth ? strlen (wwwauth) : 0)
                            + (proxyauth ? strlen (proxyauth) : 0)
                            + (range ? strlen (range) : 0)
@@ -817,12 +829,13 @@ 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%s\r\n",
+%s%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 : "",
+          cookies ? cookies : "", 
           wwwauth ? wwwauth : "", 
           proxyauth ? proxyauth : "", 
           range ? range : "",
@@ -832,6 +845,7 @@ Accept: %s\r\n\
    /* Free the temporary memory.  */
   FREE_MAYBE (wwwauth);
   FREE_MAYBE (proxyauth);
+  FREE_MAYBE (cookies);
 
   /* Send the request to server.  */
 #ifdef HAVE_SSL
@@ -989,6 +1003,10 @@ Accept: %s\r\n\
        if (header_process (hdr, "Last-Modified", header_strdup,
                            &hs->remote_time))
          goto done_header;
+      /* Try getting cookies. */
+      if (opt.cookies)
+       if (header_process (hdr, "Set-Cookie", set_cookie_header_cb, u))
+         goto done_header;
       /* Try getting www-authentication.  */
       if (!authenticate_h)
        if (header_process (hdr, "WWW-Authenticate", header_strdup,
@@ -1170,8 +1188,8 @@ Accept: %s\r\n\
            {
              logprintf (LOG_NOTQUIET,
                         _("\
-
-    The server does not support continued download;
+\n\
+    The server does not support continued download;\n\
     refusing to truncate `%s'.\n\n"), u->local);
              return CONTNOTSUPPORTED;
            }
@@ -1285,12 +1303,29 @@ Accept: %s\r\n\
     }
   else                         /* opt.dfp */
     {
+      extern int global_download_count;
       fp = opt.dfp;
-      if (!hs->restval)
+      /* To ensure that repeated "from scratch" downloads work for -O
+        files, we rewind the file pointer, unless restval is
+        non-zero.  (This works only when -O is used on regular files,
+        but it's still a valuable feature.)
+
+        However, this loses when more than one URL is specified on
+        the command line the second rewinds eradicates the contents
+        of the first download.  Thus we disable the above trick for
+        all the downloads except the very first one.
+
+         #### A possible solution to this would be to remember the
+        file position in the output document and to seek to that
+        position, instead of rewinding.  */
+      if (!hs->restval && global_download_count == 0)
        {
          /* This will silently fail for streams that don't correspond
             to regular files, but that's OK.  */
          rewind (fp);
+         /* ftruncate is needed because opt.dfp is opened in append
+            mode if opt.always_rest is set.  */
+         ftruncate (fileno (fp), 0);
          clearerr (fp);
        }
     }
@@ -1299,12 +1334,13 @@ Accept: %s\r\n\
      should be some overhead information.  */
   if (opt.save_headers)
     fwrite (all_headers, 1, all_length, fp);
-  reset_timer ();
+  timer = wtimer_new ();
   /* Get the contents of the document.  */
   hs->res = get_contents (sock, fp, &hs->len, hs->restval,
                          (contlen != -1 ? contlen : 0),
                          &rbuf, keep_alive);
-  hs->dltime = elapsed_time ();
+  hs->dltime = wtimer_elapsed (timer);
+  wtimer_delete (timer);
   {
     /* Close or flush the file.  We have to be careful to check for
        error here.  Checking the result of fwrite() is not enough --
@@ -1341,6 +1377,15 @@ http_loop (struct urlinfo *u, char **newloc, int *dt)
   struct http_stat hstat;      /* HTTP status */
   struct stat st;
 
+  /* 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 && opt.cookies_input && !cookies_loaded_p)
+    {
+      load_cookies (opt.cookies_input);
+      cookies_loaded_p = 1;
+    }
+
   *newloc = NULL;
 
   /* Warn on (likely bogus) wildcard usage in HTTP.  Don't use
@@ -1473,11 +1518,19 @@ File `%s' already there, will not retrieve.\n"), u->local);
       hstat.restval = 0L;
       /* Decide whether or not to restart.  */
       if (((count > 1 && (*dt & ACCEPTRANGES)) || opt.always_rest)
-         && file_exists_p (u->local))
-       if (stat (u->local, &st) == 0)
+         && file_exists_p (locf))
+       if (stat (locf, &st) == 0 && S_ISREG (st.st_mode))
          hstat.restval = st.st_size;
-      /* Decide whether to send the no-cache directive.  */
-      if (u->proxy && (count > 1 || (opt.proxy_cache == 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 ((u->proxy && count > 1) /* a */
+         || !opt.allow_cache     /* b */
+         )
        *dt |= SEND_NOCACHE;
       else
        *dt &= ~SEND_NOCACHE;
@@ -1783,20 +1836,71 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
 }
 \f
 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
-   than local timezone (mktime assumes the latter).
+   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.  */
+   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)
-    return -1;
-  tb = mktime (gmtime (&tl));
-  return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
+    {
+      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.
@@ -1805,8 +1909,8 @@ mktime_from_utc (struct tm *t)
    `+X', or at the end of the string.
 
    In extended regexp parlance, the function returns 1 if P matches
-   "^ *(GMT|[+-][0-9]|$)", 0 otherwise.  P being NULL (a valid result of
-   strptime()) is considered a failure and 0 is returned.  */
+   "^ *(GMT|[+-][0-9]|$)", 0 otherwise.  P being NULL (which strptime
+   can return) is considered a failure and 0 is returned.  */
 static int
 check_end (const char *p)
 {
@@ -1822,72 +1926,74 @@ check_end (const char *p)
     return 0;
 }
 
-/* Convert TIME_STRING time to time_t.  TIME_STRING can be in any of
-   the three formats RFC2068 allows the HTTP servers to emit --
-   RFC1123-date, RFC850-date or asctime-date.  Timezones are ignored,
-   and should be GMT.
-
-   We use strptime() to recognize various dates, which makes it a
-   little bit slacker than the RFC1123/RFC850/asctime (e.g. it always
-   allows shortened dates and months, one-digit days, etc.).  It also
-   allows more than one space anywhere where the specs require one SP.
-   The routine should probably be even more forgiving (as recommended
-   by RFC2068), but I do not have the time to write one.
-
-   Return the computed time_t representation, or -1 if all the
-   schemes fail.
-
-   Needless to say, what we *really* need here is something like
-   Marcus Hennecke's atotm(), which is forgiving, fast, to-the-point,
-   and does not use strptime().  atotm() is to be found in the sources
-   of `phttpd', a little-known HTTP server written by Peter Erikson.  */
-static time_t
+/* Convert the textual specification of time in TIME_STRING to the
+   number of seconds since the Epoch.
+
+   TIME_STRING can be in any of the three formats RFC2068 allows the
+   HTTP servers to emit -- RFC1123-date, RFC850-date or asctime-date.
+   Timezones are ignored, and should be GMT.
+
+   Return the computed time_t representation, or -1 if the conversion
+   fails.
+
+   This function uses strptime with various string formats for parsing
+   TIME_STRING.  This results in a parser that is not as lenient in
+   interpreting TIME_STRING as I would like it to be.  Being based on
+   strptime, it always allows shortened months, one-digit days, etc.,
+   but due to the multitude of formats in which time can be
+   represented, an ideal HTTP time parser would be even more
+   forgiving.  It should completely ignore things like week days and
+   concentrate only on the various forms of representing years,
+   months, days, hours, minutes, and seconds.  For example, it would
+   be nice if it accepted ISO 8601 out of the box.
+
+   I've investigated free and PD code for this purpose, but none was
+   usable.  getdate was big and unwieldy, and had potential copyright
+   issues, or so I was informed.  Dr. Marcus Hennecke's atotm(),
+   distributed with phttpd, is excellent, but we cannot use it because
+   it is not assigned to the FSF.  So I stuck it with strptime.  */
+
+time_t
 http_atotm (char *time_string)
 {
+  /* NOTE: Solaris strptime man page claims that %n and %t match white
+     space, but that's not universally available.  Instead, we simply
+     use ` ' to mean "skip all WS", which works under all strptime
+     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, %d-%b-%Y %T",         /* pseudo-RFC850:  Thu, 29-Jan-1998 22:12:57
+                                  (google.com uses this for their cookies.) */
+    "%a %b %d %T %Y"           /* asctime: Thu Jan 29 22:12:57 1998 */
+  };
+
+  int i;
   struct tm t;
 
-  /* Roger Beeman says: "This function dynamically allocates struct tm
-     t, but does no initialization.  The only field that actually
-     needs initialization is tm_isdst, since the others will be set by
-     strptime.  Since strptime does not set tm_isdst, it will return
-     the data structure with whatever data was in tm_isdst to begin
-     with.  For those of us in timezones where DST can occur, there
-     can be a one hour shift depending on the previous contents of the
-     data area where the data structure is allocated."  */
-  t.tm_isdst = -1;
+  /* According to Roger Beeman, we need to initialize tm_isdst, since
+     strptime won't do it.  */
+  t.tm_isdst = 0;
 
   /* Note that under foreign locales Solaris strptime() fails to
-     recognize English dates, which renders this function useless.  I
-     assume that other non-GNU strptime's are plagued by the same
-     disease.  We solve this by setting only LC_MESSAGES in
-     i18n_initialize(), instead of LC_ALL.
+     recognize English dates, which renders this function useless.  We
+     solve this by being careful not to affect LC_TIME when
+     initializing locale.
 
-     Another solution could be to temporarily set locale to C, invoke
+     Another solution would be to temporarily set locale to C, invoke
      strptime(), and restore it back.  This is slow and dirty,
      however, and locale support other than LC_MESSAGES can mess other
      things, so I rather chose to stick with just setting LC_MESSAGES.
 
-     Also note that none of this is necessary under GNU strptime(),
-     because it recognizes both international and local dates.  */
-
-  /* NOTE: We don't use `%n' for white space, as OSF's strptime uses
-     it to eat all white space up to (and including) a newline, and
-     the function fails if there is no newline (!).
-
-     Let's hope all strptime() implementations use ` ' to skip *all*
-     whitespace instead of just one (it works that way on all the
-     systems I've tested it on).  */
-
-  /* RFC1123: Thu, 29 Jan 1998 22:12:57 */
-  if (check_end (strptime (time_string, "%a, %d %b %Y %T", &t)))
-    return mktime_from_utc (&t);
-  /* RFC850:  Thu, 29-Jan-98 22:12:57 */
-  if (check_end (strptime (time_string, "%a, %d-%b-%y %T", &t)))
-    return mktime_from_utc (&t);
-  /* asctime: Thu Jan 29 22:12:57 1998 */
-  if (check_end (strptime (time_string, "%a %b %d %T %Y", &t)))
-    return mktime_from_utc (&t);
-  /* Failure.  */
+     GNU strptime does not have this problem because it recognizes
+     both international and local dates.  */
+
+  for (i = 0; i < ARRAY_SIZE (time_formats); i++)
+    if (check_end (strptime (time_string, time_formats[i], &t)))
+      return mktime_from_utc (&t);
+
+  /* All formats have failed.  */
   return -1;
 }
 \f
@@ -2002,10 +2108,6 @@ extract_header_attr (const char *au, const char *attr_name, char **ret)
     return 0;
 }
 
-/* Response value needs to be in lowercase, so we cannot use HEXD2ASC
-   from url.h.  See RFC 2069 2.1.2 for the syntax of response-digest.  */
-#define HEXD2asc(x) (((x) < 10) ? ((x) + '0') : ((x) - 10 + 'a'))
-
 /* 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
@@ -2017,8 +2119,8 @@ dump_hash (unsigned char *buf, const unsigned char *hash)
 
   for (i = 0; i < MD5_HASHLEN; i++, hash++)
     {
-      *buf++ = HEXD2asc (*hash >> 4);
-      *buf++ = HEXD2asc (*hash & 0xf);
+      *buf++ = XDIGIT_TO_xchar (*hash >> 4);
+      *buf++ = XDIGIT_TO_xchar (*hash & 0xf);
     }
   *buf = '\0';
 }