]> sjero.net Git - wget/blobdiff - src/cookies.c
[svn] Fix K&R incompatibilities reported by `gcc -Wtraditional'.
[wget] / src / cookies.c
index 0caa53d18d12f71d4563113c252976d16bc58b6c..eb3f879f5716ab6fe071b4080c49a8ff414f43a6 100644 (file)
@@ -77,8 +77,8 @@ time_t http_atotm PARAMS ((const char *));
    the cookies.  */
 
 struct cookie_jar {
-  /* Mapping between domains and their corresponding cookies.  */
-  struct hash_table *chains_by_domain;
+  /* Cookie chains indexed by domain.  */
+  struct hash_table *chains;
 
   int cookie_count;            /* number of cookies in the jar. */
 };
@@ -90,8 +90,8 @@ time_t cookies_now;
 struct cookie_jar *
 cookie_jar_new (void)
 {
-  struct cookie_jar *jar = xmalloc (sizeof (struct cookie_jar));
-  jar->chains_by_domain = make_nocase_string_hash_table (0);
+  struct cookie_jar *jar = xnew (struct cookie_jar);
+  jar->chains = make_nocase_string_hash_table (0);
   jar->cookie_count = 0;
   return jar;
 }
@@ -108,59 +108,65 @@ struct cookie {
                                   whole. */
 
   int permanent;               /* whether the cookie should outlive
-                                  the session */
-  time_t expiry_time;          /* time when the cookie expires */
+                                  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 */
+                                  cookie. */
 
   char *attr;                  /* cookie attribute name */
   char *value;                 /* cookie attribute value */
 
-  struct cookie_jar *jar;      /* pointer back to the cookie jar, for
-                                  convenience. */
   struct cookie *next;         /* used for chaining of cookies in the
                                   same domain. */
 };
 
 #define PORT_ANY (-1)
-#define COOKIE_EXPIRED_P(c) ((c)->expiry_time != 0 && (c)->expiry_time < cookies_now)
 
 /* Allocate and return a new, empty cookie structure. */
 
 static struct cookie *
 cookie_new (void)
 {
-  struct cookie *cookie = xmalloc (sizeof (struct cookie));
-  memset (cookie, '\0', sizeof (struct cookie));
+  struct cookie *cookie = xnew0 (struct cookie);
 
-  /* Both cookie->permanent and cookie->expiry_time are now 0.  By
-     default, we assume that the cookie is non-permanent and valid
-     until the end of the session.  */
+  /* Both cookie->permanent and cookie->expiry_time are now 0.  This
+     means that the cookie doesn't expire, but is only valid for this
+     session (i.e. not written out to disk).  */
 
   cookie->port = PORT_ANY;
   return cookie;
 }
 
+/* Non-zero if the cookie has expired.  Assumes cookies_now has been
+   set by one of the entry point functions.  */
+
+static int
+cookie_expired_p (const struct cookie *c)
+{
+  return c->expiry_time != 0 && c->expiry_time < cookies_now;
+}
+
 /* Deallocate COOKIE and its components. */
 
 static void
 delete_cookie (struct cookie *cookie)
 {
-  FREE_MAYBE (cookie->domain);
-  FREE_MAYBE (cookie->path);
-  FREE_MAYBE (cookie->attr);
-  FREE_MAYBE (cookie->value);
+  xfree_null (cookie->domain);
+  xfree_null (cookie->path);
+  xfree_null (cookie->attr);
+  xfree_null (cookie->value);
   xfree (cookie);
 }
 \f
 /* Functions for storing cookies.
 
-   All cookies can be reached beginning with jar->chains_by_domain.
-   The key in that table is the domain name, and the value is a linked
-   list of all cookies from that domain.  Every new cookie is placed
-   on the head of the list.  */
+   All cookies can be reached beginning with jar->chains.  The key in
+   that table is the domain name, and the value is a linked list of
+   all cookies from that domain.  Every new cookie is placed on the
+   head of the list.  */
 
 /* Find and return a cookie in JAR whose domain, path, and attribute
    name correspond to COOKIE.  If found, PREVPTR will point to the
@@ -175,7 +181,7 @@ find_matching_cookie (struct cookie_jar *jar, struct cookie *cookie,
 {
   struct cookie *chain, *prev;
 
-  chain = hash_table_get (jar->chains_by_domain, cookie->domain);
+  chain = hash_table_get (jar->chains, cookie->domain);
   if (!chain)
     goto nomatch;
 
@@ -209,7 +215,7 @@ store_cookie (struct cookie_jar *jar, struct cookie *cookie)
   struct cookie *chain_head;
   char *chain_key;
 
-  if (hash_table_get_pair (jar->chains_by_domain, cookie->domain,
+  if (hash_table_get_pair (jar->chains, cookie->domain,
                           &chain_key, &chain_head))
     {
       /* A chain of cookies in this domain already exists.  Check for
@@ -252,18 +258,23 @@ store_cookie (struct cookie_jar *jar, struct cookie *cookie)
       chain_key = xstrdup (cookie->domain);
     }
 
-  hash_table_put (jar->chains_by_domain, chain_key, cookie);
+  hash_table_put (jar->chains, chain_key, cookie);
   ++jar->cookie_count;
 
-  DEBUGP (("\nStored cookie %s %d%s %s %s %d %s %s %s\n",
-          cookie->domain, cookie->port,
-          cookie->port == PORT_ANY ? " (ANY)" : "",
-          cookie->path,
-          cookie->permanent ? "permanent" : "nonpermanent",
-          cookie->secure,
-          cookie->expiry_time
-          ? asctime (localtime (&cookie->expiry_time)) : "<undefined>",
-          cookie->attr, cookie->value));
+#ifdef ENABLE_DEBUG
+  if (opt.debug)
+    {
+      time_t exptime = (time_t) 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)" : "",
+              cookie->path,
+              cookie->permanent ? "permanent" : "session",
+              cookie->secure ? "secure" : "insecure",
+              cookie->expiry_time ? datetime_str (&exptime) : "none",
+              cookie->attr, cookie->value));
+    }
+#endif
 }
 
 /* Discard a cookie matching COOKIE's domain, port, path, and
@@ -277,7 +288,7 @@ discard_matching_cookie (struct cookie_jar *jar, struct cookie *cookie)
 {
   struct cookie *prev, *victim;
 
-  if (!hash_table_count (jar->chains_by_domain))
+  if (!hash_table_count (jar->chains))
     /* No elements == nothing to discard. */
     return;
 
@@ -294,18 +305,18 @@ discard_matching_cookie (struct cookie_jar *jar, struct cookie *cookie)
          char *chain_key = NULL;
          int res;
 
-         res = hash_table_get_pair (jar->chains_by_domain, victim->domain,
+         res = hash_table_get_pair (jar->chains, victim->domain,
                                     &chain_key, NULL);
          assert (res != 0);
          if (!victim->next)
            {
              /* VICTIM was the only cookie in the chain.  Destroy the
                 chain and deallocate the chain key.  */
-             hash_table_remove (jar->chains_by_domain, victim->domain);
+             hash_table_remove (jar->chains, victim->domain);
              xfree (chain_key);
            }
          else
-           hash_table_put (jar->chains_by_domain, chain_key, victim->next);
+           hash_table_put (jar->chains, chain_key, victim->next);
        }
       delete_cookie (victim);
       DEBUGP (("Discarded old cookie.\n"));
@@ -355,7 +366,7 @@ update_cookie_field (struct cookie *cookie,
     {
       if (!VALUE_NON_EMPTY)
        return 0;
-      FREE_MAYBE (cookie->domain);
+      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
         domain to "foo.com" and expect "subhost.foo.com" to get the
@@ -369,7 +380,7 @@ update_cookie_field (struct cookie *cookie,
     {
       if (!VALUE_NON_EMPTY)
        return 0;
-      FREE_MAYBE (cookie->path);
+      xfree_null (cookie->path);
       cookie->path = strdupdelim (value_b, value_e);
       return 1;
     }
@@ -389,8 +400,8 @@ update_cookie_field (struct cookie *cookie,
          cookie->expiry_time = (time_t)expires;
        }
       else
-       /* Error in expiration spec.  Assume default (cookie valid for
-          this session.)  */
+       /* Error in expiration spec.  Assume default (cookie doesn't
+          expire, but valid only for this session.)  */
        ;
 
       /* According to netscape's specification, expiry time in the
@@ -605,7 +616,8 @@ parse_set_cookies (const char *sc,
                    char *name;
                    BOUNDED_TO_ALLOCA (name_b, name_e, name);
                    logprintf (LOG_NOTQUIET,
-                              _("Error in Set-Cookie, field `%s'"), name);
+                              _("Error in Set-Cookie, field `%s'"),
+                              escnonprint (name));
                  }
                state = S_ERROR;
                break;
@@ -629,7 +641,7 @@ parse_set_cookies (const char *sc,
   if (!silent)
     logprintf (LOG_NOTQUIET,
               _("Syntax error in Set-Cookie: %s at position %d.\n"),
-              sc, p - sc);
+              escnonprint (sc), p - sc);
   return NULL;
 }
 \f
@@ -652,9 +664,9 @@ parse_set_cookies (const char *sc,
 
 /* Check whether ADDR matches <digits>.<digits>.<digits>.<digits>.
 
-  We don't want to call network functions like inet_addr() because all
-  we need is a check, preferrably one that is small, fast, and
-  well-defined.  */
+   We don't want to call network functions like inet_addr() because
+   all we need is a check, preferrably one that is small, fast, and
+   well-defined.  */
 
 static int
 numeric_address_p (const char *addr)
@@ -772,7 +784,7 @@ check_domain_match (const char *cookie_domain, const char *host)
       {
        int i;
        int known_toplevel = 0;
-       static char *known_toplevel_domains[] = {
+       static const char *known_toplevel_domains[] = {
          ".com", ".edu", ".net", ".org", ".gov", ".mil", ".int"
        };
        for (i = 0; i < countof (known_toplevel_domains); i++)
@@ -821,9 +833,9 @@ check_path_match (const char *cookie_path, const char *path)
    depending on the contents.  */
 
 void
-cookie_jar_process_set_cookie (struct cookie_jar *jar,
-                              const char *host, int port,
-                              const char *path, const char *set_cookie)
+cookie_handle_set_cookie (struct cookie_jar *jar,
+                         const char *host, int port,
+                         const char *path, const char *set_cookie)
 {
   struct cookie *cookie;
   cookies_now = time (NULL);
@@ -837,8 +849,13 @@ cookie_jar_process_set_cookie (struct cookie_jar *jar,
   if (!cookie->domain)
     {
     copy_domain:
+      /* If the domain was not provided, we use the one we're talking
+        to, and set exact match.  */
       cookie->domain = xstrdup (host);
-      cookie->port = port;
+      cookie->domain_exact = 1;
+      /* Set the port, but only if it's non-default. */
+      if (port != 80 && port != 443)
+       cookie->port = port;
     }
   else
     {
@@ -846,16 +863,26 @@ cookie_jar_process_set_cookie (struct cookie_jar *jar,
        {
          logprintf (LOG_NOTQUIET,
                     "Cookie coming from %s attempted to set domain to %s\n",
-                    host, cookie->domain);
+                    escnonprint (host), escnonprint (cookie->domain));
          xfree (cookie->domain);
          goto copy_domain;
        }
     }
 
   if (!cookie->path)
-    cookie->path = xstrdup (path);
+    {
+      /* The cookie doesn't set path: set it to the URL path, sans the
+        file part ("/dir/file" truncated to "/dir/").  */
+      char *trailing_slash = strrchr (path, '/');
+      if (trailing_slash)
+       cookie->path = strdupdelim (path, trailing_slash + 1);
+      else
+       /* no slash in the string -- can this even happen? */
+       cookie->path = xstrdup (path);
+    }
   else
     {
+      /* The cookie sets its own path; verify that it is legal. */
       if (!check_path_match (cookie->path, path))
        {
          DEBUGP (("Attempt to fake the path: %s, %s\n",
@@ -864,6 +891,9 @@ cookie_jar_process_set_cookie (struct cookie_jar *jar,
        }
     }
 
+  /* Now store the cookie, or discard an existing cookie, if
+     discarding was requested.  */
+
   if (cookie->discard_requested)
     {
       discard_matching_cookie (jar, cookie);
@@ -881,6 +911,19 @@ cookie_jar_process_set_cookie (struct cookie_jar *jar,
 /* Support for sending out cookies in HTTP requests, based on
    previously stored cookies.  Entry point is
    `build_cookies_request'.  */
+   
+/* Return a count of how many times CHR occurs in STRING. */
+
+static int
+count_char (const char *string, char chr)
+{
+  const char *p;
+  int count = 0;
+  for (p = string; *p; p++)
+    if (*p == chr)
+      ++count;
+  return count;
+}
 
 /* Find the cookie chains whose domains match HOST and store them to
    DEST.
@@ -902,7 +945,7 @@ find_chains_of_host (struct cookie_jar *jar, const char *host,
   int passes, passcnt;
 
   /* Bail out quickly if there are no cookies in the jar.  */
-  if (!hash_table_count (jar->chains_by_domain))
+  if (!hash_table_count (jar->chains))
     return 0;
 
   if (numeric_address_p (host))
@@ -922,7 +965,7 @@ find_chains_of_host (struct cookie_jar *jar, const char *host,
      srk.fer.hr's, then fer.hr's.  */
   while (1)
     {
-      struct cookie *chain = hash_table_get (jar->chains_by_domain, host);
+      struct cookie *chain = hash_table_get (jar->chains, host);
       if (chain)
        dest[dest_count++] = chain;
       if (++passcnt >= passes)
@@ -972,7 +1015,7 @@ cookie_matches_url (const struct cookie *cookie,
 {
   int pg;
 
-  if (COOKIE_EXPIRED_P (cookie))
+  if (cookie_expired_p (cookie))
     /* Ignore stale cookies.  Don't bother unchaining the cookie at
        this point -- Wget is a relatively short-lived application, and
        stale cookies will not be saved by `save_cookies'.  On the
@@ -1100,9 +1143,8 @@ goodness_comparator (const void *p1, const void *p2)
    generated, NULL is returned.  */
 
 char *
-cookie_jar_generate_cookie_header (struct cookie_jar *jar, const char *host,
-                                  int port, const char *path,
-                                  int connection_secure_p)
+cookie_header (struct cookie_jar *jar, const char *host,
+              int port, const char *path, int secflag)
 {
   struct cookie **chains;
   int chain_count;
@@ -1135,8 +1177,7 @@ cookie_jar_generate_cookie_header (struct cookie_jar *jar, const char *host,
   count = 0;
   for (i = 0; i < chain_count; i++)
     for (cookie = chains[i]; cookie; cookie = cookie->next)
-      if (cookie_matches_url (cookie, host, port, path, connection_secure_p,
-                             NULL))
+      if (cookie_matches_url (cookie, host, port, path, secflag, NULL))
        ++count;
   if (!count)
     return NULL;               /* no cookies matched */
@@ -1151,8 +1192,7 @@ cookie_jar_generate_cookie_header (struct cookie_jar *jar, const char *host,
     for (cookie = chains[i]; cookie; cookie = cookie->next)
       {
        int pg;
-       if (!cookie_matches_url (cookie, host, port, path,
-                                connection_secure_p, &pg))
+       if (!cookie_matches_url (cookie, host, port, path, secflag, &pg))
          continue;
        outgoing[ocnt].cookie = cookie;
        outgoing[ocnt].domain_goodness = strlen (cookie->domain);
@@ -1179,16 +1219,12 @@ cookie_jar_generate_cookie_header (struct cookie_jar *jar, const char *host,
     }
 
   /* Allocate output buffer:
-     "Cookie: "       -- 8
      name=value pairs -- result_size
      "; " separators  -- (count - 1) * 2
-     \r\n line ending -- 2
      \0 terminator    -- 1 */
-  result_size = 8 + result_size + (count - 1) * 2 + 2 + 1;
+  result_size = result_size + (count - 1) * 2 + 1;
   result = xmalloc (result_size);
   pos = 0;
-  strcpy (result, "Cookie: ");
-  pos += 8;
   for (i = 0; i < count; i++)
     {
       struct cookie *c = outgoing[i].cookie;
@@ -1206,8 +1242,6 @@ cookie_jar_generate_cookie_header (struct cookie_jar *jar, const char *host,
          result[pos++] = ' ';
        }
     }
-  result[pos++] = '\r';
-  result[pos++] = '\n';
   result[pos++] = '\0';
   assert (pos == result_size);
   return result;
@@ -1359,21 +1393,28 @@ cookie_jar_load (struct cookie_jar *jar, const char *file)
         malloced.)  */
       *expires_e = '\0';
       sscanf (expires_b, "%lf", &expiry);
-      if (expiry < cookies_now)
-       /* ignore stale cookie. */
-       goto abort;
-      cookie->expiry_time = expiry;
 
-      /* If the cookie has survived being saved into an external file,
-        it is obviously permanent.  */
-      cookie->permanent = 1;
+      if (expiry == 0)
+       {
+         /* EXPIRY can be 0 for session cookies saved because the
+            user specified `--keep-session-cookies' in the past.
+            They remain session cookies, and will be saved only if
+            the user has specified `keep-session-cookies' again.  */
+       }
+      else
+       {
+         if (expiry < cookies_now)
+           goto abort_cookie;  /* ignore stale cookie. */
+         cookie->expiry_time = expiry;
+         cookie->permanent = 1;
+       }
 
       store_cookie (jar, cookie);
 
     next:
       continue;
 
-    abort:
+    abort_cookie:
       delete_cookie (cookie);
     }
   fclose (fp);
@@ -1391,9 +1432,9 @@ save_cookies_mapper (void *key, void *value, void *arg)
   struct cookie *cookie = (struct cookie *)value;
   for (; cookie; cookie = cookie->next)
     {
-      if (!cookie->permanent)
+      if (!cookie->permanent && !opt.keep_session_cookies)
        continue;
-      if (COOKIE_EXPIRED_P (cookie))
+      if (cookie_expired_p (cookie))
        continue;
       if (!cookie->domain_exact)
        fputc ('.', fp);
@@ -1431,15 +1472,14 @@ cookie_jar_save (struct cookie_jar *jar, const char *file)
     }
 
   fputs ("# HTTP cookie file.\n", fp);
-  fprintf (fp, "# Generated by Wget on %s.\n", datetime_str (NULL));
+  fprintf (fp, "# Generated by Wget on %s.\n", datetime_str (&cookies_now));
   fputs ("# Edit at your own risk.\n\n", fp);
 
-  hash_table_map (jar->chains_by_domain, save_cookies_mapper, fp);
+  hash_table_map (jar->chains, save_cookies_mapper, fp);
 
   if (ferror (fp))
     logprintf (LOG_NOTQUIET, _("Error writing to `%s': %s\n"),
               file, strerror (errno));
-
   if (fclose (fp) < 0)
     logprintf (LOG_NOTQUIET, _("Error closing `%s': %s\n"),
               file, strerror (errno));
@@ -1460,7 +1500,7 @@ nuke_cookie_chain (void *value, void *key, void *arg)
   struct cookie_jar *jar = (struct cookie_jar *)arg;
 
   /* Remove the chain from the table and free the key. */
-  hash_table_remove (jar->chains_by_domain, chain_key);
+  hash_table_remove (jar->chains, chain_key);
   xfree (chain_key);
 
   /* Then delete all the cookies in the chain. */
@@ -1480,8 +1520,8 @@ nuke_cookie_chain (void *value, void *key, void *arg)
 void
 cookie_jar_delete (struct cookie_jar *jar)
 {
-  hash_table_map (jar->chains_by_domain, nuke_cookie_chain, jar);
-  hash_table_destroy (jar->chains_by_domain);
+  hash_table_map (jar->chains, nuke_cookie_chain, jar);
+  hash_table_destroy (jar->chains);
   xfree (jar);
 }
 \f