summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Orton <joe@manyfish.uk>2020-06-17 18:38:30 +0100
committerJoe Orton <jorton@apache.org>2020-06-18 08:01:27 +0100
commitb243cffee00d442204e622fe5df1623214e098d0 (patch)
treee5cc5084840cb7b639b08f356498455bca571229
parentd47a40a52dfd601a9eb967ac958776f032abe7e7 (diff)
downloadneon-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.c69
-rw-r--r--src/ne_string.h9
-rw-r--r--src/neon.vers1
-rw-r--r--test/string-tests.c32
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)
};