1
0
mirror of https://github.com/moparisthebest/wget synced 2024-07-03 16:38:41 -04:00

[svn] Another version of parse_set_cookies, along with a test suite.

This commit is contained in:
hniksic 2003-09-15 08:35:47 -07:00
parent 79157a03fd
commit 016867ca33
2 changed files with 174 additions and 90 deletions

View File

@ -1,3 +1,10 @@
2003-09-15 Hrvoje Niksic <hniksic@xemacs.org>
* cookies.c (parse_set_cookies): Fixed the parser to handle more
edge conditions.
(test_cookies): New function, contains a test suite for
parse_set_cookies.
2003-09-15 Hrvoje Niksic <hniksic@xemacs.org> 2003-09-15 Hrvoje Niksic <hniksic@xemacs.org>
* url.c (strpbrk_or_eos): Implement as a macro under Gcc. * url.c (strpbrk_or_eos): Implement as a macro under Gcc.

View File

@ -422,21 +422,6 @@ update_cookie_field (struct cookie *cookie,
&& (c) != '"' && (c) != '=' \ && (c) != '"' && (c) != '=' \
&& (c) != ';' && (c) != ',') && (c) != ';' && (c) != ',')
/* Fetch the next character without doing anything special if CH gets
set to 0. (The code executed next is expected to handle it.) */
#define FETCH1(ch, ptr) do { \
ch = *ptr++; \
} while (0)
/* Like FETCH1, but jumps to `eof' label if CH gets set to 0. */
#define FETCH(ch, ptr) do { \
FETCH1 (ch, ptr); \
if (!ch) \
goto eof; \
} while (0)
/* Parse the contents of the `Set-Cookie' header. The header looks /* Parse the contents of the `Set-Cookie' header. The header looks
like this: like this:
@ -446,19 +431,25 @@ update_cookie_field (struct cookie *cookie,
tokens. Additionally, values may be quoted. tokens. Additionally, values may be quoted.
A new cookie is returned upon success, NULL otherwise. The A new cookie is returned upon success, NULL otherwise. The
function `update_cookie_field' is used to update the fields of the specified CALLBACK function (normally `update_cookie_field' is used
newly created cookie structure. */ to update the fields of the newly created cookie structure. */
static struct cookie * static struct cookie *
parse_set_cookies (const char *sc) parse_set_cookies (const char *sc,
int (*callback) (struct cookie *,
const char *, const char *,
const char *, const char *),
int silent)
{ {
struct cookie *cookie = cookie_new (); struct cookie *cookie = cookie_new ();
enum { S_NAME_PRE, S_NAME, S_NAME_POST, /* #### Hand-written DFAs are no fun to debug. We'de be better off
S_VALUE_PRE, S_VALUE, S_VALUE_TRAILSPACE_MAYBE, to rewrite this as an inline parser. */
S_QUOTED_VALUE, S_QUOTED_VALUE_POST,
S_ATTR_ACTION, enum { S_START, S_NAME, S_NAME_POST,
S_DONE, S_ERROR } state = S_NAME_PRE; S_VALUE_PRE, S_VALUE, S_QUOTED_VALUE, S_VALUE_TRAILSPACE,
S_ATTR_ACTION, S_DONE, S_ERROR
} state = S_START;
const char *p = sc; const char *p = sc;
char c; char c;
@ -466,21 +457,21 @@ parse_set_cookies (const char *sc)
const char *name_b = NULL, *name_e = NULL; const char *name_b = NULL, *name_e = NULL;
const char *value_b = NULL, *value_e = NULL; const char *value_b = NULL, *value_e = NULL;
c = *p;
while (state != S_DONE && state != S_ERROR) while (state != S_DONE && state != S_ERROR)
{ {
switch (state) switch (state)
{ {
case S_NAME_PRE: case S_START:
/* Strip whitespace preceding the name. */
do
FETCH1 (c, p);
while (c && ISSPACE (c));
if (!c) if (!c)
state = S_DONE; state = S_DONE;
else if (ISSPACE (c))
/* Strip all whitespace preceding the name. */
c = *++p;
else if (ATTR_NAME_CHAR (c)) else if (ATTR_NAME_CHAR (c))
{ {
name_b = p - 1; name_b = p;
FETCH1 (c, p);
state = S_NAME; state = S_NAME;
} }
else else
@ -488,110 +479,111 @@ parse_set_cookies (const char *sc)
state = S_ERROR; state = S_ERROR;
break; break;
case S_NAME: case S_NAME:
if (ATTR_NAME_CHAR (c)) if (!c || c == ';' || c == '=' || ISSPACE (c))
FETCH1 (c, p);
else if (!c || c == ';' || c == '=' || ISSPACE (c))
{ {
name_e = p - 1; name_e = p;
state = S_NAME_POST; state = S_NAME_POST;
} }
else if (ATTR_NAME_CHAR (c))
c = *++p;
else else
state = S_ERROR; state = S_ERROR;
break; break;
case S_NAME_POST: case S_NAME_POST:
if (ISSPACE (c)) if (!c || c == ';')
FETCH1 (c, p);
else if (!c || c == ';')
{ {
value_b = value_e = NULL; value_b = value_e = NULL;
if (c == ';')
c = *++p;
state = S_ATTR_ACTION; state = S_ATTR_ACTION;
} }
else if (c == '=') else if (c == '=')
{ {
FETCH1 (c, p); c = *++p;
state = S_VALUE_PRE; state = S_VALUE_PRE;
} }
else if (ISSPACE (c))
/* Ignore space and keep the state. */
c = *++p;
else else
state = S_ERROR; state = S_ERROR;
break; break;
case S_VALUE_PRE: case S_VALUE_PRE:
if (ISSPACE (c)) if (!c || c == ';')
FETCH1 (c, p);
else if (c == '"')
{ {
value_b = p; value_b = value_e = p;
FETCH (c, p); if (c == ';')
state = S_QUOTED_VALUE; c = *++p;
}
else if (c == ';' || c == '\0')
{
value_b = value_e = p - 1;
state = S_ATTR_ACTION; state = S_ATTR_ACTION;
} }
else if (c == '"')
{
c = *++p;
value_b = p;
state = S_QUOTED_VALUE;
}
else if (ISSPACE (c))
c = *++p;
else else
{ {
value_b = p - 1; value_b = p;
value_e = NULL; value_e = NULL;
state = S_VALUE; state = S_VALUE;
} }
break; break;
case S_VALUE: case S_VALUE:
if (c == ';' || c == '\0') if (!c || c == ';' || ISSPACE (c))
{ {
if (!value_e) value_e = p;
value_e = p - 1; state = S_VALUE_TRAILSPACE;
state = S_ATTR_ACTION;
}
else if (ISSPACE (c))
{
value_e = p - 1;
FETCH1 (c, p);
state = S_VALUE_TRAILSPACE_MAYBE;
} }
else else
{ {
value_e = NULL; /* no trailing space */ value_e = NULL; /* no trailing space */
FETCH1 (c, p); c = *++p;
} }
break; break;
case S_VALUE_TRAILSPACE_MAYBE:
if (ISSPACE (c))
FETCH1 (c, p);
else
state = S_VALUE;
break;
case S_QUOTED_VALUE: case S_QUOTED_VALUE:
if (c == '"') if (c == '"')
{ {
value_e = p - 1; value_e = p;
FETCH1 (c, p); c = *++p;
state = S_QUOTED_VALUE_POST; state = S_VALUE_TRAILSPACE;
} }
else if (!c)
state = S_ERROR;
else else
FETCH (c, p); c = *++p;
break; break;
case S_QUOTED_VALUE_POST: case S_VALUE_TRAILSPACE:
if (c == ';' || !c) if (c == ';')
{
c = *++p;
state = S_ATTR_ACTION;
}
else if (!c)
state = S_ATTR_ACTION; state = S_ATTR_ACTION;
else if (ISSPACE (c)) else if (ISSPACE (c))
FETCH1 (c, p); c = *++p;
else else
state = S_ERROR; state = S_VALUE;
break; break;
case S_ATTR_ACTION: case S_ATTR_ACTION:
{ {
int legal = update_cookie_field (cookie, name_b, name_e, int legal = callback (cookie, name_b, name_e, value_b, value_e);
value_b, value_e);
if (!legal) if (!legal)
{ {
char *name; if (!silent)
BOUNDED_TO_ALLOCA (name_b, name_e, name); {
logprintf (LOG_NOTQUIET, char *name;
_("Error in Set-Cookie, field `%s'"), name); BOUNDED_TO_ALLOCA (name_b, name_e, name);
logprintf (LOG_NOTQUIET,
_("Error in Set-Cookie, field `%s'"), name);
}
state = S_ERROR; state = S_ERROR;
break; break;
} }
state = S_NAME_PRE; state = S_START;
} }
break; break;
case S_DONE: case S_DONE:
@ -604,16 +596,13 @@ parse_set_cookies (const char *sc)
return cookie; return cookie;
delete_cookie (cookie); delete_cookie (cookie);
if (state == S_ERROR) if (state != S_ERROR)
logprintf (LOG_NOTQUIET, _("Syntax error in Set-Cookie at character `%c'.\n"), c);
else
abort (); abort ();
return NULL;
eof: if (!silent)
delete_cookie (cookie); logprintf (LOG_NOTQUIET,
logprintf (LOG_NOTQUIET, _("Syntax error in Set-Cookie: %s at position %d.\n"),
_("Syntax error in Set-Cookie: premature end of string.\n")); sc, p - sc);
return NULL; return NULL;
} }
@ -811,7 +800,7 @@ cookie_jar_process_set_cookie (struct cookie_jar *jar,
struct cookie *cookie; struct cookie *cookie;
cookies_now = time (NULL); cookies_now = time (NULL);
cookie = parse_set_cookies (set_cookie); cookie = parse_set_cookies (set_cookie, update_cookie_field, 0);
if (!cookie) if (!cookie)
goto out; goto out;
@ -1452,3 +1441,91 @@ cookie_jar_delete (struct cookie_jar *jar)
hash_table_destroy (jar->chains_by_domain); hash_table_destroy (jar->chains_by_domain);
xfree (jar); xfree (jar);
} }
/* 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
int test_count;
char *test_results[10];
static int test_parse_cookies_callback (struct cookie *ignored,
const char *nb, const char *ne,
const char *vb, const char *ve)
{
test_results[test_count++] = strdupdelim (nb, ne);
test_results[test_count++] = strdupdelim (vb, ve);
return 1;
}
void
test_cookies (void)
{
/* Tests expected to succeed: */
static struct {
char *data;
char *results[10];
} tests_succ[] = {
{ "", {NULL} },
{ "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;
for (i = 0; i < ARRAY_SIZE (tests_succ); i++)
{
int ind;
char *data = tests_succ[i].data;
char **expected = tests_succ[i].results;
struct cookie *c;
test_count = 0;
c = parse_set_cookies (data, test_parse_cookies_callback, 1);
if (!c)
{
printf ("NULL cookie returned for valid data: %s\n", data);
continue;
}
for (ind = 0; ind < test_count; ind += 2)
{
if (!expected[ind])
break;
if (0 != strcmp (expected[ind], test_results[ind]))
printf ("Invalid name %d for '%s' (expected '%s', got '%s')\n",
ind / 2 + 1, data, expected[ind], test_results[ind]);
if (0 != strcmp (expected[ind + 1], test_results[ind + 1]))
printf ("Invalid value %d for '%s' (expected '%s', got '%s')\n",
ind / 2 + 1, data, expected[ind + 1], test_results[ind + 1]);
}
if (ind < test_count || expected[ind])
printf ("Unmatched number of results: %s\n", data);
}
for (i = 0; i < ARRAY_SIZE (tests_fail); i++)
{
struct cookie *c;
char *data = tests_fail[i];
test_count = 0;
c = parse_set_cookies (data, test_parse_cookies_callback, 1);
if (c)
printf ("Failed to report error on invalid data: %s\n", data);
}
}
#endif /* TEST_COOKIES */