2001-04-08 18:25:24 -04:00
|
|
|
|
/* Support for cookies.
|
2008-01-25 08:04:01 -05:00
|
|
|
|
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,
|
|
|
|
|
2008 Free Software Foundation, Inc.
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2001-05-27 15:35:15 -04:00
|
|
|
|
This file is part of GNU Wget.
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2001-05-27 15:35:15 -04:00
|
|
|
|
GNU Wget is free software; you can redistribute it and/or modify
|
2001-04-08 18:25:24 -04:00
|
|
|
|
it under the terms of the GNU General Public License as published by
|
2007-07-10 01:53:22 -04:00
|
|
|
|
the Free Software Foundation; either version 3 of the License, or (at
|
2001-04-08 18:25:24 -04:00
|
|
|
|
your option) any later version.
|
|
|
|
|
|
2001-05-27 15:35:15 -04:00
|
|
|
|
GNU Wget is distributed in the hope that it will be useful, but
|
2001-04-08 18:25:24 -04:00
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
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
|
2007-07-10 01:53:22 -04:00
|
|
|
|
along with Wget. If not, see <http://www.gnu.org/licenses/>.
|
2002-05-17 22:16:36 -04:00
|
|
|
|
|
2007-11-28 03:05:33 -05:00
|
|
|
|
Additional permission under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
|
|
If you modify this program, or any covered work, by linking or
|
|
|
|
|
combining it with the OpenSSL project's OpenSSL library (or a
|
|
|
|
|
modified version of that library), containing parts covered by the
|
|
|
|
|
terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
|
|
|
|
|
grants you additional permission to convey the resulting work.
|
|
|
|
|
Corresponding Source for a non-source form of such a combination
|
|
|
|
|
shall include the source code for the parts of OpenSSL used as well
|
|
|
|
|
as that of the covered work. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2005-05-24 10:13:47 -04:00
|
|
|
|
/* Written by Hrvoje Niksic. Parts are loosely inspired by the
|
|
|
|
|
cookie patch submitted by Tomasz Wegrzanowski.
|
2002-04-20 16:46:38 -04:00
|
|
|
|
|
2005-05-24 10:13:47 -04:00
|
|
|
|
This implements the client-side cookie support, as specified
|
|
|
|
|
(loosely) by Netscape's "preliminary specification", currently
|
|
|
|
|
available at:
|
2003-10-06 20:47:08 -04:00
|
|
|
|
|
2005-05-24 10:13:47 -04:00
|
|
|
|
http://wp.netscape.com/newsref/std/cookie_spec.html
|
2003-10-06 20:47:08 -04:00
|
|
|
|
|
2005-05-24 10:13:47 -04:00
|
|
|
|
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. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2007-10-18 23:50:40 -04:00
|
|
|
|
#include "wget.h"
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2005-06-19 18:34:58 -04:00
|
|
|
|
#include <string.h>
|
2001-04-08 18:25:24 -04:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <errno.h>
|
2005-06-19 18:34:58 -04:00
|
|
|
|
#include <time.h>
|
2001-04-08 18:25:24 -04:00
|
|
|
|
#include "utils.h"
|
|
|
|
|
#include "hash.h"
|
|
|
|
|
#include "cookies.h"
|
2007-08-02 23:38:21 -04:00
|
|
|
|
#include "http.h" /* for http_atotm */
|
2002-04-20 16:46:38 -04:00
|
|
|
|
|
|
|
|
|
/* Declarations of `struct cookie' and the most basic functions. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
/* Cookie jar serves as cookie storage and a means of retrieving
|
|
|
|
|
cookies efficiently. All cookies with the same domain are stored
|
|
|
|
|
in a linked list called "chain". A cookie chain can be reached by
|
|
|
|
|
looking up the domain in the cookie jar's chains_by_domain table.
|
|
|
|
|
|
|
|
|
|
For example, to reach all the cookies under google.com, one must
|
|
|
|
|
execute hash_table_get(jar->chains_by_domain, "google.com"). Of
|
|
|
|
|
course, when sending a cookie to `www.google.com', one must search
|
|
|
|
|
for cookies that belong to either `www.google.com' or `google.com'
|
|
|
|
|
-- but the point is that the code doesn't need to go through *all*
|
|
|
|
|
the cookies. */
|
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
struct cookie_jar {
|
2003-10-06 20:49:58 -04:00
|
|
|
|
/* Cookie chains indexed by domain. */
|
|
|
|
|
struct hash_table *chains;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
int cookie_count; /* number of cookies in the jar. */
|
2002-04-20 16:46:38 -04:00
|
|
|
|
};
|
2001-04-12 15:43:12 -04:00
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
/* Value set by entry point functions, so that the low-level
|
|
|
|
|
routines don't need to call time() all the time. */
|
2005-06-27 11:33:28 -04:00
|
|
|
|
static time_t cookies_now;
|
2001-04-12 15:43:12 -04:00
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
struct cookie_jar *
|
|
|
|
|
cookie_jar_new (void)
|
|
|
|
|
{
|
2003-10-31 09:55:50 -05:00
|
|
|
|
struct cookie_jar *jar = xnew (struct cookie_jar);
|
2003-10-06 20:49:58 -04:00
|
|
|
|
jar->chains = make_nocase_string_hash_table (0);
|
2002-04-20 16:46:38 -04:00
|
|
|
|
jar->cookie_count = 0;
|
|
|
|
|
return jar;
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
struct cookie {
|
2007-08-02 23:38:21 -04:00
|
|
|
|
char *domain; /* domain of the cookie */
|
|
|
|
|
int port; /* port number */
|
|
|
|
|
char *path; /* path prefix of the cookie */
|
2003-09-22 21:08:01 -04:00
|
|
|
|
|
2005-06-21 21:45:08 -04:00
|
|
|
|
unsigned discard_requested :1; /* whether cookie was created to
|
2007-08-02 23:38:21 -04:00
|
|
|
|
request discarding another
|
|
|
|
|
cookie. */
|
2005-06-21 21:45:08 -04:00
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
unsigned secure :1; /* whether cookie should be
|
|
|
|
|
transmitted over non-https
|
|
|
|
|
connections. */
|
|
|
|
|
unsigned domain_exact :1; /* whether DOMAIN must match as a
|
|
|
|
|
whole. */
|
2003-09-22 21:08:01 -04:00
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
unsigned permanent :1; /* whether the cookie should outlive
|
|
|
|
|
the session. */
|
|
|
|
|
time_t expiry_time; /* time when the cookie expires, 0
|
|
|
|
|
means undetermined. */
|
2003-09-22 21:08:01 -04:00
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
char *attr; /* cookie attribute name */
|
|
|
|
|
char *value; /* cookie attribute value */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
struct cookie *next; /* used for chaining of cookies in the
|
|
|
|
|
same domain. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
};
|
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
#define PORT_ANY (-1)
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
/* Allocate and return a new, empty cookie structure. */
|
|
|
|
|
|
|
|
|
|
static struct cookie *
|
|
|
|
|
cookie_new (void)
|
|
|
|
|
{
|
2003-10-31 09:55:50 -05:00
|
|
|
|
struct cookie *cookie = xnew0 (struct cookie);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-11-04 19:11:33 -05:00
|
|
|
|
/* 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). */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
cookie->port = PORT_ANY;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
return cookie;
|
|
|
|
|
}
|
|
|
|
|
|
2003-11-04 19:11:33 -05:00
|
|
|
|
/* Non-zero if the cookie has expired. Assumes cookies_now has been
|
|
|
|
|
set by one of the entry point functions. */
|
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
static bool
|
2003-11-04 19:11:33 -05:00
|
|
|
|
cookie_expired_p (const struct cookie *c)
|
|
|
|
|
{
|
|
|
|
|
return c->expiry_time != 0 && c->expiry_time < cookies_now;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
/* Deallocate COOKIE and its components. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
delete_cookie (struct cookie *cookie)
|
|
|
|
|
{
|
2003-11-02 14:56:37 -05:00
|
|
|
|
xfree_null (cookie->domain);
|
|
|
|
|
xfree_null (cookie->path);
|
|
|
|
|
xfree_null (cookie->attr);
|
|
|
|
|
xfree_null (cookie->value);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
xfree (cookie);
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-12 16:06:41 -04:00
|
|
|
|
/* Functions for storing cookies.
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-10-06 20:49:58 -04:00
|
|
|
|
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. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
/* 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.
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
If no matching cookie is found, return NULL. */
|
|
|
|
|
|
|
|
|
|
static struct cookie *
|
2002-04-20 16:46:38 -04:00
|
|
|
|
find_matching_cookie (struct cookie_jar *jar, struct cookie *cookie,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
struct cookie **prevptr)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
|
|
|
|
struct cookie *chain, *prev;
|
|
|
|
|
|
2003-10-06 20:49:58 -04:00
|
|
|
|
chain = hash_table_get (jar->chains, cookie->domain);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (!chain)
|
|
|
|
|
goto nomatch;
|
|
|
|
|
|
|
|
|
|
prev = NULL;
|
|
|
|
|
for (; chain; prev = chain, chain = chain->next)
|
2002-04-20 16:46:38 -04:00
|
|
|
|
if (0 == strcmp (cookie->path, chain->path)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
&& 0 == strcmp (cookie->attr, chain->attr)
|
|
|
|
|
&& cookie->port == chain->port)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
2007-08-02 23:38:21 -04:00
|
|
|
|
*prevptr = prev;
|
|
|
|
|
return chain;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nomatch:
|
|
|
|
|
*prevptr = NULL;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
/* Store COOKIE to the jar.
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
This is done by placing COOKIE at the head of its chain. However,
|
|
|
|
|
if COOKIE matches a cookie already in memory, as determined by
|
|
|
|
|
find_matching_cookie, the old cookie is unlinked and destroyed.
|
|
|
|
|
|
|
|
|
|
The key of each chain's hash table entry is allocated only the
|
|
|
|
|
first time; next hash_table_put's reuse the same key. */
|
|
|
|
|
|
|
|
|
|
static void
|
2002-04-20 16:46:38 -04:00
|
|
|
|
store_cookie (struct cookie_jar *jar, struct cookie *cookie)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
|
|
|
|
struct cookie *chain_head;
|
|
|
|
|
char *chain_key;
|
|
|
|
|
|
2003-10-06 20:49:58 -04:00
|
|
|
|
if (hash_table_get_pair (jar->chains, cookie->domain,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
&chain_key, &chain_head))
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
2002-04-20 16:46:38 -04:00
|
|
|
|
/* 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. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
struct cookie *prev;
|
2002-04-20 16:46:38 -04:00
|
|
|
|
struct cookie *victim = find_matching_cookie (jar, cookie, &prev);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
if (victim)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
/* Remove VICTIM from the chain. COOKIE will be placed at
|
|
|
|
|
the head. */
|
|
|
|
|
if (prev)
|
|
|
|
|
{
|
|
|
|
|
prev->next = victim->next;
|
|
|
|
|
cookie->next = chain_head;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* prev is NULL; apparently VICTIM was at the head of
|
|
|
|
|
the chain. This place will be taken by COOKIE, so
|
|
|
|
|
all we need to do is: */
|
|
|
|
|
cookie->next = victim->next;
|
|
|
|
|
}
|
|
|
|
|
delete_cookie (victim);
|
|
|
|
|
--jar->cookie_count;
|
|
|
|
|
DEBUGP (("Deleted old cookie (to be replaced.)\n"));
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
else
|
2007-08-02 23:38:21 -04:00
|
|
|
|
cookie->next = chain_head;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2003-10-06 20:47:08 -04:00
|
|
|
|
/* We are now creating the chain. Use a copy of cookie->domain
|
2007-08-02 23:38:21 -04:00
|
|
|
|
as the key for the life-time of the chain. Using
|
|
|
|
|
cookie->domain would be unsafe because the life-time of the
|
|
|
|
|
chain may exceed the life-time of the cookie. (Cookies may
|
|
|
|
|
be deleted from the chain by this very function.) */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
cookie->next = NULL;
|
2002-04-20 16:46:38 -04:00
|
|
|
|
chain_key = xstrdup (cookie->domain);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
2003-10-06 20:49:58 -04:00
|
|
|
|
hash_table_put (jar->chains, chain_key, cookie);
|
2002-04-20 16:46:38 -04:00
|
|
|
|
++jar->cookie_count;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2005-06-21 21:26:22 -04:00
|
|
|
|
IF_DEBUG
|
2003-11-04 19:11:33 -05:00
|
|
|
|
{
|
2005-05-18 13:22:28 -04:00
|
|
|
|
time_t exptime = cookie->expiry_time;
|
2003-11-04 19:11:33 -05:00
|
|
|
|
DEBUGP (("\nStored cookie %s %d%s %s <%s> <%s> [expiry %s] %s %s\n",
|
2007-08-02 23:38:21 -04:00
|
|
|
|
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));
|
2003-11-04 19:11:33 -05:00
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
/* 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. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
static void
|
2002-04-20 16:46:38 -04:00
|
|
|
|
discard_matching_cookie (struct cookie_jar *jar, struct cookie *cookie)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
|
|
|
|
struct cookie *prev, *victim;
|
|
|
|
|
|
2003-10-06 20:49:58 -04:00
|
|
|
|
if (!hash_table_count (jar->chains))
|
2001-04-08 18:25:24 -04:00
|
|
|
|
/* No elements == nothing to discard. */
|
|
|
|
|
return;
|
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
victim = find_matching_cookie (jar, cookie, &prev);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (victim)
|
|
|
|
|
{
|
|
|
|
|
if (prev)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
/* Simply unchain the victim. */
|
|
|
|
|
prev->next = victim->next;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
else
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
/* VICTIM was head of its chain. We need to place a new
|
|
|
|
|
cookie at the head. */
|
|
|
|
|
char *chain_key = NULL;
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
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, victim->domain);
|
|
|
|
|
xfree (chain_key);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
hash_table_put (jar->chains, chain_key, victim->next);
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
delete_cookie (victim);
|
|
|
|
|
DEBUGP (("Discarded old cookie.\n"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Functions for parsing the `Set-Cookie' header, and creating new
|
|
|
|
|
cookies from the wire. */
|
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
#define TOKEN_IS(token, string_literal) \
|
2006-02-28 15:50:37 -05:00
|
|
|
|
BOUNDED_EQUAL_NO_CASE (token.b, token.e, string_literal)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2006-02-28 15:50:37 -05:00
|
|
|
|
#define TOKEN_NON_EMPTY(token) (token.b != NULL && token.b != token.e)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
/* Parse the contents of the `Set-Cookie' header. The header looks
|
|
|
|
|
like this:
|
|
|
|
|
|
|
|
|
|
name1=value1; name2=value2; ...
|
|
|
|
|
|
|
|
|
|
Trailing semicolon is optional; spaces are allowed between all
|
|
|
|
|
tokens. Additionally, values may be quoted.
|
|
|
|
|
|
2006-02-28 15:50:37 -05:00
|
|
|
|
A new cookie is returned upon success, NULL otherwise.
|
|
|
|
|
|
|
|
|
|
The first name-value pair will be used to set the cookie's
|
|
|
|
|
attribute name and value. Subsequent parameters will be checked
|
|
|
|
|
against field names such as `domain', `path', etc. Recognized
|
|
|
|
|
fields will be parsed and the corresponding members of COOKIE
|
|
|
|
|
filled. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
static struct cookie *
|
2006-02-28 15:50:37 -05:00
|
|
|
|
parse_set_cookie (const char *set_cookie, bool silent)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
2006-02-28 15:50:37 -05:00
|
|
|
|
const char *ptr = set_cookie;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
struct cookie *cookie = cookie_new ();
|
2006-02-28 15:50:37 -05:00
|
|
|
|
param_token name, value;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2006-02-28 17:32:56 -05:00
|
|
|
|
if (!extract_param (&ptr, &name, &value, ';'))
|
2006-02-28 15:50:37 -05:00
|
|
|
|
goto error;
|
|
|
|
|
if (!value.b)
|
|
|
|
|
goto error;
|
|
|
|
|
cookie->attr = strdupdelim (name.b, name.e);
|
|
|
|
|
cookie->value = strdupdelim (value.b, value.e);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2006-02-28 17:32:56 -05:00
|
|
|
|
while (extract_param (&ptr, &name, &value, ';'))
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
2006-02-28 15:50:37 -05:00
|
|
|
|
if (TOKEN_IS (name, "domain"))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
if (!TOKEN_NON_EMPTY (value))
|
|
|
|
|
goto error;
|
|
|
|
|
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
|
|
|
|
|
cookie, and it apparently works in browsers. */
|
|
|
|
|
if (*value.b == '.')
|
|
|
|
|
++value.b;
|
|
|
|
|
cookie->domain = strdupdelim (value.b, value.e);
|
|
|
|
|
}
|
2006-02-28 15:50:37 -05:00
|
|
|
|
else if (TOKEN_IS (name, "path"))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
if (!TOKEN_NON_EMPTY (value))
|
|
|
|
|
goto error;
|
|
|
|
|
xfree_null (cookie->path);
|
|
|
|
|
cookie->path = strdupdelim (value.b, value.e);
|
|
|
|
|
}
|
2006-02-28 15:50:37 -05:00
|
|
|
|
else if (TOKEN_IS (name, "expires"))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
char *value_copy;
|
|
|
|
|
time_t expires;
|
|
|
|
|
|
|
|
|
|
if (!TOKEN_NON_EMPTY (value))
|
|
|
|
|
goto error;
|
|
|
|
|
BOUNDED_TO_ALLOCA (value.b, value.e, value_copy);
|
|
|
|
|
|
|
|
|
|
expires = http_atotm (value_copy);
|
|
|
|
|
if (expires != (time_t) -1)
|
|
|
|
|
{
|
|
|
|
|
cookie->permanent = 1;
|
|
|
|
|
cookie->expiry_time = expires;
|
|
|
|
|
/* According to netscape's specification, expiry time in
|
|
|
|
|
the past means that discarding of a matching cookie
|
|
|
|
|
is requested. */
|
|
|
|
|
if (cookie->expiry_time < cookies_now)
|
|
|
|
|
cookie->discard_requested = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Error in expiration spec. Assume default (cookie doesn't
|
|
|
|
|
expire, but valid only for this session.) */
|
|
|
|
|
;
|
|
|
|
|
}
|
2006-02-28 15:50:37 -05:00
|
|
|
|
else if (TOKEN_IS (name, "max-age"))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
double maxage = -1;
|
|
|
|
|
char *value_copy;
|
|
|
|
|
|
|
|
|
|
if (!TOKEN_NON_EMPTY (value))
|
|
|
|
|
goto error;
|
|
|
|
|
BOUNDED_TO_ALLOCA (value.b, value.e, value_copy);
|
|
|
|
|
|
|
|
|
|
sscanf (value_copy, "%lf", &maxage);
|
|
|
|
|
if (maxage == -1)
|
|
|
|
|
/* something went wrong. */
|
|
|
|
|
goto error;
|
|
|
|
|
cookie->permanent = 1;
|
|
|
|
|
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. */
|
|
|
|
|
if (maxage == 0)
|
|
|
|
|
cookie->discard_requested = 1;
|
|
|
|
|
}
|
2006-02-28 15:50:37 -05:00
|
|
|
|
else if (TOKEN_IS (name, "secure"))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
/* ignore value completely */
|
|
|
|
|
cookie->secure = 1;
|
|
|
|
|
}
|
2006-02-28 15:50:37 -05:00
|
|
|
|
else
|
2007-08-02 23:38:21 -04:00
|
|
|
|
/* Ignore unrecognized attribute. */
|
|
|
|
|
;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
2006-02-28 15:50:37 -05:00
|
|
|
|
if (*ptr)
|
|
|
|
|
/* extract_param has encountered a syntax error */
|
|
|
|
|
goto error;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2006-02-28 15:50:37 -05:00
|
|
|
|
/* The cookie has been successfully constructed; return it. */
|
|
|
|
|
return cookie;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2006-02-28 15:50:37 -05:00
|
|
|
|
error:
|
2003-09-15 11:35:47 -04:00
|
|
|
|
if (!silent)
|
|
|
|
|
logprintf (LOG_NOTQUIET,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
_("Syntax error in Set-Cookie: %s at position %d.\n"),
|
|
|
|
|
escnonprint (set_cookie), (int) (ptr - set_cookie));
|
2006-02-28 15:50:37 -05:00
|
|
|
|
delete_cookie (cookie);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-02-28 15:50:37 -05:00
|
|
|
|
|
|
|
|
|
#undef TOKEN_IS
|
|
|
|
|
#undef TOKEN_NON_EMPTY
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
/* Sanity checks. These are important, otherwise it is possible for
|
|
|
|
|
mailcious attackers to destroy important cookie information and/or
|
|
|
|
|
violate your privacy. */
|
|
|
|
|
|
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
#define REQUIRE_DIGITS(p) do { \
|
2007-10-14 17:46:24 -04:00
|
|
|
|
if (!c_isdigit (*p)) \
|
2007-08-02 23:38:21 -04:00
|
|
|
|
return false; \
|
2007-10-14 17:46:24 -04:00
|
|
|
|
for (++p; c_isdigit (*p); p++) \
|
2007-08-02 23:38:21 -04:00
|
|
|
|
; \
|
2001-04-08 18:25:24 -04:00
|
|
|
|
} while (0)
|
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
#define REQUIRE_DOT(p) do { \
|
|
|
|
|
if (*p++ != '.') \
|
|
|
|
|
return false; \
|
2001-04-08 18:25:24 -04:00
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
/* Check whether ADDR matches <digits>.<digits>.<digits>.<digits>.
|
|
|
|
|
|
2003-11-04 19:11:33 -05:00
|
|
|
|
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. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
static bool
|
2001-04-08 18:25:24 -04:00
|
|
|
|
numeric_address_p (const char *addr)
|
|
|
|
|
{
|
|
|
|
|
const char *p = addr;
|
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
REQUIRE_DIGITS (p); /* A */
|
|
|
|
|
REQUIRE_DOT (p); /* . */
|
|
|
|
|
REQUIRE_DIGITS (p); /* B */
|
|
|
|
|
REQUIRE_DOT (p); /* . */
|
|
|
|
|
REQUIRE_DIGITS (p); /* C */
|
|
|
|
|
REQUIRE_DOT (p); /* . */
|
|
|
|
|
REQUIRE_DIGITS (p); /* D */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
if (*p != '\0')
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return false;
|
|
|
|
|
return true;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check whether COOKIE_DOMAIN is an appropriate domain for HOST.
|
2001-12-01 00:08:03 -05:00
|
|
|
|
Originally I tried to make the check compliant with rfc2109, but
|
|
|
|
|
the sites deviated too often, so I had to fall back to "tail
|
|
|
|
|
matching", as defined by the original Netscape's cookie spec. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
static bool
|
2001-04-08 18:25:24 -04:00
|
|
|
|
check_domain_match (const char *cookie_domain, const char *host)
|
|
|
|
|
{
|
2001-05-09 14:15:22 -04:00
|
|
|
|
DEBUGP (("cdm: 1"));
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
/* Numeric address requires exact match. It also requires HOST to
|
2001-12-01 00:08:03 -05:00
|
|
|
|
be an IP address. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (numeric_address_p (cookie_domain))
|
2001-12-01 00:08:03 -05:00
|
|
|
|
return 0 == strcmp (cookie_domain, host);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2001-05-09 14:15:22 -04:00
|
|
|
|
DEBUGP ((" 2"));
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
/* For the sake of efficiency, check for exact match first. */
|
2003-09-22 21:08:01 -04:00
|
|
|
|
if (0 == strcasecmp (cookie_domain, host))
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return true;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2001-12-01 00:08:03 -05:00
|
|
|
|
DEBUGP ((" 3"));
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-21 00:25:36 -04:00
|
|
|
|
/* HOST must match the tail of cookie_domain. */
|
2005-06-22 15:38:10 -04:00
|
|
|
|
if (!match_tail (host, cookie_domain, true))
|
|
|
|
|
return false;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-21 00:25:36 -04:00
|
|
|
|
/* 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
|
|
|
|
|
subdomain shared by many entities. For example, "company.co.uk"
|
|
|
|
|
must not be allowed to set a cookie for ".co.uk". On the other
|
|
|
|
|
hand, "sso.redhat.de" should be able to set a cookie for
|
|
|
|
|
".redhat.de".
|
|
|
|
|
|
|
|
|
|
The only marginally sane way to handle this I can think of is to
|
|
|
|
|
reject on the basis of the length of the second-level domain name
|
|
|
|
|
(but when the top-level domain is unknown), with the assumption
|
|
|
|
|
that those of three or less characters could be reserved. For
|
|
|
|
|
example:
|
|
|
|
|
|
|
|
|
|
.co.org -> works because the TLD is known
|
|
|
|
|
.co.uk -> doesn't work because "co" is only two chars long
|
|
|
|
|
.com.au -> doesn't work because "com" is only 3 chars long
|
|
|
|
|
.cnn.uk -> doesn't work because "cnn" is also only 3 chars long (ugh)
|
|
|
|
|
.cnn.de -> doesn't work for the same reason (ugh!!)
|
|
|
|
|
.abcd.de -> works because "abcd" is 4 chars long
|
|
|
|
|
.img.cnn.de -> works because it's not trying to set the 2nd level domain
|
|
|
|
|
.cnn.co.uk -> works for the same reason
|
|
|
|
|
|
|
|
|
|
That should prevent misuse, while allowing reasonable usage. If
|
|
|
|
|
someone knows of a better way to handle this, please let me
|
|
|
|
|
know. */
|
|
|
|
|
{
|
|
|
|
|
const char *p = cookie_domain;
|
2007-08-02 23:38:21 -04:00
|
|
|
|
int dccount = 1; /* number of domain components */
|
|
|
|
|
int ldcl = 0; /* last domain component length */
|
|
|
|
|
int nldcl = 0; /* next to last domain component length */
|
2002-04-21 00:25:36 -04:00
|
|
|
|
int out;
|
|
|
|
|
if (*p == '.')
|
|
|
|
|
/* Ignore leading period in this calculation. */
|
|
|
|
|
++p;
|
|
|
|
|
DEBUGP ((" 4"));
|
|
|
|
|
for (out = 0; !out; p++)
|
|
|
|
|
switch (*p)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
case '\0':
|
|
|
|
|
out = 1;
|
|
|
|
|
break;
|
|
|
|
|
case '.':
|
|
|
|
|
if (ldcl == 0)
|
|
|
|
|
/* Empty domain component found -- the domain is invalid. */
|
|
|
|
|
return false;
|
|
|
|
|
if (*(p + 1) == '\0')
|
|
|
|
|
{
|
|
|
|
|
/* Tolerate trailing '.' by not treating the domain as
|
|
|
|
|
one ending with an empty domain component. */
|
|
|
|
|
out = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
nldcl = ldcl;
|
|
|
|
|
ldcl = 0;
|
|
|
|
|
++dccount;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
++ldcl;
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-21 00:25:36 -04:00
|
|
|
|
DEBUGP ((" 5"));
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-21 00:25:36 -04:00
|
|
|
|
if (dccount < 2)
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return false;
|
2001-05-09 14:15:22 -04:00
|
|
|
|
|
2002-04-21 00:25:36 -04:00
|
|
|
|
DEBUGP ((" 6"));
|
|
|
|
|
|
|
|
|
|
if (dccount == 2)
|
|
|
|
|
{
|
2007-08-02 23:38:21 -04:00
|
|
|
|
int i;
|
|
|
|
|
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], true))
|
|
|
|
|
{
|
|
|
|
|
known_toplevel = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!known_toplevel && nldcl <= 3)
|
|
|
|
|
return false;
|
2002-04-21 00:25:36 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-21 00:25:36 -04:00
|
|
|
|
DEBUGP ((" 7"));
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
/* Don't allow the host "foobar.com" to set a cookie for domain
|
|
|
|
|
"bar.com". */
|
2001-12-01 00:08:03 -05:00
|
|
|
|
if (*cookie_domain != '.')
|
|
|
|
|
{
|
|
|
|
|
int dlen = strlen (cookie_domain);
|
|
|
|
|
int hlen = strlen (host);
|
2001-12-01 12:48:34 -05:00
|
|
|
|
/* cookie host: hostname.foobar.com */
|
|
|
|
|
/* desired domain: bar.com */
|
|
|
|
|
/* '.' must be here in host-> ^ */
|
2001-12-01 00:08:03 -05:00
|
|
|
|
if (hlen > dlen && host[hlen - dlen - 1] != '.')
|
2007-08-02 23:38:21 -04:00
|
|
|
|
return false;
|
2001-12-01 00:08:03 -05:00
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-21 00:25:36 -04:00
|
|
|
|
DEBUGP ((" 8"));
|
2001-05-09 14:15:22 -04:00
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return true;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
2005-06-19 18:34:58 -04:00
|
|
|
|
static int path_matches (const char *, const char *);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
/* Check whether PATH begins with COOKIE_PATH. */
|
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
static bool
|
2001-04-08 18:25:24 -04:00
|
|
|
|
check_path_match (const char *cookie_path, const char *path)
|
|
|
|
|
{
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return path_matches (path, cookie_path) != 0;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
2005-06-24 08:15:03 -04:00
|
|
|
|
|
|
|
|
|
/* Prepend '/' to string S. S is copied to fresh stack-allocated
|
|
|
|
|
space and its value is modified to point to the new location. */
|
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
#define PREPEND_SLASH(s) do { \
|
|
|
|
|
char *PS_newstr = (char *) alloca (1 + strlen (s) + 1); \
|
|
|
|
|
*PS_newstr = '/'; \
|
|
|
|
|
strcpy (PS_newstr + 1, s); \
|
|
|
|
|
s = PS_newstr; \
|
2005-06-24 08:15:03 -04:00
|
|
|
|
} while (0)
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
/* 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. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
void
|
2003-11-04 19:11:33 -05:00
|
|
|
|
cookie_handle_set_cookie (struct cookie_jar *jar,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
const char *host, int port,
|
|
|
|
|
const char *path, const char *set_cookie)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
|
|
|
|
struct cookie *cookie;
|
|
|
|
|
cookies_now = time (NULL);
|
|
|
|
|
|
2005-06-24 08:15:03 -04:00
|
|
|
|
/* 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);
|
|
|
|
|
|
2006-02-28 15:50:37 -05:00
|
|
|
|
cookie = parse_set_cookie (set_cookie, false);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (!cookie)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* Sanitize parts of cookie. */
|
|
|
|
|
|
|
|
|
|
if (!cookie->domain)
|
2002-04-20 16:46:38 -04:00
|
|
|
|
{
|
|
|
|
|
copy_domain:
|
2003-11-08 17:32:52 -05:00
|
|
|
|
/* If the domain was not provided, we use the one we're talking
|
2007-08-02 23:38:21 -04:00
|
|
|
|
to, and set exact match. */
|
2002-04-20 16:46:38 -04:00
|
|
|
|
cookie->domain = xstrdup (host);
|
2003-11-08 17:32:52 -05:00
|
|
|
|
cookie->domain_exact = 1;
|
|
|
|
|
/* Set the port, but only if it's non-default. */
|
|
|
|
|
if (port != 80 && port != 443)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
cookie->port = port;
|
2002-04-20 16:46:38 -04:00
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
else
|
|
|
|
|
{
|
2002-04-20 16:46:38 -04:00
|
|
|
|
if (!check_domain_match (cookie->domain, host))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
logprintf (LOG_NOTQUIET,
|
|
|
|
|
_("Cookie coming from %s attempted to set domain to %s\n"),
|
|
|
|
|
escnonprint (host), escnonprint (cookie->domain));
|
|
|
|
|
xfree (cookie->domain);
|
|
|
|
|
goto copy_domain;
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
2003-10-06 20:47:08 -04:00
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (!cookie->path)
|
2005-04-26 09:34:33 -04:00
|
|
|
|
{
|
|
|
|
|
/* The cookie doesn't set path: set it to the URL path, sans the
|
2007-08-02 23:38:21 -04:00
|
|
|
|
file part ("/dir/file" truncated to "/dir/"). */
|
2005-04-26 09:34:33 -04:00
|
|
|
|
char *trailing_slash = strrchr (path, '/');
|
|
|
|
|
if (trailing_slash)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
cookie->path = strdupdelim (path, trailing_slash + 1);
|
2005-04-26 09:34:33 -04:00
|
|
|
|
else
|
2007-08-02 23:38:21 -04:00
|
|
|
|
/* no slash in the string -- can this even happen? */
|
|
|
|
|
cookie->path = xstrdup (path);
|
2005-04-26 09:34:33 -04:00
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
else
|
|
|
|
|
{
|
2005-04-26 09:34:33 -04:00
|
|
|
|
/* The cookie sets its own path; verify that it is legal. */
|
2002-04-20 16:46:38 -04:00
|
|
|
|
if (!check_path_match (cookie->path, path))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
DEBUGP (("Attempt to fake the path: %s, %s\n",
|
|
|
|
|
cookie->path, path));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
2005-04-26 09:34:33 -04:00
|
|
|
|
/* Now store the cookie, or discard an existing cookie, if
|
|
|
|
|
discarding was requested. */
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (cookie->discard_requested)
|
|
|
|
|
{
|
2002-04-20 16:46:38 -04:00
|
|
|
|
discard_matching_cookie (jar, cookie);
|
2002-05-07 13:22:33 -04:00
|
|
|
|
goto out;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
store_cookie (jar, cookie);
|
|
|
|
|
return;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (cookie)
|
|
|
|
|
delete_cookie (cookie);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Support for sending out cookies in HTTP requests, based on
|
|
|
|
|
previously stored cookies. Entry point is
|
|
|
|
|
`build_cookies_request'. */
|
2005-03-20 05:41:46 -05:00
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
/* Find the cookie chains whose domains match HOST and store them to
|
|
|
|
|
DEST.
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
A cookie chain is the head of a list of cookies that belong to a
|
|
|
|
|
host/domain. Given HOST "img.search.xemacs.org", this function
|
|
|
|
|
will return the chains for "img.search.xemacs.org",
|
|
|
|
|
"search.xemacs.org", and "xemacs.org" -- those of them that exist
|
|
|
|
|
(if any), that is.
|
2001-04-10 12:04:18 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
DEST should be large enough to accept (in the worst case) as many
|
|
|
|
|
elements as there are domain components of HOST. */
|
2001-04-10 12:04:18 -04:00
|
|
|
|
|
2001-12-08 20:24:41 -05:00
|
|
|
|
static int
|
2003-10-06 20:47:08 -04:00
|
|
|
|
find_chains_of_host (struct cookie_jar *jar, const char *host,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
struct cookie *dest[])
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
2003-09-22 21:08:01 -04:00
|
|
|
|
int dest_count = 0;
|
|
|
|
|
int passes, passcnt;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
/* Bail out quickly if there are no cookies in the jar. */
|
2003-10-06 20:49:58 -04:00
|
|
|
|
if (!hash_table_count (jar->chains))
|
2001-04-10 12:04:18 -04:00
|
|
|
|
return 0;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
if (numeric_address_p (host))
|
|
|
|
|
/* If host is an IP address, only check for the exact match. */
|
|
|
|
|
passes = 1;
|
|
|
|
|
else
|
|
|
|
|
/* Otherwise, check all the subdomains except the top-level (last)
|
|
|
|
|
one. As a domain with N components has N-1 dots, the number of
|
|
|
|
|
passes equals the number of dots. */
|
|
|
|
|
passes = count_char (host, '.');
|
|
|
|
|
|
|
|
|
|
passcnt = 0;
|
|
|
|
|
|
|
|
|
|
/* Find chains that match HOST, starting with exact match and
|
|
|
|
|
progressing to less specific domains. For instance, given HOST
|
|
|
|
|
fly.srk.fer.hr, first look for fly.srk.fer.hr's chain, then
|
|
|
|
|
srk.fer.hr's, then fer.hr's. */
|
|
|
|
|
while (1)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
2003-10-06 20:49:58 -04:00
|
|
|
|
struct cookie *chain = hash_table_get (jar->chains, host);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (chain)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
dest[dest_count++] = chain;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
if (++passcnt >= passes)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
break;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
host = strchr (host, '.') + 1;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
2003-09-22 21:08:01 -04:00
|
|
|
|
|
|
|
|
|
return dest_count;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If FULL_PATH begins with PREFIX, return the length of PREFIX, zero
|
|
|
|
|
otherwise. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
path_matches (const char *full_path, const char *prefix)
|
|
|
|
|
{
|
2005-06-24 08:15:03 -04:00
|
|
|
|
int len = strlen (prefix);
|
2001-12-01 10:34:55 -05:00
|
|
|
|
|
2001-12-01 00:22:17 -05:00
|
|
|
|
if (0 != strncmp (full_path, prefix, len))
|
2001-04-08 18:25:24 -04:00
|
|
|
|
/* FULL_PATH doesn't begin with PREFIX. */
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Length of PREFIX determines the quality of the match. */
|
2001-12-01 00:22:17 -05:00
|
|
|
|
return len + 1;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
/* Return true iff COOKIE matches the provided parameters of the URL
|
|
|
|
|
being downloaded: HOST, PORT, PATH, and SECFLAG.
|
2002-04-20 16:46:38 -04:00
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
If PATH_GOODNESS is non-NULL, store the "path goodness" value
|
2003-10-06 20:47:08 -04:00
|
|
|
|
there. That value is a measure of how closely COOKIE matches PATH,
|
2002-04-20 16:46:38 -04:00
|
|
|
|
used for ordering cookies. */
|
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
static bool
|
2003-10-06 20:47:08 -04:00
|
|
|
|
cookie_matches_url (const struct cookie *cookie,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
const char *host, int port, const char *path,
|
|
|
|
|
bool secflag, int *path_goodness)
|
2001-04-10 12:04:18 -04:00
|
|
|
|
{
|
|
|
|
|
int pg;
|
|
|
|
|
|
2003-11-04 19:11:33 -05:00
|
|
|
|
if (cookie_expired_p (cookie))
|
2002-04-20 16:46:38 -04:00
|
|
|
|
/* 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. */
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return false;
|
2002-04-20 16:46:38 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
if (cookie->secure && !secflag)
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* Don't transmit secure cookies over insecure connections. */
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return false;
|
2002-04-20 16:46:38 -04:00
|
|
|
|
if (cookie->port != PORT_ANY && cookie->port != port)
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return false;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
|
|
|
|
|
/* If exact domain match is required, verify that cookie's domain is
|
|
|
|
|
equal to HOST. If not, assume success on the grounds of the
|
2003-10-06 20:47:08 -04:00
|
|
|
|
cookie's chain having been found by find_chains_of_host. */
|
2003-09-22 21:08:01 -04:00
|
|
|
|
if (cookie->domain_exact
|
|
|
|
|
&& 0 != strcasecmp (host, cookie->domain))
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return false;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
|
2001-04-10 12:04:18 -04:00
|
|
|
|
pg = path_matches (path, cookie->path);
|
2005-06-22 15:38:10 -04:00
|
|
|
|
if (pg == 0)
|
|
|
|
|
return false;
|
2001-04-10 12:04:18 -04:00
|
|
|
|
|
|
|
|
|
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;
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return true;
|
2001-04-10 12:04:18 -04:00
|
|
|
|
}
|
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* A structure that points to a cookie, along with the additional
|
|
|
|
|
information about the cookie's "goodness". This allows us to sort
|
|
|
|
|
the cookies when returning them to the server, as required by the
|
|
|
|
|
spec. */
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
struct weighed_cookie {
|
|
|
|
|
struct cookie *cookie;
|
2001-04-10 12:04:18 -04:00
|
|
|
|
int domain_goodness;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
int path_goodness;
|
|
|
|
|
};
|
|
|
|
|
|
2001-04-10 12:04:18 -04:00
|
|
|
|
/* Comparator used for uniquifying the list. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
equality_comparator (const void *p1, const void *p2)
|
|
|
|
|
{
|
|
|
|
|
struct weighed_cookie *wc1 = (struct weighed_cookie *)p1;
|
|
|
|
|
struct weighed_cookie *wc2 = (struct weighed_cookie *)p2;
|
|
|
|
|
|
|
|
|
|
int namecmp = strcmp (wc1->cookie->attr, wc2->cookie->attr);
|
|
|
|
|
int valuecmp = strcmp (wc1->cookie->value, wc2->cookie->value);
|
|
|
|
|
|
|
|
|
|
/* We only really care whether both name and value are equal. We
|
|
|
|
|
return them in this order only for consistency... */
|
|
|
|
|
return namecmp ? namecmp : valuecmp;
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-09 14:15:22 -04:00
|
|
|
|
/* Eliminate duplicate cookies. "Duplicate cookies" are any two
|
2003-10-06 20:47:08 -04:00
|
|
|
|
cookies with the same attr name and value. Whenever a duplicate
|
2001-05-09 14:15:22 -04:00
|
|
|
|
pair is found, one of the cookies is removed. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
eliminate_dups (struct weighed_cookie *outgoing, int count)
|
|
|
|
|
{
|
2007-08-02 23:38:21 -04:00
|
|
|
|
struct weighed_cookie *h; /* hare */
|
|
|
|
|
struct weighed_cookie *t; /* tortoise */
|
2003-10-06 20:47:08 -04:00
|
|
|
|
struct weighed_cookie *end = outgoing + count;
|
2001-05-09 14:15:22 -04:00
|
|
|
|
|
|
|
|
|
/* We deploy a simple uniquify algorithm: first sort the array
|
2003-10-06 20:47:08 -04:00
|
|
|
|
according to our sort criteria, then copy it to itself, comparing
|
|
|
|
|
each cookie to its neighbor and ignoring the duplicates. */
|
2001-05-09 14:15:22 -04:00
|
|
|
|
|
|
|
|
|
qsort (outgoing, count, sizeof (struct weighed_cookie), equality_comparator);
|
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
/* "Hare" runs through all the entries in the array, followed by
|
|
|
|
|
"tortoise". If a duplicate is found, the hare skips it.
|
|
|
|
|
Non-duplicate entries are copied to the tortoise ptr. */
|
|
|
|
|
|
|
|
|
|
for (h = t = outgoing; h < end; h++)
|
2001-05-09 14:15:22 -04:00
|
|
|
|
{
|
2003-10-06 20:47:08 -04:00
|
|
|
|
if (h != end - 1)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
struct cookie *c0 = h[0].cookie;
|
|
|
|
|
struct cookie *c1 = h[1].cookie;
|
|
|
|
|
if (!strcmp (c0->attr, c1->attr) && !strcmp (c0->value, c1->value))
|
|
|
|
|
continue; /* ignore the duplicate */
|
|
|
|
|
}
|
2003-10-06 20:47:08 -04:00
|
|
|
|
|
|
|
|
|
/* If the hare has advanced past the tortoise (because of
|
2007-08-02 23:38:21 -04:00
|
|
|
|
previous dups), make sure the values get copied. Otherwise,
|
|
|
|
|
no copying is necessary. */
|
2003-10-06 20:47:08 -04:00
|
|
|
|
if (h != t)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
*t++ = *h;
|
2003-10-06 20:47:08 -04:00
|
|
|
|
else
|
2007-08-02 23:38:21 -04:00
|
|
|
|
t++;
|
2001-05-09 14:15:22 -04:00
|
|
|
|
}
|
2003-10-06 20:47:08 -04:00
|
|
|
|
return t - outgoing;
|
2001-05-09 14:15:22 -04:00
|
|
|
|
}
|
|
|
|
|
|
2001-04-10 12:04:18 -04:00
|
|
|
|
/* Comparator used for sorting by quality. */
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
static int
|
|
|
|
|
goodness_comparator (const void *p1, const void *p2)
|
|
|
|
|
{
|
|
|
|
|
struct weighed_cookie *wc1 = (struct weighed_cookie *)p1;
|
|
|
|
|
struct weighed_cookie *wc2 = (struct weighed_cookie *)p2;
|
2001-04-10 12:04:18 -04:00
|
|
|
|
|
|
|
|
|
/* Subtractions take `wc2' as the first argument becauase we want a
|
|
|
|
|
sort in *decreasing* order of goodness. */
|
|
|
|
|
int dgdiff = wc2->domain_goodness - wc1->domain_goodness;
|
|
|
|
|
int pgdiff = wc2->path_goodness - wc1->path_goodness;
|
|
|
|
|
|
|
|
|
|
/* Sort by domain goodness; if these are the same, sort by path
|
|
|
|
|
goodness. (The sorting order isn't really specified; maybe it
|
|
|
|
|
should be the other way around.) */
|
|
|
|
|
return dgdiff ? dgdiff : pgdiff;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
/* Generate a `Cookie' header for a request that goes to HOST:PORT and
|
2001-05-09 14:15:22 -04:00
|
|
|
|
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. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
char *
|
2003-11-04 19:11:33 -05:00
|
|
|
|
cookie_header (struct cookie_jar *jar, const char *host,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
int port, const char *path, bool secflag)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
2003-10-06 20:47:08 -04:00
|
|
|
|
struct cookie **chains;
|
2001-04-10 12:04:18 -04:00
|
|
|
|
int chain_count;
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
struct cookie *cookie;
|
|
|
|
|
struct weighed_cookie *outgoing;
|
2001-04-10 12:04:18 -04:00
|
|
|
|
int count, i, ocnt;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
char *result;
|
|
|
|
|
int result_size, pos;
|
2007-08-02 23:38:21 -04:00
|
|
|
|
PREPEND_SLASH (path); /* see cookie_handle_set_cookie */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
/* First, find the cookie chains whose domains match HOST. */
|
|
|
|
|
|
|
|
|
|
/* Allocate room for find_chains_of_host to write to. The number of
|
|
|
|
|
chains can at most equal the number of subdomains, hence
|
|
|
|
|
1+<number of dots>. */
|
|
|
|
|
chains = alloca_array (struct cookie *, 1 + count_char (host, '.'));
|
|
|
|
|
chain_count = find_chains_of_host (jar, host, chains);
|
2001-04-10 12:04:18 -04:00
|
|
|
|
|
2003-10-06 20:47:08 -04:00
|
|
|
|
/* No cookies for this host. */
|
2001-04-10 12:04:18 -04:00
|
|
|
|
if (!chain_count)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
cookies_now = time (NULL);
|
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* Now extract from the chains those cookies that match our host
|
|
|
|
|
(for domain_exact cookies), port (for cookies with port other
|
|
|
|
|
than PORT_ANY), etc. See matching_cookie for details. */
|
|
|
|
|
|
|
|
|
|
/* Count the number of matching cookies. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
count = 0;
|
2001-04-10 12:04:18 -04:00
|
|
|
|
for (i = 0; i < chain_count; i++)
|
2003-09-22 21:08:01 -04:00
|
|
|
|
for (cookie = chains[i]; cookie; cookie = cookie->next)
|
2003-11-04 19:11:33 -05:00
|
|
|
|
if (cookie_matches_url (cookie, host, port, path, secflag, NULL))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
++count;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (!count)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
return NULL; /* no cookies matched */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
/* Allocate the array. */
|
2003-10-06 20:47:08 -04:00
|
|
|
|
outgoing = alloca_array (struct weighed_cookie, count);
|
2001-04-10 12:04:18 -04:00
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* Fill the array with all the matching cookies from the chains that
|
|
|
|
|
match HOST. */
|
2001-04-10 12:04:18 -04:00
|
|
|
|
ocnt = 0;
|
|
|
|
|
for (i = 0; i < chain_count; i++)
|
2003-09-22 21:08:01 -04:00
|
|
|
|
for (cookie = chains[i]; cookie; cookie = cookie->next)
|
2001-04-10 12:04:18 -04:00
|
|
|
|
{
|
2007-08-02 23:38:21 -04:00
|
|
|
|
int pg;
|
|
|
|
|
if (!cookie_matches_url (cookie, host, port, path, secflag, &pg))
|
|
|
|
|
continue;
|
|
|
|
|
outgoing[ocnt].cookie = cookie;
|
|
|
|
|
outgoing[ocnt].domain_goodness = strlen (cookie->domain);
|
|
|
|
|
outgoing[ocnt].path_goodness = pg;
|
|
|
|
|
++ocnt;
|
2001-04-10 12:04:18 -04:00
|
|
|
|
}
|
|
|
|
|
assert (ocnt == count);
|
|
|
|
|
|
|
|
|
|
/* Eliminate duplicate cookies; that is, those whose name and value
|
2001-05-09 14:15:22 -04:00
|
|
|
|
are the same. */
|
|
|
|
|
count = eliminate_dups (outgoing, count);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2001-04-10 12:04:18 -04:00
|
|
|
|
/* Sort the array so that best-matching domains come first, and
|
|
|
|
|
that, within one domain, best-matching paths come first. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
qsort (outgoing, count, sizeof (struct weighed_cookie), goodness_comparator);
|
|
|
|
|
|
2001-04-10 12:04:18 -04:00
|
|
|
|
/* Count the space the name=value pairs will take. */
|
|
|
|
|
result_size = 0;
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
struct cookie *c = outgoing[i].cookie;
|
|
|
|
|
/* name=value */
|
|
|
|
|
result_size += strlen (c->attr) + 1 + strlen (c->value);
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
/* Allocate output buffer:
|
|
|
|
|
name=value pairs -- result_size
|
|
|
|
|
"; " separators -- (count - 1) * 2
|
|
|
|
|
\0 terminator -- 1 */
|
2003-11-29 13:40:01 -05:00
|
|
|
|
result_size = result_size + (count - 1) * 2 + 1;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
result = xmalloc (result_size);
|
|
|
|
|
pos = 0;
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
struct cookie *c = outgoing[i].cookie;
|
|
|
|
|
int namlen = strlen (c->attr);
|
|
|
|
|
int vallen = strlen (c->value);
|
|
|
|
|
|
|
|
|
|
memcpy (result + pos, c->attr, namlen);
|
|
|
|
|
pos += namlen;
|
|
|
|
|
result[pos++] = '=';
|
|
|
|
|
memcpy (result + pos, c->value, vallen);
|
|
|
|
|
pos += vallen;
|
|
|
|
|
if (i < count - 1)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
result[pos++] = ';';
|
|
|
|
|
result[pos++] = ' ';
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
result[pos++] = '\0';
|
|
|
|
|
assert (pos == result_size);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Support for loading and saving cookies. The format used for
|
2003-09-22 21:08:01 -04:00
|
|
|
|
loading and saving should be the format of the `cookies.txt' file
|
|
|
|
|
used by Netscape and Mozilla, at least the Unix versions.
|
|
|
|
|
(Apparently IE can export cookies in that format as well.) The
|
2001-04-08 18:25:24 -04:00
|
|
|
|
format goes like this:
|
|
|
|
|
|
|
|
|
|
DOMAIN DOMAIN-FLAG PATH SECURE-FLAG TIMESTAMP ATTR-NAME ATTR-VALUE
|
|
|
|
|
|
|
|
|
|
DOMAIN -- cookie domain, optionally followed by :PORT
|
|
|
|
|
DOMAIN-FLAG -- whether all hosts in the domain match
|
|
|
|
|
PATH -- cookie path
|
|
|
|
|
SECURE-FLAG -- whether cookie requires secure connection
|
|
|
|
|
TIMESTAMP -- expiry timestamp, number of seconds since epoch
|
|
|
|
|
ATTR-NAME -- name of the cookie attribute
|
|
|
|
|
ATTR-VALUE -- value of the cookie attribute (empty if absent)
|
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
The fields are separated by TABs. All fields are mandatory, except
|
|
|
|
|
for ATTR-VALUE. The `-FLAG' fields are boolean, their legal values
|
|
|
|
|
being "TRUE" and "FALSE'. Empty lines, lines consisting of
|
|
|
|
|
whitespace only, and comment lines (beginning with # optionally
|
|
|
|
|
preceded by whitespace) are ignored.
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
Example line from cookies.txt (split in two lines for readability):
|
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
.google.com TRUE / FALSE 2147368447 \
|
|
|
|
|
PREF ID=34bb47565bbcd47b:LD=en:NR=20:TM=985172580:LM=985739012
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
*/
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
/* If the region [B, E) ends with :<digits>, parse the number, return
|
|
|
|
|
it, and store new boundary (location of the `:') to DOMAIN_E_PTR.
|
|
|
|
|
If port is not specified, return 0. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
domain_port (const char *domain_b, const char *domain_e,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
const char **domain_e_ptr)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
|
|
|
|
int port = 0;
|
|
|
|
|
const char *p;
|
|
|
|
|
const char *colon = memchr (domain_b, ':', domain_e - domain_b);
|
|
|
|
|
if (!colon)
|
|
|
|
|
return 0;
|
2007-10-14 17:46:24 -04:00
|
|
|
|
for (p = colon + 1; p < domain_e && c_isdigit (*p); p++)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
port = 10 * port + (*p - '0');
|
|
|
|
|
if (p < domain_e)
|
|
|
|
|
/* Garbage following port number. */
|
|
|
|
|
return 0;
|
|
|
|
|
*domain_e_ptr = colon;
|
|
|
|
|
return port;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
#define GET_WORD(p, b, e) do { \
|
|
|
|
|
b = p; \
|
|
|
|
|
while (*p && *p != '\t') \
|
|
|
|
|
++p; \
|
|
|
|
|
e = p; \
|
|
|
|
|
if (b == e || !*p) \
|
|
|
|
|
goto next; \
|
|
|
|
|
++p; \
|
2001-04-08 18:25:24 -04:00
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
/* Load cookies from FILE. */
|
|
|
|
|
|
|
|
|
|
void
|
2002-04-20 16:46:38 -04:00
|
|
|
|
cookie_jar_load (struct cookie_jar *jar, const char *file)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
FILE *fp = fopen (file, "r");
|
|
|
|
|
if (!fp)
|
|
|
|
|
{
|
2005-06-15 16:26:37 -04:00
|
|
|
|
logprintf (LOG_NOTQUIET, _("Cannot open cookies file `%s': %s\n"),
|
2007-08-02 23:38:21 -04:00
|
|
|
|
file, strerror (errno));
|
2001-04-08 18:25:24 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
cookies_now = time (NULL);
|
|
|
|
|
|
|
|
|
|
for (; ((line = read_whole_line (fp)) != NULL); xfree (line))
|
|
|
|
|
{
|
|
|
|
|
struct cookie *cookie;
|
|
|
|
|
char *p = line;
|
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
double expiry;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
int port;
|
|
|
|
|
|
|
|
|
|
char *domain_b = NULL, *domain_e = NULL;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
char *domflag_b = NULL, *domflag_e = NULL;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
char *path_b = NULL, *path_e = NULL;
|
|
|
|
|
char *secure_b = NULL, *secure_e = NULL;
|
|
|
|
|
char *expires_b = NULL, *expires_e = NULL;
|
|
|
|
|
char *name_b = NULL, *name_e = NULL;
|
|
|
|
|
char *value_b = NULL, *value_e = NULL;
|
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* Skip leading white-space. */
|
2007-10-14 17:46:24 -04:00
|
|
|
|
while (*p && c_isspace (*p))
|
2007-08-02 23:38:21 -04:00
|
|
|
|
++p;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* Ignore empty lines. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (!*p || *p == '#')
|
2007-08-02 23:38:21 -04:00
|
|
|
|
continue;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
GET_WORD (p, domain_b, domain_e);
|
|
|
|
|
GET_WORD (p, domflag_b, domflag_e);
|
|
|
|
|
GET_WORD (p, path_b, path_e);
|
|
|
|
|
GET_WORD (p, secure_b, secure_e);
|
|
|
|
|
GET_WORD (p, expires_b, expires_e);
|
|
|
|
|
GET_WORD (p, name_b, name_e);
|
|
|
|
|
|
|
|
|
|
/* Don't use GET_WORD for value because it ends with newline,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
not TAB. */
|
2001-04-24 22:29:54 -04:00
|
|
|
|
value_b = p;
|
|
|
|
|
value_e = p + strlen (p);
|
2003-09-22 21:08:01 -04:00
|
|
|
|
if (value_e > value_b && value_e[-1] == '\n')
|
2007-08-02 23:38:21 -04:00
|
|
|
|
--value_e;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
if (value_e > value_b && value_e[-1] == '\r')
|
2007-08-02 23:38:21 -04:00
|
|
|
|
--value_e;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* Empty values are legal (I think), so don't bother checking. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
cookie = cookie_new ();
|
|
|
|
|
|
|
|
|
|
cookie->attr = strdupdelim (name_b, name_e);
|
|
|
|
|
cookie->value = strdupdelim (value_b, value_e);
|
|
|
|
|
cookie->path = strdupdelim (path_b, path_e);
|
2003-09-22 21:08:01 -04:00
|
|
|
|
cookie->secure = BOUNDED_EQUAL (secure_b, secure_e, "TRUE");
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* Curl source says, quoting Andre Garcia: "flag: A TRUE/FALSE
|
2007-08-02 23:38:21 -04:00
|
|
|
|
value indicating if all machines within a given domain can
|
|
|
|
|
access the variable. This value is set automatically by the
|
|
|
|
|
browser, depending on the value set for the domain." */
|
2003-09-22 21:08:01 -04:00
|
|
|
|
cookie->domain_exact = !BOUNDED_EQUAL (domflag_b, domflag_e, "TRUE");
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
/* DOMAIN needs special treatment because we might need to
|
2007-08-02 23:38:21 -04:00
|
|
|
|
extract the port. */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
port = domain_port (domain_b, domain_e, (const char **)&domain_e);
|
|
|
|
|
if (port)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
cookie->port = port;
|
2003-09-22 21:08:01 -04:00
|
|
|
|
|
|
|
|
|
if (*domain_b == '.')
|
2007-08-02 23:38:21 -04:00
|
|
|
|
++domain_b; /* remove leading dot internally */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
cookie->domain = strdupdelim (domain_b, domain_e);
|
|
|
|
|
|
|
|
|
|
/* safe default in case EXPIRES field is garbled. */
|
2002-04-20 16:46:38 -04:00
|
|
|
|
expiry = (double)cookies_now - 1;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2003-09-22 21:08:01 -04:00
|
|
|
|
/* I don't like changing the line, but it's safe here. (line is
|
2007-08-02 23:38:21 -04:00
|
|
|
|
malloced.) */
|
2001-04-08 18:25:24 -04:00
|
|
|
|
*expires_e = '\0';
|
2002-04-20 16:46:38 -04:00
|
|
|
|
sscanf (expires_b, "%lf", &expiry);
|
|
|
|
|
|
2003-11-04 19:11:33 -05:00
|
|
|
|
if (expiry == 0)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
/* 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. */
|
|
|
|
|
}
|
2003-11-04 19:11:33 -05:00
|
|
|
|
else
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
if (expiry < cookies_now)
|
|
|
|
|
goto abort_cookie; /* ignore stale cookie. */
|
|
|
|
|
cookie->expiry_time = expiry;
|
|
|
|
|
cookie->permanent = 1;
|
|
|
|
|
}
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2002-04-20 16:46:38 -04:00
|
|
|
|
store_cookie (jar, cookie);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
|
|
|
|
next:
|
|
|
|
|
continue;
|
|
|
|
|
|
2005-05-03 11:24:30 -04:00
|
|
|
|
abort_cookie:
|
2001-04-08 18:25:24 -04:00
|
|
|
|
delete_cookie (cookie);
|
|
|
|
|
}
|
|
|
|
|
fclose (fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Save cookies, in format described above, to FILE. */
|
|
|
|
|
|
|
|
|
|
void
|
2002-04-20 16:46:38 -04:00
|
|
|
|
cookie_jar_save (struct cookie_jar *jar, const char *file)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
|
|
|
|
FILE *fp;
|
2005-08-27 09:05:39 -04:00
|
|
|
|
hash_table_iterator iter;
|
2001-04-08 18:25:24 -04:00
|
|
|
|
|
2001-04-10 12:04:18 -04:00
|
|
|
|
DEBUGP (("Saving cookies to %s.\n", file));
|
|
|
|
|
|
2001-04-08 18:25:24 -04:00
|
|
|
|
cookies_now = time (NULL);
|
|
|
|
|
|
|
|
|
|
fp = fopen (file, "w");
|
|
|
|
|
if (!fp)
|
|
|
|
|
{
|
|
|
|
|
logprintf (LOG_NOTQUIET, _("Cannot open cookies file `%s': %s\n"),
|
2007-08-02 23:38:21 -04:00
|
|
|
|
file, strerror (errno));
|
2001-04-08 18:25:24 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fputs ("# HTTP cookie file.\n", fp);
|
2006-08-08 10:32:53 -04:00
|
|
|
|
fprintf (fp, "# Generated by Wget on %s.\n", datetime_str (cookies_now));
|
2001-04-08 18:25:24 -04:00
|
|
|
|
fputs ("# Edit at your own risk.\n\n", fp);
|
|
|
|
|
|
2005-08-27 09:05:39 -04:00
|
|
|
|
for (hash_table_iterate (jar->chains, &iter);
|
|
|
|
|
hash_table_iter_next (&iter);
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
const char *domain = iter.key;
|
|
|
|
|
struct cookie *cookie = iter.value;
|
|
|
|
|
for (; cookie; cookie = cookie->next)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
if (!cookie->permanent && !opt.keep_session_cookies)
|
|
|
|
|
continue;
|
|
|
|
|
if (cookie_expired_p (cookie))
|
|
|
|
|
continue;
|
|
|
|
|
if (!cookie->domain_exact)
|
|
|
|
|
fputc ('.', fp);
|
|
|
|
|
fputs (domain, fp);
|
|
|
|
|
if (cookie->port != PORT_ANY)
|
|
|
|
|
fprintf (fp, ":%d", cookie->port);
|
|
|
|
|
fprintf (fp, "\t%s\t%s\t%s\t%.0f\t%s\t%s\n",
|
|
|
|
|
cookie->domain_exact ? "FALSE" : "TRUE",
|
|
|
|
|
cookie->path, cookie->secure ? "TRUE" : "FALSE",
|
|
|
|
|
(double)cookie->expiry_time,
|
|
|
|
|
cookie->attr, cookie->value);
|
|
|
|
|
if (ferror (fp))
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-08-27 09:05:39 -04:00
|
|
|
|
}
|
|
|
|
|
out:
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (ferror (fp))
|
|
|
|
|
logprintf (LOG_NOTQUIET, _("Error writing to `%s': %s\n"),
|
2007-08-02 23:38:21 -04:00
|
|
|
|
file, strerror (errno));
|
2001-04-08 18:25:24 -04:00
|
|
|
|
if (fclose (fp) < 0)
|
|
|
|
|
logprintf (LOG_NOTQUIET, _("Error closing `%s': %s\n"),
|
2007-08-02 23:38:21 -04:00
|
|
|
|
file, strerror (errno));
|
2001-04-10 12:04:18 -04:00
|
|
|
|
|
2001-04-12 23:39:23 -04:00
|
|
|
|
DEBUGP (("Done saving cookies.\n"));
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clean up cookie-related data. */
|
|
|
|
|
|
|
|
|
|
void
|
2002-04-20 16:46:38 -04:00
|
|
|
|
cookie_jar_delete (struct cookie_jar *jar)
|
2001-04-08 18:25:24 -04:00
|
|
|
|
{
|
2005-08-27 09:25:47 -04:00
|
|
|
|
/* Iterate over chains (indexed by domain) and free them. */
|
|
|
|
|
hash_table_iterator iter;
|
|
|
|
|
for (hash_table_iterate (jar->chains, &iter); hash_table_iter_next (&iter); )
|
|
|
|
|
{
|
|
|
|
|
struct cookie *chain = iter.value;
|
|
|
|
|
xfree (iter.key);
|
|
|
|
|
/* Then all cookies in this chain. */
|
|
|
|
|
while (chain)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
struct cookie *next = chain->next;
|
|
|
|
|
delete_cookie (chain);
|
|
|
|
|
chain = next;
|
|
|
|
|
}
|
2005-08-27 09:25:47 -04:00
|
|
|
|
}
|
2003-10-06 20:49:58 -04:00
|
|
|
|
hash_table_destroy (jar->chains);
|
2002-04-20 16:46:38 -04:00
|
|
|
|
xfree (jar);
|
2001-04-08 18:25:24 -04:00
|
|
|
|
}
|
2003-09-15 11:35:47 -04:00
|
|
|
|
|
|
|
|
|
/* Test cases. Currently this is only tests parse_set_cookies. To
|
|
|
|
|
use, recompile Wget with -DTEST_COOKIES and call test_cookies()
|
|
|
|
|
from main. */
|
|
|
|
|
|
|
|
|
|
#ifdef TEST_COOKIES
|
|
|
|
|
void
|
|
|
|
|
test_cookies (void)
|
|
|
|
|
{
|
|
|
|
|
/* Tests expected to succeed: */
|
|
|
|
|
static struct {
|
2006-02-28 15:50:37 -05:00
|
|
|
|
const char *data;
|
|
|
|
|
const char *results[10];
|
2003-09-15 11:35:47 -04:00
|
|
|
|
} tests_succ[] = {
|
|
|
|
|
{ "arg=value", {"arg", "value", NULL} },
|
|
|
|
|
{ "arg1=value1;arg2=value2", {"arg1", "value1", "arg2", "value2", NULL} },
|
|
|
|
|
{ "arg1=value1; arg2=value2", {"arg1", "value1", "arg2", "value2", NULL} },
|
|
|
|
|
{ "arg1=value1; arg2=value2;", {"arg1", "value1", "arg2", "value2", NULL} },
|
|
|
|
|
{ "arg1=value1; arg2=value2; ", {"arg1", "value1", "arg2", "value2", NULL} },
|
|
|
|
|
{ "arg1=\"value1\"; arg2=\"\"", {"arg1", "value1", "arg2", "", NULL} },
|
|
|
|
|
{ "arg=", {"arg", "", NULL} },
|
|
|
|
|
{ "arg1=; arg2=", {"arg1", "", "arg2", "", NULL} },
|
|
|
|
|
{ "arg1 = ; arg2= ", {"arg1", "", "arg2", "", NULL} },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Tests expected to fail: */
|
|
|
|
|
static char *tests_fail[] = {
|
|
|
|
|
";",
|
|
|
|
|
"arg=\"unterminated",
|
|
|
|
|
"=empty-name",
|
|
|
|
|
"arg1=;=another-empty-name",
|
|
|
|
|
};
|
|
|
|
|
int i;
|
|
|
|
|
|
2003-09-19 10:08:37 -04:00
|
|
|
|
for (i = 0; i < countof (tests_succ); i++)
|
2003-09-15 11:35:47 -04:00
|
|
|
|
{
|
|
|
|
|
int ind;
|
2006-02-28 15:50:37 -05:00
|
|
|
|
const char *data = tests_succ[i].data;
|
|
|
|
|
const char **expected = tests_succ[i].results;
|
2003-09-15 11:35:47 -04:00
|
|
|
|
struct cookie *c;
|
|
|
|
|
|
2006-02-28 15:50:37 -05:00
|
|
|
|
c = parse_set_cookie (data, true);
|
2003-09-15 11:35:47 -04:00
|
|
|
|
if (!c)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
printf ("NULL cookie returned for valid data: %s\n", data);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2003-09-15 11:35:47 -04:00
|
|
|
|
|
2006-02-28 15:50:37 -05:00
|
|
|
|
/* Test whether extract_param handles these cases correctly. */
|
|
|
|
|
{
|
2007-08-02 23:38:21 -04:00
|
|
|
|
param_token name, value;
|
|
|
|
|
const char *ptr = data;
|
|
|
|
|
int j = 0;
|
|
|
|
|
while (extract_param (&ptr, &name, &value, ';'))
|
|
|
|
|
{
|
|
|
|
|
char *n = strdupdelim (name.b, name.e);
|
|
|
|
|
char *v = strdupdelim (value.b, value.e);
|
|
|
|
|
if (!expected[j])
|
|
|
|
|
{
|
|
|
|
|
printf ("Too many parameters for '%s'\n", data);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (0 != strcmp (expected[j], n))
|
|
|
|
|
printf ("Invalid name %d for '%s' (expected '%s', got '%s')\n",
|
|
|
|
|
j / 2 + 1, data, expected[j], n);
|
|
|
|
|
if (0 != strcmp (expected[j + 1], v))
|
|
|
|
|
printf ("Invalid value %d for '%s' (expected '%s', got '%s')\n",
|
|
|
|
|
j / 2 + 1, data, expected[j + 1], v);
|
|
|
|
|
j += 2;
|
|
|
|
|
free (n);
|
|
|
|
|
free (v);
|
|
|
|
|
}
|
|
|
|
|
if (expected[j])
|
|
|
|
|
printf ("Too few parameters for '%s'\n", data);
|
2006-02-28 15:50:37 -05:00
|
|
|
|
}
|
2003-09-15 11:35:47 -04:00
|
|
|
|
}
|
|
|
|
|
|
2003-09-19 10:08:37 -04:00
|
|
|
|
for (i = 0; i < countof (tests_fail); i++)
|
2003-09-15 11:35:47 -04:00
|
|
|
|
{
|
|
|
|
|
struct cookie *c;
|
|
|
|
|
char *data = tests_fail[i];
|
2006-02-28 15:50:37 -05:00
|
|
|
|
c = parse_set_cookie (data, true);
|
2003-09-15 11:35:47 -04:00
|
|
|
|
if (c)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
printf ("Failed to report error on invalid data: %s\n", data);
|
2003-09-15 11:35:47 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* TEST_COOKIES */
|