From: Nirbheek Chauhan Date: Thu, 30 Jul 2020 14:01:55 +0000 (+0530) Subject: gsturi: Add new API for storing unmodified userinfo / fragment X-Git-Tag: 1.19.3~740 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5195ad91260baded41b3dd65174fef7654e23802;p=platform%2Fupstream%2Fgstreamer.git gsturi: Add new API for storing unmodified userinfo / fragment New API: gst_uri_from_string_escaped() Identical to gst_uri_from_string() except that the userinfo and fragment components of the URI will not be unescaped while parsing. This is needed for correctly parsing usernames or passwords with `:` in them such as reported at: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/831 Part-of: --- diff --git a/gst/gsturi.c b/gst/gsturi.c index 84f1141..067629b 100644 --- a/gst/gsturi.c +++ b/gst/gsturi.c @@ -1497,19 +1497,8 @@ gst_uri_new_with_base (GstUri * base, const gchar * scheme, return new_uri; } -/** - * gst_uri_from_string: - * @uri: The URI string to parse. - * - * Parses a URI string into a new #GstUri object. Will return NULL if the URI - * cannot be parsed. - * - * Returns: (transfer full) (nullable): A new #GstUri object, or NULL. - * - * Since: 1.6 - */ -GstUri * -gst_uri_from_string (const gchar * uri) +static GstUri * +_gst_uri_from_string_internal (const gchar * uri, gboolean unescape) { const gchar *orig_uri = uri; GstUri *uri_obj; @@ -1545,7 +1534,10 @@ gst_uri_from_string (const gchar * uri) /* find end of userinfo */ eoui = strchr (uri, '@'); if (eoui != NULL && eoui < eoa) { - uri_obj->userinfo = g_uri_unescape_segment (uri, eoui, NULL); + if (unescape) + uri_obj->userinfo = g_uri_unescape_segment (uri, eoui, NULL); + else + uri_obj->userinfo = g_strndup (uri, eoui - uri); uri = eoui + 1; } /* find end of host */ @@ -1565,8 +1557,10 @@ gst_uri_from_string (const gchar * uri) reoh = eoh = eoa; } /* don't capture empty host strings */ - if (eoh != uri) + if (eoh != uri) { + /* always unescape hostname */ uri_obj->host = g_uri_unescape_segment (uri, eoh, NULL); + } uri = reoh; if (uri < eoa) { @@ -1620,7 +1614,10 @@ gst_uri_from_string (const gchar * uri) } } if (uri != NULL && uri[0] == '#') { - uri_obj->fragment = g_uri_unescape_string (uri + 1, NULL); + if (unescape) + uri_obj->fragment = g_uri_unescape_string (uri + 1, NULL); + else + uri_obj->fragment = g_strdup (uri + 1); } } @@ -1628,6 +1625,51 @@ gst_uri_from_string (const gchar * uri) } /** + * gst_uri_from_string: + * @uri: The URI string to parse. + * + * Parses a URI string into a new #GstUri object. Will return NULL if the URI + * cannot be parsed. + * + * Returns: (transfer full) (nullable): A new #GstUri object, or NULL. + * + * Since: 1.6 + */ +GstUri * +gst_uri_from_string (const gchar * uri) +{ + return _gst_uri_from_string_internal (uri, TRUE); +} + +/** + * gst_uri_from_string_escaped: + * @uri: The URI string to parse. + * + * Parses a URI string into a new #GstUri object. Will return NULL if the URI + * cannot be parsed. This is identical to gst_uri_from_string() except that + * the userinfo and fragment components of the URI will not be unescaped while + * parsing. + * + * Use this when you need to extract a username and password from the userinfo + * such as https://user:password@example.com since either may contain + * a URI-escaped ':' character. gst_uri_from_string() will unescape the entire + * userinfo component, which will make it impossible to know which ':' + * delineates the username and password. + * + * The same applies to the fragment component of the URI, such as + * https://example.com/path#fragment which may contain a URI-escaped '#'. + * + * Returns: (transfer full) (nullable): A new #GstUri object, or NULL. + * + * Since: 1.18 + */ +GstUri * +gst_uri_from_string_escaped (const gchar * uri) +{ + return _gst_uri_from_string_internal (uri, FALSE); +} + +/** * gst_uri_from_string_with_base: * @base: (transfer none)(nullable): The base URI to join the new URI with. * @uri: The URI string to parse. diff --git a/gst/gsturi.h b/gst/gsturi.h index 0cbeff3..6f8afed 100644 --- a/gst/gsturi.h +++ b/gst/gsturi.h @@ -236,6 +236,9 @@ GST_API GstUri * gst_uri_from_string (const gchar * uri) G_GNUC_MALLOC; GST_API +GstUri * gst_uri_from_string_escaped (const gchar * uri) G_GNUC_MALLOC; + +GST_API GstUri * gst_uri_from_string_with_base (GstUri * base, const gchar * uri) G_GNUC_MALLOC; GST_API diff --git a/tests/check/gst/gsturi.c b/tests/check/gst/gsturi.c index 8c304c5..3364ee7 100644 --- a/tests/check/gst/gsturi.c +++ b/tests/check/gst/gsturi.c @@ -336,6 +336,21 @@ struct URITest {"scheme", "us:er:pa:ss", "hostname", 123, "/path", {{"query", NULL}, \ {NULL, NULL}}, "frag#ment"}}, +#define ESCAPED_URI_TESTS \ + /* Test cases for gst_uri_from_string_escaped */ \ + {"scheme://user%20info@hostname", \ + {"scheme", "user%20info", "hostname", GST_URI_NO_PORT, NULL, {{NULL, \ + NULL}}, NULL}}, \ + {"scheme://userinfo@hostname:123/path?query#frag%23ment", \ + {"scheme", "userinfo", "hostname", 123, "/path", {{"query", NULL}, \ + {NULL, NULL}}, "frag%23ment"}}, \ + {"scheme://us%3Aer:pass@hostname", \ + {"scheme", "us%3Aer:pass", "hostname", GST_URI_NO_PORT, NULL, {{NULL, \ + NULL}}, NULL}}, \ + {"scheme://us%3Aer:pa%3Ass@hostname:123/path?query#frag%23ment", \ + {"scheme", "us%3Aer:pa%3Ass", "hostname", 123, "/path", {{"query", NULL}, \ + {NULL, NULL}}, "frag%23ment"}}, + static const struct URITest tests[] = { COMMON_URI_TESTS UNESCAPED_URI_TESTS @@ -400,6 +415,63 @@ GST_START_TEST (test_url_parsing) GST_END_TEST; + +static const struct URITest escaped_tests[] = { + COMMON_URI_TESTS ESCAPED_URI_TESTS +}; + +GST_START_TEST (test_url_parsing_escaped) +{ + GstUri *uri; + GList *list; + gchar *tmp_str; + guint i, j; + + for (i = 0; i < G_N_ELEMENTS (escaped_tests); i++) { + GST_DEBUG ("Testing URI '%s'", escaped_tests[i].str); + + uri = gst_uri_from_string_escaped (escaped_tests[i].str); + fail_unless (uri != NULL); + fail_unless_equals_string (gst_uri_get_scheme (uri), + escaped_tests[i].uri.scheme); + fail_unless_equals_string (gst_uri_get_userinfo (uri), + escaped_tests[i].uri.userinfo); + fail_unless_equals_string (gst_uri_get_host (uri), + escaped_tests[i].uri.host); + fail_unless_equals_int (gst_uri_get_port (uri), escaped_tests[i].uri.port); + tmp_str = gst_uri_get_path (uri); + fail_unless_equals_string (tmp_str, escaped_tests[i].uri.path); + g_free (tmp_str); + + for (j = 0; j < 10; j++) { + if (!escaped_tests[i].uri.query[j].key) + break; + + if (escaped_tests[i].uri.query[j].value) { + fail_unless_equals_string (gst_uri_get_query_value (uri, + escaped_tests[i].uri.query[j].key), + escaped_tests[i].uri.query[j].value); + } else { + fail_unless (gst_uri_query_has_key (uri, + escaped_tests[i].uri.query[j].key)); + } + } + list = gst_uri_get_query_keys (uri); + fail_unless_equals_int (j, g_list_length (list)); + g_list_free (list); + gst_uri_unref (uri); + } + + for (i = 0; i < G_N_ELEMENTS (unparsable_uri_tests); i++) { + GST_DEBUG ("Testing unparsable URI '%s'", unparsable_uri_tests[i]); + + uri = gst_uri_from_string (unparsable_uri_tests[i]); + fail_unless (uri == NULL); + } +} + +GST_END_TEST; + static const struct URITest url_presenting_tests[] = { /* check all URI elements present */ {.uri = {"scheme", "user:pass", "host", 1234, "/path/to/dir", @@ -1141,6 +1213,7 @@ gst_uri_suite (void) tcase_add_test (tc_chain, test_win32_uri); #endif tcase_add_test (tc_chain, test_url_parsing); + tcase_add_test (tc_chain, test_url_parsing_escaped); tcase_add_test (tc_chain, test_url_presenting); tcase_add_test (tc_chain, test_url_normalization); tcase_add_test (tc_chain, test_url_joining);