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