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 * @title: GstUriHandler
29 * @short_description: Interface to ease URI handling in plugins.
31 * The #GstURIHandler is an interface that is implemented by Source and Sink
32 * #GstElement to unify handling of URI.
34 * An application can use the following functions to quickly get an element
35 * that handles the given URI for reading or writing
36 * (gst_element_make_from_uri()).
38 * Source and Sink plugins should implement this interface when possible.
45 #define GST_DISABLE_MINIOBJECT_INLINE_FUNCTIONS
46 #include "gst_private.h"
50 #include "gstregistry.h"
52 #include <glib/gi18n-lib.h>
56 #include <glib/gprintf.h>
58 GST_DEBUG_CATEGORY_STATIC (gst_uri_handler_debug);
59 #define GST_CAT_DEFAULT gst_uri_handler_debug
61 #ifndef HAVE_STRCASESTR
62 #define strcasestr _gst_ascii_strcasestr
64 /* From https://github.com/freebsd/freebsd/blob/master/contrib/file/src/strcasestr.c
65 * Updated to use GLib types and GLib string functions
67 * Copyright (c) 1990, 1993
68 * The Regents of the University of California. All rights reserved.
70 * This code is derived from software contributed to Berkeley by
73 * Redistribution and use in source and binary forms, with or without
74 * modification, are permitted provided that the following conditions
76 * 1. Redistributions of source code must retain the above copyright
77 * notice, this list of conditions and the following disclaimer.
78 * 2. Redistributions in binary form must reproduce the above copyright
79 * notice, this list of conditions and the following disclaimer in the
80 * documentation and/or other materials provided with the distribution.
81 * 3. Neither the name of the University nor the names of its contributors
82 * may be used to endorse or promote products derived from this software
83 * without specific prior written permission.
85 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
86 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
87 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
88 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
89 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
90 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
91 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
92 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
93 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
94 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
99 * Find the first occurrence of find in s, ignore case.
103 _gst_ascii_strcasestr (const gchar * s, const gchar * find)
108 if ((c = *find++) != 0) {
109 c = g_ascii_tolower (c);
113 if ((sc = *s++) == 0)
115 } while (g_ascii_tolower (sc) != c);
116 } while (g_ascii_strncasecmp (s, find, len) != 0);
119 return (gchar *) (gintptr) (s);
124 gst_uri_handler_get_type (void)
126 static gsize urihandler_type = 0;
128 if (g_once_init_enter (&urihandler_type)) {
130 static const GTypeInfo urihandler_info = {
131 sizeof (GstURIHandlerInterface),
143 _type = g_type_register_static (G_TYPE_INTERFACE,
144 "GstURIHandler", &urihandler_info, 0);
146 GST_DEBUG_CATEGORY_INIT (gst_uri_handler_debug, "GST_URI", GST_DEBUG_BOLD,
148 g_once_init_leave (&urihandler_type, _type);
150 return urihandler_type;
154 gst_uri_error_quark (void)
156 return g_quark_from_static_string ("gst-uri-error-quark");
159 #define HEX_ESCAPE '%'
161 #ifndef GST_REMOVE_DEPRECATED
162 static const guchar acceptable[96] = { /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
163 0x00, 0x3F, 0x20, 0x20, 0x20, 0x00, 0x2C, 0x3F, 0x3F, 0x3F, 0x3F, 0x22, 0x20, 0x3F, 0x3F, 0x1C, /* 2X !"#$%&'()*+,-./ */
164 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x38, 0x20, 0x20, 0x2C, 0x20, 0x2C, /* 3X 0123456789:;<=>? */
165 0x30, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 4X @ABCDEFGHIJKLMNO */
166 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x3F, /* 5X PQRSTUVWXYZ[\]^_ */
167 0x20, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 6X `abcdefghijklmno */
168 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x3F, 0x20 /* 7X pqrstuvwxyz{|}~DEL */
173 UNSAFE_ALL = 0x1, /* Escape all unsafe characters */
174 UNSAFE_ALLOW_PLUS = 0x2, /* Allows '+' */
175 UNSAFE_PATH = 0x4, /* Allows '/' and '?' and '&' and '=' */
176 UNSAFE_DOS_PATH = 0x8, /* Allows '/' and '?' and '&' and '=' and ':' */
177 UNSAFE_HOST = 0x10, /* Allows '/' and ':' and '@' */
178 UNSAFE_SLASHES = 0x20 /* Allows all characters except for '/' and '%' */
179 } UnsafeCharacterSet;
181 /* Escape undesirable characters using %
182 * -------------------------------------
184 * This function takes a pointer to a string in which
185 * some characters may be unacceptable unescaped.
186 * It returns a string which has these characters
187 * represented by a '%' character followed by two hex digits.
189 * This routine returns a g_malloced string.
192 static const gchar hex[16] = "0123456789ABCDEF";
195 escape_string_internal (const gchar * string, UnsafeCharacterSet mask)
197 #define ACCEPTABLE_CHAR(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
204 UnsafeCharacterSet use_mask;
206 g_return_val_if_fail (mask == UNSAFE_ALL
207 || mask == UNSAFE_ALLOW_PLUS
208 || mask == UNSAFE_PATH
209 || mask == UNSAFE_DOS_PATH
210 || mask == UNSAFE_HOST || mask == UNSAFE_SLASHES, NULL);
212 if (string == NULL) {
218 for (p = string; *p != '\0'; p++) {
220 if (!ACCEPTABLE_CHAR (c)) {
223 if ((use_mask == UNSAFE_HOST) && (unacceptable || (c == '/'))) {
224 /* when escaping a host, if we hit something that needs to be escaped, or we finally
225 * hit a path separator, revert to path mode (the host segment of the url is over).
227 use_mask = UNSAFE_PATH;
231 result = g_malloc (p - string + unacceptable * 2 + 1);
234 for (q = result, p = string; *p != '\0'; p++) {
237 if (!ACCEPTABLE_CHAR (c)) {
238 *q++ = HEX_ESCAPE; /* means hex coming */
244 if ((use_mask == UNSAFE_HOST) && (!ACCEPTABLE_CHAR (c) || (c == '/'))) {
245 use_mask = UNSAFE_PATH;
258 return c >= '0' && c <= '9' ? c - '0'
259 : c >= 'A' && c <= 'F' ? c - 'A' + 10
260 : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
264 unescape_character (const char *scanner)
269 first_digit = hex_to_int (*scanner++);
270 if (first_digit < 0) {
274 second_digit = hex_to_int (*scanner);
275 if (second_digit < 0) {
279 return (first_digit << 4) | second_digit;
283 * @escaped_string: an escaped URI, path, or other string
284 * @illegal_characters: a string containing a sequence of characters
285 * considered "illegal", '\0' is automatically in this list.
287 * Decodes escaped characters (i.e. PERCENTxx sequences) in @escaped_string.
288 * Characters are encoded in PERCENTxy form, where xy is the ASCII hex code
289 * for character 16x+y.
291 * Return value: (nullable): a newly allocated string with the
292 * unescaped equivalents, or %NULL if @escaped_string contained one of
293 * the characters in @illegal_characters.
296 unescape_string (const gchar * escaped_string, const gchar * illegal_characters)
302 if (escaped_string == NULL) {
306 result = g_malloc (strlen (escaped_string) + 1);
309 for (in = escaped_string; *in != '\0'; in++) {
311 if (*in == HEX_ESCAPE) {
312 character = unescape_character (in + 1);
314 /* Check for an illegal character. We consider '\0' illegal here. */
316 || (illegal_characters != NULL
317 && strchr (illegal_characters, (char) character) != NULL)) {
323 *out++ = (char) character;
327 g_assert ((gsize) (out - result) <= strlen (escaped_string));
334 gst_uri_protocol_check_internal (const gchar * uri, gchar ** endptr)
336 gchar *check = (gchar *) uri;
338 g_assert (uri != NULL);
339 g_assert (endptr != NULL);
341 if (g_ascii_isalpha (*check)) {
343 while (g_ascii_isalnum (*check) || *check == '+'
344 || *check == '-' || *check == '.')
352 * gst_uri_protocol_is_valid:
353 * @protocol: A string
355 * Tests if the given string is a valid protocol identifier. Protocols
356 * must consist of alphanumeric characters, '+', '-' and '.' and must
357 * start with a alphabetic character. See RFC 3986 Section 3.1.
359 * Returns: %TRUE if the string is a valid protocol identifier, %FALSE otherwise.
362 gst_uri_protocol_is_valid (const gchar * protocol)
366 g_return_val_if_fail (protocol != NULL, FALSE);
368 gst_uri_protocol_check_internal (protocol, &endptr);
370 return *endptr == '\0' && ((gsize) (endptr - protocol)) >= 2;
377 * Tests if the given string is a valid URI identifier. URIs start with a valid
378 * scheme followed by ":" and maybe a string identifying the location.
380 * Returns: %TRUE if the string is a valid URI
383 gst_uri_is_valid (const gchar * uri)
387 g_return_val_if_fail (uri != NULL, FALSE);
389 gst_uri_protocol_check_internal (uri, &endptr);
391 return *endptr == ':' && ((gsize) (endptr - uri)) >= 2;
395 * gst_uri_get_protocol:
398 * Extracts the protocol out of a given valid URI. The returned string must be
399 * freed using g_free().
401 * Returns: (nullable): The protocol for this URI.
404 gst_uri_get_protocol (const gchar * uri)
408 if (!gst_uri_is_valid (uri))
411 colon = strstr (uri, ":");
413 return g_ascii_strdown (uri, colon - uri);
417 * gst_uri_has_protocol:
419 * @protocol: a protocol string (e.g. "http")
421 * Checks if the protocol of a given valid URI matches @protocol.
423 * Returns: %TRUE if the protocol matches.
426 gst_uri_has_protocol (const gchar * uri, const gchar * protocol)
430 g_return_val_if_fail (protocol != NULL, FALSE);
432 if (!gst_uri_is_valid (uri)) {
436 colon = strstr (uri, ":");
441 return (g_ascii_strncasecmp (uri, protocol, (gsize) (colon - uri)) == 0);
445 * gst_uri_get_location:
448 * Extracts the location out of a given valid URI, ie. the protocol and "://"
449 * are stripped from the URI, which means that the location returned includes
450 * the hostname if one is specified. The returned string must be freed using
453 * Free-function: g_free
455 * Returns: (transfer full) (nullable): the location for this URI. Returns
456 * %NULL if the URI isn't valid. If the URI does not contain a location, an
457 * empty string is returned.
460 gst_uri_get_location (const gchar * uri)
463 gchar *unescaped = NULL;
465 if (!gst_uri_is_valid (uri)) {
469 colon = strstr (uri, "://");
473 unescaped = unescape_string (colon + 3, "/");
475 /* On Windows an URI might look like file:///c:/foo/bar.txt or
476 * file:///c|/foo/bar.txt (some Netscape versions) and we want to
477 * return c:/foo/bar.txt as location rather than /c:/foo/bar.txt.
478 * Can't use g_filename_from_uri() here because it will only handle the
479 * file:// protocol */
481 if (unescaped != NULL && unescaped[0] == '/' &&
482 g_ascii_isalpha (unescaped[1]) &&
483 (unescaped[2] == ':' || unescaped[2] == '|')) {
485 memmove (unescaped, unescaped + 1, strlen (unescaped + 1) + 1);
489 GST_LOG ("extracted location '%s' from URI '%s'", GST_STR_NULL (unescaped),
496 * @protocol: Protocol for URI
497 * @location: (transfer none): Location for URI
499 * Constructs a URI for a given valid protocol and location.
501 * Free-function: g_free
503 * Returns: (transfer full): a new string for this URI.
505 * Deprecated: Use GstURI instead.
507 #ifndef GST_REMOVE_DEPRECATED
509 gst_uri_construct (const gchar * protocol, const gchar * location)
511 char *escaped, *proto_lowercase;
514 g_return_val_if_fail (gst_uri_protocol_is_valid (protocol), NULL);
515 g_return_val_if_fail (location != NULL, NULL);
517 proto_lowercase = g_ascii_strdown (protocol, -1);
518 escaped = escape_string_internal (location, UNSAFE_PATH);
519 retval = g_strdup_printf ("%s://%s", proto_lowercase, escaped);
521 g_free (proto_lowercase);
530 const gchar *protocol;
535 search_by_entry (GstPluginFeature * feature, gpointer search_entry)
537 const gchar *const *protocols;
538 GstElementFactory *factory;
539 SearchEntry *entry = (SearchEntry *) search_entry;
541 if (!GST_IS_ELEMENT_FACTORY (feature))
543 factory = GST_ELEMENT_FACTORY_CAST (feature);
545 if (factory->uri_type != entry->type)
548 protocols = gst_element_factory_get_uri_protocols (factory);
550 if (protocols == NULL) {
551 g_warning ("Factory '%s' implements GstUriHandler interface but returned "
552 "no supported protocols!", gst_plugin_feature_get_name (feature));
556 while (*protocols != NULL) {
557 if (g_ascii_strcasecmp (*protocols, entry->protocol) == 0)
565 sort_by_rank (GstPluginFeature * first, GstPluginFeature * second)
567 return gst_plugin_feature_get_rank (second) -
568 gst_plugin_feature_get_rank (first);
572 get_element_factories_from_uri_protocol (const GstURIType type,
573 const gchar * protocol)
575 GList *possibilities;
578 g_return_val_if_fail (protocol, NULL);
581 entry.protocol = protocol;
582 possibilities = gst_registry_feature_filter (gst_registry_get (),
583 search_by_entry, FALSE, &entry);
585 return possibilities;
589 * gst_uri_protocol_is_supported:
590 * @type: Whether to check for a source or a sink
591 * @protocol: Protocol that should be checked for (e.g. "http" or "smb")
593 * Checks if an element exists that supports the given URI protocol. Note
594 * that a positive return value does not imply that a subsequent call to
595 * gst_element_make_from_uri() is guaranteed to work.
600 gst_uri_protocol_is_supported (const GstURIType type, const gchar * protocol)
602 GList *possibilities;
604 g_return_val_if_fail (protocol, FALSE);
606 possibilities = get_element_factories_from_uri_protocol (type, protocol);
609 g_list_free (possibilities);
616 * gst_element_make_from_uri:
617 * @type: Whether to create a source or a sink
618 * @uri: URI to create an element for
619 * @elementname: (nullable): Name of created element, can be %NULL.
620 * @error: address where to store error information, or %NULL.
622 * Creates an element for handling the given URI.
624 * Returns: (transfer floating): a new element or %NULL if none
628 gst_element_make_from_uri (const GstURIType type, const gchar * uri,
629 const gchar * elementname, GError ** error)
631 GList *possibilities, *walk;
633 GstElement *ret = NULL;
635 g_return_val_if_fail (gst_is_initialized (), NULL);
636 g_return_val_if_fail (GST_URI_TYPE_IS_VALID (type), NULL);
637 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
639 if (!gst_uri_is_valid (uri)) {
640 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
641 _("Invalid URI: %s"), uri);
645 GST_DEBUG ("type:%d, uri:%s, elementname:%s", type, uri, elementname);
647 protocol = gst_uri_get_protocol (uri);
648 possibilities = get_element_factories_from_uri_protocol (type, protocol);
650 if (!possibilities) {
651 GST_DEBUG ("No %s for URI '%s'", type == GST_URI_SINK ? "sink" : "source",
653 /* The error message isn't great, but we don't expect applications to
654 * show that error to users, but call the missing plugins functions */
655 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_UNSUPPORTED_PROTOCOL,
656 _("No URI handler for the %s protocol found"), protocol);
662 possibilities = g_list_sort (possibilities, (GCompareFunc) sort_by_rank);
663 walk = possibilities;
665 GstElementFactory *factory = walk->data;
666 GError *uri_err = NULL;
668 ret = gst_element_factory_create (factory, elementname);
670 GstURIHandler *handler = GST_URI_HANDLER (ret);
672 if (gst_uri_handler_set_uri (handler, uri, &uri_err))
675 GST_WARNING ("%s didn't accept URI '%s': %s", GST_OBJECT_NAME (ret), uri,
678 if (error != NULL && *error == NULL)
679 g_propagate_error (error, uri_err);
681 g_error_free (uri_err);
683 gst_object_unref (ret);
688 gst_plugin_feature_list_free (possibilities);
690 GST_LOG_OBJECT (ret, "created %s for URL '%s'",
691 type == GST_URI_SINK ? "sink" : "source", uri);
693 /* if the first handler didn't work, but we found another one that works */
695 g_clear_error (error);
701 * gst_uri_handler_get_uri_type:
702 * @handler: A #GstURIHandler.
704 * Gets the type of the given URI handler
706 * Returns: the #GstURIType of the URI handler.
707 * Returns #GST_URI_UNKNOWN if the @handler isn't implemented correctly.
710 gst_uri_handler_get_uri_type (GstURIHandler * handler)
712 GstURIHandlerInterface *iface;
715 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN);
717 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
718 g_return_val_if_fail (iface != NULL, GST_URI_UNKNOWN);
719 g_return_val_if_fail (iface->get_type != NULL, GST_URI_UNKNOWN);
721 ret = iface->get_type (G_OBJECT_TYPE (handler));
722 g_return_val_if_fail (GST_URI_TYPE_IS_VALID (ret), GST_URI_UNKNOWN);
728 * gst_uri_handler_get_protocols:
729 * @handler: A #GstURIHandler.
731 * Gets the list of protocols supported by @handler. This list may not be
734 * Returns: (transfer none) (element-type utf8) (nullable) (array zero-terminated=1): the
735 * supported protocols. Returns %NULL if the @handler isn't
736 * implemented properly, or the @handler doesn't support any
740 gst_uri_handler_get_protocols (GstURIHandler * handler)
742 GstURIHandlerInterface *iface;
743 const gchar *const *ret;
745 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
747 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
748 g_return_val_if_fail (iface != NULL, NULL);
749 g_return_val_if_fail (iface->get_protocols != NULL, NULL);
751 ret = iface->get_protocols (G_OBJECT_TYPE (handler));
752 g_return_val_if_fail (ret != NULL, NULL);
758 * gst_uri_handler_get_uri:
759 * @handler: A #GstURIHandler
761 * Gets the currently handled URI.
763 * Returns: (transfer full) (nullable): the URI currently handled by
764 * the @handler. Returns %NULL if there are no URI currently
765 * handled. The returned string must be freed with g_free() when no
769 gst_uri_handler_get_uri (GstURIHandler * handler)
771 GstURIHandlerInterface *iface;
774 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
776 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
777 g_return_val_if_fail (iface != NULL, NULL);
778 g_return_val_if_fail (iface->get_uri != NULL, NULL);
779 ret = iface->get_uri (handler);
781 g_return_val_if_fail (gst_uri_is_valid (ret), NULL);
787 * gst_uri_handler_set_uri:
788 * @handler: A #GstURIHandler
790 * @error: address where to store a #GError in case of
793 * Tries to set the URI of the given handler.
795 * Returns: %TRUE if the URI was set successfully, else %FALSE.
798 gst_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri,
801 GstURIHandlerInterface *iface;
805 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE);
806 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
808 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
809 g_return_val_if_fail (iface != NULL, FALSE);
810 g_return_val_if_fail (iface->set_uri != NULL, FALSE);
812 if (!gst_uri_is_valid (uri)) {
813 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
814 _("Invalid URI: %s"), uri);
818 protocol = gst_uri_get_protocol (uri);
820 if (iface->get_protocols) {
821 const gchar *const *protocols;
822 const gchar *const *p;
823 gboolean found_protocol = FALSE;
825 protocols = iface->get_protocols (G_OBJECT_TYPE (handler));
826 if (protocols != NULL) {
827 for (p = protocols; *p != NULL; ++p) {
828 if (g_ascii_strcasecmp (protocol, *p) == 0) {
829 found_protocol = TRUE;
834 if (!found_protocol) {
835 g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_UNSUPPORTED_PROTOCOL,
836 _("URI scheme '%s' not supported"), protocol);
843 ret = iface->set_uri (handler, uri, error);
851 gst_file_utils_canonicalise_path (const gchar * path)
853 gchar **parts, **p, *clean_path;
857 GST_WARNING ("FIXME: canonicalise win32 path");
858 return g_strdup (path);
862 parts = g_strsplit (path, "/", -1);
866 if (strcmp (*p, ".") == 0) {
867 /* just move all following parts on top of this, incl. NUL terminator */
869 memmove (p, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *));
870 /* re-check the new current part again in the next iteration */
872 } else if (strcmp (*p, "..") == 0 && p > parts) {
873 /* just move all following parts on top of the previous part, incl.
877 memmove (p - 1, p + 1, (g_strv_length (p + 1) + 1) * sizeof (gchar *));
878 /* re-check the new current part again in the next iteration */
887 num_parts = g_strv_length (parts) + 1; /* incl. terminator */
888 parts = g_renew (gchar *, parts, num_parts + 1);
889 memmove (parts + 1, parts, num_parts * sizeof (gchar *));
890 parts[0] = g_strdup ("/");
893 clean_path = g_build_filenamev (parts);
899 file_path_contains_relatives (const gchar * path)
901 return (strstr (path, "/./") != NULL || strstr (path, "/../") != NULL ||
902 strstr (path, G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) != NULL ||
903 strstr (path, G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) != NULL);
907 * gst_filename_to_uri:
908 * @filename: (type filename): absolute or relative file name path
909 * @error: pointer to error, or %NULL
911 * Similar to g_filename_to_uri(), but attempts to handle relative file paths
912 * as well. Before converting @filename into an URI, it will be prefixed by
913 * the current working directory if it is a relative path, and then the path
914 * will be canonicalised so that it doesn't contain any './' or '../' segments.
916 * On Windows @filename should be in UTF-8 encoding.
918 * Returns: (nullable): newly-allocated URI string, or NULL on error. The caller must
919 * free the URI string with g_free() when no longer needed.
922 gst_filename_to_uri (const gchar * filename, GError ** error)
924 gchar *abs_location = NULL;
925 gchar *uri, *abs_clean;
927 g_return_val_if_fail (filename != NULL, NULL);
928 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
930 if (g_path_is_absolute (filename)) {
931 if (!file_path_contains_relatives (filename)) {
932 uri = g_filename_to_uri (filename, NULL, error);
936 abs_location = g_strdup (filename);
940 cwd = g_get_current_dir ();
941 abs_location = g_build_filename (cwd, filename, NULL);
944 if (!file_path_contains_relatives (abs_location)) {
945 uri = g_filename_to_uri (abs_location, NULL, error);
950 /* path is now absolute, but contains '.' or '..' */
951 abs_clean = gst_file_utils_canonicalise_path (abs_location);
952 GST_LOG ("'%s' -> '%s' -> '%s'", filename, abs_location, abs_clean);
953 uri = g_filename_to_uri (abs_clean, NULL, error);
958 g_free (abs_location);
959 GST_DEBUG ("'%s' -> '%s'", filename, uri);
963 /****************************************************************************
964 * GstUri - GstMiniObject to parse and merge URIs according to IETF RFC 3986
965 ****************************************************************************/
970 * @short_description: URI parsing and manipulation.
972 * A #GstUri object can be used to parse and split a URI string into its
973 * constituent parts. Two #GstUri objects can be joined to make a new #GstUri
974 * using the algorithm described in RFC3986.
977 /* Definition for GstUri object */
981 GstMiniObject mini_object;
991 GST_DEFINE_MINI_OBJECT_TYPE (GstUri, gst_uri);
993 static GstUri *_gst_uri_copy (const GstUri * uri);
994 static void _gst_uri_free (GstUri * uri);
995 static GstUri *_gst_uri_new (void);
996 static GList *_remove_dot_segments (GList * path);
998 /* private GstUri functions */
1005 g_return_val_if_fail (gst_is_initialized (), NULL);
1007 uri = GST_URI_CAST (g_slice_new0 (GstUri));
1010 gst_mini_object_init (GST_MINI_OBJECT_CAST (uri), 0, gst_uri_get_type (),
1011 (GstMiniObjectCopyFunction) _gst_uri_copy, NULL,
1012 (GstMiniObjectFreeFunction) _gst_uri_free);
1018 _gst_uri_free (GstUri * uri)
1020 g_return_if_fail (GST_IS_URI (uri));
1022 g_free (uri->scheme);
1023 g_free (uri->userinfo);
1025 g_list_free_full (uri->path, g_free);
1027 g_hash_table_unref (uri->query);
1028 g_free (uri->fragment);
1030 #ifdef USE_POISONING
1031 memset (uri, 0xff, sizeof (*uri));
1034 g_slice_free1 (sizeof (*uri), uri);
1038 _gst_uri_copy_query_table (GHashTable * orig)
1040 GHashTable *new = NULL;
1043 GHashTableIter iter;
1044 gpointer key, value;
1045 new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1046 g_hash_table_iter_init (&iter, orig);
1047 while (g_hash_table_iter_next (&iter, &key, &value)) {
1048 g_hash_table_insert (new, g_strdup (key), g_strdup (value));
1056 _gst_uri_copy (const GstUri * orig_uri)
1060 g_return_val_if_fail (GST_IS_URI (orig_uri), NULL);
1062 new_uri = _gst_uri_new ();
1065 new_uri->scheme = g_strdup (orig_uri->scheme);
1066 new_uri->userinfo = g_strdup (orig_uri->userinfo);
1067 new_uri->host = g_strdup (orig_uri->host);
1068 new_uri->port = orig_uri->port;
1069 new_uri->path = g_list_copy_deep (orig_uri->path, (GCopyFunc) g_strdup,
1071 new_uri->query = _gst_uri_copy_query_table (orig_uri->query);
1072 new_uri->fragment = g_strdup (orig_uri->fragment);
1079 * _gst_uri_compare_lists:
1081 * Compare two lists for equality. This compares the two lists, item for item,
1082 * comparing items in the same position in the two lists. If @first is
1083 * considered less than @second the result will be negative. If @first is
1084 * considered to be more than @second then the result will be positive. If the
1085 * lists are considered to be equal then the result will be 0. If two lists
1086 * have the same items, but one list is shorter than the other, then the
1087 * shorter list is considered to be less than the longer list.
1090 _gst_uri_compare_lists (GList * first, GList * second, GCompareFunc cmp_fn)
1095 for (itr1 = first, itr2 = second;
1096 itr1 != NULL || itr2 != NULL; itr1 = itr1->next, itr2 = itr2->next) {
1101 result = cmp_fn (itr1->data, itr2->data);
1110 _GST_URI_NORMALIZE_LOWERCASE = 1,
1111 _GST_URI_NORMALIZE_UPPERCASE = 2
1112 } _GstUriNormalizations;
1115 * Find the first character that hasn't been normalized according to the @flags.
1118 _gst_uri_first_non_normalized_char (gchar * str, guint flags)
1125 for (pos = str; *pos; pos++) {
1126 if ((flags & _GST_URI_NORMALIZE_UPPERCASE) && g_ascii_islower (*pos))
1128 if ((flags & _GST_URI_NORMALIZE_LOWERCASE) && g_ascii_isupper (*pos))
1135 _gst_uri_normalize_lowercase (gchar * str)
1138 gboolean ret = FALSE;
1140 for (pos = _gst_uri_first_non_normalized_char (str,
1141 _GST_URI_NORMALIZE_LOWERCASE);
1143 pos = _gst_uri_first_non_normalized_char (pos + 1,
1144 _GST_URI_NORMALIZE_LOWERCASE)) {
1145 *pos = g_ascii_tolower (*pos);
1152 #define _gst_uri_normalize_scheme _gst_uri_normalize_lowercase
1153 #define _gst_uri_normalize_hostname _gst_uri_normalize_lowercase
1156 _gst_uri_normalize_path (GList ** path)
1160 new_path = _remove_dot_segments (*path);
1161 if (_gst_uri_compare_lists (new_path, *path, (GCompareFunc) g_strcmp0) != 0) {
1162 g_list_free_full (*path, g_free);
1166 g_list_free_full (new_path, g_free);
1172 _gst_uri_normalize_str_noop (gchar * str)
1178 _gst_uri_normalize_table_noop (GHashTable * table)
1183 #define _gst_uri_normalize_userinfo _gst_uri_normalize_str_noop
1184 #define _gst_uri_normalize_query _gst_uri_normalize_table_noop
1185 #define _gst_uri_normalize_fragment _gst_uri_normalize_str_noop
1187 /* RFC 3986 functions */
1190 _merge (GList * base, GList * path)
1192 GList *ret, *path_copy, *last;
1194 path_copy = g_list_copy_deep (path, (GCopyFunc) g_strdup, NULL);
1195 /* if base is NULL make path absolute */
1197 if (path_copy != NULL && path_copy->data != NULL) {
1198 path_copy = g_list_prepend (path_copy, NULL);
1203 ret = g_list_copy_deep (base, (GCopyFunc) g_strdup, NULL);
1204 last = g_list_last (ret);
1205 ret = g_list_remove_link (ret, last);
1206 g_list_free_full (last, g_free);
1207 ret = g_list_concat (ret, path_copy);
1213 _remove_dot_segments (GList * path)
1215 GList *out, *elem, *next;
1220 out = g_list_copy_deep (path, (GCopyFunc) g_strdup, NULL);
1222 for (elem = out; elem; elem = next) {
1224 if (elem->data == NULL && elem != out && next != NULL) {
1225 out = g_list_delete_link (out, elem);
1226 } else if (g_strcmp0 (elem->data, ".") == 0) {
1227 g_free (elem->data);
1228 out = g_list_delete_link (out, elem);
1229 } else if (g_strcmp0 (elem->data, "..") == 0) {
1230 GList *prev = g_list_previous (elem);
1231 if (prev && (prev != out || prev->data != NULL)) {
1232 g_free (prev->data);
1233 out = g_list_delete_link (out, prev);
1235 g_free (elem->data);
1237 out = g_list_delete_link (out, elem);
1239 /* path ends in '/..' We need to keep the last '/' */
1249 _gst_uri_escape_userinfo (const gchar * userinfo)
1251 return g_uri_escape_string (userinfo,
1252 G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO, FALSE);
1256 _gst_uri_escape_host (const gchar * host)
1258 return g_uri_escape_string (host,
1259 G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS, FALSE);
1263 _gst_uri_escape_host_colon (const gchar * host)
1265 return g_uri_escape_string (host,
1266 G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS ":", FALSE);
1270 _gst_uri_escape_path_segment (const gchar * segment)
1272 return g_uri_escape_string (segment,
1273 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT, FALSE);
1277 _gst_uri_escape_http_query_element (const gchar * element)
1281 ret = g_uri_escape_string (element, "!$'()*,;:@/?= ", FALSE);
1282 for (c = ret; *c; c++)
1289 _gst_uri_escape_fragment (const gchar * fragment)
1291 return g_uri_escape_string (fragment,
1292 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH "?", FALSE);
1296 _gst_uri_string_to_list (const gchar * str, const gchar * sep, gboolean convert,
1299 GList *new_list = NULL;
1302 guint pct_sep_len = 0;
1303 gchar *pct_sep = NULL;
1306 if (convert && !unescape) {
1307 pct_sep = g_strdup_printf ("%%%2.2X", (guint) (*sep));
1311 split_str = g_strsplit (str, sep, -1);
1314 for (next_elem = split_str; *next_elem; next_elem += 1) {
1315 gchar *elem = *next_elem;
1316 if (*elem == '\0') {
1317 new_list = g_list_prepend (new_list, NULL);
1319 if (convert && !unescape) {
1321 for (next_sep = strcasestr (elem, pct_sep); next_sep;
1322 next_sep = strcasestr (next_sep + 1, pct_sep)) {
1324 memmove (next_sep + 1, next_sep + pct_sep_len,
1325 strlen (next_sep + pct_sep_len) + 1);
1329 *next_elem = g_uri_unescape_string (elem, NULL);
1333 new_list = g_list_prepend (new_list, g_strdup (elem));
1337 g_strfreev (split_str);
1338 if (convert && !unescape)
1342 return g_list_reverse (new_list);
1346 _gst_uri_string_to_table (const gchar * str, const gchar * part_sep,
1347 const gchar * kv_sep, gboolean convert, gboolean unescape)
1349 GHashTable *new_table = NULL;
1352 gchar *pct_part_sep = NULL, *pct_kv_sep = NULL;
1353 gchar **split_parts;
1355 new_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1357 if (convert && !unescape) {
1358 pct_part_sep = g_strdup_printf ("%%%2.2X", (guint) (*part_sep));
1359 pct_kv_sep = g_strdup_printf ("%%%2.2X", (guint) (*kv_sep));
1362 split_parts = g_strsplit (str, part_sep, -1);
1365 for (next_part = split_parts; *next_part; next_part += 1) {
1366 gchar *part = *next_part;
1369 /* if we are converting percent encoded versions of separators then
1370 * substitute the part separator now. */
1371 if (convert && !unescape) {
1373 for (next_sep = strcasestr (part, pct_part_sep); next_sep;
1374 next_sep = strcasestr (next_sep + 1, pct_part_sep)) {
1375 *next_sep = *part_sep;
1376 memmove (next_sep + 1, next_sep + 3, strlen (next_sep + 3) + 1);
1379 /* find the key/value separator within the part */
1380 kv_sep_pos = g_strstr_len (part, -1, kv_sep);
1381 if (kv_sep_pos == NULL) {
1383 key = g_uri_unescape_string (part, NULL);
1385 key = g_strdup (part);
1390 key = g_uri_unescape_segment (part, kv_sep_pos, NULL);
1391 value = g_uri_unescape_string (kv_sep_pos + 1, NULL);
1393 key = g_strndup (part, kv_sep_pos - part);
1394 value = g_strdup (kv_sep_pos + 1);
1397 /* if we are converting percent encoded versions of separators then
1398 * substitute the key/value separator in both key and value now. */
1399 if (convert && !unescape) {
1401 for (next_sep = strcasestr (key, pct_kv_sep); next_sep;
1402 next_sep = strcasestr (next_sep + 1, pct_kv_sep)) {
1403 *next_sep = *kv_sep;
1404 memmove (next_sep + 1, next_sep + 3, strlen (next_sep + 3) + 1);
1407 for (next_sep = strcasestr (value, pct_kv_sep); next_sep;
1408 next_sep = strcasestr (next_sep + 1, pct_kv_sep)) {
1409 *next_sep = *kv_sep;
1410 memmove (next_sep + 1, next_sep + 3, strlen (next_sep + 3) + 1);
1414 /* add value to the table */
1415 g_hash_table_insert (new_table, key, value);
1419 g_strfreev (split_parts);
1420 if (convert && !unescape) {
1421 g_free (pct_part_sep);
1422 g_free (pct_kv_sep);
1431 * Method definitions.
1436 * @scheme: (nullable): The scheme for the new URI.
1437 * @userinfo: (nullable): The user-info for the new URI.
1438 * @host: (nullable): The host name for the new URI.
1439 * @port: The port number for the new URI or %GST_URI_NO_PORT.
1440 * @path: (nullable): The path for the new URI with '/' separating path
1442 * @query: (nullable): The query string for the new URI with '&' separating
1443 * query elements. Elements containing '&' characters
1444 * should encode them as "%26".
1445 * @fragment: (nullable): The fragment name for the new URI.
1447 * Creates a new #GstUri object with the given URI parts. The path and query
1448 * strings will be broken down into their elements. All strings should not be
1449 * escaped except where indicated.
1451 * Returns: (transfer full): A new #GstUri object.
1456 gst_uri_new (const gchar * scheme, const gchar * userinfo, const gchar * host,
1457 guint port, const gchar * path, const gchar * query, const gchar * fragment)
1461 new_uri = _gst_uri_new ();
1463 new_uri->scheme = g_strdup (scheme);
1464 new_uri->userinfo = g_strdup (userinfo);
1465 new_uri->host = g_strdup (host);
1466 new_uri->port = port;
1467 new_uri->path = _gst_uri_string_to_list (path, "/", FALSE, FALSE);
1468 new_uri->query = _gst_uri_string_to_table (query, "&", "=", TRUE, FALSE);
1469 new_uri->fragment = g_strdup (fragment);
1476 * gst_uri_new_with_base:
1477 * @base: (transfer none)(nullable): The base URI to join the new URI to.
1478 * @scheme: (nullable): The scheme for the new URI.
1479 * @userinfo: (nullable): The user-info for the new URI.
1480 * @host: (nullable): The host name for the new URI.
1481 * @port: The port number for the new URI or %GST_URI_NO_PORT.
1482 * @path: (nullable): The path for the new URI with '/' separating path
1484 * @query: (nullable): The query string for the new URI with '&' separating
1485 * query elements. Elements containing '&' characters
1486 * should encode them as "%26".
1487 * @fragment: (nullable): The fragment name for the new URI.
1489 * Like gst_uri_new(), but joins the new URI onto a base URI.
1491 * Returns: (transfer full): The new URI joined onto @base.
1496 gst_uri_new_with_base (GstUri * base, const gchar * scheme,
1497 const gchar * userinfo, const gchar * host, guint port, const gchar * path,
1498 const gchar * query, const gchar * fragment)
1500 GstUri *new_rel_uri;
1503 g_return_val_if_fail (base == NULL || GST_IS_URI (base), NULL);
1505 new_rel_uri = gst_uri_new (scheme, userinfo, host, port, path, query,
1507 new_uri = gst_uri_join (base, new_rel_uri);
1508 gst_uri_unref (new_rel_uri);
1514 _gst_uri_from_string_internal (const gchar * uri, gboolean unescape)
1516 const gchar *orig_uri = uri;
1519 uri_obj = _gst_uri_new ();
1521 if (uri_obj && uri != NULL) {
1524 /* be helpful and skip initial white space */
1525 while (*uri == '\v' || g_ascii_isspace (*uri))
1528 if (g_ascii_isalpha (uri[i])) {
1529 /* find end of scheme name */
1531 while (g_ascii_isalnum (uri[i]) || uri[i] == '+' || uri[i] == '-' ||
1535 if (i > 0 && uri[i] == ':') {
1537 uri_obj->scheme = g_strndup (uri, i);
1540 if (uri[0] == '/' && uri[1] == '/') {
1541 const gchar *eoa, *eoui, *eoh, *reoh;
1542 /* get authority [userinfo@]host[:port] */
1544 /* find end of authority */
1545 eoa = uri + strcspn (uri, "/?#");
1547 /* find end of userinfo */
1548 eoui = strchr (uri, '@');
1549 if (eoui != NULL && eoui < eoa) {
1551 uri_obj->userinfo = g_uri_unescape_segment (uri, eoui, NULL);
1553 uri_obj->userinfo = g_strndup (uri, eoui - uri);
1556 /* find end of host */
1557 if (uri[0] == '[') {
1558 eoh = strchr (uri, ']');
1559 if (eoh == NULL || eoh > eoa) {
1560 GST_DEBUG ("Unable to parse the host part of the URI '%s'.",
1562 gst_uri_unref (uri_obj);
1568 reoh = eoh = strchr (uri, ':');
1569 if (eoh == NULL || eoh > eoa)
1572 /* don't capture empty host strings */
1574 /* always unescape hostname */
1575 uri_obj->host = g_uri_unescape_segment (uri, eoh, NULL);
1580 /* if port number is malformed then we can't parse this */
1581 if (uri[0] != ':' || strspn (uri + 1, "0123456789") != eoa - uri - 1) {
1582 GST_DEBUG ("Unable to parse host/port part of the URI '%s'.",
1584 gst_uri_unref (uri_obj);
1587 /* otherwise treat port as unsigned decimal number */
1590 uri_obj->port = uri_obj->port * 10 + g_ascii_digit_value (*uri);
1596 if (uri != NULL && uri[0] != '\0') {
1599 len = strcspn (uri, "?#");
1600 if (uri[len] == '\0') {
1601 uri_obj->path = _gst_uri_string_to_list (uri, "/", FALSE, TRUE);
1605 gchar *path_str = g_strndup (uri, len);
1606 uri_obj->path = _gst_uri_string_to_list (path_str, "/", FALSE, TRUE);
1612 if (uri != NULL && uri[0] == '?') {
1615 eoq = strchr (++uri, '#');
1617 uri_obj->query = _gst_uri_string_to_table (uri, "&", "=", TRUE, TRUE);
1621 gchar *query_str = g_strndup (uri, eoq - uri);
1622 uri_obj->query = _gst_uri_string_to_table (query_str, "&", "=", TRUE,
1629 if (uri != NULL && uri[0] == '#') {
1631 uri_obj->fragment = g_uri_unescape_string (uri + 1, NULL);
1633 uri_obj->fragment = g_strdup (uri + 1);
1641 * gst_uri_from_string:
1642 * @uri: The URI string to parse.
1644 * Parses a URI string into a new #GstUri object. Will return NULL if the URI
1647 * Returns: (transfer full) (nullable): A new #GstUri object, or NULL.
1652 gst_uri_from_string (const gchar * uri)
1654 return _gst_uri_from_string_internal (uri, TRUE);
1658 * gst_uri_from_string_escaped:
1659 * @uri: The URI string to parse.
1661 * Parses a URI string into a new #GstUri object. Will return NULL if the URI
1662 * cannot be parsed. This is identical to gst_uri_from_string() except that
1663 * the userinfo and fragment components of the URI will not be unescaped while
1666 * Use this when you need to extract a username and password from the userinfo
1667 * such as https://user:password@example.com since either may contain
1668 * a URI-escaped ':' character. gst_uri_from_string() will unescape the entire
1669 * userinfo component, which will make it impossible to know which ':'
1670 * delineates the username and password.
1672 * The same applies to the fragment component of the URI, such as
1673 * https://example.com/path#fragment which may contain a URI-escaped '#'.
1675 * Returns: (transfer full) (nullable): A new #GstUri object, or NULL.
1680 gst_uri_from_string_escaped (const gchar * uri)
1682 return _gst_uri_from_string_internal (uri, FALSE);
1686 * gst_uri_from_string_with_base:
1687 * @base: (transfer none)(nullable): The base URI to join the new URI with.
1688 * @uri: The URI string to parse.
1690 * Like gst_uri_from_string() but also joins with a base URI.
1692 * Returns: (transfer full) (nullable): A new #GstUri object.
1697 gst_uri_from_string_with_base (GstUri * base, const gchar * uri)
1699 GstUri *new_rel_uri;
1702 g_return_val_if_fail (base == NULL || GST_IS_URI (base), NULL);
1704 new_rel_uri = gst_uri_from_string (uri);
1708 new_uri = gst_uri_join (base, new_rel_uri);
1709 gst_uri_unref (new_rel_uri);
1716 * @first: First #GstUri to compare.
1717 * @second: Second #GstUri to compare.
1719 * Compares two #GstUri objects to see if they represent the same normalized
1722 * Returns: %TRUE if the normalized versions of the two URI's would be equal.
1727 gst_uri_equal (const GstUri * first, const GstUri * second)
1729 gchar *first_norm = NULL, *second_norm = NULL;
1730 GList *first_norm_list = NULL, *second_norm_list = NULL;
1731 const gchar *first_cmp, *second_cmp;
1732 GHashTableIter table_iter;
1733 gpointer key, value;
1736 g_return_val_if_fail ((first == NULL || GST_IS_URI (first)) &&
1737 (second == NULL || GST_IS_URI (second)), FALSE);
1739 if (first == second)
1742 if (first == NULL || second == NULL)
1745 if (first->port != second->port)
1748 /* work out a version of field value (normalized or not) to compare.
1749 * first_cmp, second_cmp will be the values to compare later.
1750 * first_norm, second_norm will be non-NULL if normalized versions are used,
1751 * and need to be freed later.
1753 #define GST_URI_NORMALIZED_FIELD(pos, field, norm_fn, flags) \
1754 pos##_cmp = pos->field; \
1755 if (_gst_uri_first_non_normalized_char ((gchar*)pos##_cmp, flags) != NULL) { \
1756 pos##_norm = g_strdup (pos##_cmp); \
1757 norm_fn (pos##_norm); \
1758 pos##_cmp = pos##_norm; \
1761 /* compare two string values, normalizing if needed */
1762 #define GST_URI_NORMALIZED_CMP_STR(field, norm_fn, flags) \
1763 GST_URI_NORMALIZED_FIELD (first, field, norm_fn, flags) \
1764 GST_URI_NORMALIZED_FIELD (second, field, norm_fn, flags) \
1765 result = g_strcmp0 (first_cmp, second_cmp); \
1766 g_free (first_norm); \
1767 first_norm = NULL; \
1768 g_free (second_norm); \
1769 second_norm = NULL; \
1770 if (result != 0) return FALSE
1772 /* compare two string values */
1773 #define GST_URI_CMP_STR(field) \
1774 if (g_strcmp0 (first->field, second->field) != 0) return FALSE
1776 /* compare two GLists, normalize lists if needed before comparison */
1777 #define GST_URI_NORMALIZED_CMP_LIST(field, norm_fn, copy_fn, cmp_fn, free_fn) \
1778 first_norm_list = g_list_copy_deep (first->field, (GCopyFunc) copy_fn, NULL); \
1779 norm_fn (&first_norm_list); \
1780 second_norm_list = g_list_copy_deep (second->field, (GCopyFunc) copy_fn, NULL); \
1781 norm_fn (&second_norm_list); \
1782 result = _gst_uri_compare_lists (first_norm_list, second_norm_list, (GCompareFunc) cmp_fn); \
1783 g_list_free_full (first_norm_list, free_fn); \
1784 g_list_free_full (second_norm_list, free_fn); \
1785 if (result != 0) return FALSE
1787 GST_URI_CMP_STR (userinfo);
1789 GST_URI_CMP_STR (fragment);
1791 GST_URI_NORMALIZED_CMP_STR (scheme, _gst_uri_normalize_scheme,
1792 _GST_URI_NORMALIZE_LOWERCASE);
1794 GST_URI_NORMALIZED_CMP_STR (host, _gst_uri_normalize_hostname,
1795 _GST_URI_NORMALIZE_LOWERCASE);
1797 GST_URI_NORMALIZED_CMP_LIST (path, _gst_uri_normalize_path, g_strdup,
1800 if (first->query == NULL && second->query != NULL)
1802 if (first->query != NULL && second->query == NULL)
1804 if (first->query != NULL) {
1805 if (g_hash_table_size (first->query) != g_hash_table_size (second->query))
1808 g_hash_table_iter_init (&table_iter, first->query);
1809 while (g_hash_table_iter_next (&table_iter, &key, &value)) {
1810 if (!g_hash_table_contains (second->query, key))
1812 result = g_strcmp0 (g_hash_table_lookup (second->query, key), value);
1817 #undef GST_URI_NORMALIZED_CMP_STR
1818 #undef GST_URI_CMP_STR
1819 #undef GST_URI_NORMALIZED_CMP_LIST
1820 #undef GST_URI_NORMALIZED_FIELD
1827 * @base_uri: (transfer none) (nullable): The base URI to join another to.
1828 * @ref_uri: (transfer none) (nullable): The reference URI to join onto the
1831 * Join a reference URI onto a base URI using the method from RFC 3986.
1832 * If either URI is %NULL then the other URI will be returned with the ref count
1835 * Returns: (transfer full) (nullable): A #GstUri which represents the base
1836 * with the reference URI joined on.
1841 gst_uri_join (GstUri * base_uri, GstUri * ref_uri)
1843 const gchar *r_scheme;
1846 g_return_val_if_fail ((base_uri == NULL || GST_IS_URI (base_uri)) &&
1847 (ref_uri == NULL || GST_IS_URI (ref_uri)), NULL);
1849 if (base_uri == NULL && ref_uri == NULL)
1851 if (base_uri == NULL) {
1852 g_return_val_if_fail (GST_IS_URI (ref_uri), NULL);
1853 return gst_uri_ref (ref_uri);
1855 if (ref_uri == NULL) {
1856 g_return_val_if_fail (GST_IS_URI (base_uri), NULL);
1857 return gst_uri_ref (base_uri);
1860 g_return_val_if_fail (GST_IS_URI (base_uri) && GST_IS_URI (ref_uri), NULL);
1862 t = _gst_uri_new ();
1867 /* process according to RFC3986 */
1868 r_scheme = ref_uri->scheme;
1869 if (r_scheme != NULL && g_strcmp0 (base_uri->scheme, r_scheme) == 0) {
1872 if (r_scheme != NULL) {
1873 t->scheme = g_strdup (r_scheme);
1874 t->userinfo = g_strdup (ref_uri->userinfo);
1875 t->host = g_strdup (ref_uri->host);
1876 t->port = ref_uri->port;
1877 t->path = _remove_dot_segments (ref_uri->path);
1878 t->query = _gst_uri_copy_query_table (ref_uri->query);
1880 if (ref_uri->host != NULL) {
1881 t->userinfo = g_strdup (ref_uri->userinfo);
1882 t->host = g_strdup (ref_uri->host);
1883 t->port = ref_uri->port;
1884 t->path = _remove_dot_segments (ref_uri->path);
1885 t->query = _gst_uri_copy_query_table (ref_uri->query);
1887 if (ref_uri->path == NULL) {
1888 t->path = g_list_copy_deep (base_uri->path, (GCopyFunc) g_strdup, NULL);
1889 if (ref_uri->query != NULL)
1890 t->query = _gst_uri_copy_query_table (ref_uri->query);
1892 t->query = _gst_uri_copy_query_table (base_uri->query);
1894 if (ref_uri->path->data == NULL)
1895 t->path = _remove_dot_segments (ref_uri->path);
1897 GList *mrgd = _merge (base_uri->path, ref_uri->path);
1898 t->path = _remove_dot_segments (mrgd);
1899 g_list_free_full (mrgd, g_free);
1901 t->query = _gst_uri_copy_query_table (ref_uri->query);
1903 t->userinfo = g_strdup (base_uri->userinfo);
1904 t->host = g_strdup (base_uri->host);
1905 t->port = base_uri->port;
1907 t->scheme = g_strdup (base_uri->scheme);
1909 t->fragment = g_strdup (ref_uri->fragment);
1915 * gst_uri_join_strings:
1916 * @base_uri: The percent-encoded base URI.
1917 * @ref_uri: The percent-encoded reference URI to join to the @base_uri.
1919 * This is a convenience function to join two URI strings and return the result.
1920 * The returned string should be g_free()'d after use.
1922 * Returns: (transfer full) (nullable): A string representing the percent-encoded join of
1928 gst_uri_join_strings (const gchar * base_uri, const gchar * ref_uri)
1930 GstUri *base, *result;
1933 base = gst_uri_from_string (base_uri);
1937 result = gst_uri_from_string_with_base (base, ref_uri);
1939 gst_uri_unref (base);
1943 result_uri = gst_uri_to_string (result);
1944 gst_uri_unref (base);
1945 gst_uri_unref (result);
1951 * gst_uri_is_writable:
1952 * @uri: The #GstUri object to test.
1954 * Check if it is safe to write to this #GstUri.
1956 * Check if the refcount of @uri is exactly 1, meaning that no other
1957 * reference exists to the #GstUri and that the #GstUri is therefore writable.
1959 * Modification of a #GstUri should only be done after verifying that it is
1962 * Returns: %TRUE if it is safe to write to the object.
1967 gst_uri_is_writable (const GstUri * uri)
1969 g_return_val_if_fail (GST_IS_URI (uri), FALSE);
1970 return gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (uri));
1974 * gst_uri_make_writable:
1975 * @uri: (transfer full): The #GstUri object to make writable.
1977 * Make the #GstUri writable.
1979 * Checks if @uri is writable, and if so the original object is returned. If
1980 * not, then a writable copy is made and returned. This gives away the
1981 * reference to @uri and returns a reference to the new #GstUri.
1982 * If @uri is %NULL then %NULL is returned.
1984 * Returns: (transfer full): A writable version of @uri.
1989 gst_uri_make_writable (GstUri * uri)
1991 g_return_val_if_fail (GST_IS_URI (uri), NULL);
1993 GST_URI_CAST (gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (uri)));
1997 * gst_uri_to_string:
1998 * @uri: This #GstUri to convert to a string.
2000 * Convert the URI to a string.
2002 * Returns the URI as held in this object as a #gchar* nul-terminated string.
2003 * The caller should g_free() the string once they are finished with it.
2004 * The string is put together as described in RFC 3986.
2006 * Returns: (transfer full): The string version of the URI.
2011 gst_uri_to_string (const GstUri * uri)
2016 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2018 uri_str = g_string_new (NULL);
2020 if (uri->scheme != NULL)
2021 g_string_append_printf (uri_str, "%s:", uri->scheme);
2023 if (uri->userinfo != NULL || uri->host != NULL ||
2024 uri->port != GST_URI_NO_PORT)
2025 g_string_append (uri_str, "//");
2027 if (uri->userinfo != NULL) {
2028 escaped = _gst_uri_escape_userinfo (uri->userinfo);
2029 g_string_append_printf (uri_str, "%s@", escaped);
2033 if (uri->host != NULL) {
2034 if (strchr (uri->host, ':') != NULL) {
2035 escaped = _gst_uri_escape_host_colon (uri->host);
2036 g_string_append_printf (uri_str, "[%s]", escaped);
2039 escaped = _gst_uri_escape_host (uri->host);
2040 g_string_append (uri_str, escaped);
2045 if (uri->port != GST_URI_NO_PORT)
2046 g_string_append_printf (uri_str, ":%u", uri->port);
2048 if (uri->path != NULL) {
2049 escaped = gst_uri_get_path_string (uri);
2050 g_string_append (uri_str, escaped);
2055 g_string_append (uri_str, "?");
2056 escaped = gst_uri_get_query_string (uri);
2057 g_string_append (uri_str, escaped);
2061 if (uri->fragment != NULL) {
2062 escaped = _gst_uri_escape_fragment (uri->fragment);
2063 g_string_append_printf (uri_str, "#%s", escaped);
2067 return g_string_free (uri_str, FALSE);
2071 * gst_uri_is_normalized:
2072 * @uri: (nullable): The #GstUri to test to see if it is normalized.
2074 * Tests the @uri to see if it is normalized. A %NULL @uri is considered to be
2077 * Returns: TRUE if the URI is normalized or is %NULL.
2082 gst_uri_is_normalized (const GstUri * uri)
2090 g_return_val_if_fail (GST_IS_URI (uri), FALSE);
2092 /* check for non-normalized characters in uri parts */
2093 if (_gst_uri_first_non_normalized_char (uri->scheme,
2094 _GST_URI_NORMALIZE_LOWERCASE) != NULL ||
2095 /*_gst_uri_first_non_normalized_char (uri->userinfo,
2096 _GST_URI_NORMALIZE_PERCENTAGES) != NULL || */
2097 _gst_uri_first_non_normalized_char (uri->host,
2098 _GST_URI_NORMALIZE_LOWERCASE /*| _GST_URI_NORMALIZE_PERCENTAGES */ )
2100 /*|| _gst_uri_first_non_normalized_char (uri->path,
2101 _GST_URI_NORMALIZE_PERCENTAGES) != NULL
2102 || _gst_uri_first_non_normalized_char (uri->query,
2103 _GST_URI_NORMALIZE_PERCENTAGES) != NULL
2104 || _gst_uri_first_non_normalized_char (uri->fragment,
2105 _GST_URI_NORMALIZE_PERCENTAGES) != NULL */ )
2108 /* also check path has had dot segments removed */
2109 new_path = _remove_dot_segments (uri->path);
2111 (_gst_uri_compare_lists (new_path, uri->path,
2112 (GCompareFunc) g_strcmp0) == 0);
2113 g_list_free_full (new_path, g_free);
2118 * gst_uri_normalize:
2119 * @uri: (transfer none): The #GstUri to normalize.
2121 * Normalization will remove extra path segments ("." and "..") from the URI. It
2122 * will also convert the scheme and host name to lower case and any
2123 * percent-encoded values to uppercase.
2125 * The #GstUri object must be writable. Check with gst_uri_is_writable() or use
2126 * gst_uri_make_writable() first.
2128 * Returns: TRUE if the URI was modified.
2133 gst_uri_normalize (GstUri * uri)
2135 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2137 return _gst_uri_normalize_scheme (uri->scheme) |
2138 _gst_uri_normalize_userinfo (uri->userinfo) |
2139 _gst_uri_normalize_hostname (uri->host) |
2140 _gst_uri_normalize_path (&uri->path) |
2141 _gst_uri_normalize_query (uri->query) |
2142 _gst_uri_normalize_fragment (uri->fragment);
2146 * gst_uri_get_scheme:
2147 * @uri: (nullable): This #GstUri object.
2149 * Get the scheme name from the URI or %NULL if it doesn't exist.
2150 * If @uri is %NULL then returns %NULL.
2152 * Returns: (nullable): The scheme from the #GstUri object or %NULL.
2155 gst_uri_get_scheme (const GstUri * uri)
2157 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2158 return (uri ? uri->scheme : NULL);
2162 * gst_uri_set_scheme:
2163 * @uri: (transfer none)(nullable): The #GstUri to modify.
2164 * @scheme: The new scheme to set or %NULL to unset the scheme.
2166 * Set or unset the scheme for the URI.
2168 * Returns: %TRUE if the scheme was set/unset successfully.
2173 gst_uri_set_scheme (GstUri * uri, const gchar * scheme)
2176 return scheme == NULL;
2177 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2179 if (uri->scheme == scheme)
2182 g_free (uri->scheme);
2183 uri->scheme = g_strdup (scheme);
2189 * gst_uri_get_userinfo:
2190 * @uri: (nullable): This #GstUri object.
2192 * Get the userinfo (usually in the form "username:password") from the URI
2193 * or %NULL if it doesn't exist. If @uri is %NULL then returns %NULL.
2195 * Returns: (nullable): The userinfo from the #GstUri object or %NULL.
2200 gst_uri_get_userinfo (const GstUri * uri)
2202 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2203 return (uri ? uri->userinfo : NULL);
2207 * gst_uri_set_userinfo:
2208 * @uri: (transfer none)(nullable): The #GstUri to modify.
2209 * @userinfo: The new user-information string to set or %NULL to unset.
2211 * Set or unset the user information for the URI.
2213 * Returns: %TRUE if the user information was set/unset successfully.
2218 gst_uri_set_userinfo (GstUri * uri, const gchar * userinfo)
2221 return userinfo == NULL;
2222 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2224 if (uri->userinfo == userinfo)
2226 g_free (uri->userinfo);
2227 uri->userinfo = g_strdup (userinfo);
2234 * @uri: (nullable): This #GstUri object.
2236 * Get the host name from the URI or %NULL if it doesn't exist.
2237 * If @uri is %NULL then returns %NULL.
2239 * Returns: (nullable): The host name from the #GstUri object or %NULL.
2244 gst_uri_get_host (const GstUri * uri)
2246 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2247 return (uri ? uri->host : NULL);
2252 * @uri: (transfer none)(nullable): The #GstUri to modify.
2253 * @host: The new host string to set or %NULL to unset.
2255 * Set or unset the host for the URI.
2257 * Returns: %TRUE if the host was set/unset successfully.
2262 gst_uri_set_host (GstUri * uri, const gchar * host)
2265 return host == NULL;
2266 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2268 if (uri->host == host)
2272 uri->host = g_strdup (host);
2279 * @uri: (nullable): This #GstUri object.
2281 * Get the port number from the URI or %GST_URI_NO_PORT if it doesn't exist.
2282 * If @uri is %NULL then returns %GST_URI_NO_PORT.
2284 * Returns: The port number from the #GstUri object or %GST_URI_NO_PORT.
2289 gst_uri_get_port (const GstUri * uri)
2291 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), GST_URI_NO_PORT);
2292 return (uri ? uri->port : GST_URI_NO_PORT);
2297 * @uri: (transfer none)(nullable): The #GstUri to modify.
2298 * @port: The new port number to set or %GST_URI_NO_PORT to unset.
2300 * Set or unset the port number for the URI.
2302 * Returns: %TRUE if the port number was set/unset successfully.
2307 gst_uri_set_port (GstUri * uri, guint port)
2310 return port == GST_URI_NO_PORT;
2311 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2320 * @uri: (nullable): The #GstUri to get the path from.
2322 * Extract the path string from the URI object.
2324 * Returns: (transfer full) (nullable): The path from the URI. Once finished
2325 * with the string should be g_free()'d.
2330 gst_uri_get_path (const GstUri * uri)
2332 GList *path_segment;
2333 const gchar *sep = "";
2338 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2342 ret = g_string_new (NULL);
2344 for (path_segment = uri->path; path_segment;
2345 path_segment = path_segment->next) {
2346 g_string_append (ret, sep);
2347 if (path_segment->data) {
2348 g_string_append (ret, path_segment->data);
2353 return g_string_free (ret, FALSE);
2358 * @uri: (transfer none) (nullable): The #GstUri to modify.
2359 * @path: (nullable): The new path to set with path segments separated by '/', or use %NULL
2360 * to unset the path.
2362 * Sets or unsets the path in the URI.
2364 * Returns: %TRUE if the path was set successfully.
2369 gst_uri_set_path (GstUri * uri, const gchar * path)
2372 return path == NULL;
2373 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2375 g_list_free_full (uri->path, g_free);
2376 uri->path = _gst_uri_string_to_list (path, "/", FALSE, FALSE);
2382 * gst_uri_get_path_string:
2383 * @uri: (nullable): The #GstUri to get the path from.
2385 * Extract the path string from the URI object as a percent encoded URI path.
2387 * Returns: (transfer full) (nullable): The path from the URI. Once finished
2388 * with the string should be g_free()'d.
2393 gst_uri_get_path_string (const GstUri * uri)
2395 GList *path_segment;
2396 const gchar *sep = "";
2402 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2406 ret = g_string_new (NULL);
2408 for (path_segment = uri->path; path_segment;
2409 path_segment = path_segment->next) {
2410 g_string_append (ret, sep);
2411 if (path_segment->data) {
2412 escaped = _gst_uri_escape_path_segment (path_segment->data);
2413 g_string_append (ret, escaped);
2419 return g_string_free (ret, FALSE);
2423 * gst_uri_set_path_string:
2424 * @uri: (transfer none)(nullable): The #GstUri to modify.
2425 * @path: The new percent encoded path to set with path segments separated by
2426 * '/', or use %NULL to unset the path.
2428 * Sets or unsets the path in the URI.
2430 * Returns: %TRUE if the path was set successfully.
2435 gst_uri_set_path_string (GstUri * uri, const gchar * path)
2438 return path == NULL;
2439 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2441 g_list_free_full (uri->path, g_free);
2442 uri->path = _gst_uri_string_to_list (path, "/", FALSE, TRUE);
2447 * gst_uri_get_path_segments:
2448 * @uri: (nullable): The #GstUri to get the path from.
2450 * Get a list of path segments from the URI.
2452 * Returns: (transfer full) (element-type gchar*): A #GList of path segment
2453 * strings or %NULL if no path segments are available. Free the list
2454 * when no longer needed with g_list_free_full(list, g_free).
2459 gst_uri_get_path_segments (const GstUri * uri)
2463 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2466 ret = g_list_copy_deep (uri->path, (GCopyFunc) g_strdup, NULL);
2473 * gst_uri_set_path_segments:
2474 * @uri: (transfer none)(nullable): The #GstUri to modify.
2475 * @path_segments: (transfer full)(nullable)(element-type gchar*): The new
2478 * Replace the path segments list in the URI.
2480 * Returns: %TRUE if the path segments were set successfully.
2485 gst_uri_set_path_segments (GstUri * uri, GList * path_segments)
2487 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), FALSE);
2491 g_list_free_full (path_segments, g_free);
2492 return path_segments == NULL;
2495 g_return_val_if_fail (gst_uri_is_writable (uri), FALSE);
2497 g_list_free_full (uri->path, g_free);
2498 uri->path = path_segments;
2503 * gst_uri_append_path:
2504 * @uri: (transfer none)(nullable): The #GstUri to modify.
2505 * @relative_path: (nullable): Relative path to append to the end of the current path.
2507 * Append a path onto the end of the path in the URI. The path is not
2508 * normalized, call #gst_uri_normalize() to normalize the path.
2510 * Returns: %TRUE if the path was appended successfully.
2515 gst_uri_append_path (GstUri * uri, const gchar * relative_path)
2517 GList *rel_path_list;
2520 return relative_path == NULL;
2521 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2526 GList *last_elem = g_list_last (uri->path);
2527 if (last_elem->data == NULL) {
2528 uri->path = g_list_delete_link (uri->path, last_elem);
2531 rel_path_list = _gst_uri_string_to_list (relative_path, "/", FALSE, FALSE);
2532 /* if path was absolute, make it relative by removing initial NULL element */
2533 if (rel_path_list && rel_path_list->data == NULL) {
2534 rel_path_list = g_list_delete_link (rel_path_list, rel_path_list);
2536 uri->path = g_list_concat (uri->path, rel_path_list);
2541 * gst_uri_append_path_segment:
2542 * @uri: (transfer none)(nullable): The #GstUri to modify.
2543 * @path_segment: (nullable): The path segment string to append to the URI path.
2545 * Append a single path segment onto the end of the URI path.
2547 * Returns: %TRUE if the path was appended successfully.
2552 gst_uri_append_path_segment (GstUri * uri, const gchar * path_segment)
2555 return path_segment == NULL;
2556 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2560 /* if base path ends in a directory (i.e. last element is NULL), remove it */
2561 if (uri->path && g_list_last (uri->path)->data == NULL) {
2562 uri->path = g_list_delete_link (uri->path, g_list_last (uri->path));
2564 uri->path = g_list_append (uri->path, g_strdup (path_segment));
2569 * gst_uri_get_query_string:
2570 * @uri: (nullable): The #GstUri to get the query string from.
2572 * Get a percent encoded URI query string from the @uri.
2574 * Returns: (transfer full) (nullable): A percent encoded query string. Use
2575 * g_free() when no longer needed.
2580 gst_uri_get_query_string (const GstUri * uri)
2582 GHashTableIter iter;
2583 gpointer key, value;
2584 const gchar *sep = "";
2590 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2594 ret = g_string_new (NULL);
2595 g_hash_table_iter_init (&iter, uri->query);
2596 while (g_hash_table_iter_next (&iter, &key, &value)) {
2597 g_string_append (ret, sep);
2598 escaped = _gst_uri_escape_http_query_element (key);
2599 g_string_append (ret, escaped);
2602 escaped = _gst_uri_escape_http_query_element (value);
2603 g_string_append_printf (ret, "=%s", escaped);
2609 return g_string_free (ret, FALSE);
2613 * gst_uri_set_query_string:
2614 * @uri: (transfer none)(nullable): The #GstUri to modify.
2615 * @query: (nullable): The new percent encoded query string to use to populate the query
2616 * table, or use %NULL to unset the query table.
2618 * Sets or unsets the query table in the URI.
2620 * Returns: %TRUE if the query table was set successfully.
2625 gst_uri_set_query_string (GstUri * uri, const gchar * query)
2628 return query == NULL;
2630 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2633 g_hash_table_unref (uri->query);
2634 uri->query = _gst_uri_string_to_table (query, "&", "=", TRUE, TRUE);
2640 * gst_uri_get_query_table:
2641 * @uri: (nullable): The #GstUri to get the query table from.
2643 * Get the query table from the URI. Keys and values in the table are freed
2644 * with g_free when they are deleted. A value may be %NULL to indicate that
2645 * the key should appear in the query string in the URI, but does not have a
2646 * value. Free the returned #GHashTable with #g_hash_table_unref() when it is
2647 * no longer required. Modifying this hash table will modify the query in the
2650 * Returns: (transfer full) (element-type gchar* gchar*) (nullable): The query
2651 * hash table from the URI.
2656 gst_uri_get_query_table (const GstUri * uri)
2660 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2664 return g_hash_table_ref (uri->query);
2668 * gst_uri_set_query_table:
2669 * @uri: (transfer none)(nullable): The #GstUri to modify.
2670 * @query_table: (transfer none)(nullable)(element-type gchar* gchar*): The new
2671 * query table to use.
2673 * Set the query table to use in the URI. The old table is unreferenced and a
2674 * reference to the new one is used instead. A value if %NULL for @query_table
2675 * will remove the query string from the URI.
2677 * Returns: %TRUE if the new table was successfully used for the query table.
2682 gst_uri_set_query_table (GstUri * uri, GHashTable * query_table)
2684 GHashTable *old_table = NULL;
2687 return query_table == NULL;
2688 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2690 if (uri->query == query_table)
2693 old_table = uri->query;
2695 uri->query = g_hash_table_ref (query_table);
2699 g_hash_table_unref (old_table);
2705 * gst_uri_set_query_value:
2706 * @uri: (transfer none)(nullable): The #GstUri to modify.
2707 * @query_key: (transfer none): The key for the query entry.
2708 * @query_value: (transfer none)(nullable): The value for the key.
2710 * This inserts or replaces a key in the query table. A @query_value of %NULL
2711 * indicates that the key has no associated value, but will still be present in
2714 * Returns: %TRUE if the query table was successfully updated.
2719 gst_uri_set_query_value (GstUri * uri, const gchar * query_key,
2720 const gchar * query_value)
2724 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2727 uri->query = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
2730 g_hash_table_insert (uri->query, g_strdup (query_key),
2731 g_strdup (query_value));
2737 * gst_uri_remove_query_key:
2738 * @uri: (transfer none)(nullable): The #GstUri to modify.
2739 * @query_key: The key to remove.
2741 * Remove an entry from the query table by key.
2743 * Returns: %TRUE if the key existed in the table and was removed.
2748 gst_uri_remove_query_key (GstUri * uri, const gchar * query_key)
2754 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2758 result = g_hash_table_remove (uri->query, query_key);
2759 /* if this was the last query entry, remove the query string completely */
2760 if (result && g_hash_table_size (uri->query) == 0) {
2761 g_hash_table_unref (uri->query);
2768 * gst_uri_query_has_key:
2769 * @uri: (nullable): The #GstUri to examine.
2770 * @query_key: The key to lookup.
2772 * Check if there is a query table entry for the @query_key key.
2774 * Returns: %TRUE if @query_key exists in the URI query table.
2779 gst_uri_query_has_key (const GstUri * uri, const gchar * query_key)
2783 g_return_val_if_fail (GST_IS_URI (uri), FALSE);
2787 return g_hash_table_contains (uri->query, query_key);
2791 * gst_uri_get_query_value:
2792 * @uri: (nullable): The #GstUri to examine.
2793 * @query_key: The key to lookup.
2795 * Get the value associated with the @query_key key. Will return %NULL if the
2796 * key has no value or if the key does not exist in the URI query table. Because
2797 * %NULL is returned for both missing keys and keys with no value, you should
2798 * use gst_uri_query_has_key() to determine if a key is present in the URI
2801 * Returns: (nullable): The value for the given key, or %NULL if not found.
2806 gst_uri_get_query_value (const GstUri * uri, const gchar * query_key)
2810 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2814 return g_hash_table_lookup (uri->query, query_key);
2818 * gst_uri_get_query_keys:
2819 * @uri: (nullable): The #GstUri to examine.
2821 * Get a list of the query keys from the URI.
2823 * Returns: (transfer container) (element-type gchar*): A list of keys from
2824 * the URI query. Free the list with g_list_free().
2829 gst_uri_get_query_keys (const GstUri * uri)
2833 g_return_val_if_fail (GST_IS_URI (uri), NULL);
2837 return g_hash_table_get_keys (uri->query);
2841 * gst_uri_get_fragment:
2842 * @uri: (nullable): This #GstUri object.
2844 * Get the fragment name from the URI or %NULL if it doesn't exist.
2845 * If @uri is %NULL then returns %NULL.
2847 * Returns: (nullable): The host name from the #GstUri object or %NULL.
2852 gst_uri_get_fragment (const GstUri * uri)
2854 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2855 return (uri ? uri->fragment : NULL);
2859 * gst_uri_set_fragment:
2860 * @uri: (transfer none)(nullable): The #GstUri to modify.
2861 * @fragment: (nullable): The fragment string to set.
2863 * Sets the fragment string in the URI. Use a value of %NULL in @fragment to
2864 * unset the fragment string.
2866 * Returns: %TRUE if the fragment was set/unset successfully.
2871 gst_uri_set_fragment (GstUri * uri, const gchar * fragment)
2874 return fragment == NULL;
2875 g_return_val_if_fail (GST_IS_URI (uri) && gst_uri_is_writable (uri), FALSE);
2877 if (uri->fragment == fragment)
2880 g_free (uri->fragment);
2881 uri->fragment = g_strdup (fragment);
2886 * gst_uri_get_media_fragment_table:
2887 * @uri: (nullable): The #GstUri to get the fragment table from.
2889 * Get the media fragment table from the URI, as defined by "Media Fragments URI 1.0".
2890 * Hash table returned by this API is a list of "key-value" pairs, and the each
2891 * pair is generated by splitting "URI fragment" per "&" sub-delims, then "key"
2892 * and "value" are split by "=" sub-delims. The "key" returned by this API may
2893 * be undefined keyword by standard.
2894 * A value may be %NULL to indicate that the key should appear in the fragment
2895 * string in the URI, but does not have a value. Free the returned #GHashTable
2896 * with #g_hash_table_unref() when it is no longer required.
2897 * Modifying this hash table does not affect the fragment in the URI.
2899 * See more about Media Fragments URI 1.0 (W3C) at https://www.w3.org/TR/media-frags/
2901 * Returns: (transfer full) (element-type gchar* gchar*) (nullable): The
2902 * fragment hash table from the URI.
2907 gst_uri_get_media_fragment_table (const GstUri * uri)
2909 g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
2913 return _gst_uri_string_to_table (uri->fragment, "&", "=", TRUE, TRUE);
2918 * @uri: This #GstUri object.
2920 * Create a new #GstUri object with the same data as this #GstUri object.
2921 * If @uri is %NULL then returns %NULL.
2923 * Returns: (transfer full): A new #GstUri object which is a copy of this
2929 gst_uri_copy (const GstUri * uri)
2931 return GST_URI_CAST (gst_mini_object_copy (GST_MINI_OBJECT_CONST_CAST (uri)));
2936 * @uri: (transfer none): This #GstUri object.
2938 * Add a reference to this #GstUri object. See gst_mini_object_ref() for further
2941 * Returns: This object with the reference count incremented.
2946 gst_uri_ref (GstUri * uri)
2948 return GST_URI_CAST (gst_mini_object_ref (GST_MINI_OBJECT_CAST (uri)));
2953 * @uri: (transfer full): This #GstUri object.
2955 * Decrement the reference count to this #GstUri object.
2957 * If the reference count drops to 0 then finalize this object.
2959 * See gst_mini_object_unref() for further info.
2964 gst_uri_unref (GstUri * uri)
2966 gst_mini_object_unref (GST_MINI_OBJECT_CAST (uri));
2970 * gst_clear_uri: (skip)
2971 * @uri_ptr: a pointer to a #GstUri reference
2973 * Clears a reference to a #GstUri.
2975 * @uri_ptr must not be %NULL.
2977 * If the reference is %NULL then this function does nothing. Otherwise, the
2978 * reference count of the uri is decreased and the pointer is set to %NULL.
2983 gst_clear_uri (GstUri ** uri_ptr)
2985 gst_clear_mini_object ((GstMiniObject **) uri_ptr);