*.c: Don't cast to GST_OBJECT when reffing or unreffing. Large source-munging commit!!!
[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 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include "gst_private.h"
28 #include "gsturi.h"
29 #include "gstinfo.h"
30 #include "gstregistrypool.h"
31 #include "gstmarshal.h"
32
33 #include <string.h>
34
35 GST_DEBUG_CATEGORY_STATIC (gst_uri_handler_debug);
36 #define GST_CAT_DEFAULT gst_uri_handler_debug
37
38 static void gst_uri_handler_base_init (gpointer g_class);
39
40 GType
41 gst_uri_handler_get_type (void)
42 {
43   static GType urihandler_type = 0;
44
45   if (!urihandler_type) {
46     static const GTypeInfo urihandler_info = {
47       sizeof (GstURIHandlerInterface),
48       gst_uri_handler_base_init,
49       NULL,
50       NULL,
51       NULL,
52       NULL,
53       0,
54       0,
55       NULL,
56       NULL
57     };
58
59     urihandler_type = g_type_register_static (G_TYPE_INTERFACE,
60         "GstURIHandler", &urihandler_info, 0);
61
62     GST_DEBUG_CATEGORY_INIT (gst_uri_handler_debug, "GST_URI", GST_DEBUG_BOLD,
63         "handling of URIs");
64   }
65   return urihandler_type;
66 }
67 static void
68 gst_uri_handler_base_init (gpointer g_class)
69 {
70   static gboolean initialized = FALSE;
71
72   if (!initialized) {
73     g_signal_new ("new-uri", GST_TYPE_URI_HANDLER, G_SIGNAL_RUN_LAST,
74         G_STRUCT_OFFSET (GstURIHandlerInterface, new_uri), NULL, NULL,
75         gst_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
76     initialized = TRUE;
77   }
78 }
79
80 static const guchar acceptable[96] = {  /* X0   X1   X2   X3   X4   X5   X6   X7   X8   X9   XA   XB   XC   XD   XE   XF */
81   0x00, 0x3F, 0x20, 0x20, 0x20, 0x00, 0x2C, 0x3F, 0x3F, 0x3F, 0x3F, 0x22, 0x20, 0x3F, 0x3F, 0x1C,       /* 2X  !"#$%&'()*+,-./   */
82   0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x38, 0x20, 0x20, 0x2C, 0x20, 0x2C,       /* 3X 0123456789:;<=>?   */
83   0x30, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,       /* 4X @ABCDEFGHIJKLMNO   */
84   0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x20, 0x3F,       /* 5X PQRSTUVWXYZ[\]^_   */
85   0x20, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,       /* 6X `abcdefghijklmno   */
86   0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x3F, 0x20        /* 7X pqrstuvwxyz{|}~DEL */
87 };
88
89 typedef enum
90 {
91   UNSAFE_ALL = 0x1,             /* Escape all unsafe characters   */
92   UNSAFE_ALLOW_PLUS = 0x2,      /* Allows '+'  */
93   UNSAFE_PATH = 0x4,            /* Allows '/' and '?' and '&' and '='  */
94   UNSAFE_DOS_PATH = 0x8,        /* Allows '/' and '?' and '&' and '=' and ':' */
95   UNSAFE_HOST = 0x10,           /* Allows '/' and ':' and '@' */
96   UNSAFE_SLASHES = 0x20         /* Allows all characters except for '/' and '%' */
97 } UnsafeCharacterSet;
98
99 #define HEX_ESCAPE '%'
100
101 /*  Escape undesirable characters using %
102  *  -------------------------------------
103  *
104  * This function takes a pointer to a string in which
105  * some characters may be unacceptable unescaped.
106  * It returns a string which has these characters
107  * represented by a '%' character followed by two hex digits.
108  *
109  * This routine returns a g_malloced string.
110  */
111
112 static const gchar hex[16] = "0123456789ABCDEF";
113
114 static gchar *
115 escape_string_internal (const gchar * string, UnsafeCharacterSet mask)
116 {
117 #define ACCEPTABLE_CHAR(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
118
119   const gchar *p;
120   gchar *q;
121   gchar *result;
122   guchar c;
123   gint unacceptable;
124   UnsafeCharacterSet use_mask;
125
126   g_return_val_if_fail (mask == UNSAFE_ALL
127       || mask == UNSAFE_ALLOW_PLUS
128       || mask == UNSAFE_PATH
129       || mask == UNSAFE_DOS_PATH
130       || mask == UNSAFE_HOST || mask == UNSAFE_SLASHES, NULL);
131
132   if (string == NULL) {
133     return NULL;
134   }
135
136   unacceptable = 0;
137   use_mask = mask;
138   for (p = string; *p != '\0'; p++) {
139     c = *p;
140     if (!ACCEPTABLE_CHAR (c)) {
141       unacceptable++;
142     }
143     if ((use_mask == UNSAFE_HOST) && (unacceptable || (c == '/'))) {
144       /* when escaping a host, if we hit something that needs to be escaped, or we finally
145        * hit a path separator, revert to path mode (the host segment of the url is over).
146        */
147       use_mask = UNSAFE_PATH;
148     }
149   }
150
151   result = g_malloc (p - string + unacceptable * 2 + 1);
152
153   use_mask = mask;
154   for (q = result, p = string; *p != '\0'; p++) {
155     c = *p;
156
157     if (!ACCEPTABLE_CHAR (c)) {
158       *q++ = HEX_ESCAPE;        /* means hex coming */
159       *q++ = hex[c >> 4];
160       *q++ = hex[c & 15];
161     } else {
162       *q++ = c;
163     }
164     if ((use_mask == UNSAFE_HOST) && (!ACCEPTABLE_CHAR (c) || (c == '/'))) {
165       use_mask = UNSAFE_PATH;
166     }
167   }
168
169   *q = '\0';
170
171   return result;
172 }
173
174 /**
175  * escape_string:
176  * @string: string to be escaped
177  *
178  * Escapes @string, replacing any and all special characters 
179  * with equivalent escape sequences.
180  *
181  * Return value: a newly allocated string equivalent to @string
182  * but with all special characters escaped
183  **/
184 gchar *
185 escape_string (const gchar * string)
186 {
187   return escape_string_internal (string, UNSAFE_ALL);
188 }
189
190 static int
191 hex_to_int (gchar c)
192 {
193   return c >= '0' && c <= '9' ? c - '0'
194       : c >= 'A' && c <= 'F' ? c - 'A' + 10
195       : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
196 }
197
198 static int
199 unescape_character (const char *scanner)
200 {
201   int first_digit;
202   int second_digit;
203
204   first_digit = hex_to_int (*scanner++);
205   if (first_digit < 0) {
206     return -1;
207   }
208
209   second_digit = hex_to_int (*scanner++);
210   if (second_digit < 0) {
211     return -1;
212   }
213
214   return (first_digit << 4) | second_digit;
215 }
216
217 /**
218  * unescape_string:
219  * @escaped_string: an escaped URI, path, or other string
220  * @illegal_characters: a string containing a sequence of characters
221  * considered "illegal", '\0' is automatically in this list.
222  *
223  * Decodes escaped characters (i.e. PERCENTxx sequences) in @escaped_string.
224  * Characters are encoded in PERCENTxy form, where xy is the ASCII hex code 
225  * for character 16x+y.
226  * 
227  * Return value: a newly allocated string with the unescaped equivalents, 
228  * or %NULL if @escaped_string contained one of the characters 
229  * in @illegal_characters.
230  **/
231 static char *
232 unescape_string (const gchar * escaped_string, const gchar * illegal_characters)
233 {
234   const gchar *in;
235   gchar *out, *result;
236   gint character;
237
238   if (escaped_string == NULL) {
239     return NULL;
240   }
241
242   result = g_malloc (strlen (escaped_string) + 1);
243
244   out = result;
245   for (in = escaped_string; *in != '\0'; in++) {
246     character = *in;
247     if (*in == HEX_ESCAPE) {
248       character = unescape_character (in + 1);
249
250       /* Check for an illegal character. We consider '\0' illegal here. */
251       if (character <= 0
252           || (illegal_characters != NULL
253               && strchr (illegal_characters, (char) character) != NULL)) {
254         g_free (result);
255         return NULL;
256       }
257       in += 2;
258     }
259     *out++ = (char) character;
260   }
261
262   *out = '\0';
263   g_assert (out - result <= strlen (escaped_string));
264   return result;
265
266 }
267
268
269 static void
270 gst_uri_protocol_check_internal (const gchar * uri, gchar ** endptr)
271 {
272   gchar *check = (gchar *) uri;
273
274   g_assert (uri != NULL);
275   g_assert (endptr != NULL);
276
277   if (g_ascii_isalpha (*check)) {
278     check++;
279     while (g_ascii_isalnum (*check))
280       check++;
281   }
282
283   *endptr = check;
284 }
285
286 /**
287  * gst_uri_protocol_is_valid:
288  * @protocol: string to check
289  *
290  * Tests if the given string is a valid protocol identifier. Protocols
291  * must consist of alphanumeric characters and not start with a number.
292  *
293  * Returns: TRUE if the string is a valid protocol identifier
294  */
295 gboolean
296 gst_uri_protocol_is_valid (const gchar * protocol)
297 {
298   gchar *endptr;
299
300   g_return_val_if_fail (protocol != NULL, FALSE);
301
302   gst_uri_protocol_check_internal (protocol, &endptr);
303
304   return *endptr == '\0' && endptr != protocol;
305 }
306
307 /**
308  * gst_uri_is_valid:
309  * @uri: string to check
310  *
311  * Tests if the given string is a valid URI identifier. URIs start with a valid
312  * protocol followed by "://" and a string identifying the location.
313  *
314  * Returns: TRUE if the string is a valid URI
315  */
316 gboolean
317 gst_uri_is_valid (const gchar * uri)
318 {
319   gchar *endptr;
320
321   g_return_val_if_fail (uri != NULL, FALSE);
322
323   gst_uri_protocol_check_internal (uri, &endptr);
324
325   return (*endptr == ':' && *(endptr + 1) == '/' && *(endptr + 2) == '/');
326 }
327
328 /**
329  * gst_uri_get_protocol:
330  * @uri: URI to get protocol from
331  *
332  * Extracts the protocol out of a given valid URI. The returned string must be
333  * freed using g_free().
334  *
335  * Returns: The protocol for this URI.
336  */
337 gchar *
338 gst_uri_get_protocol (const gchar * uri)
339 {
340   gchar *colon;
341
342   g_return_val_if_fail (uri != NULL, NULL);
343   g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
344
345   colon = strstr (uri, "://");
346
347   return g_strndup (uri, colon - uri);
348 }
349
350 /**
351  * gst_uri_get_location:
352  * @uri: URI to get the location from
353  *
354  * Extracts the location out of a given valid URI. So the protocol and "://"
355  * are stripped from the URI. The returned string must be freed using 
356  * g_free().
357  *
358  * Returns: The location for this URI.
359  */
360 gchar *
361 gst_uri_get_location (const gchar * uri)
362 {
363   gchar *colon;
364   gchar *location, *unescaped;
365
366   g_return_val_if_fail (uri != NULL, NULL);
367   g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
368
369   colon = strstr (uri, "://");
370
371   location = g_strdup (colon + 3);
372
373   unescaped = unescape_string (location, "/");
374   g_free (location);
375
376   return unescaped;
377 }
378
379 /**
380  * gst_uri_construct:
381  * @protocol: protocol for URI
382  * @location: location for URI
383  *
384  * Constructs a URI for a given valid protocol and location.
385  *
386  * Returns: a new string for this URI
387  */
388 gchar *
389 gst_uri_construct (const gchar * protocol, const gchar * location)
390 {
391   char *escaped;
392   char *retval;
393
394   g_return_val_if_fail (gst_uri_protocol_is_valid (protocol), NULL);
395   g_return_val_if_fail (location != NULL, NULL);
396
397   escaped = escape_string (location);
398   retval = g_strdup_printf ("%s://%s", protocol, escaped);
399   g_free (escaped);
400
401   return retval;
402 }
403
404 typedef struct
405 {
406   GstURIType type;
407   gchar *protocol;
408 }
409 SearchEntry;
410
411 static gboolean
412 search_by_entry (GstPluginFeature * feature, gpointer search_entry)
413 {
414   gchar **protocols;
415   GstElementFactory *factory;
416   SearchEntry *entry = (SearchEntry *) search_entry;
417
418   if (!GST_IS_ELEMENT_FACTORY (feature))
419     return FALSE;
420   factory = GST_ELEMENT_FACTORY (feature);
421
422   if (gst_element_factory_get_uri_type (factory) != entry->type)
423     return FALSE;
424
425   protocols = gst_element_factory_get_uri_protocols (factory);
426   /* must be set when uri type is valid */
427   g_assert (protocols);
428   while (*protocols != NULL) {
429     if (strcmp (*protocols, entry->protocol) == 0)
430       return TRUE;
431     protocols++;
432   }
433   return FALSE;
434 }
435
436 static gint
437 sort_by_rank (gconstpointer a, gconstpointer b)
438 {
439   GstPluginFeature *first = GST_PLUGIN_FEATURE (a);
440   GstPluginFeature *second = GST_PLUGIN_FEATURE (b);
441
442   return gst_plugin_feature_get_rank (second) -
443       gst_plugin_feature_get_rank (first);
444 }
445
446 /**
447  * gst_element_make_from_uri:
448  * @type: wether to create a source or a sink
449  * @uri: URI to create element for
450  * @elementname: optional name of created element
451  *
452  * Creates an element for handling the given URI. 
453  * 
454  * Returns: a new element or NULL if none could be created
455  */
456 GstElement *
457 gst_element_make_from_uri (const GstURIType type, const gchar * uri,
458     const gchar * elementname)
459 {
460   GList *possibilities, *walk;
461   SearchEntry entry;
462   GstElement *ret = NULL;
463
464   g_return_val_if_fail (GST_URI_TYPE_IS_VALID (type), NULL);
465   g_return_val_if_fail (gst_uri_is_valid (uri), NULL);
466
467   entry.type = type;
468   entry.protocol = gst_uri_get_protocol (uri);
469   possibilities =
470       gst_registry_pool_feature_filter (search_by_entry, FALSE, &entry);
471   g_free (entry.protocol);
472
473   if (!possibilities) {
474     GST_DEBUG ("No %s for URI '%s'", type == GST_URI_SINK ? "sink" : "source",
475         uri);
476     return NULL;
477   }
478
479   possibilities = g_list_sort (possibilities, sort_by_rank);
480   walk = possibilities;
481   while (walk) {
482     if ((ret = gst_element_factory_create (GST_ELEMENT_FACTORY (walk->data),
483                 elementname)) != NULL) {
484       GstURIHandler *handler = GST_URI_HANDLER (ret);
485
486       if (gst_uri_handler_set_uri (handler, uri))
487         break;
488       gst_object_unref (ret);
489       ret = NULL;
490     }
491     walk = walk->next;
492   }
493   g_list_free (possibilities);
494
495   GST_LOG_OBJECT (ret, "created %s for URL '%s'",
496       type == GST_URI_SINK ? "sink" : "source", uri);
497   return ret;
498 }
499
500 /**
501  * gst_uri_handler_get_uri_type:
502  * @handler: Handler to query type of
503  *
504  * Gets the type of a URI handler
505  *
506  * Returns: the type of the URI handler
507  */
508 guint
509 gst_uri_handler_get_uri_type (GstURIHandler * handler)
510 {
511   GstURIHandlerInterface *iface;
512   guint ret;
513
514   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN);
515
516   iface = GST_URI_HANDLER_GET_INTERFACE (handler);
517   g_return_val_if_fail (iface != NULL, GST_URI_UNKNOWN);
518   g_return_val_if_fail (iface->get_type != NULL, GST_URI_UNKNOWN);
519   ret = iface->get_type ();
520   g_return_val_if_fail (GST_URI_TYPE_IS_VALID (ret), GST_URI_UNKNOWN);
521
522   return ret;
523 }
524
525 /**
526  * gst_uri_handler_get_protocols:
527  * @handler: Handler to get protocols for
528  *
529  * Gets the list of supported protocols for this handler. This list may not be
530  * modified.
531  *
532  * Returns: the supported protocols
533  */
534 gchar **
535 gst_uri_handler_get_protocols (GstURIHandler * handler)
536 {
537   GstURIHandlerInterface *iface;
538   gchar **ret;
539
540   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
541
542   iface = GST_URI_HANDLER_GET_INTERFACE (handler);
543   g_return_val_if_fail (iface != NULL, NULL);
544   g_return_val_if_fail (iface->get_protocols != NULL, NULL);
545   ret = iface->get_protocols ();
546   g_return_val_if_fail (ret != NULL, NULL);
547
548   return ret;
549 }
550
551 /**
552  * gst_uri_handler_get_uri:
553  * @handler: handler to query URI of
554  *
555  * Gets the currently handled URI of the handler or NULL, if none is set.
556  *
557  * Returns: the URI
558  */
559 G_CONST_RETURN gchar *
560 gst_uri_handler_get_uri (GstURIHandler * handler)
561 {
562   GstURIHandlerInterface *iface;
563   const gchar *ret;
564
565   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
566
567   iface = GST_URI_HANDLER_GET_INTERFACE (handler);
568   g_return_val_if_fail (iface != NULL, NULL);
569   g_return_val_if_fail (iface->get_uri != NULL, NULL);
570   ret = iface->get_uri (handler);
571   if (ret != NULL)
572     g_return_val_if_fail (gst_uri_is_valid (ret), NULL);
573
574   return ret;
575 }
576
577 /**
578  * gst_uri_handler_set_uri:
579  * @handler: handler to set URI of
580  * @uri: URI to set
581  *
582  * Tries to set the URI of the given handler and returns TRUE if it succeeded.
583  *
584  * Returns: TRUE, if the URI was set successfully
585  */
586 gboolean
587 gst_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri)
588 {
589   GstURIHandlerInterface *iface;
590
591   g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE);
592   g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
593
594   iface = GST_URI_HANDLER_GET_INTERFACE (handler);
595   g_return_val_if_fail (iface != NULL, FALSE);
596   g_return_val_if_fail (iface->set_uri != NULL, FALSE);
597   return iface->set_uri (handler, uri);
598 }
599
600 /**
601  * gst_uri_handler_new_uri:
602  * @handler: handler with a new URI
603  * @uri: new URI or NULL if it was unset
604  *
605  * Emits the new-uri event for a given handler, when that handler has a new URI.
606  * This function should only be called by URI handlers themselves.
607  */
608 void
609 gst_uri_handler_new_uri (GstURIHandler * handler, const gchar * uri)
610 {
611   g_return_if_fail (GST_IS_URI_HANDLER (handler));
612
613   g_signal_emit_by_name (handler, "new-uri", uri);
614 }