mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-22 12:12:35 +03:00
Work on cookies to bring our behaviour closer to the spec and other browsers:
+ Improve handling of quoted cookies -- now processes nested quotes correctly + Improve cookie output -- now knows which version it's outputting for and processes things appropriately + Add assertion that we're dealing with a domain cookie in the case where the cookie domain and URL host part don't match during validation. + Tidy up fix for broken domain cookie setting -- it's now less confusing to read. + Preserve cookie value quoting, regardless of its necessity. + Modify Cookie file format to save value_was_quoted flag -- version number bumped to 101. + Add more testcases. svn path=/trunk/netsurf/; revision=3708
This commit is contained in:
parent
2fa8e656a1
commit
e5e2eb09f6
411
content/urldb.c
411
content/urldb.c
@ -111,6 +111,7 @@
|
|||||||
struct cookie_internal_data {
|
struct cookie_internal_data {
|
||||||
char *name; /**< Cookie name */
|
char *name; /**< Cookie name */
|
||||||
char *value; /**< Cookie value */
|
char *value; /**< Cookie value */
|
||||||
|
bool value_was_quoted; /**< Value was quoted in Set-Cookie: */
|
||||||
char *comment; /**< Cookie comment */
|
char *comment; /**< Cookie comment */
|
||||||
bool domain_from_set; /**< Domain came from Set-Cookie: header */
|
bool domain_from_set; /**< Domain came from Set-Cookie: header */
|
||||||
char *domain; /**< Domain */
|
char *domain; /**< Domain */
|
||||||
@ -269,14 +270,17 @@ static int urldb_search_match_prefix(const struct host_part *a,
|
|||||||
/* Cookies */
|
/* Cookies */
|
||||||
static struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
static struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
||||||
const char **cookie);
|
const char **cookie);
|
||||||
static bool urldb_parse_avpair(struct cookie_internal_data *c, char *n, char *v);
|
static bool urldb_parse_avpair(struct cookie_internal_data *c, char *n,
|
||||||
static bool urldb_insert_cookie(struct cookie_internal_data *c, const char *scheme,
|
char *v, bool was_quoted);
|
||||||
const char *url);
|
static bool urldb_insert_cookie(struct cookie_internal_data *c,
|
||||||
|
const char *scheme, const char *url);
|
||||||
static void urldb_free_cookie(struct cookie_internal_data *c);
|
static void urldb_free_cookie(struct cookie_internal_data *c);
|
||||||
static bool urldb_concat_cookie(struct cookie_internal_data *c, int *used,
|
static bool urldb_concat_cookie(struct cookie_internal_data *c, int version,
|
||||||
int *alloc, char **buf);
|
int *used, int *alloc, char **buf);
|
||||||
static void urldb_delete_cookie_hosts(const char *domain, const char *path, const char *name, struct host_part *parent);
|
static void urldb_delete_cookie_hosts(const char *domain, const char *path,
|
||||||
static void urldb_delete_cookie_paths(const char *domain, const char *path, const char *name, struct path_data *parent);
|
const char *name, struct host_part *parent);
|
||||||
|
static void urldb_delete_cookie_paths(const char *domain, const char *path,
|
||||||
|
const char *name, struct path_data *parent);
|
||||||
static void urldb_save_cookie_hosts(FILE *fp, struct host_part *parent);
|
static void urldb_save_cookie_hosts(FILE *fp, struct host_part *parent);
|
||||||
static void urldb_save_cookie_paths(FILE *fp, struct path_data *parent);
|
static void urldb_save_cookie_paths(FILE *fp, struct path_data *parent);
|
||||||
|
|
||||||
@ -296,7 +300,7 @@ static struct search_node *search_trees[NUM_SEARCH_TREES] = {
|
|||||||
&empty, &empty, &empty, &empty
|
&empty, &empty, &empty, &empty
|
||||||
};
|
};
|
||||||
|
|
||||||
#define COOKIE_FILE_VERSION 100
|
#define COOKIE_FILE_VERSION 101
|
||||||
#define URL_FILE_VERSION 106
|
#define URL_FILE_VERSION 106
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2364,6 +2368,8 @@ char *urldb_get_cookie(const char *url)
|
|||||||
const struct host_part *h;
|
const struct host_part *h;
|
||||||
struct cookie_internal_data *c;
|
struct cookie_internal_data *c;
|
||||||
int count = 0, version = COOKIE_RFC2965;
|
int count = 0, version = COOKIE_RFC2965;
|
||||||
|
struct cookie_internal_data **matched_cookies;
|
||||||
|
int matched_cookies_size = 20;
|
||||||
int ret_alloc = 4096, ret_used = 1;
|
int ret_alloc = 4096, ret_used = 1;
|
||||||
char *path;
|
char *path;
|
||||||
char *ret;
|
char *ret;
|
||||||
@ -2383,15 +2389,43 @@ char *urldb_get_cookie(const char *url)
|
|||||||
|
|
||||||
scheme = p->scheme;
|
scheme = p->scheme;
|
||||||
|
|
||||||
ret = malloc(ret_alloc);
|
matched_cookies = malloc(matched_cookies_size *
|
||||||
if (!ret)
|
sizeof(struct cookie_internal_data *));
|
||||||
|
if (!matched_cookies)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
#define GROW_MATCHED_COOKIES \
|
||||||
|
do { \
|
||||||
|
if (count == matched_cookies_size) { \
|
||||||
|
struct cookie_internal_data **temp; \
|
||||||
|
temp = realloc(matched_cookies, \
|
||||||
|
(matched_cookies_size + 20) * \
|
||||||
|
sizeof(struct cookie_internal_data *)); \
|
||||||
|
\
|
||||||
|
if (temp == NULL) { \
|
||||||
|
free(path); \
|
||||||
|
free(ret); \
|
||||||
|
free(matched_cookies); \
|
||||||
|
return NULL; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
matched_cookies = temp; \
|
||||||
|
matched_cookies_size += 20; \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
ret = malloc(ret_alloc);
|
||||||
|
if (!ret) {
|
||||||
|
free(matched_cookies);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ret[0] = '\0';
|
ret[0] = '\0';
|
||||||
|
|
||||||
res = url_path(url, &path);
|
res = url_path(url, &path);
|
||||||
if (res != URL_FUNC_OK) {
|
if (res != URL_FUNC_OK) {
|
||||||
free(ret);
|
free(ret);
|
||||||
|
free(matched_cookies);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2417,20 +2451,16 @@ char *urldb_get_cookie(const char *url)
|
|||||||
* ignore */
|
* ignore */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!urldb_concat_cookie(c, &ret_used,
|
matched_cookies[count++] = c;
|
||||||
&ret_alloc, &ret)) {
|
|
||||||
free(path);
|
GROW_MATCHED_COOKIES;
|
||||||
free(ret);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->version < (unsigned int)version)
|
if (c->version < (unsigned int)version)
|
||||||
version = c->version;
|
version = c->version;
|
||||||
|
|
||||||
c->last_used = now;
|
c->last_used = now;
|
||||||
cookies_update(c->domain, (struct cookie_data *)c);
|
cookies_update(c->domain,
|
||||||
|
(struct cookie_data *)c);
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2457,12 +2487,9 @@ char *urldb_get_cookie(const char *url)
|
|||||||
* => ignore */
|
* => ignore */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!urldb_concat_cookie(c, &ret_used,
|
matched_cookies[count++] = c;
|
||||||
&ret_alloc, &ret)) {
|
|
||||||
free(path);
|
GROW_MATCHED_COOKIES;
|
||||||
free(ret);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->version < (unsigned int) version)
|
if (c->version < (unsigned int) version)
|
||||||
version = c->version;
|
version = c->version;
|
||||||
@ -2470,7 +2497,6 @@ char *urldb_get_cookie(const char *url)
|
|||||||
c->last_used = now;
|
c->last_used = now;
|
||||||
cookies_update(c->domain,
|
cookies_update(c->domain,
|
||||||
(struct cookie_data *)c);
|
(struct cookie_data *)c);
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2502,19 +2528,15 @@ char *urldb_get_cookie(const char *url)
|
|||||||
* => ignore */
|
* => ignore */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!urldb_concat_cookie(c, &ret_used,
|
matched_cookies[count++] = c;
|
||||||
&ret_alloc, &ret)) {
|
|
||||||
free(path);
|
GROW_MATCHED_COOKIES;
|
||||||
free(ret);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->version < (unsigned int) version)
|
if (c->version < (unsigned int) version)
|
||||||
version = c->version;
|
version = c->version;
|
||||||
|
|
||||||
c->last_used = now;
|
c->last_used = now;
|
||||||
cookies_update(c->domain, (struct cookie_data *)c);
|
cookies_update(c->domain, (struct cookie_data *)c);
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2538,20 +2560,15 @@ char *urldb_get_cookie(const char *url)
|
|||||||
/* secure cookie for insecure host. ignore */
|
/* secure cookie for insecure host. ignore */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!urldb_concat_cookie(c, &ret_used, &ret_alloc,
|
matched_cookies[count++] = c;
|
||||||
&ret)) {
|
|
||||||
free(path);
|
GROW_MATCHED_COOKIES;
|
||||||
free(ret);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c->version < (unsigned int)version)
|
if (c->version < (unsigned int)version)
|
||||||
version = c->version;
|
version = c->version;
|
||||||
|
|
||||||
c->last_used = now;
|
c->last_used = now;
|
||||||
cookies_update(c->domain, (struct cookie_data *)c);
|
cookies_update(c->domain, (struct cookie_data *)c);
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2561,35 +2578,51 @@ char *urldb_get_cookie(const char *url)
|
|||||||
/* No cookies found */
|
/* No cookies found */
|
||||||
free(path);
|
free(path);
|
||||||
free(ret);
|
free(ret);
|
||||||
|
free(matched_cookies);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* and build output string */
|
/* and build output string */
|
||||||
|
if (version > COOKIE_NETSCAPE) {
|
||||||
|
sprintf(ret, "$Version=%d", version);
|
||||||
|
ret_used = strlen(ret) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (!urldb_concat_cookie(matched_cookies[i], version,
|
||||||
|
&ret_used, &ret_alloc, &ret)) {
|
||||||
|
free(path);
|
||||||
|
free(ret);
|
||||||
|
free(matched_cookies);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version == COOKIE_NETSCAPE) {
|
||||||
|
/* Old-style cookies => no version & skip "; " */
|
||||||
|
memmove(ret, ret + 2, ret_used - 2);
|
||||||
|
ret_used -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now, shrink the output buffer to the required size */
|
||||||
{
|
{
|
||||||
char *temp;
|
char *temp = realloc(ret, ret_used);
|
||||||
if (version > 0)
|
|
||||||
temp = malloc(12 + ret_used);
|
|
||||||
else
|
|
||||||
temp = malloc(ret_used);
|
|
||||||
if (!temp) {
|
if (!temp) {
|
||||||
free(path);
|
free(path);
|
||||||
free(ret);
|
free(ret);
|
||||||
|
free(matched_cookies);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version > 0)
|
|
||||||
sprintf(temp, "$Version=%d%s", version, ret);
|
|
||||||
else {
|
|
||||||
/* Old-style cookies => no version & skip "; " */
|
|
||||||
sprintf(temp, "%s", ret + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(path);
|
|
||||||
free(ret);
|
|
||||||
ret = temp;
|
ret = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
free(matched_cookies);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
#undef GROW_MATCHED_COOKIES
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2726,6 +2759,20 @@ bool urldb_set_cookie(const char *header, const char *url,
|
|||||||
int hlen, dlen;
|
int hlen, dlen;
|
||||||
char *domain = c->domain;
|
char *domain = c->domain;
|
||||||
|
|
||||||
|
/* c->domain must be a domain cookie here because:
|
||||||
|
* c->domain is either:
|
||||||
|
* + specified in the header as a domain cookie
|
||||||
|
* (non-domain cookies in the header are ignored
|
||||||
|
* by urldb_parse_cookie / urldb_parse_avpair)
|
||||||
|
* + defaulted to the URL's host part
|
||||||
|
* (by urldb_parse_cookie if no valid domain was
|
||||||
|
* specified in the header)
|
||||||
|
*
|
||||||
|
* The latter will pass the strcasecmp above, which
|
||||||
|
* leaves the former (i.e. a domain cookie)
|
||||||
|
*/
|
||||||
|
assert(c->domain[0] == '.');
|
||||||
|
|
||||||
/* 4.3.2:iii */
|
/* 4.3.2:iii */
|
||||||
if (url_host_is_ip_address(host)) {
|
if (url_host_is_ip_address(host)) {
|
||||||
/* IP address, so no partial match */
|
/* IP address, so no partial match */
|
||||||
@ -2754,7 +2801,9 @@ bool urldb_set_cookie(const char *header, const char *url,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If you believe the spec, H should contain no
|
/* 4.3.2:iv Ensure H contains no dots
|
||||||
|
*
|
||||||
|
* If you believe the spec, H should contain no
|
||||||
* dots in _any_ cookie. Unfortunately, however,
|
* dots in _any_ cookie. Unfortunately, however,
|
||||||
* reality differs in that many sites send domain
|
* reality differs in that many sites send domain
|
||||||
* cookies of the form .foo.com from hosts such
|
* cookies of the form .foo.com from hosts such
|
||||||
@ -2763,18 +2812,16 @@ bool urldb_set_cookie(const char *header, const char *url,
|
|||||||
* expect, regardless of any potential security
|
* expect, regardless of any potential security
|
||||||
* implications.
|
* implications.
|
||||||
*
|
*
|
||||||
* Ensure that we're dealing with a domain cookie
|
* This is what code conforming to the spec would
|
||||||
* here for extra paranoia.
|
* look like:
|
||||||
|
*
|
||||||
|
* for (int i = 0; i < (hlen - dlen); i++) {
|
||||||
|
* if (host[i] == '.') {
|
||||||
|
* urldb_free_cookie(c);
|
||||||
|
* goto error;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
if (c->domain[0] != '.') {
|
|
||||||
/* 4.3.2:iv Ensure H contains no dots */
|
|
||||||
for (int i = 0; i < (hlen - dlen); i++) {
|
|
||||||
if (host[i] == '.') {
|
|
||||||
urldb_free_cookie(c);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now insert into database */
|
/* Now insert into database */
|
||||||
@ -2815,6 +2862,7 @@ struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
|||||||
char *n = name, *v = value;
|
char *n = name, *v = value;
|
||||||
bool had_equals = false;
|
bool had_equals = false;
|
||||||
bool quoted = false;
|
bool quoted = false;
|
||||||
|
bool was_quoted = false;
|
||||||
url_func_result res;
|
url_func_result res;
|
||||||
|
|
||||||
assert(url && cookie && *cookie);
|
assert(url && cookie && *cookie);
|
||||||
@ -2831,8 +2879,12 @@ struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
|||||||
for (cur = *cookie; *cur && *cur != '\r' && *cur != '\n'; cur++) {
|
for (cur = *cookie; *cur && *cur != '\r' && *cur != '\n'; cur++) {
|
||||||
if (had_equals && (*cur == '"' || *cur == '\'')) {
|
if (had_equals && (*cur == '"' || *cur == '\'')) {
|
||||||
/* Only values may be quoted */
|
/* Only values may be quoted */
|
||||||
quoted = !quoted;
|
if (cur == *cookie || *(cur - 1) != '\\') {
|
||||||
continue;
|
/* Only unescaped quotes count */
|
||||||
|
was_quoted = quoted;
|
||||||
|
quoted = !quoted;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!quoted && !had_equals && *cur == '=') {
|
if (!quoted && !had_equals && *cur == '=') {
|
||||||
@ -2848,7 +2900,7 @@ struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
|||||||
*n = '\0';
|
*n = '\0';
|
||||||
*v = '\0';
|
*v = '\0';
|
||||||
|
|
||||||
if (!urldb_parse_avpair(c, name, value)) {
|
if (!urldb_parse_avpair(c, name, value, was_quoted)) {
|
||||||
/* Memory exhausted */
|
/* Memory exhausted */
|
||||||
urldb_free_cookie(c);
|
urldb_free_cookie(c);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2858,6 +2910,7 @@ struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
|||||||
n = name;
|
n = name;
|
||||||
v = value;
|
v = value;
|
||||||
had_equals = false;
|
had_equals = false;
|
||||||
|
was_quoted = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2877,8 +2930,8 @@ struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
|||||||
* immediately following semicolon (or end of input if none
|
* immediately following semicolon (or end of input if none
|
||||||
* found). Then, consider the input characters between
|
* found). Then, consider the input characters between
|
||||||
* these two positions. If any of these characters is an
|
* these two positions. If any of these characters is an
|
||||||
* '=', we must assume that the comma signified the end of
|
* '=', we must assume that the comma signified the end of
|
||||||
* the current cookie.
|
* the current cookie.
|
||||||
*
|
*
|
||||||
* This holds as the first avpair of any cookie must be
|
* This holds as the first avpair of any cookie must be
|
||||||
* NAME=VALUE, so the '=' is guaranteed to appear in the
|
* NAME=VALUE, so the '=' is guaranteed to appear in the
|
||||||
@ -2913,6 +2966,7 @@ struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Accumulate into buffers, always leaving space for a NUL */
|
/* Accumulate into buffers, always leaving space for a NUL */
|
||||||
|
/** \todo is silently truncating overlong names/values wise? */
|
||||||
if (!had_equals) {
|
if (!had_equals) {
|
||||||
if (n < name + 1023)
|
if (n < name + 1023)
|
||||||
*n++ = *cur;
|
*n++ = *cur;
|
||||||
@ -2926,7 +2980,7 @@ struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
|||||||
*n = '\0';
|
*n = '\0';
|
||||||
*v = '\0';
|
*v = '\0';
|
||||||
|
|
||||||
if (!urldb_parse_avpair(c, name, value)) {
|
if (!urldb_parse_avpair(c, name, value, was_quoted)) {
|
||||||
/* Memory exhausted */
|
/* Memory exhausted */
|
||||||
urldb_free_cookie(c);
|
urldb_free_cookie(c);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2984,9 +3038,11 @@ struct cookie_internal_data *urldb_parse_cookie(const char *url,
|
|||||||
* \param c Cookie struct to populate
|
* \param c Cookie struct to populate
|
||||||
* \param n Name component
|
* \param n Name component
|
||||||
* \param v Value component
|
* \param v Value component
|
||||||
|
* \param was_quoted Whether ::v was quoted in the input
|
||||||
* \return true on success, false on memory exhaustion
|
* \return true on success, false on memory exhaustion
|
||||||
*/
|
*/
|
||||||
bool urldb_parse_avpair(struct cookie_internal_data *c, char *n, char *v)
|
bool urldb_parse_avpair(struct cookie_internal_data *c, char *n, char *v,
|
||||||
|
bool was_quoted)
|
||||||
{
|
{
|
||||||
int vlen;
|
int vlen;
|
||||||
|
|
||||||
@ -3073,6 +3129,7 @@ bool urldb_parse_avpair(struct cookie_internal_data *c, char *n, char *v)
|
|||||||
} else if (!c->name) {
|
} else if (!c->name) {
|
||||||
c->name = strdup(n);
|
c->name = strdup(n);
|
||||||
c->value = strdup(v);
|
c->value = strdup(v);
|
||||||
|
c->value_was_quoted = was_quoted;
|
||||||
if (!c->name || !c->value)
|
if (!c->name || !c->value)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3197,25 +3254,83 @@ void urldb_free_cookie(struct cookie_internal_data *c)
|
|||||||
* Concatenate a cookie into the provided buffer
|
* Concatenate a cookie into the provided buffer
|
||||||
*
|
*
|
||||||
* \param c Cookie to concatenate
|
* \param c Cookie to concatenate
|
||||||
|
* \param version The version of the cookie string to output
|
||||||
* \param used Pointer to amount of buffer used (updated)
|
* \param used Pointer to amount of buffer used (updated)
|
||||||
* \param alloc Pointer to allocated size of buffer (updated)
|
* \param alloc Pointer to allocated size of buffer (updated)
|
||||||
* \param buf Pointer to Pointer to buffer (updated)
|
* \param buf Pointer to Pointer to buffer (updated)
|
||||||
* \return true on success, false on memory exhaustion
|
* \return true on success, false on memory exhaustion
|
||||||
*/
|
*/
|
||||||
bool urldb_concat_cookie(struct cookie_internal_data *c, int *used,
|
bool urldb_concat_cookie(struct cookie_internal_data *c, int version,
|
||||||
int *alloc, char **buf)
|
int *used, int *alloc, char **buf)
|
||||||
{
|
{
|
||||||
int clen;
|
/* Combined (A)BNF for the Cookie: request header:
|
||||||
|
*
|
||||||
|
* CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
* CTL = <any US-ASCII control character
|
||||||
|
* (octets 0 - 31) and DEL (127)>
|
||||||
|
* CR = <US-ASCII CR, carriage return (13)>
|
||||||
|
* LF = <US-ASCII LF, linefeed (10)>
|
||||||
|
* SP = <US-ASCII SP, space (32)>
|
||||||
|
* HT = <US-ASCII HT, horizontal-tab (9)>
|
||||||
|
* <"> = <US-ASCII double-quote mark (34)>
|
||||||
|
*
|
||||||
|
* CRLF = CR LF
|
||||||
|
*
|
||||||
|
* LWS = [CRLF] 1*( SP | HT )
|
||||||
|
*
|
||||||
|
* TEXT = <any OCTET except CTLs,
|
||||||
|
* but including LWS>
|
||||||
|
*
|
||||||
|
* token = 1*<any CHAR except CTLs or separators>
|
||||||
|
* separators = "(" | ")" | "<" | ">" | "@"
|
||||||
|
* | "," | ";" | ":" | "\" | <">
|
||||||
|
* | "/" | "[" | "]" | "?" | "="
|
||||||
|
* | "{" | "}" | SP | HT
|
||||||
|
*
|
||||||
|
* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
|
||||||
|
* qdtext = <any TEXT except <">>
|
||||||
|
* quoted-pair = "\" CHAR
|
||||||
|
*
|
||||||
|
* attr = token
|
||||||
|
* value = word
|
||||||
|
* word = token | quoted-string
|
||||||
|
*
|
||||||
|
* cookie = "Cookie:" cookie-version
|
||||||
|
* 1*((";" | ",") cookie-value)
|
||||||
|
* cookie-value = NAME "=" VALUE [";" path] [";" domain]
|
||||||
|
* cookie-version = "$Version" "=" value
|
||||||
|
* NAME = attr
|
||||||
|
* VALUE = value
|
||||||
|
* path = "$Path" "=" value
|
||||||
|
* domain = "$Domain" "=" value
|
||||||
|
*
|
||||||
|
* A note on quoted-string handling:
|
||||||
|
* The cookie data stored in the db is verbatim (i.e. sans enclosing
|
||||||
|
* <">, if any, and with all quoted-pairs intact) thus all that we
|
||||||
|
* need to do here is ensure that value strings which were quoted
|
||||||
|
* in Set-Cookie or which include any of the separators are quoted
|
||||||
|
* before use.
|
||||||
|
*
|
||||||
|
* A note on cookie-value separation:
|
||||||
|
* We use semicolons for all separators, including between
|
||||||
|
* cookie-values. This simplifies things and is backwards compatible.
|
||||||
|
*/
|
||||||
|
const char * const separators = "()<>@,;:\\\"/[]?={} \t";
|
||||||
|
|
||||||
|
int max_len;
|
||||||
|
|
||||||
assert(c && used && alloc && buf && *buf);
|
assert(c && used && alloc && buf && *buf);
|
||||||
|
|
||||||
clen = 2 + strlen(c->name) + 1 + strlen(c->value) +
|
/* "; " cookie-value
|
||||||
|
* We allow for the possibility that values are quoted
|
||||||
|
*/
|
||||||
|
max_len = 2 + strlen(c->name) + 1 + strlen(c->value) + 2 +
|
||||||
(c->path_from_set ?
|
(c->path_from_set ?
|
||||||
8 + strlen(c->path) : 0) +
|
8 + strlen(c->path) + 2 : 0) +
|
||||||
(c->domain_from_set ?
|
(c->domain_from_set ?
|
||||||
10 + strlen(c->domain) : 0);
|
10 + strlen(c->domain) + 2 : 0);
|
||||||
|
|
||||||
if (*used + clen >= *alloc) {
|
if (*used + max_len >= *alloc) {
|
||||||
char *temp = realloc(*buf, *alloc + 4096);
|
char *temp = realloc(*buf, *alloc + 4096);
|
||||||
if (!temp) {
|
if (!temp) {
|
||||||
return false;
|
return false;
|
||||||
@ -3224,17 +3339,72 @@ bool urldb_concat_cookie(struct cookie_internal_data *c, int *used,
|
|||||||
*alloc += 4096;
|
*alloc += 4096;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \todo Quote value strings iff version > 0 */
|
if (version == COOKIE_NETSCAPE) {
|
||||||
sprintf(*buf + *used - 1, "; %s=%s%s%s%s%s",
|
/* Original Netscape cookie */
|
||||||
c->name, c->value,
|
sprintf(*buf + *used - 1, "; %s=", c->name);
|
||||||
(c->path_from_set ? "; $Path=" : "" ),
|
*used += 2 + strlen(c->name) + 1;
|
||||||
(c->path_from_set ? c->path : "" ),
|
|
||||||
// (c->path_from_set ? "\"" : ""),
|
/* The Netscape spec doesn't mention quoting of cookie values.
|
||||||
(c->domain_from_set ? "; $Domain=" : ""),
|
* RFC 2109 $10.1.3 indicates that values must not be quoted.
|
||||||
(c->domain_from_set ? c->domain : "")
|
*
|
||||||
// ,(c->domain_from_set ? "\"" : "")
|
* However, other browsers preserve quoting, so we should, too
|
||||||
);
|
*/
|
||||||
*used += clen;
|
if (c->value_was_quoted) {
|
||||||
|
sprintf(*buf + *used - 1, "\"%s\"", c->value);
|
||||||
|
*used += 1 + strlen(c->value) + 1;
|
||||||
|
} else {
|
||||||
|
/** \todo should we %XX-encode [;HT,SP] ? */
|
||||||
|
/** \todo Should we strip escaping backslashes? */
|
||||||
|
sprintf(*buf + *used - 1, "%s", c->value);
|
||||||
|
*used += strlen(c->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't send path/domain information -- that's what the
|
||||||
|
* Netscape spec suggests we should do, anyway. */
|
||||||
|
} else {
|
||||||
|
/* RFC2109 or RFC2965 cookie */
|
||||||
|
sprintf(*buf + *used - 1, "; %s=", c->name);
|
||||||
|
*used += 2 + strlen(c->name) + 1;
|
||||||
|
|
||||||
|
/* Value needs quoting if it contains any separator or if
|
||||||
|
* it needs preserving from the Set-Cookie header */
|
||||||
|
if (c->value_was_quoted ||
|
||||||
|
strpbrk(c->value, separators) != NULL) {
|
||||||
|
sprintf(*buf + *used - 1, "\"%s\"", c->value);
|
||||||
|
*used += 1 + strlen(c->value) + 1;
|
||||||
|
} else {
|
||||||
|
sprintf(*buf + *used - 1, "%s", c->value);
|
||||||
|
*used += strlen(c->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->path_from_set) {
|
||||||
|
/* Path, quoted if necessary */
|
||||||
|
sprintf(*buf + *used - 1, "; $Path=");
|
||||||
|
*used += 8;
|
||||||
|
|
||||||
|
if (strpbrk(c->path, separators) != NULL) {
|
||||||
|
sprintf(*buf + *used - 1, "\"%s\"", c->path);
|
||||||
|
*used += 1 + strlen(c->path) + 1;
|
||||||
|
} else {
|
||||||
|
sprintf(*buf + *used - 1, "%s", c->path);
|
||||||
|
*used += strlen(c->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->domain_from_set) {
|
||||||
|
/* Domain, quoted if necessary */
|
||||||
|
sprintf(*buf + *used - 1, "; $Domain=");
|
||||||
|
*used += 10;
|
||||||
|
|
||||||
|
if (strpbrk(c->domain, separators) != NULL) {
|
||||||
|
sprintf(*buf + *used - 1, "\"%s\"", c->domain);
|
||||||
|
*used += 1 + strlen(c->domain) + 1;
|
||||||
|
} else {
|
||||||
|
sprintf(*buf + *used - 1, "%s", c->domain);
|
||||||
|
*used += strlen(c->domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3280,7 +3450,7 @@ void urldb_load_cookies(const char *filename)
|
|||||||
*domain, *path, *name, *value, *scheme, *url,
|
*domain, *path, *name, *value, *scheme, *url,
|
||||||
*comment;
|
*comment;
|
||||||
int version, domain_specified, path_specified,
|
int version, domain_specified, path_specified,
|
||||||
secure, no_destroy;
|
secure, no_destroy, value_quoted;
|
||||||
time_t expires, last_used;
|
time_t expires, last_used;
|
||||||
|
|
||||||
if(s[0] == 0 || s[0] == '#')
|
if(s[0] == 0 || s[0] == '#')
|
||||||
@ -3296,7 +3466,8 @@ void urldb_load_cookies(const char *filename)
|
|||||||
if (strncasecmp(s, "Version:", 8) == 0) {
|
if (strncasecmp(s, "Version:", 8) == 0) {
|
||||||
FIND_T; SKIP_T; file_version = atoi(p);
|
FIND_T; SKIP_T; file_version = atoi(p);
|
||||||
|
|
||||||
if (file_version != COOKIE_FILE_VERSION) {
|
if (file_version < 100 &&
|
||||||
|
file_version > COOKIE_FILE_VERSION) {
|
||||||
LOG(("Unknown Cookie file version"));
|
LOG(("Unknown Cookie file version"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3321,6 +3492,11 @@ void urldb_load_cookies(const char *filename)
|
|||||||
SKIP_T; no_destroy = atoi(p); FIND_T;
|
SKIP_T; no_destroy = atoi(p); FIND_T;
|
||||||
SKIP_T; name = p; FIND_T;
|
SKIP_T; name = p; FIND_T;
|
||||||
SKIP_T; value = p; FIND_T;
|
SKIP_T; value = p; FIND_T;
|
||||||
|
if (file_version > 100) {
|
||||||
|
SKIP_T; value_quoted = atoi(p); FIND_T;
|
||||||
|
} else {
|
||||||
|
value_quoted = 0;
|
||||||
|
}
|
||||||
SKIP_T; scheme = p; FIND_T;
|
SKIP_T; scheme = p; FIND_T;
|
||||||
SKIP_T; url = p; FIND_T;
|
SKIP_T; url = p; FIND_T;
|
||||||
|
|
||||||
@ -3340,6 +3516,7 @@ void urldb_load_cookies(const char *filename)
|
|||||||
|
|
||||||
c->name = strdup(name);
|
c->name = strdup(name);
|
||||||
c->value = strdup(value);
|
c->value = strdup(value);
|
||||||
|
c->value_was_quoted = value_quoted;
|
||||||
c->comment = strdup(comment);
|
c->comment = strdup(comment);
|
||||||
c->domain_from_set = domain_specified;
|
c->domain_from_set = domain_specified;
|
||||||
c->domain = strdup(domain);
|
c->domain = strdup(domain);
|
||||||
@ -3446,11 +3623,11 @@ void urldb_save_cookies(const char *filename)
|
|||||||
"#\n"
|
"#\n"
|
||||||
"# Version\tDomain\tDomain from Set-Cookie\tPath\t"
|
"# Version\tDomain\tDomain from Set-Cookie\tPath\t"
|
||||||
"Path from Set-Cookie\tSecure\tExpires\tLast used\t"
|
"Path from Set-Cookie\tSecure\tExpires\tLast used\t"
|
||||||
"No destroy\tName\tValue\tScheme\tURL\tComment\n",
|
"No destroy\tName\tValue\tValue was quoted\tScheme\t"
|
||||||
|
"URL\tComment\n",
|
||||||
COOKIE_FILE_VERSION);
|
COOKIE_FILE_VERSION);
|
||||||
fprintf(fp, "Version:\t%d\n", COOKIE_FILE_VERSION);
|
fprintf(fp, "Version:\t%d\n", COOKIE_FILE_VERSION);
|
||||||
|
|
||||||
|
|
||||||
urldb_save_cookie_hosts(fp, &db_root);
|
urldb_save_cookie_hosts(fp, &db_root);
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@ -3492,12 +3669,13 @@ void urldb_save_cookie_paths(FILE *fp, struct path_data *parent)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
fprintf(fp, "%d\t%s\t%d\t%s\t%d\t%d\t%d\t%d\t%d\t"
|
fprintf(fp, "%d\t%s\t%d\t%s\t%d\t%d\t%d\t%d\t%d\t"
|
||||||
"%s\t%s\t%s\t%s\t%s\n",
|
"%s\t%s\t%d\t%s\t%s\t%s\n",
|
||||||
c->version, c->domain,
|
c->version, c->domain,
|
||||||
c->domain_from_set, c->path,
|
c->domain_from_set, c->path,
|
||||||
c->path_from_set, c->secure,
|
c->path_from_set, c->secure,
|
||||||
(int)c->expires, (int)c->last_used,
|
(int)c->expires, (int)c->last_used,
|
||||||
c->no_destroy, c->name, c->value,
|
c->no_destroy, c->name, c->value,
|
||||||
|
c->value_was_quoted,
|
||||||
parent->scheme ? parent->scheme
|
parent->scheme ? parent->scheme
|
||||||
: "unused",
|
: "unused",
|
||||||
parent->url ? parent->url : "unused",
|
parent->url ? parent->url : "unused",
|
||||||
@ -3836,8 +4014,47 @@ int main(void)
|
|||||||
/* Test partial domain match with IP address failing */
|
/* Test partial domain match with IP address failing */
|
||||||
assert(urldb_set_cookie("name=value;Domain=.foo.org\r\n", "http://192.168.0.1/", NULL) == false);
|
assert(urldb_set_cookie("name=value;Domain=.foo.org\r\n", "http://192.168.0.1/", NULL) == false);
|
||||||
|
|
||||||
|
/* Test handling of non-domain cookie sent by server (domain part should
|
||||||
|
* be ignored) */
|
||||||
|
assert(urldb_set_cookie("foo=value;Domain=blah.com\r\n", "http://www.example.com/", NULL));
|
||||||
|
assert(strcmp(urldb_get_cookie("http://www.example.com/"), "foo=value") == 0);
|
||||||
|
|
||||||
|
/* Test handling of domain cookie from wrong host (strictly invalid but
|
||||||
|
* required to support the real world) */
|
||||||
|
assert(urldb_set_cookie("name=value;Domain=.example.com\r\n", "http://foo.bar.example.com/", NULL));
|
||||||
|
assert(strcmp(urldb_get_cookie("http://www.example.com/"), "foo=value; name=value") == 0);
|
||||||
|
|
||||||
|
/* Test presence of separators in cookie value */
|
||||||
|
assert(urldb_set_cookie("name=\"value=foo\\\\bar\\\\\\\";\\\\baz=quux\";Version=1\r\n", "http://www.example.org/", NULL));
|
||||||
|
assert(strcmp(urldb_get_cookie("http://www.example.org/"), "$Version=1; name=\"value=foo\\\\bar\\\\\\\";\\\\baz=quux\"") == 0);
|
||||||
|
|
||||||
|
/* Test cookie with blank value */
|
||||||
|
assert(urldb_set_cookie("a=\r\n", "http://www.example.net/", NULL));
|
||||||
|
assert(strcmp(urldb_get_cookie("http://www.example.net/"), "a=") == 0);
|
||||||
|
|
||||||
|
/* Test specification of multiple cookies in one header */
|
||||||
|
assert(urldb_set_cookie("a=b, foo=bar; Path=/\r\n", "http://www.example.net/", NULL));
|
||||||
|
assert(strcmp(urldb_get_cookie("http://www.example.net/"), "foo=bar; a=b") == 0);
|
||||||
|
|
||||||
|
/* Test use of separators in unquoted cookie value */
|
||||||
|
assert(urldb_set_cookie("foo=moo@foo:blah?moar\\ text\r\n", "http://example.com/", NULL));
|
||||||
|
assert(strcmp(urldb_get_cookie("http://example.com/"), "foo=moo@foo:blah?moar\\ text; name=value") == 0);
|
||||||
|
|
||||||
|
/* Test use of unnecessary quotes */
|
||||||
|
assert(urldb_set_cookie("foo=\"hello\";Version=1,bar=bat\r\n", "http://example.com/", NULL));
|
||||||
|
assert(strcmp(urldb_get_cookie("http://example.com/"), "bar=bat; foo=\"hello\"; name=value") == 0);
|
||||||
|
|
||||||
urldb_dump();
|
urldb_dump();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
gcc -g -o urldbtest -std=c99 -DTEST_URLDB -I.
|
||||||
|
`pkg-config --cflags --libs libxml-2.0 cairo librsvg-2.0 libcurl`
|
||||||
|
content/urldb.c utils/url.c utils/utils.c utils/messages.c
|
||||||
|
utils/hashtable.c utils/filename.c
|
||||||
|
*/
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ struct url_data {
|
|||||||
struct cookie_data {
|
struct cookie_data {
|
||||||
const char *name; /**< Cookie name */
|
const char *name; /**< Cookie name */
|
||||||
const char *value; /**< Cookie value */
|
const char *value; /**< Cookie value */
|
||||||
|
const char value_was_quoted; /**< Value was quoted in Set-Cookie: */
|
||||||
const char *comment; /**< Cookie comment */
|
const char *comment; /**< Cookie comment */
|
||||||
const bool domain_from_set; /**< Domain came from Set-Cookie: header */
|
const bool domain_from_set; /**< Domain came from Set-Cookie: header */
|
||||||
const char *domain; /**< Domain */
|
const char *domain; /**< Domain */
|
||||||
|
Loading…
Reference in New Issue
Block a user