[libsoup/strict-param-parsing] soup-headers: add strict parameter parsing functions
- From: Claudio Saavedra <csaavedra src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/strict-param-parsing] soup-headers: add strict parameter parsing functions
- Date: Mon, 24 Sep 2018 15:39:15 +0000 (UTC)
commit 5854f97d9dc01bd681751186fbd8537760138087
Author: Claudio Saavedra <csaavedra igalia com>
Date: Mon Sep 24 17:10:24 2018 +0300
soup-headers: add strict parameter parsing functions
These functions extend the existing parameter-parsing functions
but differ in that they return NULL if there are any duplicated
parameters. It is noted in their documentation that they
are not recommended to parse header fields that might contain
RFC5987-encoded parameters.
Add tests for the parameter parsing methods that cover the
different cases, including RFC5789-encoded parameters.
docs/reference/libsoup-2.4-sections.txt | 2 +
libsoup/soup-headers.c | 71 +++++++++++++++++++++++++++---
libsoup/soup-headers.h | 4 ++
libsoup/soup-version.h.in | 15 +++++++
tests/header-parsing-test.c | 76 +++++++++++++++++++++++++++++++++
5 files changed, 163 insertions(+), 5 deletions(-)
---
diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt
index a9b21384..354c0783 100644
--- a/docs/reference/libsoup-2.4-sections.txt
+++ b/docs/reference/libsoup-2.4-sections.txt
@@ -785,6 +785,8 @@ soup_header_free_list
soup_header_contains
soup_header_parse_param_list
soup_header_parse_semi_param_list
+soup_header_parse_param_list_strict
+soup_header_parse_semi_param_list_strict
soup_header_free_param_list
soup_header_g_string_append_param
soup_header_g_string_append_param_quoted
diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c
index 271d2a63..18ded840 100644
--- a/libsoup/soup-headers.c
+++ b/libsoup/soup-headers.c
@@ -710,12 +710,12 @@ decode_rfc5987 (char *encoded_string)
}
static GHashTable *
-parse_param_list (const char *header, char delim)
+parse_param_list (const char *header, char delim, gboolean strict)
{
GHashTable *params;
GSList *list, *iter;
char *item, *eq, *name_end, *value;
- gboolean override;
+ gboolean override, duplicated;
params = g_hash_table_new_full (soup_str_case_hash,
soup_str_case_equal,
@@ -751,7 +751,14 @@ parse_param_list (const char *header, char delim)
} else
value = NULL;
- if (override || !g_hash_table_lookup (params, item))
+ duplicated = g_hash_table_lookup_extended (params, item, NULL, NULL);
+
+ if (strict && duplicated) {
+ soup_header_free_param_list (params);
+ params = NULL;
+ g_free (item);
+ break;
+ } else if (override || !duplicated)
g_hash_table_replace (params, item, value);
else
g_free (item);
@@ -784,7 +791,7 @@ soup_header_parse_param_list (const char *header)
{
g_return_val_if_fail (header != NULL, NULL);
- return parse_param_list (header, ',');
+ return parse_param_list (header, ',', FALSE);
}
/**
@@ -812,7 +819,61 @@ soup_header_parse_semi_param_list (const char *header)
{
g_return_val_if_fail (header != NULL, NULL);
- return parse_param_list (header, ';');
+ return parse_param_list (header, ';', FALSE);
+}
+
+/**
+ * soup_header_parse_param_list_strict:
+ * @header: a header value
+ *
+ * A strict version of soup_header_parse_param_list()
+ * that bails out if there are duplicate parameters.
+ * Note that this function will treat RFC5987-encoded
+ * parameters as duplicated if an ASCII version is also
+ * present. For header fields that might contain
+ * RFC5987-encoded parameters, use
+ * soup_header_parse_param_list() instead.
+ *
+ * Return value: (element-type utf8 utf8) (transfer full) (nullable):
+ * a #GHashTable of list elements, which can be freed with
+ * soup_header_free_param_list() or %NULL if there are duplicate
+ * elements.
+ *
+ * Since: 2.66
+ **/
+GHashTable *
+soup_header_parse_param_list_strict (const char *header)
+{
+ g_return_val_if_fail (header != NULL, NULL);
+
+ return parse_param_list (header, ',', TRUE);
+}
+
+/**
+ * soup_header_parse_semi_param_list_strict:
+ * @header: a header value
+ *
+ * A strict version of soup_header_parse_semi_param_list()
+ * that bails out if there are duplicate parameters.
+ * Note that this function will treat RFC5987-encoded
+ * parameters as duplicated if an ASCII version is also
+ * present. For header fields that might contain
+ * RFC5987-encoded parameters, use
+ * soup_header_parse_semi_param_list() instead.
+ *
+ * Return value: (element-type utf8 utf8) (transfer full) (nullable):
+ * a #GHashTable of list elements, which can be freed with
+ * soup_header_free_param_list() or %NULL if there are duplicate
+ * elements.
+ *
+ * Since: 2.66
+ **/
+GHashTable *
+soup_header_parse_semi_param_list_strict (const char *header)
+{
+ g_return_val_if_fail (header != NULL, NULL);
+
+ return parse_param_list (header, ';', TRUE);
}
/**
diff --git a/libsoup/soup-headers.h b/libsoup/soup-headers.h
index 79f92a7b..73515629 100644
--- a/libsoup/soup-headers.h
+++ b/libsoup/soup-headers.h
@@ -57,6 +57,10 @@ SOUP_AVAILABLE_IN_2_4
GHashTable *soup_header_parse_param_list (const char *header);
SOUP_AVAILABLE_IN_2_24
GHashTable *soup_header_parse_semi_param_list (const char *header);
+SOUP_AVAILABLE_IN_2_66
+GHashTable *soup_header_parse_param_list_strict (const char *header);
+SOUP_AVAILABLE_IN_2_66
+GHashTable *soup_header_parse_semi_param_list_strict (const char *header);
SOUP_AVAILABLE_IN_2_4
void soup_header_free_param_list (GHashTable *param_list);
diff --git a/libsoup/soup-version.h.in b/libsoup/soup-version.h.in
index 0ec2b9eb..5d2362b8 100644
--- a/libsoup/soup-version.h.in
+++ b/libsoup/soup-version.h.in
@@ -66,6 +66,7 @@ G_BEGIN_DECLS
#define SOUP_VERSION_2_56 (G_ENCODE_VERSION (2, 56))
#define SOUP_VERSION_2_58 (G_ENCODE_VERSION (2, 58))
#define SOUP_VERSION_2_62 (G_ENCODE_VERSION (2, 62))
+#define SOUP_VERSION_2_66 (G_ENCODE_VERSION (2, 66))
/* evaluates to the current stable version; for development cycles,
* this means the next stable target
@@ -374,6 +375,20 @@ G_BEGIN_DECLS
# define SOUP_AVAILABLE_IN_2_62 _SOUP_EXTERN
#endif
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_66
+# define SOUP_DEPRECATED_IN_2_66 G_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_66_FOR(f) G_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_66
+# define SOUP_DEPRECATED_IN_2_66_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_66
+# define SOUP_AVAILABLE_IN_2_66 G_UNAVAILABLE(2, 66) _SOUP_EXTERN
+#else
+# define SOUP_AVAILABLE_IN_2_66 _SOUP_EXTERN
+#endif
+
SOUP_AVAILABLE_IN_2_42
guint soup_get_major_version (void);
diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c
index 9cf06cee..31edfd02 100644
--- a/tests/header-parsing-test.c
+++ b/tests/header-parsing-test.c
@@ -794,6 +794,46 @@ static struct QValueTest {
};
static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests);
+static struct ParamListTest {
+ gboolean strict;
+ const char *header_value;
+ struct ParamListResult {
+ const char * param;
+ const char * value;
+ } results[3];
+} paramlisttests[] = {
+ { TRUE,
+ "UserID=JohnDoe; Max-Age=3600; Version=1",
+ { { "UserID", "JohnDoe" },
+ { "Max-Age", "3600" },
+ { "Version", "1" },
+ }
+ },
+
+ { TRUE,
+ "form-data; name=\"fieldName\"; filename=\"filename.jpg\"",
+ { { "form-data", NULL },
+ { "name", "fieldName" },
+ { "filename", "filename.jpg" },
+ },
+ },
+
+ { FALSE,
+ "form-data; form-data; filename=\"filename.jpg\"",
+ { { "form-data", NULL },
+ { "filename", "filename.jpg" },
+ },
+ },
+
+ { FALSE,
+ "attachment; filename*=UTF-8''t%C3%A9st.txt; filename=\"test.txt\"",
+ { { "attachment", NULL },
+ { "filename", "t\xC3\xA9st.txt" },
+ },
+ },
+};
+static const int num_paramlisttests = G_N_ELEMENTS (paramlisttests);
+
static void
check_headers (Header *headers, SoupMessageHeaders *hdrs)
{
@@ -949,6 +989,41 @@ do_qvalue_tests (void)
}
}
+static void
+do_param_list_tests (void)
+{
+ int i, j, n_params;
+ GHashTable* params;
+
+ for (i = 0; i < num_paramlisttests; i++) {
+ params = soup_header_parse_semi_param_list (paramlisttests[i].header_value);
+ g_assert_nonnull (params);
+ n_params = paramlisttests[i].strict ? 3 : 2;
+ g_assert_cmpuint (g_hash_table_size (params), ==, n_params);
+ for (j = 0; j < n_params; j++) {
+ g_assert_cmpstr (g_hash_table_lookup (params, paramlisttests[i].results[j].param),
+ ==, paramlisttests[i].results[j].value);
+ }
+ soup_header_free_param_list (params);
+ }
+
+ for (i = 0; i < num_paramlisttests; i++) {
+ params = soup_header_parse_semi_param_list_strict (paramlisttests[i].header_value);
+ if (paramlisttests[i].strict) {
+ g_assert_nonnull (params);
+ n_params = 3;
+ g_assert_cmpuint (g_hash_table_size (params), ==, n_params);
+ for (j = 0; j < n_params; j++) {
+ g_assert_cmpstr (g_hash_table_lookup (params,
paramlisttests[i].results[j].param),
+ ==, paramlisttests[i].results[j].value);
+ }
+ soup_header_free_param_list (params);
+ } else {
+ g_assert_null (params);
+ }
+ }
+}
+
#define RFC5987_TEST_FILENAME "t\xC3\xA9st.txt"
#define RFC5987_TEST_FALLBACK_FILENAME "test.txt"
@@ -1175,6 +1250,7 @@ main (int argc, char **argv)
g_test_add_func ("/header-parsing/request", do_request_tests);
g_test_add_func ("/header-parsing/response", do_response_tests);
g_test_add_func ("/header-parsing/qvalue", do_qvalue_tests);
+ g_test_add_func ("/header-parsing/param-list", do_param_list_tests);
g_test_add_func ("/header-parsing/content-disposition", do_content_disposition_tests);
g_test_add_func ("/header-parsing/content-type", do_content_type_tests);
g_test_add_func ("/header-parsing/append-param", do_append_param_tests);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]