2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
4 * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
5 * Copyright (C) 2014 David Waring, British Broadcasting Corporation
6 * <david.waring@rd.bbc.co.uk>
8 * gsturi.c: register URI handlers and IETF RFC 3986 URI manipulations.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the
22 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 * SECTION:gsturihandler
28 * @short_description: Interface to ease URI handling in plugins.
30 * The URIHandler is an interface that is implemented by Source and Sink
31 * #GstElement to simplify then handling of URI.
33 * An application can use the following functions to quickly get an element
34 * that handles the given URI for reading or writing
35 * (gst_element_make_from_uri()).
37 * Source and Sink plugins should implement this interface when possible.
44 #include "gst_private.h"
48 #include "gstregistry.h"
50 #include "gst-i18n-lib.h"
54 #include <glib/gprintf.h>
56 GST_DEBUG_CATEGORY_STATIC (gst_uri_handler_debug);
57 #define GST_CAT_DEFAULT gst_uri_handler_debug
60 gst_uri_handler_get_type (void)
62 static volatile gsize urihandler_type = 0;
64 if (g_once_init_enter (&urihandler_type)) {
66 static const GTypeInfo urihandler_info = {
67 sizeof (GstURIHandlerInterface),
79 _type = g_type_register_static (G_TYPE_INTERFACE,
80 "GstURIHandler", &urihandler_info, 0);
82 GST_DEBUG_CATEGORY_INIT (gst_uri_handler_debug, "GST_URI", GST_DEBUG_BOLD,
84 g_once_init_leave (&urihandler_type, _type);
86 return urihandler_type;
90 gst_uri_error_quark (void)
92 return g_quark_from_static_string ("gst-uri-error-quark");
95 static const guchar acceptable[96] = { /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
96 0x00, 0x3F, 0x20, 0x20, 0x20, 0x00, 0x2C, 0x3F, 0x3F, 0x3F, 0x3F, 0x22, 0x20, 0x3F, 0x3F, 0x1C, /* 2X !"#$%&'()*+,-./ */
97 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x38, 0x20, 0x20, 0x2C, 0x20, 0x2C, /* 3X 0123456789:;<=>? */
98 0x30, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 4X @ABCDEFGHIJKLMNO */
99 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x3F, /* 5X PQRSTUVWXYZ[\]^_ */
100 0x20, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 6X `abcdefghijklmno */
101 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x3F, 0x20 /* 7X pqrstuvwxyz{|}~DEL */
106 UNSAFE_ALL = 0x1, /* Escape all unsafe characters */
107 UNSAFE_ALLOW_PLUS = 0x2, /* Allows '+' */
108 UNSAFE_PATH = 0x4, /* Allows '/' and '?' and '&' and '=' */
109 UNSAFE_DOS_PATH = 0x8, /* Allows '/' and '?' and '&' and '=' and ':' */
110 UNSAFE_HOST = 0x10, /* Allows '/' and ':' and '@' */
111 UNSAFE_SLASHES = 0x20 /* Allows all characters except for '/' and '%' */
112 } UnsafeCharacterSet;
114 #define HEX_ESCAPE '%'
116 /* Escape undesirable characters using %
117 * -------------------------------------
119 * This function takes a pointer to a string in which
120 * some characters may be unacceptable unescaped.
121 * It returns a string which has these characters
122 * represented by a '%' character followed by two hex digits.
124 * This routine returns a g_malloced string.
127 static const gchar hex[16] = "0123456789ABCDEF";
130 escape_string_internal (const gchar * string, UnsafeCharacterSet mask)
132 #define ACCEPTABLE_CHAR(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
139 UnsafeCharacterSet use_mask;
141 g_return_val_if_fail (mask == UNSAFE_ALL
142 || mask == UNSAFE_ALLOW_PLUS
143 || mask == UNSAFE_PATH
144 || mask == UNSAFE_DOS_PATH
145 || mask == UNSAFE_HOST || mask == UNSAFE_SLASHES, NULL);
147 if (string == NULL) {
153 for (p = string; *p != '\0'; p++) {
155 if (!ACCEPTABLE_CHAR (c)) {
158 if ((use_mask == UNSAFE_HOST) && (unacceptable || (c == '/'))) {
159 /* when escaping a host, if we hit something that needs to be escaped, or we finally
160 * hit a path separator, revert to path mode (the host segment of the url is over).
162 use_mask = UNSAFE_PATH;
166 result = g_malloc (p - string + unacceptable * 2 + 1);
169 for (q = result, p = string; *p != '\0'; p++) {
172 if (!ACCEPTABLE_CHAR (c)) {
173 *q++ = HEX_ESCAPE; /* means hex coming */
179 if ((use_mask == UNSAFE_HOST) && (!ACCEPTABLE_CHAR (c) || (c == '/'))) {
180 use_mask = UNSAFE_PATH;
190 * @string: string to be escaped
192 * Escapes @string, replacing any and all special characters
193 * with equivalent escape sequences.
195 * Return value: a newly allocated string equivalent to @string
196 * but with all special characters escaped
199 escape_string (const gchar * string)
201 return escape_string_internal (string, UNSAFE_ALL);
207 return c >= '0' && c <= '9' ? c - '0'
208 : c >= 'A' && c <= 'F' ? c - 'A' + 10
209 : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
213 unescape_character (const char *scanner)
218 first_digit = hex_to_int (*scanner++);
219 if (first_digit < 0) {
223 second_digit = hex_to_int (*scanner);
224 if (second_digit < 0) {
228 return (first_digit << 4) | second_digit;
232 * @escaped_string: an escaped URI, path, or other string
233 * @illegal_characters: a string containing a sequence of characters
234 * considered "illegal", '\0' is automatically in this list.
236 * Decodes escaped characters (i.e. PERCENTxx sequences) in @escaped_string.
237 * Characters are encoded in PERCENTxy form, where xy is the ASCII hex code
238 * for character 16x+y.
240 * Return value: (nullable): a newly allocated string with the
241 * unescaped equivalents, or %NULL if @escaped_string contained one of
242 * the characters in @illegal_characters.
245 unescape_string (const gchar * escaped_string, const gchar * illegal_characters)
251 if (escaped_string == NULL) {
255 result = g_malloc (strlen (escaped_string) + 1);
258 for (in = escaped_string; *in != '\0'; in++) {
260 if (*in == HEX_ESCAPE) {
261 character = unescape_character (in + 1);
263 /* Check for an illegal character. We consider '\0' illegal here. */
265 || (illegal_characters != NULL
266 && strchr (illegal_characters, (char) character) != NULL)) {
272 *out++ = (char) character;
276 g_assert ((gsize) (out - result) <= strlen (escaped_string));
283 gst_uri_protocol_check_internal (const gchar * uri, gchar ** endptr)
285 gchar *check = (gchar *) uri;
287 g_assert (uri != NULL);
288 g_assert (endptr != NULL);
290 if (g_ascii_isalpha (*check)) {
292 while (g_ascii_isalnum (*check) || *check == '+'
293 || *check == '-' || *check == '.')
301 * gst_uri_protocol_is_valid:
302 * @protocol: A string
304 * Tests if the given string is a valid protocol identifier. Protocols
305 * must consist of alphanumeric characters, '+', '-' and '.' and must
306 * start with a alphabetic character. See RFC 3986 Section 3.1.
308 * Returns: %TRUE if the string is a valid protocol identifier, %FALSE otherwise.
311 gst_uri_protocol_is_valid (const gchar * protocol)
315 g_return_val_if_fail (protocol != NULL, FALSE);
317 gst_uri_protocol_check_internal (protocol, &endptr);
319 return *endptr == '\0' && ((gsize) (endptr - protocol)) >= 2;
326 * Tests if the given string is a valid URI identifier. URIs start with a valid
327 * scheme followed by ":" and maybe a string identifying the location.
329 * Returns: %TRUE if the string is a valid URI
332 gst_uri_is_valid (const gchar * uri)
336 g_return_val_if_fail (uri != NULL, FALSE);
338 gst_uri_protocol_check_internal (uri, &endptr);
340 return *endptr == ':' && ((gsize) (endptr - uri)) >= 2;
344 * gst_uri_get_protocol:
347 * Extracts the protocol out of a given valid URI. The returned string must be
348 * freed using g_free().
350 * Returns: The protocol for this URI.
353 gst_uri_get_protocol (const gchar * uri)
357 g_return_val_if_fail (uri != NULL, NULL);
358 g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
360 colon = strstr (uri, ":");
362 return g_ascii_strdown (uri, colon - uri);
366 * gst_uri_has_protocol:
368 * @protocol: a protocol string (e.g. "http")
370 * Checks if the protocol of a given valid URI matches @protocol.
372 * Returns: %TRUE if the protocol matches.
375 gst_uri_has_protocol (const gchar * uri, const gchar * protocol)
379 g_return_val_if_fail (uri != NULL, FALSE);
380 g_return_val_if_fail (protocol != NULL, FALSE);
381 g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
383 colon = strstr (uri, ":");
388 return (g_ascii_strncasecmp (uri, protocol, (gsize) (colon - uri)) == 0);
392 * gst_uri_get_location:
395 * Extracts the location out of a given valid URI, ie. the protocol and "://"
396 * are stripped from the URI, which means that the location returned includes
397 * the hostname if one is specified. The returned string must be freed using
400 * Free-function: g_free
402 * Returns: (transfer full): the location for this URI. Returns %NULL if the
403 * URI isn't valid. If the URI does not contain a location, an empty
404 * string is returned.
407 gst_uri_get_location (const gchar * uri)
410 gchar *unescaped = NULL;
412 g_return_val_if_fail (uri != NULL, NULL);
413 g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
415 colon = strstr (uri, "://");
419 unescaped = unescape_string (colon + 3, "/");
421 /* On Windows an URI might look like file:///c:/foo/bar.txt or
422 * file:///c|/foo/bar.txt (some Netscape versions) and we want to
423 * return c:/foo/bar.txt as location rather than /c:/foo/bar.txt.
424 * Can't use g_filename_from_uri() here because it will only handle the
425 * file:// protocol */
427 if (unescaped != NULL && unescaped[0] == '/' &&
428 g_ascii_isalpha (unescaped[1]) &&
429 (unescaped[2] == ':' || unescaped[2] == '|')) {
431 memmove (unescaped, unescaped + 1, strlen (unescaped + 1) + 1);
435 GST_LOG ("extracted location '%s' from URI '%s'", GST_STR_NULL (unescaped),
442 * @protocol: Protocol for URI
443 * @location: (transfer none): Location for URI
445 * Constructs a URI for a given valid protocol and location.
447 * Free-function: g_free
449 * Returns: (transfer full): a new string for this URI. Returns %NULL if the
450 * given URI protocol is not valid, or the given location is %NULL.
453 gst_uri_construct (const gchar * protocol, const gchar * location)
455 char *escaped, *proto_lowercase;
458 g_return_val_if_fail (gst_uri_protocol_is_valid (protocol), NULL);
459 g_return_val_if_fail (location != NULL, NULL);
461 proto_lowercase = g_ascii_strdown (protocol, -1);
462 escaped = escape_string (location);
463 retval = g_strdup_printf ("%s://%s", proto_lowercase, escaped);
465 g_free (proto_lowercase);
473 const gchar *protocol;
478 search_by_entry (GstPluginFeature * feature, gpointer search_entry)
480 const gchar *const *protocols;
481 GstElementFactory *factory;
482 SearchEntry *entry = (SearchEntry *) search_entry;
484 if (!GST_IS_ELEMENT_FACTORY (feature))
486 factory = GST_ELEMENT_FACTORY_CAST (feature);
488 if (factory->uri_type != entry->type)
491 protocols = gst_element_factory_get_uri_protocols (factory);
493 if (protocols == NULL) {
494 g_warning ("Factory '%s' implements GstUriHandler interface but returned "
495 "no supported protocols!", gst_plugin_feature_get_name (feature));
499 while (*protocols != NULL) {
500 if (g_ascii_strcasecmp (*protocols, entry->protocol) == 0)
508 sort_by_rank (GstPluginFeature * first, GstPluginFeature * second)
510 return gst_plugin_feature_get_rank (second) -
511 gst_plugin_feature_get_rank (first);
515 get_element_factories_from_uri_protocol (const GstURIType type,
516 const gchar * protocol)
518 GList *possibilities;
521 g_return_val_if_fail (protocol, NULL);
524 entry.protocol = protocol;
525 possibilities = gst_registry_feature_filter (gst_registry_get (),
526 search_by_entry, FALSE, &entry);
528 return possibilities;
532 * gst_uri_protocol_is_supported:
533 * @type: Whether to check for a source or a sink
534 * @protocol: Protocol that should be checked for (e.g. "http" or "smb")
536 * Checks if an element exists that supports the given URI protocol. Note
537 * that a positive return value does not imply that a subsequent call to
538 * gst_element_make_from_uri() is guaranteed to work.
543 gst_uri_protocol_is_supported (const GstURIType type, const gchar * protocol)
545 GList *possibilities;
547 g_return_val_if_fail (protocol, FALSE);
549 possibilities = get_element_factories_from_uri_protocol (type, protocol);
552 g_list_free (possibilities);
559 * gst_element_make_from_uri:
560 * @type: Whether to create a source or a sink
561 * @uri: URI to create an element for
562 * @elementname: (allow-none): Name of created element, can be %NULL.
563 * @error: (allow-none): address where to store error information, or %NULL.
565 * Creates an element for handling the given URI.
567 * Returns: (transfer floating): a new element or %NULL if none could be created
570 gst_element_make_from_uri (const GstURIType type, const gchar * uri,
571 const gchar * elementname, GError ** error)
573 GList *possibilities, *walk;
575 GstElement *ret = NULL;
577 g_return_val_if_fail (gst_is_initialized (), NULL);
578 g_return_val_if_fail (GST_URI_TYPE_IS_VALID (type), NULL);
579 g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
580 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
582 GST_DEBUG ("type:%d, uri:%s, elementname:%s", type, uri, elementname);
584 protocol = gst_uri_get_protocol (uri);
585 possibilities = get_element_factories_from_uri_protocol (type, protocol);
587 if (!possibilities) {
588 GST_DEBUG ("No %s for URI '%s'", type == GST_URI_SINK ? "sink" : "source",
590 /* The error message isn't great, but we don't expect applications to
591 * show that error to users, but call the missing plugins functions */
592 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_UNSUPPORTED_PROTOCOL,
593 _("No URI handler for the %s protocol found"), protocol);
599 possibilities = g_list_sort (possibilities, (GCompareFunc) sort_by_rank);
600 walk = possibilities;
602 GstElementFactory *factory = walk->data;
603 GError *uri_err = NULL;
605 ret = gst_element_factory_create (factory, elementname);
607 GstURIHandler *handler = GST_URI_HANDLER (ret);
609 if (gst_uri_handler_set_uri (handler, uri, &uri_err))
612 GST_WARNING ("%s didn't accept URI '%s': %s", GST_OBJECT_NAME (ret), uri,
615 if (error != NULL && *error == NULL)
616 g_propagate_error (error, uri_err);
618 g_error_free (uri_err);
620 gst_object_unref (ret);
625 gst_plugin_feature_list_free (possibilities);
627 GST_LOG_OBJECT (ret, "created %s for URL '%s'",
628 type == GST_URI_SINK ? "sink" : "source", uri);
630 /* if the first handler didn't work, but we found another one that works */
632 g_clear_error (error);
638 * gst_uri_handler_get_uri_type:
639 * @handler: A #GstURIHandler.
641 * Gets the type of the given URI handler
643 * Returns: the #GstURIType of the URI handler.
644 * Returns #GST_URI_UNKNOWN if the @handler isn't implemented correctly.
647 gst_uri_handler_get_uri_type (GstURIHandler * handler)
649 GstURIHandlerInterface *iface;
652 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN);
654 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
655 g_return_val_if_fail (iface != NULL, GST_URI_UNKNOWN);
656 g_return_val_if_fail (iface->get_type != NULL, GST_URI_UNKNOWN);
658 ret = iface->get_type (G_OBJECT_TYPE (handler));
659 g_return_val_if_fail (GST_URI_TYPE_IS_VALID (ret), GST_URI_UNKNOWN);
665 * gst_uri_handler_get_protocols:
666 * @handler: A #GstURIHandler.
668 * Gets the list of protocols supported by @handler. This list may not be
671 * Returns: (transfer none) (element-type utf8) (nullable): the
672 * supported protocols. Returns %NULL if the @handler isn't
673 * implemented properly, or the @handler doesn't support any
677 gst_uri_handler_get_protocols (GstURIHandler * handler)
679 GstURIHandlerInterface *iface;
680 const gchar *const *ret;
682 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
684 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
685 g_return_val_if_fail (iface != NULL, NULL);
686 g_return_val_if_fail (iface->get_protocols != NULL, NULL);
688 ret = iface->get_protocols (G_OBJECT_TYPE (handler));
689 g_return_val_if_fail (ret != NULL, NULL);
695 * gst_uri_handler_get_uri:
696 * @handler: A #GstURIHandler
698 * Gets the currently handled URI.
700 * Returns: (transfer full) (nullable): the URI currently handled by
701 * the @handler. Returns %NULL if there are no URI currently
702 * handled. The returned string must be freed with g_free() when no
706 gst_uri_handler_get_uri (GstURIHandler * handler)
708 GstURIHandlerInterface *iface;
711 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
713 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
714 g_return_val_if_fail (iface != NULL, NULL);
715 g_return_val_if_fail (iface->get_uri != NULL, NULL);
716 ret = iface->get_uri (handler);
718 g_return_val_if_fail (gst_uri_is_valid (ret), NULL);
724 * gst_uri_handler_set_uri:
725 * @handler: A #GstURIHandler
727 * @error: (allow-none): address where to store a #GError in case of
730 * Tries to set the URI of the given handler.
732 * Returns: %TRUE if the URI was set successfully, else %FALSE.
735 gst_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri,
738 GstURIHandlerInterface *iface;
742 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE);
743 g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
744 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
746 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
747 g_return_val_if_fail (iface != NULL, FALSE);
748 g_return_val_if_fail (iface->set_uri != NULL, FALSE);
750 protocol = gst_uri_get_protocol (uri);
752 if (iface->get_protocols) {
753 const gchar *const *protocols;
754 const gchar *const *p;
755 gboolean found_protocol = FALSE;
757 protocols = iface->get_protocols (G_OBJECT_TYPE (handler));
758 if (protocols != NULL) {
759 for (p = protocols; *p != NULL; ++p) {
760 if (g_ascii_strcasecmp (protocol, *p) == 0) {
761 found_protocol = TRUE;
766 if (!found_protocol) {
767 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_UNSUPPORTED_PROTOCOL,
768 _("URI scheme '%s' not supported"), protocol);
775 ret = iface->set_uri (handler, uri, error);
783 gst_file_utils_canonicalise_path (const gchar * path)
785 gchar **parts, **p, *clean_path;
789 GST_WARNING ("FIXME: canonicalise win32 path");
790 return g_strdup (path);
794 parts = g_strsplit (path, "/", -1);
798 if (strcmp (*p, ".") == 0) {
799 /* just move all following parts on top of this, incl. NUL terminator */
801 memmove (p, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *));
802 /* re-check the new current part again in the next iteration */
804 } else if (strcmp (*p, "..") == 0 && p > parts) {
805 /* just move all following parts on top of the previous part, incl.
809 memmove (p - 1, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *));
810 /* re-check the new current part again in the next iteration */
819 num_parts = g_strv_length (parts) + 1; /* incl. terminator */
820 parts = g_renew (gchar *, parts, num_parts + 1);
821 memmove (parts + 1, parts, num_parts * sizeof (gchar *));
822 parts[0] = g_strdup ("/");
825 clean_path = g_build_filenamev (parts);
831 file_path_contains_relatives (const gchar * path)
833 return (strstr (path, "/./") != NULL || strstr (path, "/../") != NULL ||
834 strstr (path, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) != NULL ||
835 strstr (path, G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) != NULL);
839 * gst_filename_to_uri:
840 * @filename: absolute or relative file name path
841 * @error: pointer to error, or %NULL
843 * Similar to g_filename_to_uri(), but attempts to handle relative file paths
844 * as well. Before converting @filename into an URI, it will be prefixed by
845 * the current working directory if it is a relative path, and then the path
846 * will be canonicalised so that it doesn't contain any './' or '../' segments.
848 * On Windows #filename should be in UTF-8 encoding.
851 gst_filename_to_uri (const gchar * filename, GError ** error)
853 gchar *abs_location = NULL;
854 gchar *uri, *abs_clean;
856 g_return_val_if_fail (filename != NULL, NULL);
857 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
859 if (g_path_is_absolute (filename)) {
860 if (!file_path_contains_relatives (filename)) {
861 uri = g_filename_to_uri (filename, NULL, error);
865 abs_location = g_strdup (filename);
869 cwd = g_get_current_dir ();
870 abs_location = g_build_filename (cwd, filename, NULL);
873 if (!file_path_contains_relatives (abs_location)) {
874 uri = g_filename_to_uri (abs_location, NULL, error);
879 /* path is now absolute, but contains '.' or '..' */
880 abs_clean = gst_file_utils_canonicalise_path (abs_location);
881 GST_LOG ("'%s' -> '%s' -> '%s'", filename, abs_location, abs_clean);
882 uri = g_filename_to_uri (abs_clean, NULL, error);
887 g_free (abs_location);
888 GST_DEBUG ("'%s' -> '%s'", filename, uri);
892 /****************************************************************************
893 * GstUri - GstMiniObject to parse and merge URIs according to IETF RFC 3986
894 ****************************************************************************/
898 * @short_description: URI parsing and manipulation.
900 * A #GstUri object can be used to parse and split a URI string into its
901 * constituant parts. Two #GstUri objects can be joined to make a new #GstUri
902 * using the algorithm described in RFC3986.
905 /* Definition for GstUri object */
909 GstMiniObject mini_object;
919 GST_DEFINE_MINI_OBJECT_TYPE (GstUri, gst_uri);
921 static GstUri *_gst_uri_copy (const GstUri * uri);
922 static void _gst_uri_free (GstUri * uri);
923 static GstUri *_gst_uri_new (void);
924 static GList *_remove_dot_segments (GList * path);
926 /** private GstUri functions **/
932 uri = GST_URI_CAST (g_slice_new0 (GstUri));
935 gst_mini_object_init (GST_MINI_OBJECT_CAST (uri), 0, gst_uri_get_type (),
936 (GstMiniObjectCopyFunction) _gst_uri_copy, NULL,
937 (GstMiniObjectFreeFunction) _gst_uri_free);
943 _gst_uri_free (GstUri * uri)
945 g_return_if_fail (GST_IS_URI (uri));
947 g_free (uri->scheme);
948 g_free (uri->userinfo);
950 g_list_free_full (uri->path, g_free);
952 g_hash_table_unref (uri->query);
953 g_free (uri->fragment);
955 g_slice_free1 (sizeof (*uri), uri);
959 _gst_uri_copy_query_table (GHashTable * orig)
961 GHashTable *new = NULL;
966 new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
967 g_hash_table_iter_init (&iter, orig);
968 while (g_hash_table_iter_next (&iter, &key, &value)) {
969 g_hash_table_insert (new, g_strdup (key), g_strdup (value));
977 _gst_uri_copy (const GstUri * orig_uri)
981 g_return_val_if_fail (GST_IS_URI (orig_uri), NULL);
983 new_uri = _gst_uri_new ();
986 new_uri->scheme = g_strdup (orig_uri->scheme);
987 new_uri->userinfo = g_strdup (orig_uri->userinfo);
988 new_uri->host = g_strdup (orig_uri->host);
989 new_uri->port = orig_uri->port;
990 new_uri->path = g_list_copy_deep (orig_uri->path, (GCopyFunc) g_strdup,
992 new_uri->query = _gst_uri_copy_query_table (orig_uri->query);
993 new_uri->fragment = g_strdup (orig_uri->fragment);
1000 * _gst_uri_compare_lists:
1002 * Compare two lists for equality. This compares the two lists, item for item,
1003 * comparing items in the same position in the two lists. If @first is
1004 * considered less than @second the result will be negative. If @first is
1005 * considered to be more than @second then the result will be positive. If the
1006 * lists are considered to be equal then the result will be 0. If two lists
1007 * have the same items, but one list is shorter than the other, then the
1008 * shorter list is considered to be less than the longer list.
1011 _gst_uri_compare_lists (GList * first, GList * second, GCompareFunc cmp_fn)
1016 for (itr1 = first, itr2 = second;
1017 itr1 != NULL || itr2 != NULL; itr1 = itr1->next, itr2 = itr2->next) {
1022 result = cmp_fn (itr1->data, itr2->data);
1031 _GST_URI_NORMALIZE_LOWERCASE = 1,
1032 _GST_URI_NORMALIZE_UPPERCASE = 2
1033 } _GstUriNormalizations;
1036 * Find the first character that hasn't been normalized according to the @flags.
1039 _gst_uri_first_non_normalized_char (gchar * str, guint flags)
1046 for (pos = str; *pos; pos++) {
1047 if ((flags & _GST_URI_NORMALIZE_UPPERCASE) && g_ascii_islower (*pos))
1049 if ((flags & _GST_URI_NORMALIZE_LOWERCASE) && g_ascii_isupper (*pos))
1056 _gst_uri_normalize_lowercase (gchar * str)
1059 gboolean ret = FALSE;
1061 for (pos = _gst_uri_first_non_normalized_char (str,
1062 _GST_URI_NORMALIZE_LOWERCASE);
1064 pos = _gst_uri_first_non_normalized_char (pos + 1,
1065 _GST_URI_NORMALIZE_LOWERCASE)) {
1066 *pos = g_ascii_tolower (*pos);
1073 #define _gst_uri_normalize_scheme _gst_uri_normalize_lowercase
1074 #define _gst_uri_normalize_hostname _gst_uri_normalize_lowercase
1077 _gst_uri_normalize_path (GList ** path)
1081 new_path = _remove_dot_segments (*path);
1082 if (_gst_uri_compare_lists (new_path, *path, (GCompareFunc) g_strcmp0) != 0) {
1083 g_list_free_full (*path, g_free);
1087 g_list_free_full (new_path, g_free);
1093 _gst_uri_normalize_str_noop (gchar * str)
1099 _gst_uri_normalize_table_noop (GHashTable * table)
1104 #define _gst_uri_normalize_userinfo _gst_uri_normalize_str_noop
1105 #define _gst_uri_normalize_query _gst_uri_normalize_table_noop
1106 #define _gst_uri_normalize_fragment _gst_uri_normalize_str_noop
1108 /** RFC 3986 functions **/
1111 _merge (GList * base, GList * path)
1113 GList *ret, *path_copy, *last;
1115 path_copy = g_list_copy_deep (path, (GCopyFunc) g_strdup, NULL);
1116 /* if base is NULL make path absolute */
1118 if (path_copy != NULL && path_copy->data != NULL) {
1119 path_copy = g_list_prepend (path_copy, NULL);
1124 ret = g_list_copy_deep (base, (GCopyFunc) g_strdup, NULL);
1125 last = g_list_last (ret);
1126 ret = g_list_remove_link (ret, last);
1127 g_list_free_full (last, g_free);
1128 ret = g_list_concat (ret, path_copy);
1134 _remove_dot_segments (GList * path)
1136 GList *out, *elem, *next;
1141 out = g_list_copy_deep (path, (GCopyFunc) g_strdup, NULL);
1143 for (elem = out; elem; elem = next) {
1145 if (elem->data == NULL && elem != out && next != NULL) {
1146 out = g_list_delete_link (out, elem);
1147 } else if (g_strcmp0 (elem->data, ".") == 0) {
1148 g_free (elem->data);
1149 out = g_list_delete_link (out, elem);
1150 } else if (g_strcmp0 (elem->data, "..") == 0) {
1151 GList *prev = g_list_previous (elem);
1152 if (prev && (prev != out || prev->data != NULL)) {
1153 g_free (prev->data);
1154 out = g_list_delete_link (out, prev);
1156 g_free (elem->data);
1157 out = g_list_delete_link (out, elem);
1165 _gst_uri_escape_userinfo (const gchar * userinfo)
1167 return g_uri_escape_string (userinfo,
1168 G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO, FALSE);
1172 _gst_uri_escape_host (const gchar * host)
1174 return g_uri_escape_string (host,
1175 G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, FALSE);
1179 _gst_uri_escape_path_segment (const gchar * segment)
1181 return g_uri_escape_string (segment,
1182 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT, FALSE);
1186 _gst_uri_escape_http_query_element (const gchar * element)
1190 ret = g_uri_escape_string (element, "!$'()*,;:@/? ", FALSE);
1191 for (c = ret; *c; c++)
1198 _gst_uri_escape_fragment (const gchar * fragment)
1200 return g_uri_escape_string (fragment,
1201 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH "?", FALSE);
1205 _gst_uri_string_to_list (const gchar * str, const gchar * sep, gboolean convert,
1208 GList *new_list = NULL;
1211 guint pct_sep_len = 0;
1215 if (convert && !unescape) {
1216 pct_sep = g_strdup_printf ("%%%2.2X", (guint) (*sep));
1220 split_str = g_strsplit (str, sep, -1);
1223 for (next_elem = split_str; *next_elem; next_elem += 1) {
1224 gchar *elem = *next_elem;
1225 if (*elem == '\0') {
1226 new_list = g_list_append (new_list, NULL);
1228 if (convert && !unescape) {
1230 for (next_sep = strcasestr (elem, pct_sep); next_sep;
1231 next_sep = strcasestr (next_sep + 1, pct_sep)) {
1233 memmove (next_sep + 1, next_sep + pct_sep_len,
1234 strlen (next_sep + pct_sep_len) + 1);
1238 *next_elem = g_uri_unescape_string (elem, NULL);
1242 new_list = g_list_append (new_list, g_strdup (elem));
1246 g_strfreev (split_str);
1247 if (convert && !unescape)
1255 _gst_uri_string_to_table (const gchar * str, const gchar * part_sep,
1256 const gchar * kv_sep, gboolean convert, gboolean unescape)
1258 GHashTable *new_table = NULL;
1261 gchar *pct_part_sep = NULL, *pct_kv_sep = NULL;
1262 gchar **split_parts;
1264 new_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1266 if (convert && !unescape) {
1267 pct_part_sep = g_strdup_printf ("%%%2.2X", (guint) (*part_sep));
1268 pct_kv_sep = g_strdup_printf ("%%%2.2X", (guint) (*kv_sep));
1271 split_parts = g_strsplit (str, part_sep, -1);
1274 for (next_part = split_parts; *next_part; next_part += 1) {
1275 gchar *part = *next_part;
1278 /* if we are converting percent encoded versions of separators then
1279 * substitute the part separator now. */
1280 if (convert && !unescape) {
1282 for (next_sep = strcasestr (part, pct_part_sep); next_sep;
1283 next_sep = strcasestr (next_sep + 1, pct_part_sep)) {
1284 *next_sep = *part_sep;
1285 memmove (next_sep + 1, next_sep + 3, strlen (next_sep + 3) + 1);
1288 /* find the key/value separator within the part */
1289 kv_sep_pos = g_strstr_len (part, -1, kv_sep);
1290 if (kv_sep_pos == NULL) {
1292 key = g_uri_unescape_string (part, NULL);
1294 key = g_strdup (part);
1299 key = g_uri_unescape_segment (part, kv_sep_pos, NULL);
1300 value = g_uri_unescape_string (kv_sep_pos + 1, NULL);
1302 key = g_strndup (part, kv_sep_pos - part);
1303 value = g_strdup (kv_sep_pos + 1);
1306 /* if we are converting percent encoded versions of separators then
1307 * substitute the key/value separator in both key and value now. */
1308 if (convert && !unescape) {
1310 for (next_sep = strcasestr (key, pct_kv_sep); next_sep;
1311 next_sep = strcasestr (next_sep + 1, pct_kv_sep)) {
1312 *next_sep = *kv_sep;
1313 memmove (next_sep + 1, next_sep + 3, strlen (next_sep + 3) + 1);
1316 for (next_sep = strcasestr (value, pct_kv_sep); next_sep;
1317 next_sep = strcasestr (next_sep + 1, pct_kv_sep)) {
1318 *next_sep = *kv_sep;
1319 memmove (next_sep + 1, next_sep + 3, strlen (next_sep + 3) + 1);
1323 /* add value to the table */
1324 g_hash_table_insert (new_table, key, value);
1328 g_strfreev (split_parts);
1329 if (convert && !unescape) {
1330 g_free (pct_part_sep);
1331 g_free (pct_kv_sep);
1340 * Method definitions.
1345 * @scheme: (nullable): The scheme for the new URI.
1346 * @userinfo: (nullable): The user-info for the new URI.
1347 * @host: (nullable): The host name for the new URI.
1348 * @port: The port number for the new URI or %GST_URI_NO_PORT.
1349 * @path: (nullable): The path for the new URI with '/' separating path
1351 * @query: (nullable): The query string for the new URI with '&' separating
1352 * query elements. Elements containing '&' characters
1353 * should encode them as "%26".
1354 * @fragment: (nullable): The fragment name for the new URI.
1356 * Creates a new #GstUri object with the given URI parts. The path and query
1357 * strings will be broken down into their elements. All strings should not be
1358 * escaped except where indicated.
1360 * Returns: (transfer full): A new #GstUri object.
1365 gst_uri_new (const gchar * scheme, const gchar * userinfo, const gchar * host,
1366 guint port, const gchar * path, const gchar * query, const gchar * fragment)
1370 new_uri = _gst_uri_new ();
1372 new_uri->scheme = g_strdup (scheme);
1373 new_uri->userinfo = g_strdup (userinfo);
1374 new_uri->host = g_strdup (host);
1375 new_uri->port = port;
1376 new_uri->path = _gst_uri_string_to_list (path, "/", FALSE, FALSE);
1377 new_uri->query = _gst_uri_string_to_table (query, "&", "=", TRUE, FALSE);
1378 new_uri->fragment = g_strdup (fragment);
1385 * gst_uri_new_with_base:
1386 * @base: (transfer none)(nullable): The base URI to join the new URI to.
1387 * @scheme: (nullable): The scheme for the new URI.
1388 * @userinfo: (nullable): The user-info for the new URI.
1389 * @host: (nullable): The host name for the new URI.
1390 * @port: The port number for the new URI or %GST_URI_NO_PORT.
1391 * @path: (nullable): The path for the new URI with '/' separating path
1393 * @query: (nullable): The query string for the new URI with '&' separating
1394 * query elements. Elements containing '&' characters
1395 * should encode them as "%26".
1396 * @fragment: (nullable): The fragment name for the new URI.
1398 * Like gst_uri_new(), but joins the new URI onto a base URI.
1400 * Returns: (transfer full): The new URI joined onto @base.
1405 gst_uri_new_with_base (GstUri * base, const gchar * scheme,
1406 const gchar * userinfo, const gchar * host, guint port, const gchar * path,
1407 const gchar * query, const gchar * fragment)
1409 GstUri *new_rel_uri;
1412 g_return_val_if_fail (base == NULL || GST_IS_URI (base), NULL);
1414 new_rel_uri = gst_uri_new (scheme, userinfo, host, port, path, query,
1416 new_uri = gst_uri_join (base, new_rel_uri);
1417 gst_uri_unref (new_rel_uri);
1423 * gst_uri_from_string:
1424 * @uri: The URI string to parse.
1426 * Parses a URI string into a new #GstUri object.
1428 * Returns: (transfer full): A new #GstUri object.
1433 gst_uri_from_string (const gchar * uri)
1437 uri_obj = _gst_uri_new ();
1439 if (uri_obj && uri != NULL) {
1441 if (g_ascii_isalpha (uri[i])) {
1442 /* find end of scheme name */
1444 while (g_ascii_isalnum (uri[i]) || uri[i] == '+' || uri[i] == '-' ||
1448 if (i > 0 && uri[i] == ':') {
1450 uri_obj->scheme = g_strndup (uri, i);
1453 if (uri[0] == '/' && uri[1] == '/') {
1454 const gchar *eoa, *eoui, *eoh;
1455 /* get authority [userinfo@]host[:port] */
1457 /* find end of authority */
1458 eoa = strchr (uri, '/');
1460 eoa = uri + strlen (uri);
1461 /* find end of userinfo */
1462 eoui = strchr (uri, '@');
1463 if (eoui != NULL && eoui < eoa) {
1464 uri_obj->userinfo = g_uri_unescape_segment (uri, eoui, NULL);
1467 /* find end of host */
1468 if (uri[0] == '[') {
1469 eoh = strchr (uri, ']');
1470 if (eoh == NULL || eoh >= eoa)
1473 eoh = strchr (uri, ':');
1474 if (eoh == NULL || eoh >= eoa)
1479 uri_obj->host = g_uri_unescape_segment (uri, eoh + 1, NULL);
1482 /* if port number is malformed, do best effort and concat string */
1483 if (uri[0] != ':' || strspn (uri + 1, "0123456789") != eoa - uri - 1) {
1484 gchar *tmp = uri_obj->host;
1485 uri_obj->host = g_malloc (strlen (uri_obj->host) + eoa - uri + 1);
1486 g_strlcpy (g_stpcpy (uri_obj->host, tmp), uri, eoa - uri + 1);
1489 /* otherwise treat port as unsigned decimal number */
1492 uri_obj->port = uri_obj->port * 10 + g_ascii_digit_value (*uri);
1499 if (uri != NULL && uri[0] != '\0') {
1502 len = strcspn (uri, "?#");
1503 if (uri[len] == '\0') {
1504 uri_obj->path = _gst_uri_string_to_list (uri, "/", FALSE, TRUE);
1508 gchar *path_str = g_strndup (uri, len);
1509 uri_obj->path = _gst_uri_string_to_list (path_str, "/", FALSE, TRUE);
1515 if (uri != NULL && uri[0] == '?') {
1518 eoq = strchr (++uri, '#');
1520 uri_obj->query = _gst_uri_string_to_table (uri, "&", "=", TRUE, TRUE);
1524 gchar *query_str = g_strndup (uri, eoq - uri);
1525 uri_obj->query = _gst_uri_string_to_table (query_str, "&", "=", TRUE,
1532 if (uri != NULL && uri[0] == '#') {
1533 uri_obj->fragment = g_uri_unescape_string (uri + 1, NULL);
1541 * gst_uri_from_string_with_base:
1542 * @base: (transfer none)(nullable): The base URI to join the new URI with.
1543 * @uri: The URI string to parse.
1545 * Like gst_uri_from_string() but also joins with a base URI.
1547 * Returns: (transfer full): A new #GstUri object.
1552 gst_uri_from_string_with_base (GstUri * base, const gchar * uri)
1554 GstUri *new_rel_uri;
1557 g_return_val_if_fail (base == NULL || GST_IS_URI (base), NULL);
1559 new_rel_uri = gst_uri_from_string (uri);
1560 new_uri = gst_uri_join (base, new_rel_uri);
1561 gst_uri_unref (new_rel_uri);
1568 * @first: First #GstUri to compare.
1569 * @second: Second #GstUri to compare.
1571 * Compares two #GstUri objects to see if they represent the same normalized
1574 * Returns: %TRUE if the normalized versions of the two URI's would be equal.
1579 gst_uri_equal (const GstUri * first, const GstUri * second)
1581 gchar *first_norm = NULL, *second_norm = NULL;
1582 GList *first_norm_list = NULL, *second_norm_list = NULL;
1583 const gchar *first_cmp, *second_cmp;
1584 GHashTableIter table_iter;
1585 gpointer key, value;
1588 g_return_val_if_fail ((first == NULL || GST_IS_URI (first)) &&
1589 (second == NULL || GST_IS_URI (second)), FALSE);
1591 if (first == second)
1594 if (first == NULL || second == NULL)
1597 if (first->port != second->port)
1600 /* work out a version of field value (normalized or not) to compare.
1601 * first_cmp, second_cmp will be the values to compare later.
1602 * first_norm, second_norm will be non-NULL if normalized versions are used,
1603 * and need to be freed later.
1605 #define GST_URI_NORMALIZED_FIELD(pos, field, norm_fn, flags) \
1606 pos##_cmp = pos->field; \
1607 if (_gst_uri_first_non_normalized_char ((gchar*)pos##_cmp, flags) != NULL) { \
1608 pos##_norm = g_strdup (pos##_cmp); \
1609 norm_fn (pos##_norm); \
1610 pos##_cmp = pos##_norm; \
1613 /* compare two string values, normalizing if needed */
1614 #define GST_URI_NORMALIZED_CMP_STR(field, norm_fn, flags) \
1615 GST_URI_NORMALIZED_FIELD (first, field, norm_fn, flags) \
1616 GST_URI_NORMALIZED_FIELD (second, field, norm_fn, flags) \
1617 result = g_strcmp0 (first_cmp, second_cmp); \
1618 g_free (first_norm); \
1619 first_norm = NULL; \
1620 g_free (second_norm); \
1621 second_norm = NULL; \
1622 if (result != 0) return FALSE
1624 /* compare two string values */
1625 #define GST_URI_CMP_STR(field) \
1626 if (g_strcmp0 (first->field, second->field) != 0) return FALSE
1628 /* compare two GLists, normalize lists if needed before comparison */
1629 #define GST_URI_NORMALIZED_CMP_LIST(field, norm_fn, copy_fn, cmp_fn, free_fn) \
1630 first_norm_list = g_list_copy_deep (first->field, (GCopyFunc) copy_fn, NULL); \
1631 norm_fn (&first_norm_list); \
1632 second_norm_list = g_list_copy_deep (second->field, (GCopyFunc) copy_fn, NULL); \
1633 norm_fn (&second_norm_list); \
1634 result = _gst_uri_compare_lists (first_norm_list, second_norm_list, (GCompareFunc) cmp_fn); \
1635 g_list_free_full (first_norm_list, free_fn); \
1636 g_list_free_full (second_norm_list, free_fn); \
1637 if (result != 0) return FALSE
1639 GST_URI_CMP_STR (userinfo);
1641 GST_URI_CMP_STR (fragment);
1643 GST_URI_NORMALIZED_CMP_STR (scheme, _gst_uri_normalize_scheme,
1644 _GST_URI_NORMALIZE_LOWERCASE);
1646 GST_URI_NORMALIZED_CMP_STR (host, _gst_uri_normalize_hostname,
1647 _GST_URI_NORMALIZE_LOWERCASE);
1649 GST_URI_NORMALIZED_CMP_LIST (path, _gst_uri_normalize_path, g_strdup,
1652 if (first->query == NULL && second->query != NULL)
1654 if (first->query != NULL && second->query == NULL)
1656 if (first->query != NULL) {
1657 if (g_hash_table_size (first->query) != g_hash_table_size (second->query))
1660 g_hash_table_iter_init (&table_iter, first->query);
1661 while (g_hash_table_iter_next (&table_iter, &key, &value)) {
1662 if (!g_hash_table_contains (second->query, key))
1664 result = g_strcmp0 (g_hash_table_lookup (second->query, key), value);
1669 #undef GST_URI_NORMALIZED_CMP_STR
1670 #undef GST_URI_CMP_STR
1671 #undef GST_URI_NORMALIZED_CMP_LIST
1672 #undef GST_URI_NORMALIZED_FIELD
1679 * @base_uri: (transfer none)(nullable): The base URI to join another to.
1680 * @ref_uri: (transfer none)(nullable): The reference URI to join onto the
1683 * Join a reference URI onto a base URI using the method from RFC 3986.
1684 * If either URI is %NULL then the other URI will be returned with the ref count
1687 * Returns: (transfer full): A #GstUri which represents the base with the
1688 * reference URI joined on.
1693 gst_uri_join (GstUri * base_uri, GstUri * ref_uri)
1695 const gchar *r_scheme;
1698 g_return_val_if_fail ((base_uri == NULL || GST_IS_URI (base_uri)) &&
1699 (ref_uri == NULL || GST_IS_URI (ref_uri)), NULL);
1701 if (base_uri == NULL && ref_uri == NULL)
1703 if (base_uri == NULL) {
1704 g_return_val_if_fail (GST_IS_URI (ref_uri), NULL);
1705 return gst_uri_ref (ref_uri);
1707 if (ref_uri == NULL) {
1708 g_return_val_if_fail (GST_IS_URI (base_uri), NULL);
1709 return gst_uri_ref (base_uri);
1712 g_return_val_if_fail (GST_IS_URI (base_uri) && GST_IS_URI (ref_uri), NULL);
1714 t = _gst_uri_new ();
1719 /* process according to RFC3986 */
1720 r_scheme = ref_uri->scheme;
1721 if (r_scheme != NULL && g_strcmp0 (base_uri->scheme, r_scheme) == 0) {
1724 if (r_scheme != NULL) {
1725 t->scheme = g_strdup (r_scheme);
1726 t->userinfo = g_strdup (ref_uri->userinfo);
1727 t->host = g_strdup (ref_uri->host);
1728 t->port = ref_uri->port;
1729 t->path = _remove_dot_segments (ref_uri->path);
1730 t->query = _gst_uri_copy_query_table (ref_uri->query);
1732 if (ref_uri->host != NULL) {
1733 t->userinfo = g_strdup (ref_uri->userinfo);
1734 t->host = g_strdup (ref_uri->host);
1735 t->port = ref_uri->port;
1736 t->path = _remove_dot_segments (ref_uri->path);
1737 t->query = _gst_uri_copy_query_table (ref_uri->query);
1739 if (ref_uri->path == NULL) {
1740 t->path = g_list_copy_deep (base_uri->path, (GCopyFunc) g_strdup, NULL);
1741 if (ref_uri->query != NULL)
1742 t->query = _gst_uri_copy_query_table (ref_uri->query);
1744 t->query = _gst_uri_copy_query_table (base_uri->query);
1746 if (ref_uri->path->data == NULL)
1747 t->path = _remove_dot_segments (ref_uri->path);
1749 GList *mrgd = _merge (base_uri->path, ref_uri->path);
1750 t->path = _remove_dot_segments (mrgd);
1751 g_list_free_full (mrgd, g_free);
1753 t->query = _gst_uri_copy_query_table (ref_uri->query);
1755 t->userinfo = g_strdup (base_uri->userinfo);
1756 t->host = g_strdup (base_uri->host);
1757 t->port = base_uri->port;
1759 t->scheme = g_strdup (base_uri->scheme);
1761 t->fragment = g_strdup (ref_uri->fragment);
1767 * gst_uri_join_strings:
1768 * @base_uri: The percent-encoded base URI.
1769 * @ref_uri: The percent-encoded reference URI to join to the @base_uri.
1771 * This is a convenience function to join two URI strings and return the result.
1772 * The returned string should be g_free()'d after use.
1774 * Returns: (transfer full): A string representing the percent-encoded join of
1780 gst_uri_join_strings (const gchar * base_uri, const gchar * ref_uri)
1782 GstUri *base, *result;
1785 base = gst_uri_from_string (base_uri);
1786 result = gst_uri_from_string_with_base (base, ref_uri);
1787 result_uri = gst_uri_to_string (result);
1788 gst_uri_unref (base);
1789 gst_uri_unref (result);
1795 * gst_uri_is_writable:
1796 * @uri: The #GstUri object to test.
1798 * Check if it is safe to write to this #GstUri.
1800 * Check if the refcount of @uri is exactly 1, meaning that no other
1801 * reference exists to the #GstUri and that the #GstUri is therefore writable.
1803 * Modification of a #GstUri should only be done after verifying that it is
1806 * Returns: %TRUE if it is safe to write to the object.
1811 gst_uri_is_writable (const GstUri * uri)
1813 g_return_val_if_fail (GST_IS_URI (uri), FALSE);
1814 return gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (uri));
1818 * gst_uri_make_writable:
1819 * @uri: (transfer full): The #GstUri object to make writable.
1821 * Make the #GstUri writable.
1823 * Checks if @uri is writable, and if so the original object is returned. If
1824 * not, then a writable copy is made and returned. This gives away the
1825 * reference to @uri and returns a reference to the new #GstUri.
1826 * If @uri is %NULL then %NULL is returned.
1828 * Returns: (transfer full): A writable version of @uri.
1833 gst_uri_make_writable (GstUri * uri)
1835 g_return_val_if_fail (GST_IS_URI (uri), NULL);
1837 GST_URI_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (uri)));
1841 * gst_uri_to_string:
1842 * @uri: This #GstUri to convert to a string.
1844 * Convert the URI to a string.
1846 * Returns the URI as held in this object as a gchar* %NUL terminated string.
1847 * The caller should g_free() the string once they are finished with it.
1848 * The string is put together as described in RFC 3986.
1850 * Returns: (transfer full): The string version of the URI.
1855 gst_uri_to_string (const GstUri * uri)
1860 g_return_val_if_fail (GST_IS_URI (uri), NULL);
1862 uri_str = g_string_new (NULL);
1864 if (uri->scheme != NULL)
1865 g_string_append_printf (uri_str, "%s:", uri->scheme);
1867 if (uri->userinfo != NULL || uri->host != NULL ||
1868 uri->port != GST_URI_NO_PORT)
1869 g_string_append (uri_str, "//");
1871 if (uri->userinfo != NULL) {
1872 escaped = _gst_uri_escape_userinfo (uri->userinfo);
1873 g_string_append_printf (uri_str, "%s@", escaped);
1877 if (uri->host != NULL) {
1878 escaped = _gst_uri_escape_host (uri->host);
1879 g_string_append (uri_str, escaped);
1883 if (uri->port != GST_URI_NO_PORT)
1884 g_string_append_printf (uri_str, ":%u", uri->port);
1886 if (uri->path != NULL) {
1887 escaped = gst_uri_get_path_string (uri);
1888 g_string_append (uri_str, escaped);
1893 g_string_append (uri_str, "?");
1894 escaped = gst_uri_get_query_string (uri);
1895 g_string_append (uri_str, escaped);
1899 if (uri->fragment != NULL) {
1900 escaped = _gst_uri_escape_fragment (uri->fragment);
1901 g_string_append_printf (uri_str, "#%s", escaped);
1905 return g_string_free (uri_str, FALSE);
1909 * gst_uri_is_normalized:
1910 * @uri: The #GstUri to test to see if it is normalized.
1912 * Tests the @uri to see if it is normalized. A %NULL @uri is considered to be
1915 * Returns: TRUE if the URI is normalized or is %NULL.
1920 gst_uri_is_normalized (const GstUri * uri)
1928 g_return_val_if_fail (GST_IS_URI (uri), FALSE);
1930 /* check for non-normalized characters in uri parts */
1931 if (_gst_uri_first_non_normalized_char (uri->scheme,
1932 _GST_URI_NORMALIZE_LOWERCASE) != NULL ||
1933 /*_gst_uri_first_non_normalized_char (uri->userinfo,
1934 _GST_URI_NORMALIZE_PERCENTAGES) != NULL || */
1935 _gst_uri_first_non_normalized_char (uri->host,
1936 _GST_URI_NORMALIZE_LOWERCASE /*| _GST_URI_NORMALIZE_PERCENTAGES */ )
1938 /*|| _gst_uri_first_non_normalized_char (uri->path,
1939 _GST_URI_NORMALIZE_PERCENTAGES) != NULL
1940 || _gst_uri_first_non_normalized_char (uri->query,
1941 _GST_URI_NORMALIZE_PERCENTAGES) != NULL
1942 || _gst_uri_first_non_normalized_char (uri->fragment,
1943 _GST_URI_NORMALIZE_PERCENTAGES) != NULL */ )
1946 /* also check path has had dot segments removed */
1947 new_path = _remove_dot_segments (uri->path);
1949 (_gst_uri_compare_lists (new_path, uri->path,
1950 (GCompareFunc) g_strcmp0) == 0);
1951 g_list_free_full (new_path, g_free);
1956 * gst_uri_normalize:
1957 * @uri: (transfer none): The #GstUri to normalize.
1959 * Normalization will remove extra path segments ("." and "..") from the URI. It
1960 * will also convert the scheme and host name to lower case and any
1961 * percent-encoded values to uppercase.
1963 * The #GstUri object must be writable. Check with gst_uri_is_writable() or use
1964 * gst_uri_make_writable() first.
1966 * Returns: TRUE if the URI was modified.
1971 gst_uri_normalize (GstUri * uri)
1973 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
1975 return _gst_uri_normalize_scheme (uri->scheme) |
1976 _gst_uri_normalize_userinfo (uri->userinfo) |
1977 _gst_uri_normalize_hostname (uri->host) |
1978 _gst_uri_normalize_path (&uri->path) |
1979 _gst_uri_normalize_query (uri->query) |
1980 _gst_uri_normalize_fragment (uri->fragment);
1984 * gst_uri_get_scheme:
1985 * @uri: (nullable): This #GstUri object.
1987 * Get the scheme name from the URI or %NULL if it doesn't exist.
1988 * If @uri is %NULL then returns %NULL.
1990 * Returns: The scheme from the #GstUri object or %NULL.
1993 gst_uri_get_scheme (const GstUri * uri)
1995 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
1996 return (uri ? uri->scheme : NULL);
2000 * gst_uri_set_scheme:
2001 * @uri: (transfer none)(nullable): The #GstUri to modify.
2002 * @scheme: The new scheme to set or %NULL to unset the scheme.
2004 * Set or unset the scheme for the URI.
2006 * Returns: %TRUE if the scheme was set/unset successfully.
2011 gst_uri_set_scheme (GstUri * uri, const gchar * scheme)
2014 return scheme == NULL;
2015 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2017 g_free (uri->scheme);
2018 uri->scheme = g_strdup (scheme);
2024 * gst_uri_get_userinfo:
2025 * @uri: (nullable): This #GstUri object.
2027 * Get the userinfo (usually in the form "username:password") from the URI
2028 * or %NULL if it doesn't exist. If @uri is %NULL then returns %NULL.
2030 * Returns: The userinfo from the #GstUri object or %NULL.
2035 gst_uri_get_userinfo (const GstUri * uri)
2037 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2038 return (uri ? uri->userinfo : NULL);
2042 * gst_uri_set_userinfo:
2043 * @uri: (transfer none)(nullable): The #GstUri to modify.
2044 * @userinfo: The new user-information string to set or %NULL to unset.
2046 * Set or unset the user information for the URI.
2048 * Returns: %TRUE if the user information was set/unset successfully.
2053 gst_uri_set_userinfo (GstUri * uri, const gchar * userinfo)
2056 return userinfo == NULL;
2057 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2059 g_free (uri->userinfo);
2060 uri->userinfo = g_strdup (userinfo);
2067 * @uri: (nullable): This #GstUri object.
2069 * Get the host name from the URI or %NULL if it doesn't exist.
2070 * If @uri is %NULL then returns %NULL.
2072 * Returns: The host name from the #GstUri object or %NULL.
2077 gst_uri_get_host (const GstUri * uri)
2079 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2080 return (uri ? uri->host : NULL);
2085 * @uri: (transfer none)(nullable): The #GstUri to modify.
2086 * @host: The new host string to set or %NULL to unset.
2088 * Set or unset the host for the URI.
2090 * Returns: %TRUE if the host was set/unset successfully.
2095 gst_uri_set_host (GstUri * uri, const gchar * host)
2098 return host == NULL;
2099 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2102 uri->host = g_strdup (host);
2109 * @uri: (nullable): This #GstUri object.
2111 * Get the port number from the URI or %GST_URI_NO_PORT if it doesn't exist.
2112 * If @uri is %NULL then returns %GST_URI_NO_PORT.
2114 * Returns: The port number from the #GstUri object or %GST_URI_NO_PORT.
2119 gst_uri_get_port (const GstUri * uri)
2121 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), GST_URI_NO_PORT);
2122 return (uri ? uri->port : GST_URI_NO_PORT);
2127 * @uri: (transfer none)(nullable): The #GstUri to modify.
2128 * @port: The new port number to set or %GST_URI_NO_PORT to unset.
2130 * Set or unset the port number for the URI.
2132 * Returns: %TRUE if the port number was set/unset successfully.
2137 gst_uri_set_port (GstUri * uri, guint port)
2140 return port == GST_URI_NO_PORT;
2141 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2150 * @uri: The #GstUri to get the path from.
2152 * Extract the path string from the URI object.
2154 * Returns: (transfer full): The path from the URI. Once finished with the
2155 * string should be g_free()'d.
2160 gst_uri_get_path (const GstUri * uri)
2162 GList *path_segment;
2163 const gchar *sep = "";
2168 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2172 ret = g_string_new (NULL);
2174 for (path_segment = uri->path; path_segment;
2175 path_segment = path_segment->next) {
2176 g_string_append (ret, sep);
2177 if (path_segment->data) {
2178 g_string_append (ret, path_segment->data);
2183 return g_string_free (ret, FALSE);
2188 * @uri: (transfer none)(nullable): The #GstUri to modify.
2189 * @path: The new path to set with path segments separated by '/', or use %NULL
2190 * to unset the path.
2192 * Sets or unsets the path in the URI.
2194 * Returns: %TRUE if the path was set successfully.
2199 gst_uri_set_path (GstUri * uri, const gchar * path)
2202 return path == NULL;
2203 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2205 g_list_free_full (uri->path, g_free);
2206 uri->path = _gst_uri_string_to_list (path, "/", FALSE, FALSE);
2212 * gst_uri_get_path_string:
2213 * @uri: The #GstUri to get the path from.
2215 * Extract the path string from the URI object as a percent encoded URI path.
2217 * Returns: (transfer full): The path from the URI. Once finished with the
2218 * string should be g_free()'d.
2223 gst_uri_get_path_string (const GstUri * uri)
2225 GList *path_segment;
2226 const gchar *sep = "";
2232 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2236 ret = g_string_new (NULL);
2238 for (path_segment = uri->path; path_segment;
2239 path_segment = path_segment->next) {
2240 g_string_append (ret, sep);
2241 if (path_segment->data) {
2242 escaped = _gst_uri_escape_path_segment (path_segment->data);
2243 g_string_append (ret, escaped);
2249 return g_string_free (ret, FALSE);
2253 * gst_uri_set_path_string:
2254 * @uri: (transfer none)(nullable): The #GstUri to modify.
2255 * @path: The new percent encoded path to set with path segments separated by
2256 * '/', or use %NULL to unset the path.
2258 * Sets or unsets the path in the URI.
2260 * Returns: %TRUE if the path was set successfully.
2265 gst_uri_set_path_string (GstUri * uri, const gchar * path)
2268 return path == NULL;
2269 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2271 g_list_free_full (uri->path, g_free);
2272 uri->path = _gst_uri_string_to_list (path, "/", FALSE, TRUE);
2277 * gst_uri_get_path_segments:
2278 * @uri: (nullable): The #GstUri to get the path from.
2280 * Get a list of path segments from the URI.
2282 * Returns: (transfer full)(element-type gchar*): A #GList of path segment
2283 * strings or %NULL if no path segments are available. Free the list
2284 * when no longer needed with g_list_free_full(list, g_free).
2289 gst_uri_get_path_segments (const GstUri * uri)
2293 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2296 ret = g_list_copy_deep (uri->path, (GCopyFunc) g_strdup, NULL);
2303 * gst_uri_set_path_segments:
2304 * @uri: (transfer none)(nullable): The #GstUri to modify.
2305 * @path_segments: (transfer full)(nullable)(element-type gchar*): The new
2308 * Replace the path segments list in the URI.
2310 * Returns: %TRUE if the path segments were set successfully.
2315 gst_uri_set_path_segments (GstUri * uri, GList * path_segments)
2317 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), FALSE);
2321 g_list_free_full (path_segments, g_free);
2322 return path_segments == NULL;
2325 g_return_val_if_fail (gst_uri_is_writable (uri), FALSE);
2327 g_list_free_full (uri->path, g_free);
2328 uri->path = path_segments;
2333 * gst_uri_append_path:
2334 * @uri: (transfer none)(nullable): The #GstUri to modify.
2335 * @relative_path: Relative path to append to the end of the current path.
2337 * Append a path onto the end of the path in the URI. The path is not
2338 * normalized, call #gst_uri_normalize() to normalize the path.
2340 * Returns: %TRUE if the path was appended successfully.
2345 gst_uri_append_path (GstUri * uri, const gchar * relative_path)
2347 GList *rel_path_list;
2350 return relative_path == NULL;
2351 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2356 GList *last_elem = g_list_last (uri->path);
2357 if (last_elem->data == NULL) {
2358 uri->path = g_list_delete_link (uri->path, last_elem);
2361 rel_path_list = _gst_uri_string_to_list (relative_path, "/", FALSE, FALSE);
2362 /* if path was absolute, make it relative by removing initial NULL element */
2363 if (rel_path_list && rel_path_list->data == NULL) {
2364 rel_path_list = g_list_delete_link (rel_path_list, rel_path_list);
2366 uri->path = g_list_concat (uri->path, rel_path_list);
2371 * gst_uri_append_path_segment:
2372 * @uri: (transfer none)(nullable): The #GstUri to modify.
2373 * @path_segment: The path segment string to append to the URI path.
2375 * Append a single path segment onto the end of the URI path.
2377 * Returns: %TRUE if the path was appended successfully.
2382 gst_uri_append_path_segment (GstUri * uri, const gchar * path_segment)
2385 return path_segment == NULL;
2386 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2390 /* if base path ends in a directory (i.e. last element is NULL), remove it */
2391 if (uri->path && g_list_last (uri->path)->data == NULL) {
2392 uri->path = g_list_delete_link (uri->path, g_list_last (uri->path));
2394 uri->path = g_list_append (uri->path, g_strdup (path_segment));
2399 * gst_uri_get_query_string:
2400 * @uri: (nullable): The #GstUri to get the query string from.
2402 * Get a percent encoded URI query string from the @uri.
2404 * Returns: (transfer full): A percent encoded query string. Use g_free() when
2410 gst_uri_get_query_string (const GstUri * uri)
2412 GHashTableIter iter;
2413 gpointer key, value;
2414 const gchar *sep = "";
2420 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2424 ret = g_string_new (NULL);
2425 g_hash_table_iter_init (&iter, uri->query);
2426 while (g_hash_table_iter_next (&iter, &key, &value)) {
2427 g_string_append (ret, sep);
2428 escaped = _gst_uri_escape_http_query_element (key);
2429 g_string_append (ret, escaped);
2432 escaped = _gst_uri_escape_http_query_element (value);
2433 g_string_append_printf (ret, "=%s", escaped);
2439 return g_string_free (ret, FALSE);
2443 * gst_uri_set_query_string:
2444 * @uri: (transfer none)(nullable): The #GstUri to modify.
2445 * @query: The new percent encoded query string to use to populate the query
2446 * table, or use %NULL to unset the query table.
2448 * Sets or unsets the query table in the URI.
2450 * Returns: %TRUE if the query table was set successfully.
2455 gst_uri_set_query_string (GstUri * uri, const gchar * query)
2458 return query == NULL;
2460 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2463 g_hash_table_unref (uri->query);
2464 uri->query = _gst_uri_string_to_table (query, "&", "=", TRUE, TRUE);
2470 * gst_uri_get_query_table:
2471 * @uri: (nullable): The #GstUri to get the query table from.
2473 * Get the query table from the URI. Keys and values in the table are freed
2474 * with g_free when they are deleted. A value may be %NULL to indicate that
2475 * the key should appear in the query string in the URI, but does not have a
2476 * value. Free the returned #GHashTable with #g_hash_table_unref() when it is
2477 * no longer required. Modifying this hash table will modify the query in the
2480 * Returns: (transfer full)(element-type gchar* gchar*): The query hash table
2486 gst_uri_get_query_table (const GstUri * uri)
2490 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2494 return g_hash_table_ref (uri->query);
2498 * gst_uri_set_query_table:
2499 * @uri: (transfer none)(nullable): The #GstUri to modify.
2500 * @query_table: (transfer none)(nullable)(element-type gchar* gchar*): The new
2501 * query table to use.
2503 * Set the query table to use in the URI. The old table is unreferenced and a
2504 * reference to the new one is used instead. A value if %NULL for @query_table
2505 * will remove the query string from the URI.
2507 * Returns: %TRUE if the new table was sucessfully used for the query table.
2512 gst_uri_set_query_table (GstUri * uri, GHashTable * query_table)
2514 GHashTable *old_table = NULL;
2517 return query_table == NULL;
2518 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2520 old_table = uri->query;
2522 uri->query = g_hash_table_ref (query_table);
2526 g_hash_table_unref (old_table);
2532 * gst_uri_set_query_value:
2533 * @uri: (transfer none)(nullable): The #GstUri to modify.
2534 * @query_key: (transfer none): The key for the query entry.
2535 * @query_value: (transfer none)(nullable): The value for the key.
2537 * This inserts or replaces a key in the query table. A @query_value of %NULL
2538 * indicates that the key has no associated value, but will still be present in
2541 * Returns: %TRUE if the query table was sucessfully updated.
2546 gst_uri_set_query_value (GstUri * uri, const gchar * query_key,
2547 const gchar * query_value)
2551 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2554 uri->query = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
2557 g_hash_table_insert (uri->query, g_strdup (query_key),
2558 g_strdup (query_value));
2564 * gst_uri_remove_query_key:
2565 * @uri: (transfer none)(nullable): The #GstUri to modify.
2566 * @query_key: The key to remove.
2568 * Remove an entry from the query table by key.
2570 * Returns: %TRUE if the key existed in the table and was removed.
2575 gst_uri_remove_query_key (GstUri * uri, const gchar * query_key)
2581 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2585 result = g_hash_table_remove (uri->query, query_key);
2586 /* if this was the last query entry, remove the query string completely */
2587 if (result && g_hash_table_size (uri->query) == 0) {
2588 g_hash_table_unref (uri->query);
2595 * gst_uri_query_has_key:
2596 * @uri: (nullable): The #GstUri to examine.
2597 * @query_key: The key to lookup.
2599 * Check if there is a query table entry for the @query_key key.
2601 * Returns: %TRUE if @query_key exists in the URI query table.
2606 gst_uri_query_has_key (const GstUri * uri, const gchar * query_key)
2610 g_return_val_if_fail (GST_IS_URI (uri), FALSE);
2614 return g_hash_table_contains (uri->query, query_key);
2618 * gst_uri_get_query_value:
2619 * @uri: (nullable): The #GstUri to examine.
2620 * @query_key: The key to lookup.
2622 * Get the value associated with the @query_key key. Will return %NULL if the
2623 * key has no value or if the key does not exist in the URI query table. Because
2624 * %NULL is returned for both missing keys and keys with no value, you should
2625 * use gst_uri_query_has_key() to determine if a key is present in the URI
2628 * Returns: The value for the given key, or %NULL if not found.
2633 gst_uri_get_query_value (const GstUri * uri, const gchar * query_key)
2637 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2641 return g_hash_table_lookup (uri->query, query_key);
2645 * gst_uri_get_query_keys:
2646 * @uri: (nullable): The #GstUri to examine.
2648 * Get a list of the query keys from the URI.
2650 * Returns: (transfer container)(element-type gchar*): A list of keys from
2651 * the URI query. Free the list with g_list_free().
2656 gst_uri_get_query_keys (const GstUri * uri)
2660 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2664 return g_hash_table_get_keys (uri->query);
2668 * gst_uri_get_fragment:
2669 * @uri: (nullable): This #GstUri object.
2671 * Get the fragment name from the URI or %NULL if it doesn't exist.
2672 * If @uri is %NULL then returns %NULL.
2674 * Returns: The host name from the #GstUri object or %NULL.
2679 gst_uri_get_fragment (const GstUri * uri)
2681 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2682 return (uri ? uri->fragment : NULL);
2686 * gst_uri_set_fragment:
2687 * @uri: (transfer none)(nullable): The #GstUri to modify.
2688 * @fragment: (nullable): The fragment string to set.
2690 * Sets the fragment string in the URI. Use a value of %NULL in @fragment to
2691 * unset the fragment string.
2693 * Returns: %TRUE if the fragment was set/unset successfully.
2698 gst_uri_set_fragment (GstUri * uri, const gchar * fragment)
2701 return fragment == NULL;
2702 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2704 g_free (uri->fragment);
2705 uri->fragment = g_strdup (fragment);