From e7b58b878b287060ffb2ad19a3ff6369384a1d5e Mon Sep 17 00:00:00 2001 From: Hubert Tarasiuk Date: Fri, 26 Jun 2015 02:09:51 +0200 Subject: [PATCH 09/12] Move some Metalink-related code from http.c to metalink.c. * src/http.c: Move find_key_value, has_key, find_key_values. * src/metalink.c: To here. * src/metalink.h: Make them non-static and add prototypes here. --- src/http.c | 317 -------------------------------------------------------- src/metalink.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/metalink.h | 10 ++ 3 files changed, 332 insertions(+), 317 deletions(-) diff --git a/src/http.c b/src/http.c index e2e98f5..fe0aebd 100644 --- a/src/http.c +++ b/src/http.c @@ -2462,198 +2462,6 @@ set_content_type (int *dt, const char *type) } #ifdef HAVE_METALINK - -/* - Find value of given key. This is intended for Link header, but will - work with any header that uses ';' as field separator and '=' as key-value - separator. - - Link = "Link" ":" #link-value - link-value = "<" URI-Reference ">" *( ";" link-param ) - link-param = ( ( "rel" "=" relation-types ) - | ( "anchor" "=" <"> URI-Reference <"> ) - | ( "rev" "=" relation-types ) - | ( "hreflang" "=" Language-Tag ) - | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) ) - | ( "title" "=" quoted-string ) - | ( "title*" "=" ext-value ) - | ( "type" "=" ( media-type | quoted-mt ) ) - | ( link-extension ) ) - link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] ) - | ( ext-name-star "=" ext-value ) - ext-name-star = parmname "*" ; reserved for RFC2231-profiled - ; extensions. Whitespace NOT - ; allowed in between. - ptoken = 1*ptokenchar - ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "(" - | ")" | "*" | "+" | "-" | "." | "/" | DIGIT - | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA - | "[" | "]" | "^" | "_" | "`" | "{" | "|" - | "}" | "~" - media-type = type-name "/" subtype-name - quoted-mt = <"> media-type <"> - relation-types = relation-type - | <"> relation-type *( 1*SP relation-type ) <"> - relation-type = reg-rel-type | ext-rel-type - reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" ) - ext-rel-type = URI - - See more: rfc5988 -*/ -static bool -find_key_value (const char *start, const char *end, const char *key, char **value) -{ - const char *eq; - size_t key_len = strlen (key); - const char *val_beg, *val_end; - const char *key_beg; - - key_beg = start; - - while (key_beg + key_len + 1 < end) - { - /* Skip whitespaces. */ - while (key_beg + key_len + 1 < end && c_isspace (*key_beg)) - key_beg++; - if (strncmp (key_beg, key, key_len)) - { - /* Find next token. */ - while (key_beg + key_len + 1 < end && *key_beg != ';') - key_beg++; - key_beg++; - continue; - } - else - { - /* Find equals sign. */ - eq = key_beg + key_len; - while (eq < end && c_isspace (*eq)) - eq++; - if (eq == end) - return false; - if (*eq != '=') - { - key_beg++; - continue; - } - - val_beg = eq + 1; - while (val_beg < end && c_isspace (*val_beg)) - val_beg++; - if (val_beg == end) - return false; - val_end = val_beg + 1; - while (val_end < end && *val_end != ';' && !c_isspace (*val_end)) - val_end++; - *value = xstrndup (val_beg, val_end - val_beg); - return true; - } - } - *value = NULL; - return false; -} - -/* This is to check if given token exists in HTTP header. Tokens are - separated by ';'. */ -static bool -has_key (const char *start, const char *end, const char *key) -{ - const char *pos; /* Here would the token start. */ - size_t key_len = strlen (key); - - pos = start; - while (pos + key_len <= end) - { - /* Skip whitespaces at beginning. */ - while (pos + key_len <= end && c_isspace (*pos)) - pos++; - - /* Does the prefix of pos match our key? */ - if (strncmp (key, pos, key_len)) - { - /* This was not a match. - Skip all characters until beginning of next token. */ - while (pos + key_len <= end && *pos != ';') - pos++; - pos++; - continue; - } - - /* key is prefix of pos. Is it the exact token or just a prefix? */ - pos += key_len; - while (pos < end && c_isspace (*pos)) - pos++; - if (pos == end || *pos == ';') - return true; - - /* This was not a match (just a prefix). - Skip all characters until beginning of next token. */ - while (pos + key_len <= end && *pos != ';') - pos++; - pos++; - } - return false; -} - -/* Find all key=value pairs delimited with ';' or ','. This is intended for - Digest header parsing. - The usage is: - - const char *pos; - for (pos = header_beg; pos = find_key_values (pos, header_end, &key, &val); pos++) - { - ... - } - - */ -static const char * -find_key_values (const char *start, const char *end, char **key, char **value) -{ - const char *key_start, *key_end; - const char *eq; - const char *val_start, *val_end; - - eq = start; - while (eq < end && *eq != '=') - { - /* Skip tokens without =value part. */ - if (*eq == ';' || *eq == ',') - start = eq + 1; - eq++; - } - - if (eq >= end) - return NULL; - - key_start = start; - while (key_start < eq && c_isspace (*key_start)) - key_start++; - - key_end = eq - 1; - while (key_end > key_start && c_isspace (*key_end)) - key_end--; - key_end++; - - val_start = eq + 1; - while (val_start < end && c_isspace (*val_start)) - val_start++; - - val_end = val_start; - - while (val_end < end && *val_end != ';' && - *val_end != ',' && !c_isspace (*val_end)) - val_end++; - - *key = xstrndup (key_start, key_end - key_start); - *value = xstrndup (val_start, val_end - val_start); - - /* Skip trailing whitespaces. */ - while (val_end < end && c_isspace (*val_end)) - val_end++; - - return val_end; -} - /* Will return proper metalink_t structure if enough data was found in http response resp. Otherwise returns NULL. Two exit points: one for success and one for failure. */ @@ -4999,131 +4807,6 @@ ensure_extension (struct http_stat *hs, const char *ext, int *dt) } #ifdef TESTING - -const char * -test_find_key_values (void) -{ - static const char *header_data = "key1=val1;key2=val2 ;key3=val3; key4=val4"\ - " ; key5=val5;key6 =val6;key7= val7; "\ - "key8 = val8 ; key9 = val9 "\ - " ,key10= val10,key11,key12=val12"; - static const struct - { - const char *key; - const char *val; - } test_array[] = - { - { "key1", "val1" }, - { "key2", "val2" }, - { "key3", "val3" }, - { "key4", "val4" }, - { "key5", "val5" }, - { "key6", "val6" }, - { "key7", "val7" }, - { "key8", "val8" }, - { "key9", "val9" }, - { "key10", "val10" }, - { "key12", "val12" } - }; - const char *pos; - char *key, *value; - size_t i = 0; - - for (pos = header_data; (pos = find_key_values (pos, - header_data + strlen (header_data), - &key, &value)); pos++) - { - mu_assert ("test_find_key_values: wrong result", - !strcmp (test_array[i].val, value) && - !strcmp (test_array[i].key, key)); - xfree (key); - xfree (value); - i++; - } - - return NULL; -} - -const char * -test_find_key_value (void) -{ - static const char *header_data = "key1=val1;key2=val2 ;key3=val3; key4=val4"\ - " ; key5=val5;key6 =val6;key7= val7; "\ - "key8 = val8 ; key9 = val9 "; - static const struct - { - const char *key; - const char *val; - bool result; - } test_array[] = - { - { "key1", "val1", true }, - { "key2", "val2", true }, - { "key3", "val3", true }, - { "key4", "val4", true }, - { "key5", "val5", true }, - { "key6", "val6", true }, - { "key7", "val7", true }, - { "key8", "val8", true }, - { "key9", "val9", true }, - { "key10", NULL, false }, - { "ey1", NULL, false }, - { "dey1", NULL, false } - }; - size_t i; - - for (i=0; i < countof (test_array); ++i) - { - bool result; - char *value; - - result = find_key_value (header_data, - header_data + strlen(header_data), - test_array[i].key, &value); - - mu_assert ("test_find_key_value: wrong result", - result == test_array[i].result && - ((!test_array[i].result && !value) || - !strcmp (value, test_array[i].val))); - - xfree (value); - } - - return NULL; -} - -const char * -test_has_key (void) -{ - static const char *header_data = "key1=val2;token1;xyz; token2;xyz;token3 ;"\ - "xyz; token4 ;xyz; token5 "; - struct - { - const char *token; - bool result; - } test_array[] = - { - { "key1=val2", true }, - { "token1", true }, - { "token2", true }, - { "token3", true }, - { "token4", true }, - { "token5", true }, - { "token6", false }, - { "oken1", false }, - { "poken1", false }, - { "key1=val2", true } - }; - size_t i; - - for (i = 0; i < countof (test_array); ++i) - mu_assert ("test_has_key: wrong result", - has_key (header_data, header_data + strlen (header_data), - test_array[i].token) == test_array[i].result); - - return NULL; -} - const char * test_parse_content_disposition(void) { diff --git a/src/metalink.c b/src/metalink.c index 962dd94..f2c4175 100644 --- a/src/metalink.c +++ b/src/metalink.c @@ -35,6 +35,7 @@ as that of the covered work. */ #include "exits.h" #include "utils.h" #include "sha256.h" +#include "xstrndup.h" #include #include /* For unlink. */ #include @@ -43,6 +44,10 @@ as that of the covered work. */ #include /* For open and close. */ #endif +#ifdef TESTING +#include "test.h" +#endif + /* Loop through all files in metalink structure and retrieve them. Returns RETROK if all files were downloaded. Returns last retrieval error (from retrieve_url) if some files @@ -445,4 +450,321 @@ int metalink_res_cmp (const void* v1, const void* v2) return 0; } +/* + Find value of given key. This is intended for Link header, but will + work with any header that uses ';' as field separator and '=' as key-value + separator. + + Link = "Link" ":" #link-value + link-value = "<" URI-Reference ">" *( ";" link-param ) + link-param = ( ( "rel" "=" relation-types ) + | ( "anchor" "=" <"> URI-Reference <"> ) + | ( "rev" "=" relation-types ) + | ( "hreflang" "=" Language-Tag ) + | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) ) + | ( "title" "=" quoted-string ) + | ( "title*" "=" ext-value ) + | ( "type" "=" ( media-type | quoted-mt ) ) + | ( link-extension ) ) + link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] ) + | ( ext-name-star "=" ext-value ) + ext-name-star = parmname "*" ; reserved for RFC2231-profiled + ; extensions. Whitespace NOT + ; allowed in between. + ptoken = 1*ptokenchar + ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "(" + | ")" | "*" | "+" | "-" | "." | "/" | DIGIT + | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA + | "[" | "]" | "^" | "_" | "`" | "{" | "|" + | "}" | "~" + media-type = type-name "/" subtype-name + quoted-mt = <"> media-type <"> + relation-types = relation-type + | <"> relation-type *( 1*SP relation-type ) <"> + relation-type = reg-rel-type | ext-rel-type + reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" ) + ext-rel-type = URI + + See more: rfc5988 +*/ +bool +find_key_value (const char *start, const char *end, const char *key, char **value) +{ + const char *eq; + size_t key_len = strlen (key); + const char *val_beg, *val_end; + const char *key_beg; + + key_beg = start; + + while (key_beg + key_len + 1 < end) + { + /* Skip whitespaces. */ + while (key_beg + key_len + 1 < end && c_isspace (*key_beg)) + key_beg++; + if (strncmp (key_beg, key, key_len)) + { + /* Find next token. */ + while (key_beg + key_len + 1 < end && *key_beg != ';') + key_beg++; + key_beg++; + continue; + } + else + { + /* Find equals sign. */ + eq = key_beg + key_len; + while (eq < end && c_isspace (*eq)) + eq++; + if (eq == end) + return false; + if (*eq != '=') + { + key_beg++; + continue; + } + + val_beg = eq + 1; + while (val_beg < end && c_isspace (*val_beg)) + val_beg++; + if (val_beg == end) + return false; + val_end = val_beg + 1; + while (val_end < end && *val_end != ';' && !c_isspace (*val_end)) + val_end++; + *value = xstrndup (val_beg, val_end - val_beg); + return true; + } + } + *value = NULL; + return false; +} + +/* This is to check if given token exists in HTTP header. Tokens are + separated by ';'. */ +bool +has_key (const char *start, const char *end, const char *key) +{ + const char *pos; /* Here would the token start. */ + size_t key_len = strlen (key); + + pos = start; + while (pos + key_len <= end) + { + /* Skip whitespaces at beginning. */ + while (pos + key_len <= end && c_isspace (*pos)) + pos++; + + /* Does the prefix of pos match our key? */ + if (strncmp (key, pos, key_len)) + { + /* This was not a match. + Skip all characters until beginning of next token. */ + while (pos + key_len <= end && *pos != ';') + pos++; + pos++; + continue; + } + + /* key is prefix of pos. Is it the exact token or just a prefix? */ + pos += key_len; + while (pos < end && c_isspace (*pos)) + pos++; + if (pos == end || *pos == ';') + return true; + + /* This was not a match (just a prefix). + Skip all characters until beginning of next token. */ + while (pos + key_len <= end && *pos != ';') + pos++; + pos++; + } + return false; +} + +/* Find all key=value pairs delimited with ';' or ','. This is intended for + Digest header parsing. + The usage is: + + const char *pos; + for (pos = header_beg; pos = find_key_values (pos, header_end, &key, &val); pos++) + { + ... + } + + */ +const char * +find_key_values (const char *start, const char *end, char **key, char **value) +{ + const char *key_start, *key_end; + const char *eq; + const char *val_start, *val_end; + + eq = start; + while (eq < end && *eq != '=') + { + /* Skip tokens without =value part. */ + if (*eq == ';' || *eq == ',') + start = eq + 1; + eq++; + } + + if (eq >= end) + return NULL; + + key_start = start; + while (key_start < eq && c_isspace (*key_start)) + key_start++; + + key_end = eq - 1; + while (key_end > key_start && c_isspace (*key_end)) + key_end--; + key_end++; + + val_start = eq + 1; + while (val_start < end && c_isspace (*val_start)) + val_start++; + + val_end = val_start; + + while (val_end < end && *val_end != ';' && + *val_end != ',' && !c_isspace (*val_end)) + val_end++; + + *key = xstrndup (key_start, key_end - key_start); + *value = xstrndup (val_start, val_end - val_start); + + /* Skip trailing whitespaces. */ + while (val_end < end && c_isspace (*val_end)) + val_end++; + + return val_end; +} + +#ifdef TESTING +const char * +test_find_key_values (void) +{ + static const char *header_data = "key1=val1;key2=val2 ;key3=val3; key4=val4"\ + " ; key5=val5;key6 =val6;key7= val7; "\ + "key8 = val8 ; key9 = val9 "\ + " ,key10= val10,key11,key12=val12"; + static const struct + { + const char *key; + const char *val; + } test_array[] = + { + { "key1", "val1" }, + { "key2", "val2" }, + { "key3", "val3" }, + { "key4", "val4" }, + { "key5", "val5" }, + { "key6", "val6" }, + { "key7", "val7" }, + { "key8", "val8" }, + { "key9", "val9" }, + { "key10", "val10" }, + { "key12", "val12" } + }; + const char *pos; + char *key, *value; + size_t i = 0; + + for (pos = header_data; (pos = find_key_values (pos, + header_data + strlen (header_data), + &key, &value)); pos++) + { + mu_assert ("test_find_key_values: wrong result", + !strcmp (test_array[i].val, value) && + !strcmp (test_array[i].key, key)); + xfree (key); + xfree (value); + i++; + } + + return NULL; +} + +const char * +test_find_key_value (void) +{ + static const char *header_data = "key1=val1;key2=val2 ;key3=val3; key4=val4"\ + " ; key5=val5;key6 =val6;key7= val7; "\ + "key8 = val8 ; key9 = val9 "; + static const struct + { + const char *key; + const char *val; + bool result; + } test_array[] = + { + { "key1", "val1", true }, + { "key2", "val2", true }, + { "key3", "val3", true }, + { "key4", "val4", true }, + { "key5", "val5", true }, + { "key6", "val6", true }, + { "key7", "val7", true }, + { "key8", "val8", true }, + { "key9", "val9", true }, + { "key10", NULL, false }, + { "ey1", NULL, false }, + { "dey1", NULL, false } + }; + size_t i; + + for (i=0; i < countof (test_array); ++i) + { + bool result; + char *value; + + result = find_key_value (header_data, + header_data + strlen(header_data), + test_array[i].key, &value); + + mu_assert ("test_find_key_value: wrong result", + result == test_array[i].result && + ((!test_array[i].result && !value) || + !strcmp (value, test_array[i].val))); + + xfree (value); + } + + return NULL; +} + +const char * +test_has_key (void) +{ + static const char *header_data = "key1=val2;token1;xyz; token2;xyz;token3 ;"\ + "xyz; token4 ;xyz; token5 "; + struct + { + const char *token; + bool result; + } test_array[] = + { + { "key1=val2", true }, + { "token1", true }, + { "token2", true }, + { "token3", true }, + { "token4", true }, + { "token5", true }, + { "token6", false }, + { "oken1", false }, + { "poken1", false }, + { "key1=val2", true } + }; + size_t i; + + for (i = 0; i < countof (test_array); ++i) + mu_assert ("test_has_key: wrong result", + has_key (header_data, header_data + strlen (header_data), + test_array[i].token) == test_array[i].result); + + return NULL; +} +#endif + #endif /* HAVE_METALINK */ diff --git a/src/metalink.h b/src/metalink.h index 202c545..ec91b07 100644 --- a/src/metalink.h +++ b/src/metalink.h @@ -47,4 +47,14 @@ uerr_t retrieve_from_metalink (const metalink_t *metalink); int metalink_res_cmp (const void *res1, const void *res2); +bool find_key_value (const char *start, + const char *end, + const char *key, + char **value); +bool has_key (const char *start, const char *end, const char *key); +const char *find_key_values (const char *start, + const char *end, + char **key, + char **value); + #endif /* METALINK_H */ -- 2.4.3