2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
5 * gsturi.c: register URI handlers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
24 * SECTION:gsturihandler
25 * @short_description: Interface to ease URI handling in plugins.
27 * The URIHandler is an interface that is implemented by Source and Sink
28 * #GstElement to simplify then handling of URI.
30 * An application can use the following functions to quickly get an element
31 * that handles the given URI for reading or writing
32 * (gst_element_make_from_uri()).
34 * Source and Sink plugins should implement this interface when possible.
36 * Last reviewed on 2005-11-09 (0.9.4)
43 #include "gst_private.h"
46 #include "gstmarshal.h"
47 #include "gstregistry.h"
51 GST_DEBUG_CATEGORY_STATIC (gst_uri_handler_debug);
52 #define GST_CAT_DEFAULT gst_uri_handler_debug
54 static void gst_uri_handler_base_init (gpointer g_class);
57 gst_uri_handler_get_type (void)
59 static GType urihandler_type = 0;
61 if (G_UNLIKELY (urihandler_type == 0)) {
62 static const GTypeInfo urihandler_info = {
63 sizeof (GstURIHandlerInterface),
64 gst_uri_handler_base_init,
75 urihandler_type = g_type_register_static (G_TYPE_INTERFACE,
76 "GstURIHandler", &urihandler_info, 0);
78 GST_DEBUG_CATEGORY_INIT (gst_uri_handler_debug, "GST_URI", GST_DEBUG_BOLD,
81 return urihandler_type;
84 gst_uri_handler_base_init (gpointer g_class)
86 static gboolean initialized = FALSE;
91 * GstURIHandler::new-uri:
92 * @handler: The #GstURIHandler which emitted the signal
93 * @uri: The new URI, or NULL if the URI was removed
95 * The URI of the given @handler has changed.
98 g_signal_new ("new-uri", GST_TYPE_URI_HANDLER, G_SIGNAL_RUN_LAST,
99 G_STRUCT_OFFSET (GstURIHandlerInterface, new_uri), NULL, NULL,
100 gst_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
105 static const guchar acceptable[96] = { /* X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 XA XB XC XD XE XF */
106 0x00, 0x3F, 0x20, 0x20, 0x20, 0x00, 0x2C, 0x3F, 0x3F, 0x3F, 0x3F, 0x22, 0x20, 0x3F, 0x3F, 0x1C, /* 2X !"#$%&'()*+,-./ */
107 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x38, 0x20, 0x20, 0x2C, 0x20, 0x2C, /* 3X 0123456789:;<=>? */
108 0x30, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 4X @ABCDEFGHIJKLMNO */
109 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x3F, /* 5X PQRSTUVWXYZ[\]^_ */
110 0x20, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, /* 6X `abcdefghijklmno */
111 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x3F, 0x20 /* 7X pqrstuvwxyz{|}~DEL */
116 UNSAFE_ALL = 0x1, /* Escape all unsafe characters */
117 UNSAFE_ALLOW_PLUS = 0x2, /* Allows '+' */
118 UNSAFE_PATH = 0x4, /* Allows '/' and '?' and '&' and '=' */
119 UNSAFE_DOS_PATH = 0x8, /* Allows '/' and '?' and '&' and '=' and ':' */
120 UNSAFE_HOST = 0x10, /* Allows '/' and ':' and '@' */
121 UNSAFE_SLASHES = 0x20 /* Allows all characters except for '/' and '%' */
122 } UnsafeCharacterSet;
124 #define HEX_ESCAPE '%'
126 /* Escape undesirable characters using %
127 * -------------------------------------
129 * This function takes a pointer to a string in which
130 * some characters may be unacceptable unescaped.
131 * It returns a string which has these characters
132 * represented by a '%' character followed by two hex digits.
134 * This routine returns a g_malloced string.
137 static const gchar hex[16] = "0123456789ABCDEF";
140 escape_string_internal (const gchar * string, UnsafeCharacterSet mask)
142 #define ACCEPTABLE_CHAR(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
149 UnsafeCharacterSet use_mask;
151 g_return_val_if_fail (mask == UNSAFE_ALL
152 || mask == UNSAFE_ALLOW_PLUS
153 || mask == UNSAFE_PATH
154 || mask == UNSAFE_DOS_PATH
155 || mask == UNSAFE_HOST || mask == UNSAFE_SLASHES, NULL);
157 if (string == NULL) {
163 for (p = string; *p != '\0'; p++) {
165 if (!ACCEPTABLE_CHAR (c)) {
168 if ((use_mask == UNSAFE_HOST) && (unacceptable || (c == '/'))) {
169 /* when escaping a host, if we hit something that needs to be escaped, or we finally
170 * hit a path separator, revert to path mode (the host segment of the url is over).
172 use_mask = UNSAFE_PATH;
176 result = g_malloc (p - string + unacceptable * 2 + 1);
179 for (q = result, p = string; *p != '\0'; p++) {
182 if (!ACCEPTABLE_CHAR (c)) {
183 *q++ = HEX_ESCAPE; /* means hex coming */
189 if ((use_mask == UNSAFE_HOST) && (!ACCEPTABLE_CHAR (c) || (c == '/'))) {
190 use_mask = UNSAFE_PATH;
201 * @string: string to be escaped
203 * Escapes @string, replacing any and all special characters
204 * with equivalent escape sequences.
206 * Return value: a newly allocated string equivalent to @string
207 * but with all special characters escaped
210 escape_string (const gchar * string)
212 return escape_string_internal (string, UNSAFE_ALL);
218 return c >= '0' && c <= '9' ? c - '0'
219 : c >= 'A' && c <= 'F' ? c - 'A' + 10
220 : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
224 unescape_character (const char *scanner)
229 first_digit = hex_to_int (*scanner++);
230 if (first_digit < 0) {
234 second_digit = hex_to_int (*scanner++);
235 if (second_digit < 0) {
239 return (first_digit << 4) | second_digit;
244 * @escaped_string: an escaped URI, path, or other string
245 * @illegal_characters: a string containing a sequence of characters
246 * considered "illegal", '\0' is automatically in this list.
248 * Decodes escaped characters (i.e. PERCENTxx sequences) in @escaped_string.
249 * Characters are encoded in PERCENTxy form, where xy is the ASCII hex code
250 * for character 16x+y.
252 * Return value: a newly allocated string with the unescaped equivalents,
253 * or %NULL if @escaped_string contained one of the characters
254 * in @illegal_characters.
257 unescape_string (const gchar * escaped_string, const gchar * illegal_characters)
263 if (escaped_string == NULL) {
267 result = g_malloc (strlen (escaped_string) + 1);
270 for (in = escaped_string; *in != '\0'; in++) {
272 if (*in == HEX_ESCAPE) {
273 character = unescape_character (in + 1);
275 /* Check for an illegal character. We consider '\0' illegal here. */
277 || (illegal_characters != NULL
278 && strchr (illegal_characters, (char) character) != NULL)) {
284 *out++ = (char) character;
288 g_assert ((size_t) (out - result) <= strlen (escaped_string));
295 gst_uri_protocol_check_internal (const gchar * uri, gchar ** endptr)
297 gchar *check = (gchar *) uri;
299 g_assert (uri != NULL);
300 g_assert (endptr != NULL);
302 if (g_ascii_isalpha (*check)) {
304 while (g_ascii_isalnum (*check))
312 * gst_uri_protocol_is_valid:
313 * @protocol: A string
315 * Tests if the given string is a valid protocol identifier. Protocols
316 * must consist of alphanumeric characters and not start with a number.
318 * Returns: TRUE if the string is a valid protocol identifier, FALSE otherwise.
321 gst_uri_protocol_is_valid (const gchar * protocol)
325 g_return_val_if_fail (protocol != NULL, FALSE);
327 gst_uri_protocol_check_internal (protocol, &endptr);
329 return *endptr == '\0' && endptr != protocol;
336 * Tests if the given string is a valid URI identifier. URIs start with a valid
337 * protocol followed by "://" and maybe a string identifying the location.
339 * Returns: TRUE if the string is a valid URI
342 gst_uri_is_valid (const gchar * uri)
346 g_return_val_if_fail (uri != NULL, FALSE);
348 gst_uri_protocol_check_internal (uri, &endptr);
350 return (*endptr == ':' && *(endptr + 1) == '/' && *(endptr + 2) == '/');
354 * gst_uri_get_protocol:
357 * Extracts the protocol out of a given valid URI. The returned string must be
358 * freed using g_free().
360 * Returns: The protocol for this URI.
363 gst_uri_get_protocol (const gchar * uri)
367 g_return_val_if_fail (uri != NULL, NULL);
368 g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
370 colon = strstr (uri, "://");
372 return g_strndup (uri, colon - uri);
376 * gst_uri_has_protocol:
377 * @uri: an URI string
378 * @protocol: a protocol string (e.g. "http")
380 * Returns TRUE if the protocol of a given valid URI matches @protocol.
385 gst_uri_has_protocol (const gchar * uri, const gchar * protocol)
389 g_return_val_if_fail (uri != NULL, FALSE);
390 g_return_val_if_fail (protocol != NULL, FALSE);
391 g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
393 colon = strstr (uri, "://");
398 return (strncmp (uri, protocol, (size_t) (colon - uri)) == 0);
402 * gst_uri_get_location:
405 * Extracts the location out of a given valid URI. So the protocol and "://"
406 * are stripped from the URI. The returned string must be freed using
409 * Returns: The location for this URI. Returns NULL if the URI isn't valid.
412 gst_uri_get_location (const gchar * uri)
415 gchar *location, *unescaped;
417 g_return_val_if_fail (uri != NULL, NULL);
418 g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
420 colon = strstr (uri, "://");
422 location = g_strdup (colon + 3);
424 unescaped = unescape_string (location, "/");
432 * @protocol: Protocol for URI
433 * @location: Location for URI
435 * Constructs a URI for a given valid protocol and location.
437 * Returns: a new string for this URI. Returns NULL if the given URI protocol
438 * is not valid, or the given location is NULL.
441 gst_uri_construct (const gchar * protocol, const gchar * location)
446 g_return_val_if_fail (gst_uri_protocol_is_valid (protocol), NULL);
447 g_return_val_if_fail (location != NULL, NULL);
449 escaped = escape_string (location);
450 retval = g_strdup_printf ("%s://%s", protocol, escaped);
464 search_by_entry (GstPluginFeature * feature, gpointer search_entry)
467 GstElementFactory *factory;
468 SearchEntry *entry = (SearchEntry *) search_entry;
470 if (!GST_IS_ELEMENT_FACTORY (feature))
472 factory = GST_ELEMENT_FACTORY (feature);
474 if (gst_element_factory_get_uri_type (factory) != entry->type)
477 protocols = gst_element_factory_get_uri_protocols (factory);
478 /* must be set when uri type is valid */
479 g_assert (protocols);
480 while (*protocols != NULL) {
481 if (strcmp (*protocols, entry->protocol) == 0)
489 sort_by_rank (gconstpointer a, gconstpointer b)
491 GstPluginFeature *first = GST_PLUGIN_FEATURE (a);
492 GstPluginFeature *second = GST_PLUGIN_FEATURE (b);
494 return gst_plugin_feature_get_rank (second) -
495 gst_plugin_feature_get_rank (first);
499 * gst_element_make_from_uri:
500 * @type: Wether to create a source or a sink
501 * @uri: URI to create an element for
502 * @elementname: Name of created element, can be NULL.
504 * Creates an element for handling the given URI.
506 * Returns: a new element or NULL if none could be created
509 gst_element_make_from_uri (const GstURIType type, const gchar * uri,
510 const gchar * elementname)
512 GList *possibilities, *walk;
514 GstElement *ret = NULL;
516 g_return_val_if_fail (GST_URI_TYPE_IS_VALID (type), NULL);
517 g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
520 entry.protocol = gst_uri_get_protocol (uri);
521 possibilities = gst_registry_feature_filter (gst_registry_get_default (),
522 search_by_entry, FALSE, &entry);
523 g_free (entry.protocol);
525 if (!possibilities) {
526 GST_DEBUG ("No %s for URI '%s'", type == GST_URI_SINK ? "sink" : "source",
531 possibilities = g_list_sort (possibilities, sort_by_rank);
532 walk = possibilities;
534 if ((ret = gst_element_factory_create (GST_ELEMENT_FACTORY (walk->data),
535 elementname)) != NULL) {
536 GstURIHandler *handler = GST_URI_HANDLER (ret);
538 if (gst_uri_handler_set_uri (handler, uri))
540 gst_object_unref (ret);
545 g_list_free (possibilities);
547 GST_LOG_OBJECT (ret, "created %s for URL '%s'",
548 type == GST_URI_SINK ? "sink" : "source", uri);
553 * gst_uri_handler_get_uri_type:
554 * @handler: A #GstURIHandler.
556 * Gets the type of the given URI handler
558 * Returns: the #GstURIType of the URI handler.
559 * Returns #GST_URI_UNKNOWN if the @handler isn't implemented correctly.
562 gst_uri_handler_get_uri_type (GstURIHandler * handler)
564 GstURIHandlerInterface *iface;
567 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN);
569 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
570 g_return_val_if_fail (iface != NULL, GST_URI_UNKNOWN);
571 g_return_val_if_fail (iface->get_type != NULL, GST_URI_UNKNOWN);
572 ret = iface->get_type ();
573 g_return_val_if_fail (GST_URI_TYPE_IS_VALID (ret), GST_URI_UNKNOWN);
579 * gst_uri_handler_get_protocols:
580 * @handler: A #GstURIHandler.
582 * Gets the list of protocols supported by @handler. This list may not be
585 * Returns: the supported protocols.
586 * Returns NULL if the @handler isn't implemented properly, or the @handler
587 * doesn't support any protocols.
590 gst_uri_handler_get_protocols (GstURIHandler * handler)
592 GstURIHandlerInterface *iface;
595 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
597 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
598 g_return_val_if_fail (iface != NULL, NULL);
599 g_return_val_if_fail (iface->get_protocols != NULL, NULL);
600 ret = iface->get_protocols ();
601 g_return_val_if_fail (ret != NULL, NULL);
607 * gst_uri_handler_get_uri:
608 * @handler: A #GstURIHandler
610 * Gets the currently handled URI.
612 * Returns: the URI currently handler by the @handler.
613 * Returns NULL if there are no URI currently handled.
615 G_CONST_RETURN gchar *
616 gst_uri_handler_get_uri (GstURIHandler * handler)
618 GstURIHandlerInterface *iface;
621 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
623 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
624 g_return_val_if_fail (iface != NULL, NULL);
625 g_return_val_if_fail (iface->get_uri != NULL, NULL);
626 ret = iface->get_uri (handler);
628 g_return_val_if_fail (gst_uri_is_valid (ret), NULL);
634 * gst_uri_handler_set_uri:
635 * @handler: A #GstURIHandler
638 * Tries to set the URI of the given handler.
640 * Returns: TRUE if the URI was set successfully, else FALSE.
643 gst_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri)
645 GstURIHandlerInterface *iface;
647 g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE);
648 g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
650 iface = GST_URI_HANDLER_GET_INTERFACE (handler);
651 g_return_val_if_fail (iface != NULL, FALSE);
652 g_return_val_if_fail (iface->set_uri != NULL, FALSE);
653 return iface->set_uri (handler, uri);
657 * gst_uri_handler_new_uri:
658 * @handler: A #GstURIHandler
659 * @uri: new URI or NULL if it was unset
661 * Emits the new-uri signal for a given handler, when that handler has a new URI.
662 * This function should only be called by URI handlers themselves.
665 gst_uri_handler_new_uri (GstURIHandler * handler, const gchar * uri)
667 g_return_if_fail (GST_IS_URI_HANDLER (handler));
669 g_signal_emit_by_name (handler, "new-uri", uri);