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;
/* 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 */
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) {
}
}
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);
}
}
}
/**
+ * 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.
{"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
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",
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);