[libsoup/hsts: 1/36] soup-headers: add strict parameter parsing functions



commit 3aec004c1d352a5a0340ce8e0573c13b86165821
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..8565570f 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): 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:
+ * @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): 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]