]> sjero.net Git - wget/blobdiff - src/cookies.c
[svn] Move extern declarations to .h files.
[wget] / src / cookies.c
index eb3f879f5716ab6fe071b4080c49a8ff414f43a6..10710eb618f2d079b05c3c13da026897b7314a40 100644 (file)
@@ -27,40 +27,35 @@ 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.  */
 
-/* Written by Hrvoje Niksic.  Parts are loosely inspired by cookie
-   code submitted by Tomasz Wegrzanowski.
+/* Written by Hrvoje Niksic.  Parts are loosely inspired by the
+   cookie patch submitted by Tomasz Wegrzanowski.
 
-   Ideas for future work:
+   This implements the client-side cookie support, as specified
+   (loosely) by Netscape's "preliminary specification", currently
+   available at:
 
-   * Implement limits on cookie-related sizes, such as max. cookie
-     size, max. number of cookies, etc.
+       http://wp.netscape.com/newsref/std/cookie_spec.html
 
-   * Add more "cookie jar" methods, such as methods to iterate over
-     stored cookies, to clear temporary cookies, to perform
-     intelligent auto-saving, etc.
-
-   * Support `Set-Cookie2' and `Cookie2' headers?  Does anyone really
-     use them?  */
+   rfc2109 is not supported because of its incompatibilities with the
+   above widely-used specification.  rfc2965 is entirely ignored,
+   since popular client software doesn't implement it, and even the
+   sites that do send Set-Cookie2 also emit Set-Cookie for
+   compatibility.  */
 
 #include <config.h>
 
 #include <stdio.h>
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif
+#include <string.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <errno.h>
+#include <time.h>
 
 #include "wget.h"
 #include "utils.h"
 #include "hash.h"
 #include "cookies.h"
-
-/* This should *really* be in a .h file!  */
-time_t http_atotm PARAMS ((const char *));
+#include "http.h"              /* for http_atotm */
 \f
 /* Declarations of `struct cookie' and the most basic functions. */
 
@@ -85,7 +80,7 @@ struct cookie_jar {
 
 /* Value set by entry point functions, so that the low-level
    routines don't need to call time() all the time.  */
-time_t cookies_now;
+static time_t cookies_now;
 
 struct cookie_jar *
 cookie_jar_new (void)
@@ -101,21 +96,21 @@ struct cookie {
   int port;                    /* port number */
   char *path;                  /* path prefix of the cookie */
 
-  int secure;                  /* whether cookie should be
+  unsigned discard_requested :1; /* whether cookie was created to
+                                  request discarding another
+                                  cookie. */
+
+  unsigned secure :1;          /* whether cookie should be
                                   transmitted over non-https
                                   connections. */
-  int domain_exact;            /* whether DOMAIN must match as a
+  unsigned domain_exact :1;    /* whether DOMAIN must match as a
                                   whole. */
 
-  int permanent;               /* whether the cookie should outlive
+  unsigned permanent :1;       /* whether the cookie should outlive
                                   the session. */
   time_t expiry_time;          /* time when the cookie expires, 0
                                   means undetermined. */
 
-  int discard_requested;       /* whether cookie was created to
-                                  request discarding another
-                                  cookie. */
-
   char *attr;                  /* cookie attribute name */
   char *value;                 /* cookie attribute value */
 
@@ -143,7 +138,7 @@ cookie_new (void)
 /* Non-zero if the cookie has expired.  Assumes cookies_now has been
    set by one of the entry point functions.  */
 
-static int
+static bool
 cookie_expired_p (const struct cookie *c)
 {
   return c->expiry_time != 0 && c->expiry_time < cookies_now;
@@ -261,10 +256,9 @@ store_cookie (struct cookie_jar *jar, struct cookie *cookie)
   hash_table_put (jar->chains, chain_key, cookie);
   ++jar->cookie_count;
 
-#ifdef ENABLE_DEBUG
-  if (opt.debug)
+  IF_DEBUG
     {
-      time_t exptime = (time_t) cookie->expiry_time;
+      time_t exptime = cookie->expiry_time;
       DEBUGP (("\nStored cookie %s %d%s %s <%s> <%s> [expiry %s] %s %s\n",
               cookie->domain, cookie->port,
               cookie->port == PORT_ANY ? " (ANY)" : "",
@@ -274,7 +268,6 @@ store_cookie (struct cookie_jar *jar, struct cookie *cookie)
               cookie->expiry_time ? datetime_str (&exptime) : "none",
               cookie->attr, cookie->value));
     }
-#endif
 }
 
 /* Discard a cookie matching COOKIE's domain, port, path, and
@@ -343,10 +336,10 @@ discard_matching_cookie (struct cookie_jar *jar, struct cookie *cookie)
    it will parse the values of the fields it recognizes and fill the
    corresponding fields in COOKIE.
 
-   Returns 1 on success.  Returns zero in case a syntax error is
+   Returns true on success.  Returns false in case a syntax error is
    found; such a cookie should be discarded.  */
 
-static int
+static bool
 update_cookie_field (struct cookie *cookie,
                     const char *name_b, const char *name_e,
                     const char *value_b, const char *value_e)
@@ -356,16 +349,16 @@ update_cookie_field (struct cookie *cookie,
   if (!cookie->attr)
     {
       if (!VALUE_EXISTS)
-       return 0;
+       return false;
       cookie->attr = strdupdelim (name_b, name_e);
       cookie->value = strdupdelim (value_b, value_e);
-      return 1;
+      return true;
     }
 
   if (NAME_IS ("domain"))
     {
       if (!VALUE_NON_EMPTY)
-       return 0;
+       return false;
       xfree_null (cookie->domain);
       /* Strictly speaking, we should set cookie->domain_exact if the
         domain doesn't begin with a dot.  But many sites set the
@@ -374,15 +367,15 @@ update_cookie_field (struct cookie *cookie,
       if (*value_b == '.')
        ++value_b;
       cookie->domain = strdupdelim (value_b, value_e);
-      return 1;
+      return true;
     }
   else if (NAME_IS ("path"))
     {
       if (!VALUE_NON_EMPTY)
-       return 0;
+       return false;
       xfree_null (cookie->path);
       cookie->path = strdupdelim (value_b, value_e);
-      return 1;
+      return true;
     }
   else if (NAME_IS ("expires"))
     {
@@ -390,14 +383,14 @@ update_cookie_field (struct cookie *cookie,
       time_t expires;
 
       if (!VALUE_NON_EMPTY)
-       return 0;
+       return false;
       BOUNDED_TO_ALLOCA (value_b, value_e, value_copy);
 
       expires = http_atotm (value_copy);
-      if (expires != -1)
+      if (expires != (time_t) -1)
        {
          cookie->permanent = 1;
-         cookie->expiry_time = (time_t)expires;
+         cookie->expiry_time = expires;
        }
       else
        /* Error in expiration spec.  Assume default (cookie doesn't
@@ -410,7 +403,7 @@ update_cookie_field (struct cookie *cookie,
       if (cookie->expiry_time < cookies_now)
        cookie->discard_requested = 1;
 
-      return 1;
+      return true;
     }
   else if (NAME_IS ("max-age"))
     {
@@ -418,13 +411,13 @@ update_cookie_field (struct cookie *cookie,
       char *value_copy;
 
       if (!VALUE_NON_EMPTY)
-       return 0;
+       return false;
       BOUNDED_TO_ALLOCA (value_b, value_e, value_copy);
 
       sscanf (value_copy, "%lf", &maxage);
       if (maxage == -1)
        /* something went wrong. */
-       return 0;
+       return false;
       cookie->permanent = 1;
       cookie->expiry_time = cookies_now + maxage;
 
@@ -433,22 +426,22 @@ update_cookie_field (struct cookie *cookie,
       if (maxage == 0)
        cookie->discard_requested = 1;
 
-      return 1;
+      return true;
     }
   else if (NAME_IS ("secure"))
     {
       /* ignore value completely */
       cookie->secure = 1;
-      return 1;
+      return true;
     }
   else
     /* Unrecognized attribute; ignore it. */
-    return 1;
+    return true;
 }
 
 #undef NAME_IS
 
-/* Returns non-zero for characters that are legal in the name of an
+/* Returns true for characters that are legal in the name of an
    attribute.  This used to allow only alphanumerics, '-', and '_',
    but we need to be more lenient because a number of sites wants to
    use weirder attribute names.  rfc2965 "informally specifies"
@@ -474,10 +467,10 @@ update_cookie_field (struct cookie *cookie,
 
 static struct cookie *
 parse_set_cookies (const char *sc,
-                  int (*callback) (struct cookie *,
-                                   const char *, const char *,
-                                   const char *, const char *),
-                  int silent)
+                  bool (*callback) (struct cookie *,
+                                    const char *, const char *,
+                                    const char *, const char *),
+                  bool silent)
 {
   struct cookie *cookie = cookie_new ();
 
@@ -608,7 +601,7 @@ parse_set_cookies (const char *sc,
          break;
        case S_ATTR_ACTION:
          {
-           int legal = callback (cookie, name_b, name_e, value_b, value_e);
+           bool legal = callback (cookie, name_b, name_e, value_b, value_e);
            if (!legal)
              {
                if (!silent)
@@ -641,7 +634,7 @@ parse_set_cookies (const char *sc,
   if (!silent)
     logprintf (LOG_NOTQUIET,
               _("Syntax error in Set-Cookie: %s at position %d.\n"),
-              escnonprint (sc), p - sc);
+              escnonprint (sc), (int) (p - sc));
   return NULL;
 }
 \f
@@ -652,14 +645,14 @@ parse_set_cookies (const char *sc,
 
 #define REQUIRE_DIGITS(p) do {                 \
   if (!ISDIGIT (*p))                           \
-    return 0;                                  \
+    return false;                              \
   for (++p; ISDIGIT (*p); p++)                 \
     ;                                          \
 } while (0)
 
 #define REQUIRE_DOT(p) do {                    \
   if (*p++ != '.')                             \
-    return 0;                                  \
+    return false;                              \
 } while (0)
 
 /* Check whether ADDR matches <digits>.<digits>.<digits>.<digits>.
@@ -668,7 +661,7 @@ parse_set_cookies (const char *sc,
    all we need is a check, preferrably one that is small, fast, and
    well-defined.  */
 
-static int
+static bool
 numeric_address_p (const char *addr)
 {
   const char *p = addr;
@@ -682,8 +675,8 @@ numeric_address_p (const char *addr)
   REQUIRE_DIGITS (p);          /* D */
 
   if (*p != '\0')
-    return 0;
-  return 1;
+    return false;
+  return true;
 }
 
 /* Check whether COOKIE_DOMAIN is an appropriate domain for HOST.
@@ -691,7 +684,7 @@ numeric_address_p (const char *addr)
    the sites deviated too often, so I had to fall back to "tail
    matching", as defined by the original Netscape's cookie spec.  */
 
-static int
+static bool
 check_domain_match (const char *cookie_domain, const char *host)
 {
   DEBUGP (("cdm: 1"));
@@ -705,13 +698,13 @@ check_domain_match (const char *cookie_domain, const char *host)
 
   /* For the sake of efficiency, check for exact match first. */
   if (0 == strcasecmp (cookie_domain, host))
-    return 1;
+    return true;
 
   DEBUGP ((" 3"));
 
   /* HOST must match the tail of cookie_domain. */
-  if (!match_tail (host, cookie_domain, 1))
-    return 0;
+  if (!match_tail (host, cookie_domain, true))
+    return false;
 
   /* We know that COOKIE_DOMAIN is a subset of HOST; however, we must
      make sure that somebody is not trying to set the cookie for a
@@ -757,7 +750,7 @@ check_domain_match (const char *cookie_domain, const char *host)
        case '.':
          if (ldcl == 0)
            /* Empty domain component found -- the domain is invalid. */
-           return 0;
+           return false;
          if (*(p + 1) == '\0')
            {
              /* Tolerate trailing '.' by not treating the domain as
@@ -776,25 +769,25 @@ check_domain_match (const char *cookie_domain, const char *host)
     DEBUGP ((" 5"));
 
     if (dccount < 2)
-      return 0;
+      return false;
 
     DEBUGP ((" 6"));
 
     if (dccount == 2)
       {
        int i;
-       int known_toplevel = 0;
+       int known_toplevel = false;
        static const char *known_toplevel_domains[] = {
          ".com", ".edu", ".net", ".org", ".gov", ".mil", ".int"
        };
        for (i = 0; i < countof (known_toplevel_domains); i++)
-         if (match_tail (cookie_domain, known_toplevel_domains[i], 1))
+         if (match_tail (cookie_domain, known_toplevel_domains[i], true))
            {
-             known_toplevel = 1;
+             known_toplevel = true;
              break;
            }
        if (!known_toplevel && nldcl <= 3)
-         return 0;
+         return false;
       }
   }
 
@@ -810,23 +803,34 @@ check_domain_match (const char *cookie_domain, const char *host)
       /* desired domain:             bar.com */
       /* '.' must be here in host-> ^        */
       if (hlen > dlen && host[hlen - dlen - 1] != '.')
-       return 0;
+       return false;
     }
 
   DEBUGP ((" 8"));
 
-  return 1;
+  return true;
 }
 
-static int path_matches PARAMS ((const char *, const char *));
+static int path_matches (const char *, const char *);
 
 /* Check whether PATH begins with COOKIE_PATH. */
 
-static int
+static bool
 check_path_match (const char *cookie_path, const char *path)
 {
-  return path_matches (path, cookie_path);
+  return path_matches (path, cookie_path) != 0;
 }
+
+/* Prepend '/' to string S.  S is copied to fresh stack-allocated
+   space and its value is modified to point to the new location.  */
+
+#define PREPEND_SLASH(s) do {                                  \
+  char *PS_newstr = (char *) alloca (1 + strlen (s) + 1);      \
+  *PS_newstr = '/';                                            \
+  strcpy (PS_newstr + 1, s);                                   \
+  s = PS_newstr;                                               \
+} while (0)
+
 \f
 /* Process the HTTP `Set-Cookie' header.  This results in storing the
    cookie or discarding a matching one, or ignoring it completely, all
@@ -840,7 +844,12 @@ cookie_handle_set_cookie (struct cookie_jar *jar,
   struct cookie *cookie;
   cookies_now = time (NULL);
 
-  cookie = parse_set_cookies (set_cookie, update_cookie_field, 0);
+  /* Wget's paths don't begin with '/' (blame rfc1808), but cookie
+     usage assumes /-prefixed paths.  Until the rest of Wget is fixed,
+     simply prepend slash to PATH.  */
+  PREPEND_SLASH (path);
+
+  cookie = parse_set_cookies (set_cookie, update_cookie_field, false);
   if (!cookie)
     goto out;
 
@@ -862,7 +871,7 @@ cookie_handle_set_cookie (struct cookie_jar *jar,
       if (!check_domain_match (cookie->domain, host))
        {
          logprintf (LOG_NOTQUIET,
-                    "Cookie coming from %s attempted to set domain to %s\n",
+                    _("Cookie coming from %s attempted to set domain to %s\n"),
                     escnonprint (host), escnonprint (cookie->domain));
          xfree (cookie->domain);
          goto copy_domain;
@@ -982,16 +991,7 @@ find_chains_of_host (struct cookie_jar *jar, const char *host,
 static int
 path_matches (const char *full_path, const char *prefix)
 {
-  int len;
-
-  if (*prefix != '/')
-    /* Wget's HTTP paths do not begin with '/' (the URL code treats it
-       as a mere separator, inspired by rfc1808), but the '/' is
-       assumed when matching against the cookie stuff.  */
-    return 0;
-
-  ++prefix;
-  len = strlen (prefix);
+  int len = strlen (prefix);
 
   if (0 != strncmp (full_path, prefix, len))
     /* FULL_PATH doesn't begin with PREFIX. */
@@ -1001,17 +1001,17 @@ path_matches (const char *full_path, const char *prefix)
   return len + 1;
 }
 
-/* Return non-zero iff COOKIE matches the provided parameters of the
-   URL being downloaded: HOST, PORT, PATH, and SECFLAG.
+/* Return true iff COOKIE matches the provided parameters of the URL
+   being downloaded: HOST, PORT, PATH, and SECFLAG.
 
    If PATH_GOODNESS is non-NULL, store the "path goodness" value
    there.  That value is a measure of how closely COOKIE matches PATH,
    used for ordering cookies.  */
 
-static int
+static bool
 cookie_matches_url (const struct cookie *cookie,
                    const char *host, int port, const char *path,
-                   int secflag, int *path_goodness)
+                   bool secflag, int *path_goodness)
 {
   int pg;
 
@@ -1021,31 +1021,31 @@ cookie_matches_url (const struct cookie *cookie,
        stale cookies will not be saved by `save_cookies'.  On the
        other hand, this function should be as efficient as
        possible.  */
-    return 0;
+    return false;
 
   if (cookie->secure && !secflag)
     /* Don't transmit secure cookies over insecure connections.  */
-    return 0;
+    return false;
   if (cookie->port != PORT_ANY && cookie->port != port)
-    return 0;
+    return false;
 
   /* If exact domain match is required, verify that cookie's domain is
      equal to HOST.  If not, assume success on the grounds of the
      cookie's chain having been found by find_chains_of_host.  */
   if (cookie->domain_exact
       && 0 != strcasecmp (host, cookie->domain))
-    return 0;
+    return false;
 
   pg = path_matches (path, cookie->path);
-  if (!pg)
-    return 0;
+  if (pg == 0)
+    return false;
 
   if (path_goodness)
     /* If the caller requested path_goodness, we return it.  This is
        an optimization, so that the caller doesn't need to call
        path_matches() again.  */
     *path_goodness = pg;
-  return 1;
+  return true;
 }
 
 /* A structure that points to a cookie, along with the additional
@@ -1144,7 +1144,7 @@ goodness_comparator (const void *p1, const void *p2)
 
 char *
 cookie_header (struct cookie_jar *jar, const char *host,
-              int port, const char *path, int secflag)
+              int port, const char *path, bool secflag)
 {
   struct cookie **chains;
   int chain_count;
@@ -1154,6 +1154,7 @@ cookie_header (struct cookie_jar *jar, const char *host,
   int count, i, ocnt;
   char *result;
   int result_size, pos;
+  PREPEND_SLASH (path);                /* see cookie_handle_set_cookie */
 
   /* First, find the cookie chains whose domains match HOST. */
 
@@ -1317,7 +1318,7 @@ cookie_jar_load (struct cookie_jar *jar, const char *file)
   FILE *fp = fopen (file, "r");
   if (!fp)
     {
-      logprintf (LOG_NOTQUIET, "Cannot open cookies file `%s': %s\n",
+      logprintf (LOG_NOTQUIET, _("Cannot open cookies file `%s': %s\n"),
                 file, strerror (errno));
       return;
     }
@@ -1533,13 +1534,13 @@ cookie_jar_delete (struct cookie_jar *jar)
 int test_count;
 char *test_results[10];
 
-static int test_parse_cookies_callback (struct cookie *ignored,
-                                       const char *nb, const char *ne,
-                                       const char *vb, const char *ve)
+static bool test_parse_cookies_callback (struct cookie *ignored,
+                                        const char *nb, const char *ne,
+                                        const char *vb, const char *ve)
 {
   test_results[test_count++] = strdupdelim (nb, ne);
   test_results[test_count++] = strdupdelim (vb, ve);
-  return 1;
+  return true;
 }
 
 void
@@ -1579,7 +1580,7 @@ test_cookies (void)
       struct cookie *c;
 
       test_count = 0;
-      c = parse_set_cookies (data, test_parse_cookies_callback, 1);
+      c = parse_set_cookies (data, test_parse_cookies_callback, true);
       if (!c)
        {
          printf ("NULL cookie returned for valid data: %s\n", data);