From 29a66192cf2ceafcc434be59e052c82098a1f824 Mon Sep 17 00:00:00 2001 From: hniksic Date: Sat, 20 Apr 2002 13:46:38 -0700 Subject: [PATCH] [svn] Cookie interface and implementation changes from and . --- src/ChangeLog | 28 ++++ src/cookies.c | 358 +++++++++++++++++++++++++------------------------- src/cookies.h | 20 +-- src/http.c | 43 ++++-- src/init.c | 6 +- src/main.c | 6 +- 6 files changed, 262 insertions(+), 199 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index d7cb1058..9573ba32 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,31 @@ +2002-04-20 Hrvoje Niksic + + * init.c: Ditto. + + * main.c: Ditto. + + * http.c: Use the new interface. + + * cookies.c: Provide an OO-style "cookie jar" interface to enable + separate cookie jars. + + * http.c (http_atotm): Declare argument as const. + +2002-04-20 Hrvoje Niksic + + * cookies.c (cookie_new): Default to PORT_ANY. + (find_cookie_chain_exact): Only search by DOMAIN. + (find_matching_cookie): Also check that PORT matches. + (store_cookie): Only match the domain. + (set_cookie_header_cb): When a cookie "fakes" a domain, assume it + is valid for that host rather than discarding it completely. + (find_matching_chains): Don't search by PORT. + (matching_cookie): Also match PORT. + (load_cookies): Set the port if specified, otherwise leave it as + ANY. + (save_cookies_mapper): Save the port if specified, otherwise leave + it empty. + 2002-04-19 Thomas Lussnig * init.c: The option `egdfile' was not in sort order. diff --git a/src/cookies.c b/src/cookies.c index 18e6c8f9..360d55f8 100644 --- a/src/cookies.c +++ b/src/cookies.c @@ -1,5 +1,5 @@ /* Support for cookies. - Copyright (C) 2001 Free Software Foundation, Inc. + Copyright (C) 2001, 2002 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -18,7 +18,13 @@ along with Wget; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Written by Hrvoje Niksic. Parts are loosely inspired by cookie - code submitted by Tomasz Wegrzanowski. */ + code submitted by Tomasz Wegrzanowski. + + TODO: Implement limits on cookie-related sizes, such as max. cookie + size, max. number of cookies, etc. Add more "cookie jar" methods, + such as methods to over stored cookies, to clear temporary cookies, + to perform intelligent auto-saving, etc. Ultimately support + `Set-Cookie2' and `Cookie2' headers. */ #include @@ -35,23 +41,34 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "wget.h" #include "utils.h" #include "hash.h" -#include "url.h" #include "cookies.h" -/* Hash table that maps domain names to cookie chains. */ - -static struct hash_table *cookies_hash_table; - -/* This should be set by entry points in this file, so the low-level - functions don't need to call time() all the time. */ - -static time_t cookies_now; - /* This should *really* be in a .h file! */ -time_t http_atotm PARAMS ((char *)); - +time_t http_atotm PARAMS ((const char *)); -/* Definition of `struct cookie' and the most basic functions. */ +/* Declarations of `struct cookie' and the most basic functions. */ + +struct cookie_jar { + /* Hash table that maps domain names to cookie chains. A "cookie + chain" is a linked list of cookies that belong to the same + domain. */ + struct hash_table *chains_by_domain; + + int cookie_count; /* number of cookies in the 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; + +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); + jar->cookie_count = 0; + return jar; +} struct cookie { char *domain; /* domain of the cookie */ @@ -62,7 +79,7 @@ struct cookie { connections. */ int permanent; /* whether the cookie should outlive the session */ - unsigned long expiry_time; /* time when the cookie expires */ + time_t expiry_time; /* time when the cookie expires */ int discard_requested; /* whether cookie was created to request discarding another cookie */ @@ -70,10 +87,15 @@ struct 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 * @@ -82,13 +104,11 @@ cookie_new (void) struct cookie *cookie = xmalloc (sizeof (struct cookie)); memset (cookie, '\0', sizeof (struct cookie)); - /* If we don't know better, assume cookie is non-permanent and valid - for the entire session. */ - cookie->expiry_time = ~(unsigned long)0; - - /* Assume default port. */ - cookie->port = 80; + /* 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. */ + cookie->port = PORT_ANY; return cookie; } @@ -106,59 +126,33 @@ delete_cookie (struct cookie *cookie) /* Functions for storing cookies. - All cookies can be referenced through cookies_hash_table. 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_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. */ -/* Write "HOST:PORT" to a stack-allocated area and make RESULT point - to that area. RESULT should be a character pointer. Useful for - creating HOST:PORT strings, which are the keys in the hash - table. */ - -#define SET_HOSTPORT(host, port, result) do { \ - int HP_len = strlen (host); \ - result = alloca (HP_len + 1 + numdigit (port) + 1); \ - memcpy (result, host, HP_len); \ - result[HP_len] = ':'; \ - number_to_string (result + HP_len + 1, port); \ -} while (0) - -/* Find cookie chain that corresponds to DOMAIN (exact) and PORT. */ - -static struct cookie * -find_cookie_chain_exact (const char *domain, int port) -{ - char *key; - if (!cookies_hash_table) - return NULL; - SET_HOSTPORT (domain, port, key); - return hash_table_get (cookies_hash_table, key); -} - -/* Find and return the cookie whose domain, path, and attribute name - correspond to COOKIE. If found, PREVPTR will point to the location - of the cookie previous in chain, or NULL if the found cookie is the - head of a chain. +/* Find and return a cookie in JAR whose domain, path, and attribute + name correspond to COOKIE. If found, PREVPTR will point to the + location of the cookie previous in chain, or NULL if the found + cookie is the head of a chain. If no matching cookie is found, return NULL. */ static struct cookie * -find_matching_cookie (struct cookie *cookie, struct cookie **prevptr) +find_matching_cookie (struct cookie_jar *jar, struct cookie *cookie, + struct cookie **prevptr) { struct cookie *chain, *prev; - if (!cookies_hash_table) - goto nomatch; - - chain = find_cookie_chain_exact (cookie->domain, cookie->port); + chain = hash_table_get (jar->chains_by_domain, cookie->domain); if (!chain) goto nomatch; prev = NULL; for (; chain; prev = chain, chain = chain->next) - if (!strcmp (cookie->path, chain->path) - && !strcmp (cookie->attr, chain->attr)) + if (0 == strcmp (cookie->path, chain->path) + && 0 == strcmp (cookie->attr, chain->attr) + && cookie->port == chain->port) { *prevptr = prev; return chain; @@ -169,7 +163,7 @@ find_matching_cookie (struct cookie *cookie, struct cookie **prevptr) return NULL; } -/* Store COOKIE to memory. +/* Store COOKIE to the jar. This is done by placing COOKIE at the head of its chain. However, if COOKIE matches a cookie already in memory, as determined by @@ -179,29 +173,19 @@ find_matching_cookie (struct cookie *cookie, struct cookie **prevptr) first time; next hash_table_put's reuse the same key. */ static void -store_cookie (struct cookie *cookie) +store_cookie (struct cookie_jar *jar, struct cookie *cookie) { struct cookie *chain_head; - char *hostport; char *chain_key; - if (!cookies_hash_table) - /* If the hash table is not initialized, do so now, because we'll - need to store things. */ - cookies_hash_table = make_nocase_string_hash_table (0); - - /* Initialize hash table key. */ - SET_HOSTPORT (cookie->domain, cookie->port, hostport); - - if (hash_table_get_pair (cookies_hash_table, hostport, + if (hash_table_get_pair (jar->chains_by_domain, cookie->domain, &chain_key, &chain_head)) { - /* There already exists a chain of cookies with this exact - domain. We need to check for duplicates -- if an existing - cookie exactly matches our domain, path and name, we replace - it. */ + /* A chain of cookies in this domain already exists. Check for + duplicates -- if an extant cookie exactly matches our domain, + port, path, and name, replace it. */ struct cookie *prev; - struct cookie *victim = find_matching_cookie (cookie, &prev); + struct cookie *victim = find_matching_cookie (jar, cookie, &prev); if (victim) { @@ -220,6 +204,7 @@ store_cookie (struct cookie *cookie) cookie->next = victim->next; } delete_cookie (victim); + --jar->cookie_count; DEBUGP (("Deleted old cookie (to be replaced.)\n")); } else @@ -232,36 +217,39 @@ store_cookie (struct cookie *cookie) that, because it might get deallocated by the above code at some point later. */ cookie->next = NULL; - chain_key = xstrdup (hostport); + chain_key = xstrdup (cookie->domain); } - hash_table_put (cookies_hash_table, chain_key, cookie); + hash_table_put (jar->chains_by_domain, chain_key, cookie); + ++jar->cookie_count; - DEBUGP (("\nStored cookie %s %d %s %s %d %s %s %s\n", - cookie->domain, cookie->port, cookie->path, + 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, - asctime (localtime ((time_t *)&cookie->expiry_time)), + cookie->expiry_time + ? asctime (localtime (&cookie->expiry_time)) : "", cookie->attr, cookie->value)); } -/* Discard a cookie matching COOKIE's domain, path, and attribute - name. This gets called when we encounter a cookie whose expiry - date is in the past, or whose max-age is set to 0. The former - corresponds to netscape cookie spec, while the latter is specified - by rfc2109. */ +/* Discard a cookie matching COOKIE's domain, port, path, and + attribute name. This gets called when we encounter a cookie whose + expiry date is in the past, or whose max-age is set to 0. The + former corresponds to netscape cookie spec, while the latter is + specified by rfc2109. */ static void -discard_matching_cookie (struct cookie *cookie) +discard_matching_cookie (struct cookie_jar *jar, struct cookie *cookie) { struct cookie *prev, *victim; - if (!cookies_hash_table - || !hash_table_count (cookies_hash_table)) + if (!hash_table_count (jar->chains_by_domain)) /* No elements == nothing to discard. */ return; - victim = find_matching_cookie (cookie, &prev); + victim = find_matching_cookie (jar, cookie, &prev); if (victim) { if (prev) @@ -271,25 +259,21 @@ discard_matching_cookie (struct cookie *cookie) { /* VICTIM was head of its chain. We need to place a new cookie at the head. */ - - char *hostport; char *chain_key = NULL; int res; - SET_HOSTPORT (victim->domain, victim->port, hostport); - res = hash_table_get_pair (cookies_hash_table, hostport, + res = hash_table_get_pair (jar->chains_by_domain, 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 (cookies_hash_table, hostport); + hash_table_remove (jar->chains_by_domain, victim->domain); xfree (chain_key); } else - hash_table_put (cookies_hash_table, chain_key, victim->next); + hash_table_put (jar->chains_by_domain, chain_key, victim->next); } delete_cookie (victim); DEBUGP (("Discarded old cookie.\n")); @@ -365,12 +349,11 @@ update_cookie_field (struct cookie *cookie, if (expires != -1) { cookie->permanent = 1; - cookie->expiry_time = (unsigned long)expires; + cookie->expiry_time = (time_t)expires; } else /* Error in expiration spec. Assume default (cookie valid for - this session.) #### Should we return 0 and invalidate the - cookie? */ + this session.) */ ; /* According to netscape's specification, expiry time in the @@ -392,10 +375,10 @@ update_cookie_field (struct cookie *cookie, sscanf (value_copy, "%lf", &maxage); if (maxage == -1) - /* something is wrong. */ + /* something went wrong. */ return 0; cookie->permanent = 1; - cookie->expiry_time = (unsigned long)cookies_now + (unsigned long)maxage; + cookie->expiry_time = cookies_now + maxage; /* According to rfc2109, a cookie with max-age of 0 means that discarding of a matching cookie is requested. */ @@ -678,7 +661,10 @@ static int check_domain_match (const char *cookie_domain, const char *host) { static char *special_toplevel_domains[] = { - ".com", ".edu", ".net", ".org", ".gov", ".mil", ".int" + /* This is a total crock of shit, but we're living with it until + something better is devised. */ + ".com", ".edu", ".net", ".org", ".gov", ".mil", ".int", + ".de", ".fr", ".hr" }; int i, required_dots; @@ -748,62 +734,64 @@ check_path_match (const char *cookie_path, const char *path) return path_matches (path, cookie_path); } -/* Parse the `Set-Cookie' header and, if the cookie is legal, store it - to memory. */ +/* Process the HTTP `Set-Cookie' header. This results in storing the + cookie or discarding a matching one, or ignoring it completely, all + depending on the contents. */ -int -set_cookie_header_cb (const char *hdr, void *closure) +void +cookie_jar_process_set_cookie (struct cookie_jar *jar, + const char *host, int port, + const char *path, const char *set_cookie) { - struct url *u = (struct url *)closure; struct cookie *cookie; - cookies_now = time (NULL); - cookie = parse_set_cookies (hdr); + cookie = parse_set_cookies (set_cookie); if (!cookie) goto out; /* Sanitize parts of cookie. */ if (!cookie->domain) - cookie->domain = xstrdup (u->host); + { + copy_domain: + cookie->domain = xstrdup (host); + cookie->port = port; + } else { - if (!check_domain_match (cookie->domain, u->host)) + if (!check_domain_match (cookie->domain, host)) { - DEBUGP (("Attempt to fake the domain: %s, %s\n", - cookie->domain, u->host)); - goto out; + logprintf (LOG_NOTQUIET, + "Cookie coming from %s attempted to set domain to %s\n", + host, cookie->domain); + goto copy_domain; } } if (!cookie->path) - cookie->path = xstrdup (u->path); + cookie->path = xstrdup (path); else { - if (!check_path_match (cookie->path, u->path)) + if (!check_path_match (cookie->path, path)) { DEBUGP (("Attempt to fake the path: %s, %s\n", - cookie->path, u->path)); + cookie->path, path)); goto out; } } - cookie->port = u->port; - if (cookie->discard_requested) { - discard_matching_cookie (cookie); + discard_matching_cookie (jar, cookie); delete_cookie (cookie); - return 1; } - store_cookie (cookie); - return 1; + store_cookie (jar, cookie); + return; out: if (cookie) delete_cookie (cookie); - return 1; } /* Support for sending out cookies in HTTP requests, based on @@ -820,13 +808,13 @@ set_cookie_header_cb (const char *hdr, void *closure) ++st_count; \ } while (0) -/* Store cookie chains that match HOST, PORT. Since more than one - chain can match, the matches are written to STORE. No more than - SIZE matches are written; if more matches are present, return the - number of chains that would have been written. */ +/* Store cookie chains that match HOST. Since more than one chain can + match, the matches are written to STORE. No more than SIZE matches + are written; if more matches are present, return the number of + chains that would have been written. */ static int -find_matching_chains (const char *host, int port, +find_matching_chains (struct cookie_jar *jar, const char *host, struct cookie *store[], int size) { struct cookie *chain; @@ -834,13 +822,13 @@ find_matching_chains (const char *host, int port, char *hash_key; int count = 0; - if (!cookies_hash_table) + if (!hash_table_count (jar->chains_by_domain)) return 0; - SET_HOSTPORT (host, port, hash_key); + STRDUP_ALLOCA (hash_key, host); - /* Exact match. */ - chain = hash_table_get (cookies_hash_table, hash_key); + /* Look for an exact match. */ + chain = hash_table_get (jar->chains_by_domain, hash_key); if (chain) STORE_CHAIN (chain, store, size, count); @@ -855,7 +843,7 @@ find_matching_chains (const char *host, int port, loop. */ char *p = strchr (hash_key, '.'); assert (p != NULL); - chain = hash_table_get (cookies_hash_table, p); + chain = hash_table_get (jar->chains_by_domain, p); if (chain) STORE_CHAIN (chain, store, size, count); hash_key = p + 1; @@ -888,20 +876,33 @@ path_matches (const char *full_path, const char *prefix) return len + 1; } +/* Return non-zero iff COOKIE matches the given PATH, PORT, and + security flag. HOST is not a flag because it is assumed that the + cookie comes from the correct chain. + + If PATH_GOODNESS is non-NULL, store the "path goodness" there. The + said goodness is a measure of how well COOKIE matches PATH. It is + used for ordering cookies. */ + static int -matching_cookie (const struct cookie *cookie, const char *path, +matching_cookie (const struct cookie *cookie, const char *path, int port, int connection_secure_p, int *path_goodness) { int pg; - if (cookie->expiry_time < cookies_now) - /* Ignore stale cookies. There is no need to unchain the cookie - at this point -- Wget is a relatively short-lived application, - and stale cookies will not be saved by `save_cookies'. */ + 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 + other hand, this function should be as efficient as + possible. */ return 0; + if (cookie->secure && !connection_secure_p) /* Don't transmit secure cookies over an insecure connection. */ return 0; + if (cookie->port != PORT_ANY && cookie->port != port) + return 0; pg = path_matches (path, cookie->path); if (!pg) return 0; @@ -992,15 +993,16 @@ goodness_comparator (const void *p1, const void *p2) return dgdiff ? dgdiff : pgdiff; } -/* Build a `Cookie' header for a request that goes to HOST:PORT and +/* Generate a `Cookie' header for a request that goes to HOST:PORT and requests PATH from the server. The resulting string is allocated with `malloc', and the caller is responsible for freeing it. If no cookies pertain to this request, i.e. no cookie header should be generated, NULL is returned. */ char * -build_cookies_request (const char *host, int port, const char *path, - int connection_secure_p) +cookie_jar_generate_cookie_header (struct cookie_jar *jar, const char *host, + int port, const char *path, + int connection_secure_p) { struct cookie *chain_default_store[20]; struct cookie **all_chains = chain_default_store; @@ -1014,7 +1016,7 @@ build_cookies_request (const char *host, int port, const char *path, int result_size, pos; again: - chain_count = find_matching_chains (host, port, all_chains, chain_store_size); + chain_count = find_matching_chains (jar, host, all_chains, chain_store_size); if (chain_count > chain_store_size) { /* It's extremely unlikely that more than 20 chains will ever @@ -1035,7 +1037,7 @@ build_cookies_request (const char *host, int port, const char *path, count = 0; for (i = 0; i < chain_count; i++) for (cookie = all_chains[i]; cookie; cookie = cookie->next) - if (matching_cookie (cookie, path, connection_secure_p, NULL)) + if (matching_cookie (cookie, path, port, connection_secure_p, NULL)) ++count; if (!count) /* No matching cookies. */ @@ -1051,7 +1053,7 @@ build_cookies_request (const char *host, int port, const char *path, for (cookie = all_chains[i]; cookie; cookie = cookie->next) { int pg; - if (!matching_cookie (cookie, path, connection_secure_p, &pg)) + if (!matching_cookie (cookie, path, port, connection_secure_p, &pg)) continue; outgoing[ocnt].cookie = cookie; outgoing[ocnt].domain_goodness = strlen (cookie->domain); @@ -1185,7 +1187,7 @@ domain_port (const char *domain_b, const char *domain_e, /* Load cookies from FILE. */ void -load_cookies (const char *file) +cookie_jar_load (struct cookie_jar *jar, const char *file) { char *line; FILE *fp = fopen (file, "r"); @@ -1202,6 +1204,7 @@ load_cookies (const char *file) struct cookie *cookie; char *p = line; + double expiry; int port; char *domain_b = NULL, *domain_e = NULL; @@ -1252,24 +1255,25 @@ load_cookies (const char *file) port = domain_port (domain_b, domain_e, (const char **)&domain_e); if (port) cookie->port = port; - else - cookie->port = cookie->secure ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT; - cookie->domain = strdupdelim (domain_b, domain_e); /* safe default in case EXPIRES field is garbled. */ - cookie->expiry_time = cookies_now - 1; + expiry = (double)cookies_now - 1; /* I don't like changing the line, but it's completely safe. (line is malloced.) */ *expires_e = '\0'; - sscanf (expires_b, "%lu", &cookie->expiry_time); - if (cookie->expiry_time < cookies_now) + 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; - store_cookie (cookie); + store_cookie (jar, cookie); next: continue; @@ -1294,12 +1298,15 @@ save_cookies_mapper (void *key, void *value, void *arg) { if (!chain->permanent) continue; - if (chain->expiry_time < cookies_now) + if (COOKIE_EXPIRED_P (chain)) continue; - fprintf (fp, "%s\t%s\t%s\t%s\t%lu\t%s\t%s\n", - domain, *domain == '.' ? "TRUE" : "FALSE", + fputs (domain, fp); + if (chain->port != PORT_ANY) + fprintf (fp, ":%d", chain->port); + fprintf (fp, "\t%s\t%s\t%s\t%.0f\t%s\t%s\n", + *domain == '.' ? "TRUE" : "FALSE", chain->path, chain->secure ? "TRUE" : "FALSE", - chain->expiry_time, + (double)chain->expiry_time, chain->attr, chain->value); if (ferror (fp)) return 1; /* stop mapping */ @@ -1310,15 +1317,10 @@ save_cookies_mapper (void *key, void *value, void *arg) /* Save cookies, in format described above, to FILE. */ void -save_cookies (const char *file) +cookie_jar_save (struct cookie_jar *jar, const char *file) { FILE *fp; - if (!cookies_hash_table - || !hash_table_count (cookies_hash_table)) - /* no cookies stored; nothing to do. */ - return; - DEBUGP (("Saving cookies to %s.\n", file)); cookies_now = time (NULL); @@ -1335,7 +1337,7 @@ save_cookies (const char *file) fprintf (fp, "# Generated by Wget on %s.\n", datetime_str (NULL)); fputs ("# Edit at your own risk.\n\n", fp); - hash_table_map (cookies_hash_table, save_cookies_mapper, fp); + hash_table_map (jar->chains_by_domain, save_cookies_mapper, fp); if (ferror (fp)) logprintf (LOG_NOTQUIET, _("Error writing to `%s': %s\n"), @@ -1348,14 +1350,20 @@ save_cookies (const char *file) DEBUGP (("Done saving cookies.\n")); } +/* Destroy all the elements in the chain and unhook it from the cookie + jar. This is written in the form of a callback to hash_table_map + and used by cookie_jar_delete to delete all the cookies in a + jar. */ + static int -delete_cookie_chain_mapper (void *value, void *key, void *arg_ignored) +nuke_cookie_chain (void *value, void *key, void *arg) { char *chain_key = (char *)value; struct cookie *chain = (struct cookie *)key; + struct cookie_jar *jar = (struct cookie_jar *)arg; /* Remove the chain from the table and free the key. */ - hash_table_remove (cookies_hash_table, chain_key); + hash_table_remove (jar->chains_by_domain, chain_key); xfree (chain_key); /* Then delete all the cookies in the chain. */ @@ -1373,11 +1381,9 @@ delete_cookie_chain_mapper (void *value, void *key, void *arg_ignored) /* Clean up cookie-related data. */ void -cookies_cleanup (void) +cookie_jar_delete (struct cookie_jar *jar) { - if (!cookies_hash_table) - return; - hash_table_map (cookies_hash_table, delete_cookie_chain_mapper, NULL); - hash_table_destroy (cookies_hash_table); - cookies_hash_table = NULL; + hash_table_map (jar->chains_by_domain, nuke_cookie_chain, jar); + hash_table_destroy (jar->chains_by_domain); + xfree (jar); } diff --git a/src/cookies.h b/src/cookies.h index c660a200..9e731326 100644 --- a/src/cookies.h +++ b/src/cookies.h @@ -1,5 +1,5 @@ /* Support for cookies. - Copyright (C) 2001 Free Software Foundation, Inc. + Copyright (C) 2001, 2002 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -20,16 +20,18 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef COOKIES_H #define COOKIES_H -/* struct cookie is not exported; this file only exports functions for - manipulating cookie contents. */ +struct cookie_jar; -int set_cookie_header_cb PARAMS ((const char *, void *)); +struct cookie_jar *cookie_jar_new PARAMS ((void)); +void cookie_jar_delete PARAMS ((struct cookie_jar *)); -char *build_cookies_request PARAMS ((const char *, int, const char *, int)); +void cookie_jar_process_set_cookie PARAMS ((struct cookie_jar *, const char *, + int, const char *, const char *)); +char *cookie_jar_generate_cookie_header PARAMS ((struct cookie_jar *, + const char *, int, + const char *, int)); -void load_cookies PARAMS ((const char *)); -void save_cookies PARAMS ((const char *)); - -void cookies_cleanup PARAMS ((void)); +void cookie_jar_load PARAMS ((struct cookie_jar *, const char *)); +void cookie_jar_save PARAMS ((struct cookie_jar *, const char *)); #endif /* COOKIES_H */ diff --git a/src/http.c b/src/http.c index 3c22bf6c..b10960ac 100644 --- a/src/http.c +++ b/src/http.c @@ -69,6 +69,7 @@ extern int errno; #endif static int cookies_loaded_p; +struct cookie_jar *wget_cookie_jar; #define TEXTHTML_S "text/html" #define HTTP_ACCEPT "*/*" @@ -334,6 +335,22 @@ http_process_connection (const char *hdr, void *arg) *flag = 1; return 1; } + +/* Commit the cookie to the cookie jar. */ + +int +http_process_set_cookie (const char *hdr, void *arg) +{ + struct url *u = (struct url *)arg; + + /* The jar should have been created by now. */ + assert (wget_cookie_jar != NULL); + + cookie_jar_process_set_cookie (wget_cookie_jar, u->host, u->port, u->path, + hdr); + return 1; +} + /* Persistent connections. Currently, we cache the most recently used connection as persistent, provided that the HTTP server agrees to @@ -584,7 +601,7 @@ static char *basic_authentication_encode PARAMS ((const char *, const char *, const char *)); static int known_authentication_scheme_p PARAMS ((const char *)); -time_t http_atotm PARAMS ((char *)); +time_t http_atotm PARAMS ((const char *)); #define BEGINS_WITH(line, string_constant) \ (!strncasecmp (line, string_constant, sizeof (string_constant) - 1) \ @@ -891,13 +908,14 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy) request_keep_alive = NULL; if (opt.cookies) - cookies = build_cookies_request (u->host, u->port, u->path, + cookies = cookie_jar_generate_cookie_header (wget_cookie_jar, u->host, + u->port, u->path, #ifdef HAVE_SSL - u->scheme == SCHEME_HTTPS + u->scheme == SCHEME_HTTPS #else - 0 + 0 #endif - ); + ); if (opt.post_data || opt.post_file_name) { @@ -1168,7 +1186,7 @@ Accept: %s\r\n\ goto done_header; /* Try getting cookies. */ if (opt.cookies) - if (header_process (hdr, "Set-Cookie", set_cookie_header_cb, u)) + if (header_process (hdr, "Set-Cookie", http_process_set_cookie, u)) goto done_header; /* Try getting www-authentication. */ if (!authenticate_h) @@ -1558,10 +1576,15 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, /* 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) + if (opt.cookies) { - load_cookies (opt.cookies_input); - cookies_loaded_p = 1; + if (!wget_cookie_jar) + wget_cookie_jar = cookie_jar_new (); + if (opt.cookies_input && !cookies_loaded_p) + { + cookie_jar_load (wget_cookie_jar, opt.cookies_input); + cookies_loaded_p = 1; + } } *newloc = NULL; @@ -2155,7 +2178,7 @@ check_end (const char *p) it is not assigned to the FSF. So I stuck it with strptime. */ time_t -http_atotm (char *time_string) +http_atotm (const 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 diff --git a/src/init.c b/src/init.c index 65975d4e..d54c1613 100644 --- a/src/init.c +++ b/src/init.c @@ -53,13 +53,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "host.h" #include "recur.h" #include "netrc.h" -#include "cookies.h" /* for cookies_cleanup */ +#include "cookies.h" /* for cookie_jar_delete */ #include "progress.h" #ifndef errno extern int errno; #endif +extern struct cookie_jar *wget_cookie_jar; + /* We want tilde expansion enabled only when reading `.wgetrc' lines; otherwise, it will be performed by the shell. This variable will be set by the wgetrc-reading function. */ @@ -1057,8 +1059,8 @@ cleanup (void) http_cleanup (); cleanup_html_url (); downloaded_files_free (); - cookies_cleanup (); host_cleanup (); + cookie_jar_delete (wget_cookie_jar); { extern acc_t *netrc_list; diff --git a/src/main.c b/src/main.c index 41266b6c..bbf4a37d 100644 --- a/src/main.c +++ b/src/main.c @@ -70,6 +70,8 @@ extern int errno; struct options opt; +extern struct cookie_jar *wget_cookie_jar; + /* From log.c. */ void log_init PARAMS ((const char *, int)); void log_close PARAMS ((void)); @@ -860,8 +862,8 @@ Can't timestamp and not clobber old files at the same time.\n")); legible (opt.quota)); } - if (opt.cookies_output) - save_cookies (opt.cookies_output); + if (opt.cookies_output && wget_cookie_jar) + cookie_jar_save (wget_cookie_jar, opt.cookies_output); if (opt.convert_links && !opt.delete_after) {