gst/gsturi.c: Don't segfault on input like "tel:+1-123-555-1234".
[platform/upstream/gstreamer.git] / gst / gsturi.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gsturi.c: register URI handlers
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 /**
24  * SECTION:gsturihandler
25  * @short_description: Interface to ease URI handling in plugins.
26  *
27  * The URIHandler is an interface that is implemented by Source and Sink 
28  * #GstElement to simplify then handling of URI.
29  *
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()).
33  *
34  * Source and Sink plugins should implement this interface when possible.
35  *
36  * Last reviewed on 2005-11-09 (0.9.4)
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #  include "config.h"
41 #endif
42
43 #include "gst_private.h"
44 #include "gsturi.h"
45 #include "gstinfo.h"
46 #include "gstmarshal.h"
47 #include "gstregistry.h"
48
49 #include <string.h>
50
51 GST_DEBUG_CATEGORY_STATIC (gst_uri_handler_debug);
52 #define GST_CAT_DEFAULT gst_uri_handler_debug
53
54 static void gst_uri_handler_base_init (gpointer g_class);
55
56 GType
57 gst_uri_handler_get_type (void)
58 {
59   static GType urihandler_type = 0;
60
61   if (G_UNLIKELY (urihandler_type == 0)) {
62     static const GTypeInfo urihandler_info = {
63       sizeof (GstURIHandlerInterface),
64       gst_uri_handler_base_init,
65       NULL,
66       NULL,
67       NULL,
68       NULL,
69       0,
70       0,
71       NULL,
72       NULL
73     };
74
75     urihandler_type = g_type_register_static (G_TYPE_INTERFACE,
76         "GstURIHandler", &urihandler_info, 0);
77
78     GST_DEBUG_CATEGORY_INIT (gst_uri_handler_debug, "GST_URI", GST_DEBUG_BOLD,
79         "handling of URIs");
80   }
81   return urihandler_type;
82 }
83 static void
84 gst_uri_handler_base_init (gpointer g_class)
85 {
86   static gboolean initialized = FALSE;
87
88   if (G_UNLIKELY (!initialized)) {
89
90     /**
91      * GstURIHandler::new-uri:
92      * @handler: The #GstURIHandler which emitted the signal
93      * @uri: The new URI, or NULL if the URI was removed
94      *
95      * The URI of the given @handler has changed.
96      */
97
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);
101     initialized = TRUE;
102   }
103 }
104
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 */
112 };
113
114 typedef enum
115 {
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;
123
124 #define HEX_ESCAPE '%'
125
126 /*  Escape undesirable characters using %
127  *  -------------------------------------
128  *
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.
133  *
134  * This routine returns a g_malloced string.
135  */
136
137 static const gchar hex[16] = "0123456789ABCDEF";
138
139 static gchar *
140 escape_string_internal (const gchar * string, UnsafeCharacterSet mask)
141 {
142 #define ACCEPTABLE_CHAR(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
143
144   const gchar *p;
145   gchar *q;
146   gchar *result;
147   guchar c;
148   gint unacceptable;
149   UnsafeCharacterSet use_mask;
150
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);
156
157   if (string == NULL) {
158     return NULL;
159   }
160
161   unacceptable = 0;
162   use_mask = mask;
163   for (p = string; *p != '\0'; p++) {
164     c = *p;
165     if (!ACCEPTABLE_CHAR (c)) {
166       unacceptable++;
167     }
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).
171        */
172       use_mask = UNSAFE_PATH;
173     }
174   }
175
176   result = g_malloc (p - string + unacceptable * 2 + 1);
177
178   use_mask = mask;
179   for (q = result, p = string; *p != '\0'; p++) {
180     c = *p;
181
182     if (!ACCEPTABLE_CHAR (c)) {
183       *q++ = HEX_ESCAPE;        /* means hex coming */
184       *q++ = hex[c >> 4];
185       *q++ = hex[c & 15];
186     } else {
187       *q++ = c;
188     }
189     if ((use_mask == UNSAFE_HOST) && (!ACCEPTABLE_CHAR (c) || (c == '/'))) {
190       use_mask = UNSAFE_PATH;
191     }
192   }
193
194   *q = '\0';
195
196   return result;
197 }
198
199 /**
200  * escape_string:
201  * @string: string to be escaped
202  *
203  * Escapes @string, replacing any and all special characters
204  * with equivalent escape sequences.
205  *
206  * Return value: a newly allocated string equivalent to @string
207  * but with all special characters escaped
208  **/
209 static gchar *
210 escape_string (const gchar * string)
211 {
212   return escape_string_internal (string, UNSAFE_ALL);
213 }
214
215 static int
216 hex_to_int (gchar c)
217 {
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;
221 }
222
223 static int
224 unescape_character (const char *scanner)
225 {
226   int first_digit;
227   int second_digit;
228
229   first_digit = hex_to_int (*scanner++);
230   if (first_digit < 0) {
231     return -1;
232   }
233
234   second_digit = hex_to_int (*scanner++);
235   if (second_digit < 0) {
236     return -1;
237   }
238
239   return (first_digit << 4) | second_digit;
240 }
241
242 /**
243  * unescape_string:
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.
247  *
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.
251  *
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.
255  **/
256 static char *
257 unescape_string (const gchar * escaped_string, const gchar * illegal_characters)
258 {
259   const gchar *in;
260   gchar *out, *result;
261   gint character;
262
263   if (escaped_string == NULL) {
264     return NULL;
265   }
266
267   result = g_malloc (strlen (escaped_string) + 1);
268
269   out = result;
270   for (in = escaped_string; *in != '\0'; in++) {
271     character = *in;
272     if (*in == HEX_ESCAPE) {
273       character = unescape_character (in + 1);
274
275       /* Check for an illegal character. We consider '\0' illegal here. */
276       if (character <= 0
277           || (illegal_characters != NULL
278               && strchr (illegal_characters, (char) character) != NULL)) {
279         g_free (result);
280         return NULL;
281       }
282       in += 2;
283     }
284     *out++ = (char) character;
285   }
286
287   *out = '\0';
288   g_assert ((gsize) (out - result) <= strlen (escaped_string));
289   return result;
290
291 }
292
293
294 static void
295 gst_uri_protocol_check_internal (const gchar * uri, gchar ** endptr)
296 {
297   gchar *check = (gchar *) uri;
298
299   g_assert (uri != NULL);
300   g_assert (endptr != NULL);
301
302   if (g_ascii_isalpha (*check)) {
303     check++;
304     while (g_ascii_isalnum (*check) || *check == '+'
305         || *check == '-' || *check == '.')
306       check++;
307   }
308
309   *endptr = check;
310 }
311
312 /**
313  * gst_uri_protocol_is_valid:
314  * @protocol: A string
315  *
316  * Tests if the given string is a valid protocol identifier. Protocols
317  * must consist of alphanumeric characters, '+', '-' and '.' and must
318  * start with a alphabetic character. See RFC 3986 Section 3.1.
319  *
320  * Returns: TRUE if the string is a valid protocol identifier, FALSE otherwise.
321  */
322 gboolean
323 gst_uri_protocol_is_valid (const gchar * protocol)
324 {
325   gchar *endptr;
326
327   g_return_val_if_fail (protocol != NULL, FALSE);
328
329   gst_uri_protocol_check_internal (protocol, &endptr);
330
331   return *endptr == '\0' && endptr != protocol;
332 }
333
334 /**
335  * gst_uri_is_valid:
336  * @uri: A URI string
337  *
338  * Tests if the given string is a valid URI identifier. URIs start with a valid
339  * scheme followed by ":" and maybe a string identifying the location.
340  *
341  * Returns: TRUE if the string is a valid URI
342  */
343 gboolean
344 gst_uri_is_valid (const gchar * uri)
345 {
346   gchar *endptr;
347
348   g_return_val_if_fail (uri != NULL, FALSE);
349
350   gst_uri_protocol_check_internal (uri, &endptr);
351
352   return *endptr == ':';
353 }
354
355 /**
356  * gst_uri_get_protocol:
357  * @uri: A URI string
358  *
359  * Extracts the protocol out of a given valid URI. The returned string must be
360  * freed using g_free().
361  *
362  * Returns: The protocol for this URI.
363  */
364 gchar *
365 gst_uri_get_protocol (const gchar * uri)
366 {
367   gchar *colon;
368
369   g_return_val_if_fail (uri != NULL, NULL);
370   g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
371
372   colon = strstr (uri, ":");
373
374   return g_ascii_strdown (uri, colon - uri);
375 }
376
377 /**
378  * gst_uri_has_protocol:
379  * @uri: an URI string
380  * @protocol: a protocol string (e.g. "http")
381  *
382  * Checks if the protocol of a given valid URI matches @protocol.
383  *
384  * Returns: %TRUE if the protocol matches.
385  *
386  * Since: 0.10.4
387  */
388 gboolean
389 gst_uri_has_protocol (const gchar * uri, const gchar * protocol)
390 {
391   gchar *colon;
392
393   g_return_val_if_fail (uri != NULL, FALSE);
394   g_return_val_if_fail (protocol != NULL, FALSE);
395   g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
396
397   colon = strstr (uri, ":");
398
399   if (colon == NULL)
400     return FALSE;
401
402   return (g_ascii_strncasecmp (uri, protocol, (gsize) (colon - uri)) == 0);
403 }
404
405 /**
406  * gst_uri_get_location:
407  * @uri: A URI string
408  *
409  * Extracts the location out of a given valid URI, ie. the protocol and "://"
410  * are stripped from the URI, which means that the location returned includes
411  * the hostname if one is specified. The returned string must be freed using
412  * g_free().
413  *
414  * Returns: The location for this URI. Returns NULL if the URI isn't valid. If
415  * the URI does not contain a location, an empty string is returned.
416  */
417 gchar *
418 gst_uri_get_location (const gchar * uri)
419 {
420   const gchar *colon;
421   gchar *unescaped = NULL;
422
423   g_return_val_if_fail (uri != NULL, NULL);
424   g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
425
426   colon = strstr (uri, "://");
427   if (!colon)
428     return NULL;
429
430   unescaped = unescape_string (colon + 3, "/");
431
432   /* On Windows an URI might look like file:///c:/foo/bar.txt or
433    * file:///c|/foo/bar.txt (some Netscape versions) and we want to
434    * return c:/foo/bar.txt as location rather than /c:/foo/bar.txt.
435    * Can't use g_filename_from_uri() here because it will only handle the
436    * file:// protocol */
437 #ifdef G_OS_WIN32
438   if (unescaped != NULL && unescaped[0] == '/' &&
439       g_ascii_isalpha (unescaped[1]) &&
440       (unescaped[2] == ':' || unescaped[2] == '|')) {
441     unescaped[2] = ':';
442     g_memmove (unescaped, unescaped + 1, strlen (unescaped + 1) + 1);
443   }
444 #endif
445
446   GST_LOG ("extracted location '%s' from URI '%s'", GST_STR_NULL (unescaped),
447       uri);;
448   return unescaped;
449 }
450
451 /**
452  * gst_uri_construct:
453  * @protocol: Protocol for URI
454  * @location: Location for URI
455  *
456  * Constructs a URI for a given valid protocol and location.
457  *
458  * Returns: a new string for this URI. Returns NULL if the given URI protocol
459  * is not valid, or the given location is NULL.
460  */
461 gchar *
462 gst_uri_construct (const gchar * protocol, const gchar * location)
463 {
464   char *escaped, *proto_lowercase;
465   char *retval;
466
467   g_return_val_if_fail (gst_uri_protocol_is_valid (protocol), NULL);
468   g_return_val_if_fail (location != NULL, NULL);
469
470   proto_lowercase = g_ascii_strdown (protocol, -1);
471   escaped = escape_string (location);
472   retval = g_strdup_printf ("%s://%s", proto_lowercase, escaped);
473   g_free (escaped);
474   g_free (proto_lowercase);
475
476   return retval;
477 }
478
479 typedef struct
480 {
481   GstURIType type;
482   const gchar *protocol;
483 }
484 SearchEntry;
485
486 static gboolean
487 search_by_entry (GstPluginFeature * feature, gpointer search_entry)
488 {
489   gchar **protocols;
490   GstElementFactory *factory;
491   SearchEntry *entry = (SearchEntry *) search_entry;
492
493   if (!GST_IS_ELEMENT_FACTORY (feature))
494     return FALSE;
495   factory = GST_ELEMENT_FACTORY (feature);
496
497   if (gst_element_factory_get_uri_type (factory) != entry->type)
498     return FALSE;
499
500   protocols = gst_element_factory_get_uri_protocols (factory);
501
502   if (protocols == NULL) {
503     g_warning ("Factory '%s' implements GstUriHandler interface but returned "
504         "no supported protocols!", gst_plugin_feature_get_name (feature));
505     return FALSE;
506   }
507
508   while (*protocols != NULL) {
509     if (g_ascii_strcasecmp (*protocols, entry->protocol) == 0)
510       return TRUE;
511     protocols++;
512   }
513   return FALSE;
514 }
515
516 static gint
517 sort_by_rank (gconstpointer a, gconstpointer b)
518 {
519   GstPluginFeature *first = GST_PLUGIN_FEATURE (a);
520   GstPluginFeature *second = GST_PLUGIN_FEATURE (b);
521
522   return gst_plugin_feature_get_rank (second) -
523       gst_plugin_feature_get_rank (first);
524 }
525
526 static GList *
527 get_element_factories_from_uri_protocol (const GstURIType type,
528     const gchar * protocol)
529 {
530   GList *possibilities;
531   SearchEntry entry;
532
533   g_return_val_if_fail (protocol, NULL);
534
535   entry.type = type;
536   entry.protocol = protocol;
537   possibilities = gst_registry_feature_filter (gst_registry_get_default (),
538       search_by_entry, FALSE, &entry);
539
540   return possibilities;
541 }
542
543 /**
544  * gst_uri_protocol_is_supported:
545  * @type: Whether to check for a source or a sink
546  * @protocol: Protocol that should be checked for (e.g. "http" or "smb")
547  *
548  * Checks if an element exists that supports the given URI protocol. Note
549  * that a positive return value does not imply that a subsequent call to
550  * gst_element_make_from_uri() is guaranteed to work.
551  *
552  * Returns: TRUE
553  *
554  * Since: 0.10.13
555 */
556 gboolean
557 gst_uri_protocol_is_supported (const GstURIType type, const gchar * protocol)
558 {
559   GList *possibilities;
560
561   g_return_val_if_fail (protocol, FALSE);
562
563   possibilities = get_element_factories_from_uri_protocol (type, protocol);
564
565   if (possibilities) {
566     g_list_free (possibilities);
567     return TRUE;
568   } else
569     return FALSE;
570 }
571
572 /**
573  * gst_element_make_from_uri:
574  * @type: Whether to create a source or a sink
575  * @uri: URI to create an element for
576  * @elementname: Name of created element, can be NULL.
577  *
578  * Creates an element for handling the given URI.
579  *
580  * Returns: a new element or NULL if none could be created
581  */
582 GstElement *
583 gst_element_make_from_uri (const GstURIType type, const gchar * uri,
584     const gchar * elementname)
585 {
586   GList *possibilities, *walk;
587   gchar *protocol;
588   GstElement *ret = NULL;
589
590   g_return_val_if_fail (GST_URI_TYPE_IS_VALID (type), NULL);
591   g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
592
593   protocol = gst_uri_get_protocol (uri);
594   possibilities = get_element_factories_from_uri_protocol (type, protocol);
595   g_free (protocol);
596
597   if (!possibilities) {
598     GST_DEBUG ("No %s for URI '%s'", type == GST_URI_SINK ? "sink" : "source",
599         uri);
600     return NULL;
601   }
602
603   possibilities = g_list_sort (possibilities, sort_by_rank);
604   walk = possibilities;
605   while (walk) {
606     if ((ret = gst_element_factory_create (GST_ELEMENT_FACTORY (walk->data),
607                 elementname)) != NULL) {
608       GstURIHandler *handler = GST_URI_HANDLER (ret);
609
610       if (gst_uri_handler_set_uri (handler, uri))
611         break;
612       gst_object_unref (ret);
613       ret = NULL;
614     }
615     walk = walk->next;
616   }
617   gst_plugin_feature_list_free (possibilities);
618
619   GST_LOG_OBJECT (ret, "created %s for URL '%s'",
620       type == GST_URI_SINK ? "sink" : "source", uri);
621   return ret;
622 }
623
624 /**
625  * gst_uri_handler_get_uri_type:
626  * @handler: A #GstURIHandler.
627  *
628  * Gets the type of the given URI handler
629  *
630  * Returns: the #GstURIType of the URI handler.
631  * Returns #GST_URI_UNKNOWN if the @handler isn't implemented correctly.
632  */
633 guint
634 gst_uri_handler_get_uri_type (GstURIHandler * handler)
635 {
636   GstURIHandlerInterface *iface;
637   guint ret;
638
639   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN);
640
641   iface = GST_URI_HANDLER_GET_INTERFACE (handler);
642   g_return_val_if_fail (iface != NULL, GST_URI_UNKNOWN);
643   g_return_val_if_fail (iface->get_type != NULL, GST_URI_UNKNOWN);
644   ret = iface->get_type ();
645   g_return_val_if_fail (GST_URI_TYPE_IS_VALID (ret), GST_URI_UNKNOWN);
646
647   return ret;
648 }
649
650 /**
651  * gst_uri_handler_get_protocols:
652  * @handler: A #GstURIHandler.
653  *
654  * Gets the list of protocols supported by @handler. This list may not be
655  * modified.
656  *
657  * Returns: the supported protocols.
658  * Returns NULL if the @handler isn't implemented properly, or the @handler
659  * doesn't support any protocols.
660  */
661 gchar **
662 gst_uri_handler_get_protocols (GstURIHandler * handler)
663 {
664   GstURIHandlerInterface *iface;
665   gchar **ret;
666
667   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
668
669   iface = GST_URI_HANDLER_GET_INTERFACE (handler);
670   g_return_val_if_fail (iface != NULL, NULL);
671   g_return_val_if_fail (iface->get_protocols != NULL ||
672       iface->get_protocols_full != NULL, NULL);
673
674   if (iface->get_protocols != NULL) {
675     ret = iface->get_protocols ();
676   } else {
677     ret = iface->get_protocols_full (G_OBJECT_TYPE (handler));
678   }
679   g_return_val_if_fail (ret != NULL, NULL);
680
681   return ret;
682 }
683
684 /**
685  * gst_uri_handler_get_uri:
686  * @handler: A #GstURIHandler
687  *
688  * Gets the currently handled URI.
689  *
690  * Returns: the URI currently handled by the @handler.
691  * Returns NULL if there are no URI currently handled. The returned
692  * string must not be modified or freed.
693  */
694 G_CONST_RETURN gchar *
695 gst_uri_handler_get_uri (GstURIHandler * handler)
696 {
697   GstURIHandlerInterface *iface;
698   const gchar *ret;
699
700   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
701
702   iface = GST_URI_HANDLER_GET_INTERFACE (handler);
703   g_return_val_if_fail (iface != NULL, NULL);
704   g_return_val_if_fail (iface->get_uri != NULL, NULL);
705   ret = iface->get_uri (handler);
706   if (ret != NULL)
707     g_return_val_if_fail (gst_uri_is_valid (ret), NULL);
708
709   return ret;
710 }
711
712 /**
713  * gst_uri_handler_set_uri:
714  * @handler: A #GstURIHandler
715  * @uri: URI to set
716  *
717  * Tries to set the URI of the given handler.
718  *
719  * Returns: TRUE if the URI was set successfully, else FALSE.
720  */
721 gboolean
722 gst_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri)
723 {
724   GstURIHandlerInterface *iface;
725   gboolean ret;
726   gchar *new_uri, *protocol, *location, *colon;
727
728   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE);
729   g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
730
731   iface = GST_URI_HANDLER_GET_INTERFACE (handler);
732   g_return_val_if_fail (iface != NULL, FALSE);
733   g_return_val_if_fail (iface->set_uri != NULL, FALSE);
734
735   protocol = gst_uri_get_protocol (uri);
736
737   colon = strstr (uri, ":");
738   location = g_strdup (colon);
739
740   new_uri = g_strdup_printf ("%s%s", protocol, location);
741
742   ret = iface->set_uri (handler, uri);
743
744   g_free (new_uri);
745   g_free (location);
746   g_free (protocol);
747
748   return ret;
749 }
750
751 /**
752  * gst_uri_handler_new_uri:
753  * @handler: A #GstURIHandler
754  * @uri: new URI or NULL if it was unset
755  *
756  * Emits the new-uri signal for a given handler, when that handler has a new URI.
757  * This function should only be called by URI handlers themselves.
758  */
759 void
760 gst_uri_handler_new_uri (GstURIHandler * handler, const gchar * uri)
761 {
762   g_return_if_fail (GST_IS_URI_HANDLER (handler));
763
764   g_signal_emit_by_name (handler, "new-uri", uri);
765 }