deviceprovider: It's (transfer none) not (transfer-none)
[platform/upstream/gstreamer.git] / gst / gsturi.c
index 5333f43..4b1d47f 100644 (file)
 
 /**
  * SECTION:gsturihandler
+ * @title: GstUriHandler
  * @short_description: Interface to ease URI handling in plugins.
  *
- * The URIHandler is an interface that is implemented by Source and Sink
- * #GstElement to simplify then handling of URI.
+ * The #GstURIHandler is an interface that is implemented by Source and Sink
+ * #GstElement to unify handling of URI.
  *
  * An application can use the following functions to quickly get an element
  * that handles the given URI for reading or writing
@@ -154,6 +155,9 @@ gst_uri_error_quark (void)
   return g_quark_from_static_string ("gst-uri-error-quark");
 }
 
+#define HEX_ESCAPE '%'
+
+#ifndef GST_REMOVE_DEPRECATED
 static const guchar acceptable[96] = {  /* X0   X1   X2   X3   X4   X5   X6   X7   X8   X9   XA   XB   XC   XD   XE   XF */
   0x00, 0x3F, 0x20, 0x20, 0x20, 0x00, 0x2C, 0x3F, 0x3F, 0x3F, 0x3F, 0x22, 0x20, 0x3F, 0x3F, 0x1C,       /* 2X  !"#$%&'()*+,-./   */
   0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x38, 0x20, 0x20, 0x2C, 0x20, 0x2C,       /* 3X 0123456789:;<=>?   */
@@ -173,8 +177,6 @@ typedef enum
   UNSAFE_SLASHES = 0x20         /* Allows all characters except for '/' and '%' */
 } UnsafeCharacterSet;
 
-#define HEX_ESCAPE '%'
-
 /*  Escape undesirable characters using %
  *  -------------------------------------
  *
@@ -247,21 +249,7 @@ escape_string_internal (const gchar * string, UnsafeCharacterSet mask)
 
   return result;
 }
-
-/* escape_string:
- * @string: string to be escaped
- *
- * Escapes @string, replacing any and all special characters
- * with equivalent escape sequences.
- *
- * Return value: a newly allocated string equivalent to @string
- * but with all special characters escaped
- **/
-static gchar *
-escape_string (const gchar * string)
-{
-  return escape_string_internal (string, UNSAFE_ALL);
-}
+#endif
 
 static int
 hex_to_int (gchar c)
@@ -409,7 +397,7 @@ gst_uri_is_valid (const gchar * uri)
  * Extracts the protocol out of a given valid URI. The returned string must be
  * freed using g_free().
  *
- * Returns: The protocol for this URI.
+ * Returns: (nullable): The protocol for this URI.
  */
 gchar *
 gst_uri_get_protocol (const gchar * uri)
@@ -461,9 +449,9 @@ gst_uri_has_protocol (const gchar * uri, const gchar * protocol)
  *
  * Free-function: g_free
  *
- * Returns: (transfer full): the location for this URI. Returns %NULL if the
- *     URI isn't valid. If the URI does not contain a location, an empty
- *     string is returned.
+ * Returns: (transfer full) (nullable): the location for this URI. Returns
+ *     %NULL if the URI isn't valid. If the URI does not contain a location, an
+ *     empty string is returned.
  */
 gchar *
 gst_uri_get_location (const gchar * uri)
@@ -510,7 +498,10 @@ gst_uri_get_location (const gchar * uri)
  *
  * Returns: (transfer full): a new string for this URI. Returns %NULL if the
  *     given URI protocol is not valid, or the given location is %NULL.
+ *
+ * Deprecated: Use GstURI instead.
  */
+#ifndef GST_REMOVE_DEPRECATED
 gchar *
 gst_uri_construct (const gchar * protocol, const gchar * location)
 {
@@ -521,13 +512,14 @@ gst_uri_construct (const gchar * protocol, const gchar * location)
   g_return_val_if_fail (location != NULL, NULL);
 
   proto_lowercase = g_ascii_strdown (protocol, -1);
-  escaped = escape_string (location);
+  escaped = escape_string_internal (location, UNSAFE_PATH);
   retval = g_strdup_printf ("%s://%s", proto_lowercase, escaped);
   g_free (escaped);
   g_free (proto_lowercase);
 
   return retval;
 }
+#endif
 
 typedef struct
 {
@@ -626,7 +618,8 @@ gst_uri_protocol_is_supported (const GstURIType type, const gchar * protocol)
  *
  * Creates an element for handling the given URI.
  *
- * Returns: (transfer floating): a new element or %NULL if none could be created
+ * Returns: (transfer floating) (nullable): a new element or %NULL if none
+ * could be created
  */
 GstElement *
 gst_element_make_from_uri (const GstURIType type, const gchar * uri,
@@ -705,11 +698,11 @@ gst_element_make_from_uri (const GstURIType type, const gchar * uri,
  * Returns: the #GstURIType of the URI handler.
  * Returns #GST_URI_UNKNOWN if the @handler isn't implemented correctly.
  */
-guint
+GstURIType
 gst_uri_handler_get_uri_type (GstURIHandler * handler)
 {
   GstURIHandlerInterface *iface;
-  guint ret;
+  GstURIType ret;
 
   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN);
 
@@ -899,7 +892,7 @@ file_path_contains_relatives (const gchar * path)
 
 /**
  * gst_filename_to_uri:
- * @filename: absolute or relative file name path
+ * @filename: (type filename): absolute or relative file name path
  * @error: pointer to error, or %NULL
  *
  * Similar to g_filename_to_uri(), but attempts to handle relative file paths
@@ -908,6 +901,9 @@ file_path_contains_relatives (const gchar * path)
  * will be canonicalised so that it doesn't contain any './' or '../' segments.
  *
  * On Windows #filename should be in UTF-8 encoding.
+ *
+ * Returns: newly-allocated URI string, or NULL on error. The caller must
+ *   free the URI string with g_free() when no longer needed.
  */
 gchar *
 gst_filename_to_uri (const gchar * filename, GError ** error)
@@ -957,10 +953,11 @@ beach:
 
 /**
  * SECTION:gsturi
+ * @title: GstUri
  * @short_description: URI parsing and manipulation.
  *
  * A #GstUri object can be used to parse and split a URI string into its
- * constituant parts. Two #GstUri objects can be joined to make a new #GstUri
+ * constituent parts. Two #GstUri objects can be joined to make a new #GstUri
  * using the algorithm described in RFC3986.
  */
 
@@ -985,12 +982,15 @@ static void _gst_uri_free (GstUri * uri);
 static GstUri *_gst_uri_new (void);
 static GList *_remove_dot_segments (GList * path);
 
-/** private GstUri functions **/
+/* private GstUri functions */
 
 static GstUri *
 _gst_uri_new (void)
 {
   GstUri *uri;
+
+  g_return_val_if_fail (gst_is_initialized (), NULL);
+
   uri = GST_URI_CAST (g_slice_new0 (GstUri));
 
   if (uri)
@@ -1014,6 +1014,10 @@ _gst_uri_free (GstUri * uri)
     g_hash_table_unref (uri->query);
   g_free (uri->fragment);
 
+#ifdef USE_POISONING
+  memset (uri, 0xff, sizeof (*uri));
+#endif
+
   g_slice_free1 (sizeof (*uri), uri);
 }
 
@@ -1167,7 +1171,7 @@ _gst_uri_normalize_table_noop (GHashTable * table)
 #define _gst_uri_normalize_query _gst_uri_normalize_table_noop
 #define _gst_uri_normalize_fragment _gst_uri_normalize_str_noop
 
-/** RFC 3986 functions **/
+/* RFC 3986 functions */
 
 static GList *
 _merge (GList * base, GList * path)
@@ -1216,7 +1220,12 @@ _remove_dot_segments (GList * path)
         out = g_list_delete_link (out, prev);
       }
       g_free (elem->data);
-      out = g_list_delete_link (out, elem);
+      if (next != NULL) {
+        out = g_list_delete_link (out, elem);
+      } else {
+        /* path ends in '/..' We need to keep the last '/' */
+        elem->data = NULL;
+      }
     }
   }
 
@@ -1238,6 +1247,13 @@ _gst_uri_escape_host (const gchar * host)
 }
 
 static gchar *
+_gst_uri_escape_host_colon (const gchar * host)
+{
+  return g_uri_escape_string (host,
+      G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS ":", FALSE);
+}
+
+static gchar *
 _gst_uri_escape_path_segment (const gchar * segment)
 {
   return g_uri_escape_string (segment,
@@ -1271,7 +1287,7 @@ _gst_uri_string_to_list (const gchar * str, const gchar * sep, gboolean convert,
 
   if (str) {
     guint pct_sep_len = 0;
-    gchar *pct_sep;
+    gchar *pct_sep = NULL;
     gchar **split_str;
 
     if (convert && !unescape) {
@@ -1412,7 +1428,7 @@ _gst_uri_string_to_table (const gchar * str, const gchar * part_sep,
  *                      elements.
  * @query: (nullable): The query string for the new URI with '&' separating
  *                       query elements. Elements containing '&' characters
- *                       should encode them as "%26".
+ *                       should encode them as "&percnt;26".
  * @fragment: (nullable): The fragment name for the new URI.
  *
  * Creates a new #GstUri object with the given URI parts. The path and query
@@ -1454,7 +1470,7 @@ gst_uri_new (const gchar * scheme, const gchar * userinfo, const gchar * host,
  *                      elements.
  * @query: (nullable): The query string for the new URI with '&' separating
  *                       query elements. Elements containing '&' characters
- *                       should encode them as "%26".
+ *                       should encode them as "&percnt;26".
  * @fragment: (nullable): The fragment name for the new URI.
  *
  * Like gst_uri_new(), but joins the new URI onto a base URI.
@@ -1485,21 +1501,28 @@ gst_uri_new_with_base (GstUri * base, const gchar * scheme,
  * gst_uri_from_string:
  * @uri: The URI string to parse.
  *
- * Parses a URI string into a new #GstUri object.
+ * Parses a URI string into a new #GstUri object. Will return NULL if the URI
+ * cannot be parsed.
  *
- * Returns: (transfer full): A new #GstUri object.
+ * Returns: (transfer full) (nullable): A new #GstUri object, or NULL.
  *
  * Since: 1.6
  */
 GstUri *
 gst_uri_from_string (const gchar * uri)
 {
+  const gchar *orig_uri = uri;
   GstUri *uri_obj;
 
   uri_obj = _gst_uri_new ();
 
   if (uri_obj && uri != NULL) {
     int i = 0;
+
+    /* be helpful and skip initial white space */
+    while (*uri == '\v' || g_ascii_isspace (*uri))
+      uri++;
+
     if (g_ascii_isalpha (uri[i])) {
       /* find end of scheme name */
       i++;
@@ -1513,13 +1536,12 @@ gst_uri_from_string (const gchar * uri)
       uri += i + 1;
     }
     if (uri[0] == '/' && uri[1] == '/') {
-      const gchar *eoa, *eoui, *eoh;
+      const gchar *eoa, *eoui, *eoh, *reoh;
       /* get authority [userinfo@]host[:port] */
       uri += 2;
       /* find end of authority */
-      eoa = strchr (uri, '/');
-      if (eoa == NULL)
-        eoa = uri + strlen (uri);
+      eoa = uri + strcspn (uri, "/?#");
+
       /* find end of userinfo */
       eoui = strchr (uri, '@');
       if (eoui != NULL && eoui < eoa) {
@@ -1529,31 +1551,37 @@ gst_uri_from_string (const gchar * uri)
       /* find end of host */
       if (uri[0] == '[') {
         eoh = strchr (uri, ']');
-        if (eoh == NULL || eoh >= eoa)
-          eoh = eoa - 1;
+        if (eoh == NULL || eoh > eoa) {
+          GST_DEBUG ("Unable to parse the host part of the URI '%s'.",
+              orig_uri);
+          gst_uri_unref (uri_obj);
+          return NULL;
+        }
+        reoh = eoh + 1;
+        uri++;
       } else {
-        eoh = strchr (uri, ':');
-        if (eoh == NULL || eoh >= eoa)
-          eoh = eoa - 1;
-        else
-          eoh--;
+        reoh = eoh = strchr (uri, ':');
+        if (eoh == NULL || eoh > eoa)
+          reoh = eoh = eoa;
       }
-      uri_obj->host = g_uri_unescape_segment (uri, eoh + 1, NULL);
-      uri = eoh + 1;
+      /* don't capture empty host strings */
+      if (eoh != uri)
+        uri_obj->host = g_uri_unescape_segment (uri, eoh, NULL);
+
+      uri = reoh;
       if (uri < eoa) {
-        /* if port number is malformed, do best effort and concat string */
+        /* if port number is malformed then we can't parse this */
         if (uri[0] != ':' || strspn (uri + 1, "0123456789") != eoa - uri - 1) {
-          gchar *tmp = uri_obj->host;
-          uri_obj->host = g_malloc (strlen (uri_obj->host) + eoa - uri + 1);
-          g_strlcpy (g_stpcpy (uri_obj->host, tmp), uri, eoa - uri + 1);
-          g_free (tmp);
-        } else {
-          /* otherwise treat port as unsigned decimal number */
+          GST_DEBUG ("Unable to parse host/port part of the URI '%s'.",
+              orig_uri);
+          gst_uri_unref (uri_obj);
+          return NULL;
+        }
+        /* otherwise treat port as unsigned decimal number */
+        uri++;
+        while (uri < eoa) {
+          uri_obj->port = uri_obj->port * 10 + g_ascii_digit_value (*uri);
           uri++;
-          while (uri < eoa) {
-            uri_obj->port = uri_obj->port * 10 + g_ascii_digit_value (*uri);
-            uri++;
-          }
         }
       }
       uri = eoa;
@@ -1738,16 +1766,16 @@ gst_uri_equal (const GstUri * first, const GstUri * second)
 
 /**
  * gst_uri_join:
- * @base_uri: (transfer none)(nullable): The base URI to join another to.
- * @ref_uri: (transfer none)(nullable): The reference URI to join onto the
- *                                        base URI.
+ * @base_uri: (transfer none) (nullable): The base URI to join another to.
+ * @ref_uri: (transfer none) (nullable): The reference URI to join onto the
+ *                                       base URI.
  *
  * Join a reference URI onto a base URI using the method from RFC 3986.
  * If either URI is %NULL then the other URI will be returned with the ref count
  * increased.
  *
- * Returns: (transfer full): A #GstUri which represents the base with the
- *                           reference URI joined on.
+ * Returns: (transfer full) (nullable): A #GstUri which represents the base
+ *                                      with the reference URI joined on.
  *
  * Since: 1.6
  */
@@ -1905,7 +1933,7 @@ gst_uri_make_writable (GstUri * uri)
  *
  * Convert the URI to a string.
  *
- * Returns the URI as held in this object as a gchar* %NUL terminated string.
+ * Returns the URI as held in this object as a #gchar* nul-terminated string.
  * The caller should g_free() the string once they are finished with it.
  * The string is put together as described in RFC 3986.
  *
@@ -1937,9 +1965,15 @@ gst_uri_to_string (const GstUri * uri)
   }
 
   if (uri->host != NULL) {
-    escaped = _gst_uri_escape_host (uri->host);
-    g_string_append (uri_str, escaped);
-    g_free (escaped);
+    if (strchr (uri->host, ':') != NULL) {
+      escaped = _gst_uri_escape_host_colon (uri->host);
+      g_string_append_printf (uri_str, "[%s]", escaped);
+      g_free (escaped);
+    } else {
+      escaped = _gst_uri_escape_host (uri->host);
+      g_string_append (uri_str, escaped);
+      g_free (escaped);
+    }
   }
 
   if (uri->port != GST_URI_NO_PORT)
@@ -2049,7 +2083,7 @@ gst_uri_normalize (GstUri * uri)
  * Get the scheme name from the URI or %NULL if it doesn't exist.
  * If @uri is %NULL then returns %NULL.
  *
- * Returns: The scheme from the #GstUri object or %NULL.
+ * Returns: (nullable): The scheme from the #GstUri object or %NULL.
  */
 const gchar *
 gst_uri_get_scheme (const GstUri * uri)
@@ -2089,7 +2123,7 @@ gst_uri_set_scheme (GstUri * uri, const gchar * scheme)
  * Get the userinfo (usually in the form "username:password") from the URI
  * or %NULL if it doesn't exist. If @uri is %NULL then returns %NULL.
  *
- * Returns: The userinfo from the #GstUri object or %NULL.
+ * Returns: (nullable): The userinfo from the #GstUri object or %NULL.
  *
  * Since: 1.6
  */
@@ -2131,7 +2165,7 @@ gst_uri_set_userinfo (GstUri * uri, const gchar * userinfo)
  * Get the host name from the URI or %NULL if it doesn't exist.
  * If @uri is %NULL then returns %NULL.
  *
- * Returns: The host name from the #GstUri object or %NULL.
+ * Returns: (nullable): The host name from the #GstUri object or %NULL.
  *
  * Since: 1.6
  */
@@ -2213,8 +2247,8 @@ gst_uri_set_port (GstUri * uri, guint port)
  *
  * Extract the path string from the URI object.
  *
- * Returns: (transfer full): The path from the URI. Once finished with the
- *                           string should be g_free()'d.
+ * Returns: (transfer full): (nullable): The path from the URI. Once finished
+ *                                       with the string should be g_free()'d.
  *
  * Since: 1.6
  */
@@ -2247,7 +2281,7 @@ gst_uri_get_path (const GstUri * uri)
 
 /**
  * gst_uri_set_path:
- * @uri: (transfer none)(nullable): The #GstUri to modify.
+ * @uri: (transfer none) (nullable): The #GstUri to modify.
  * @path: The new path to set with path segments separated by '/', or use %NULL
  *        to unset the path.
  *
@@ -2276,8 +2310,8 @@ gst_uri_set_path (GstUri * uri, const gchar * path)
  *
  * Extract the path string from the URI object as a percent encoded URI path.
  *
- * Returns: (transfer full): The path from the URI. Once finished with the
- *                           string should be g_free()'d.
+ * Returns: (transfer full) (nullable): The path from the URI. Once finished
+ *                                      with the string should be g_free()'d.
  *
  * Since: 1.6
  */
@@ -2341,7 +2375,7 @@ gst_uri_set_path_string (GstUri * uri, const gchar * path)
  *
  * Get a list of path segments from the URI.
  *
- * Returns: (transfer full)(element-type gchar*): A #GList of path segment
+ * Returns: (transfer full) (element-type gchar*): A #GList of path segment
  *          strings or %NULL if no path segments are available. Free the list
  *          when no longer needed with g_list_free_full(list, g_free).
  *
@@ -2463,8 +2497,8 @@ gst_uri_append_path_segment (GstUri * uri, const gchar * path_segment)
  *
  * Get a percent encoded URI query string from the @uri.
  *
- * Returns: (transfer full): A percent encoded query string. Use g_free() when
- *          no longer needed.
+ * Returns: (transfer full) (nullable): A percent encoded query string. Use
+ *                                      g_free() when no longer needed.
  *
  * Since: 1.6
  */
@@ -2539,8 +2573,8 @@ gst_uri_set_query_string (GstUri * uri, const gchar * query)
  * no longer required. Modifying this hash table will modify the query in the
  * URI.
  *
- * Returns: (transfer full)(element-type gchar* gchar*): The query hash table
- *          from the URI.
+ * Returns: (transfer full) (element-type gchar* gchar*) (nullable): The query
+ *          hash table from the URI.
  *
  * Since: 1.6
  */
@@ -2566,7 +2600,7 @@ gst_uri_get_query_table (const GstUri * uri)
  * reference to the new one is used instead. A value if %NULL for @query_table
  * will remove the query string from the URI.
  *
- * Returns: %TRUE if the new table was sucessfully used for the query table.
+ * Returns: %TRUE if the new table was successfully used for the query table.
  *
  * Since: 1.6
  */
@@ -2600,7 +2634,7 @@ gst_uri_set_query_table (GstUri * uri, GHashTable * query_table)
  * indicates that the key has no associated value, but will still be present in
  * the query string.
  *
- * Returns: %TRUE if the query table was sucessfully updated.
+ * Returns: %TRUE if the query table was successfully updated.
  *
  * Since: 1.6
  */
@@ -2687,7 +2721,7 @@ gst_uri_query_has_key (const GstUri * uri, const gchar * query_key)
  * use gst_uri_query_has_key() to determine if a key is present in the URI
  * query.
  *
- * Returns: The value for the given key, or %NULL if not found.
+ * Returns: (nullable): The value for the given key, or %NULL if not found.
  *
  * Since: 1.6
  */
@@ -2709,7 +2743,7 @@ gst_uri_get_query_value (const GstUri * uri, const gchar * query_key)
  *
  * Get a list of the query keys from the URI.
  *
- * Returns: (transfer container)(element-type gchar*): A list of keys from
+ * Returns: (transfer container) (element-type gchar*): A list of keys from
  *          the URI query. Free the list with g_list_free().
  *
  * Since: 1.6
@@ -2733,7 +2767,7 @@ gst_uri_get_query_keys (const GstUri * uri)
  * Get the fragment name from the URI or %NULL if it doesn't exist.
  * If @uri is %NULL then returns %NULL.
  *
- * Returns: The host name from the #GstUri object or %NULL.
+ * Returns: (nullable): The host name from the #GstUri object or %NULL.
  *
  * Since: 1.6
  */
@@ -2767,3 +2801,34 @@ gst_uri_set_fragment (GstUri * uri, const gchar * fragment)
   uri->fragment = g_strdup (fragment);
   return TRUE;
 }
+
+/**
+ * gst_uri_get_media_fragment_table:
+ * @uri: (nullable): The #GstUri to get the fragment table from.
+ *
+ * Get the media fragment table from the URI, as defined by "Media Fragments URI 1.0".
+ * Hash table returned by this API is a list of "key-value" pairs, and the each
+ * pair is generated by splitting "URI fragment" per "&" sub-delims, then "key"
+ * and "value" are split by "=" sub-delims. The "key" returned by this API may
+ * be undefined keyword by standard.
+ * A value may be %NULL to indicate that the key should appear in the fragment
+ * string in the URI, but does not have a value. Free the returned #GHashTable
+ * with #g_hash_table_unref() when it is no longer required.
+ * Modifying this hash table does not affect the fragment in the URI.
+ *
+ * See more about Media Fragments URI 1.0 (W3C) at https://www.w3.org/TR/media-frags/
+ *
+ * Returns: (transfer full) (element-type gchar* gchar*) (nullable): The
+ *          fragment hash table from the URI.
+ *
+ * Since: 1.12
+ */
+GHashTable *
+gst_uri_get_media_fragment_table (const GstUri * uri)
+{
+  g_return_val_if_fail (uri == NULL || GST_IS_URI (uri), NULL);
+
+  if (!uri->fragment)
+    return NULL;
+  return _gst_uri_string_to_table (uri->fragment, "&", "=", TRUE, TRUE);
+}