source: trunk/third/gstreamer/gst/gsturi.c @ 21448

Revision 21448, 15.8 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21447, which included commits to RCS files with non-trunk default branches.
Line 
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
35GST_DEBUG_CATEGORY_STATIC (gst_uri_handler_debug);
36#define GST_CAT_DEFAULT gst_uri_handler_debug
37
38static void gst_uri_handler_base_init (gpointer g_class);
39
40GType
41gst_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}
67static void
68gst_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
80static 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
89typedef 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
112static const gchar hex[16] = "0123456789ABCDEF";
113
114static gchar *
115escape_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 **/
184gchar *
185escape_string (const gchar * string)
186{
187  return escape_string_internal (string, UNSAFE_ALL);
188}
189
190static int
191hex_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
198static int
199unescape_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 **/
231static char *
232unescape_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
269static void
270gst_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 */
295gboolean
296gst_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 */
316gboolean
317gst_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 */
337gchar *
338gst_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 */
360gchar *
361gst_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 */
388gchar *
389gst_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
404typedef struct
405{
406  GstURIType type;
407  gchar *protocol;
408}
409SearchEntry;
410
411static gboolean
412search_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
436static gint
437sort_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 */
456GstElement *
457gst_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 (GST_OBJECT (ret));
489      ret = NULL;
490    }
491  }
492  g_list_free (possibilities);
493
494  GST_LOG_OBJECT (ret, "created %s for URL '%s'",
495      type == GST_URI_SINK ? "sink" : "source", uri);
496  return ret;
497}
498
499/**
500 * gst_uri_handler_get_uri_type:
501 * @handler: Handler to query type of
502 *
503 * Gets the type of a URI handler
504 *
505 * Returns: the type of the URI handler
506 */
507guint
508gst_uri_handler_get_uri_type (GstURIHandler * handler)
509{
510  GstURIHandlerInterface *iface;
511  guint ret;
512
513  g_return_val_if_fail (GST_IS_URI_HANDLER (handler), GST_URI_UNKNOWN);
514
515  iface = GST_URI_HANDLER_GET_INTERFACE (handler);
516  g_return_val_if_fail (iface != NULL, GST_URI_UNKNOWN);
517  g_return_val_if_fail (iface->get_type != NULL, GST_URI_UNKNOWN);
518  ret = iface->get_type ();
519  g_return_val_if_fail (GST_URI_TYPE_IS_VALID (ret), GST_URI_UNKNOWN);
520
521  return ret;
522}
523
524/**
525 * gst_uri_handler_get_protocols:
526 * @handler: Handler to get protocols for
527 *
528 * Gets the list of supported protocols for this handler. This list may not be
529 * modified.
530 *
531 * Returns: the supported protocols
532 */
533gchar **
534gst_uri_handler_get_protocols (GstURIHandler * handler)
535{
536  GstURIHandlerInterface *iface;
537  gchar **ret;
538
539  g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
540
541  iface = GST_URI_HANDLER_GET_INTERFACE (handler);
542  g_return_val_if_fail (iface != NULL, NULL);
543  g_return_val_if_fail (iface->get_protocols != NULL, NULL);
544  ret = iface->get_protocols ();
545  g_return_val_if_fail (ret != NULL, NULL);
546
547  return ret;
548}
549
550/**
551 * gst_uri_handler_get_uri:
552 * @handler: handler to query URI of
553 *
554 * Gets the currently handled URI of the handler or NULL, if none is set.
555 *
556 * Returns: the URI
557 */
558G_CONST_RETURN gchar *
559gst_uri_handler_get_uri (GstURIHandler * handler)
560{
561  GstURIHandlerInterface *iface;
562  const gchar *ret;
563
564  g_return_val_if_fail (GST_IS_URI_HANDLER (handler), NULL);
565
566  iface = GST_URI_HANDLER_GET_INTERFACE (handler);
567  g_return_val_if_fail (iface != NULL, NULL);
568  g_return_val_if_fail (iface->get_uri != NULL, NULL);
569  ret = iface->get_uri (handler);
570  if (ret != NULL)
571    g_return_val_if_fail (gst_uri_is_valid (ret), NULL);
572
573  return ret;
574}
575
576/**
577 * gst_uri_handler_set_uri:
578 * @handler: handler to set URI of
579 * @uri: URI to set
580 *
581 * Tries to set the URI of the given handler and returns TRUE if it succeeded.
582 *
583 * Returns: TRUE, if the URI was set successfully
584 */
585gboolean
586gst_uri_handler_set_uri (GstURIHandler * handler, const gchar * uri)
587{
588  GstURIHandlerInterface *iface;
589
590  g_return_val_if_fail (GST_IS_URI_HANDLER (handler), FALSE);
591  g_return_val_if_fail (gst_uri_is_valid (uri), FALSE);
592
593  iface = GST_URI_HANDLER_GET_INTERFACE (handler);
594  g_return_val_if_fail (iface != NULL, FALSE);
595  g_return_val_if_fail (iface->set_uri != NULL, FALSE);
596  return iface->set_uri (handler, uri);
597}
598
599/**
600 * gst_uri_handler_new_uri:
601 * @handler: handler with a new URI
602 * @uri: new URI or NULL if it was unset
603 *
604 * Emits the new-uri event for a given handler, when that handler has a new URI.
605 * This function should only be called by URI handlers themselves.
606 */
607void
608gst_uri_handler_new_uri (GstURIHandler * handler, const gchar * uri)
609{
610  g_return_if_fail (GST_IS_URI_HANDLER (handler));
611
612  g_signal_emit_by_name (handler, "new-uri", uri);
613}
Note: See TracBrowser for help on using the repository browser.