diff options
author | Joe Orton <joe@manyfish.uk> | 2020-06-17 18:38:30 +0100 |
---|---|---|
committer | Joe Orton <jorton@apache.org> | 2020-06-18 08:01:27 +0100 |
commit | b243cffee00d442204e622fe5df1623214e098d0 (patch) | |
tree | e5cc5084840cb7b639b08f356498455bca571229 | |
parent | d47a40a52dfd601a9eb967ac958776f032abe7e7 (diff) | |
download | neon-git-b243cffee00d442204e622fe5df1623214e098d0.tar.gz |
Define new ne_strparam() interface for encoding extended parameters
per RFC 5987.
* src/ne_string.c (ne_strparam): New function.
* test/string-tests.c (strparam): New test.
-rw-r--r-- | src/ne_string.c | 69 | ||||
-rw-r--r-- | src/ne_string.h | 9 | ||||
-rw-r--r-- | src/neon.vers | 1 | ||||
-rw-r--r-- | test/string-tests.c | 32 |
4 files changed, 111 insertions, 0 deletions
diff --git a/src/ne_string.c b/src/ne_string.c index 613c9cd..187fa58 100644 --- a/src/ne_string.c +++ b/src/ne_string.c @@ -653,3 +653,72 @@ char *ne_vstrhash(unsigned int flags, va_list ap) return ne_strdup(ret); } #endif + +/* Determines whether a character is valid in a regular parameter (NQ) + * not (QT). Per https://tools.ietf.org/html/rfc5987#section-3.2.1 + * every character in attr-char is NQ, everything else is QT. */ +#define QT 3 +#define NQ 1 +static const unsigned char ext_notation[256] = { +/* 0xXX x0 x2 x4 x6 x8 xA xC xE */ +/* 0x */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* 1x */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* 2x */ QT, NQ, QT, NQ, NQ, QT, NQ, QT, QT, QT, QT, NQ, QT, NQ, NQ, QT, +/* 3x */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* 4x */ QT, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, +/* 5x */ NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, QT, QT, QT, NQ, NQ, +/* 6x */ NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, +/* 7x */ NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, NQ, QT, NQ, QT, NQ, QT, +/* 8x */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* 9x */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* Ax */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* Bx */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* Cx */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* Dx */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* Ex */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, +/* Fx */ QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT, QT +}; +#undef QT +#undef NQ + +char *ne_strparam(const char *charset, const char *lang, + const unsigned char *value) +{ + const unsigned char *p; + size_t count = 0; + char *rv, *rp; + + /* Determine length required for the value. */ + for (p = value; *p; p++) + count += ext_notation[*p]; + + /* If length == input length, no encoding is required, return + * NULL. */ + if (count == strlen((const char *)value)) return NULL; + + /* +3 accounts for '' and trailing NUL */ + rv = ne_malloc(strlen(charset) + (lang ? strlen(lang) : 0) + count + 3); + memcpy(rv, charset, strlen(charset)); + rp = rv + strlen(charset); + *rp++ = '\''; + if (lang) { + memcpy(rp, lang, strlen(lang)); + rp += strlen(lang); + } + *rp++ = '\''; + + for (p = value; *p; p++) { + if (ext_notation[*p] == 1) { + *rp++ = *p; + } + else { + *rp++ = '%'; + *rp++ = NE_HEX2ASC(*p >> 4); + *rp++ = NE_HEX2ASC(*p & 0x0f); + } + } + + *rp = '\0'; + + return rv; +} diff --git a/src/ne_string.h b/src/ne_string.h index a818005..34edd06 100644 --- a/src/ne_string.h +++ b/src/ne_string.h @@ -199,6 +199,15 @@ const unsigned char *ne_tolower_array(void) ne_attribute((const)); * ASCII hexadecimal equivalent character, in the range '0..9,'a..f' */ #define NE_HEX2ASC(x) ((char) ((x) > 9 ? ((x) - 10 + 'a') : ((x) + '0'))) +/* Encodes a extended parameter value for HTTP headers, as defined in + * RFC 5987. Returns a malloc-allocated string if the parameter + * 'value' needs to be encoded as an extended parameter, or NULL if it + * can be used as a regular parameter. The charset of the string must + * be non-NULL, but the language value can be NULL. */ +char *ne_strparam(const char *charset, const char *lang, + const unsigned char *value) + ne_attribute((nonnull (1, 3))); + NE_END_DECLS #endif /* NE_STRING_H */ diff --git a/src/neon.vers b/src/neon.vers index de4b327..b969360 100644 --- a/src/neon.vers +++ b/src/neon.vers @@ -29,4 +29,5 @@ NEON_0_31 { NEON_0_32 { ne_strhash; ne_vstrhash; + ne_strparam; }; diff --git a/test/string-tests.c b/test/string-tests.c index 03911c4..0be11dd 100644 --- a/test/string-tests.c +++ b/test/string-tests.c @@ -685,6 +685,37 @@ static int strhash(void) return OK; } +static int strparam(void) +{ + static const struct { + const char *charset, *lang; + const char *value; + const char *expect; + } ts[] = { + { "UTF-8", NULL, "foobar", NULL }, + { "UTF-8", NULL, "foo@bar", "UTF-8''foo%40bar" }, + { "UTF-8", NULL, "foo bar", "UTF-8''foo%20bar" }, + { "iso-8859-1", "en", "\xA3 rates", "iso-8859-1'en'%a3%20rates" }, + { "UTF-8", NULL, "£ and € rates", "UTF-8''%c2%a3%20and%20%e2%82%ac%20rates" }, + { NULL } + }; + unsigned n; + + for (n = 0; ts[n].charset; n++) { + char *act = ne_strparam(ts[n].charset, ts[n].lang, (const unsigned char *)ts[n].value); + + if (ts[n].expect == NULL) { + ONV(act != NULL, ("expected NULL output for '%s'", ts[n].value)); + } + else { + ONCMP(act, ts[n].expect); + } + } + + return OK; +} + + ne_test tests[] = { T(simple), T(buf_concat), @@ -714,6 +745,7 @@ ne_test tests[] = { T(buf_print), T(qappend), T(strhash), + T(strparam), T(NULL) }; |