Added gst-plugins-base-subtitles0.10-0.10.34 for Meego Harmattan 1.2
[mafwsubrenderer] / gst-plugins-base-subtitles0.10 / gst-libs / gst / rtsp / gstrtspurl.c
1 /* GStreamer
2  * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 /*
20  * Unless otherwise indicated, Source Code is licensed under MIT license.
21  * See further explanation attached in License Statement (distributed in the file
22  * LICENSE).
23  *
24  * Permission is hereby granted, free of charge, to any person obtaining a copy of
25  * this software and associated documentation files (the "Software"), to deal in
26  * the Software without restriction, including without limitation the rights to
27  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28  * of the Software, and to permit persons to whom the Software is furnished to do
29  * so, subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be included in all
32  * copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40  * SOFTWARE.
41  */
42
43 /**
44  * SECTION:gstrtspurl
45  * @short_description: handling RTSP urls
46  *  
47  * Provides helper functions to handle RTSP urls.
48  *  
49  * Last reviewed on 2007-07-25 (0.10.14)
50  */
51
52 #include <stdlib.h>
53 #include <string.h>
54
55 #include "gstrtspurl.h"
56
57 GType
58 gst_rtsp_url_get_type (void)
59 {
60   static volatile gsize url_type = 0;
61
62   if (g_once_init_enter (&url_type)) {
63     GType tmp = g_boxed_type_register_static ("GstRTSPUrl",
64         (GBoxedCopyFunc) gst_rtsp_url_copy, (GBoxedFreeFunc) gst_rtsp_url_free);
65     g_once_init_leave (&url_type, tmp);
66   }
67
68   return (GType) url_type;
69 }
70
71 static const struct
72 {
73   const char scheme[6];
74   GstRTSPLowerTrans transports;
75 } rtsp_schemes_map[] = {
76   {
77   "rtsp", GST_RTSP_LOWER_TRANS_TCP | GST_RTSP_LOWER_TRANS_UDP |
78         GST_RTSP_LOWER_TRANS_UDP_MCAST}, {
79   "rtspu", GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST}, {
80   "rtspt", GST_RTSP_LOWER_TRANS_TCP}, {
81   "rtsph", GST_RTSP_LOWER_TRANS_HTTP | GST_RTSP_LOWER_TRANS_TCP}
82 };
83
84 /* format is rtsp[u]://[user:passwd@]host[:port]/abspath[?query] where host
85  * is a host name, an IPv4 dotted decimal address ("aaa.bbb.ccc.ddd") or an
86  * [IPv6] address ("[aabb:ccdd:eeff:gghh::sstt]" note the brackets around the
87  * address to allow the distinction between ':' as an IPv6 hexgroup separator
88  * and as a host/port separator) */
89
90 /**
91  * gst_rtsp_url_parse:
92  * @urlstr: the url string to parse
93  * @url: location to hold the result.
94  *
95  * Parse the RTSP @urlstr into a newly allocated #GstRTSPUrl. Free after usage
96  * with gst_rtsp_url_free().
97  *
98  * Returns: a #GstRTSPResult.
99  */
100 GstRTSPResult
101 gst_rtsp_url_parse (const gchar * urlstr, GstRTSPUrl ** url)
102 {
103   GstRTSPUrl *res;
104   gchar *p, *delim, *at, *col;
105   gchar *host_end = NULL;
106   guint i;
107
108   g_return_val_if_fail (urlstr != NULL, GST_RTSP_EINVAL);
109   g_return_val_if_fail (url != NULL, GST_RTSP_EINVAL);
110
111   res = g_new0 (GstRTSPUrl, 1);
112
113   p = (gchar *) urlstr;
114
115   col = strstr (p, "://");
116   if (col == NULL)
117     goto invalid;
118
119   for (i = 0; i < G_N_ELEMENTS (rtsp_schemes_map); i++) {
120     if (g_ascii_strncasecmp (rtsp_schemes_map[i].scheme, p, col - p) == 0) {
121       res->transports = rtsp_schemes_map[i].transports;
122       p = col + 3;
123       break;
124     }
125   }
126
127   if (res->transports == GST_RTSP_LOWER_TRANS_UNKNOWN)
128     goto invalid;
129
130   delim = strpbrk (p, "/?");
131   at = strchr (p, '@');
132
133   if (at && delim && at > delim)
134     at = NULL;
135
136   if (at) {
137     col = strchr (p, ':');
138
139     /* must have a ':' and it must be before the '@' */
140     if (col == NULL || col > at)
141       goto invalid;
142
143     res->user = g_strndup (p, col - p);
144     col++;
145     res->passwd = g_strndup (col, at - col);
146
147     /* move to host */
148     p = at + 1;
149   }
150
151   if (*p == '[') {
152     res->family = GST_RTSP_FAM_INET6;
153
154     /* we have an IPv6 address in the URL, find the ending ] which must be
155      * before any delimiter */
156     host_end = strchr (++p, ']');
157     if (!host_end || (delim && host_end >= delim))
158       goto invalid;
159
160     /* a port specifier must follow the address immediately */
161     col = host_end[1] == ':' ? host_end + 1 : NULL;
162   } else {
163     res->family = GST_RTSP_FAM_INET;
164
165     col = strchr (p, ':');
166
167     /* we have a ':' and a delimiter but the ':' is after the delimiter, it's
168      * not really part of the hostname */
169     if (col && delim && col >= delim)
170       col = NULL;
171
172     host_end = col ? col : delim;
173   }
174
175   if (!host_end)
176     res->host = g_strdup (p);
177   else {
178     res->host = g_strndup (p, host_end - p);
179
180     if (col) {
181       res->port = strtoul (col + 1, NULL, 10);
182     } else {
183       /* no port specified, set to 0. gst_rtsp_url_get_port() will return the
184        * default port */
185       res->port = 0;
186     }
187   }
188   p = delim;
189
190   if (p && *p == '/') {
191     delim = strchr (p, '?');
192     if (!delim)
193       res->abspath = g_strdup (p);
194     else
195       res->abspath = g_strndup (p, delim - p);
196     p = delim;
197   } else {
198     res->abspath = g_strdup ("/");
199   }
200
201   if (p && *p == '?')
202     res->query = g_strdup (p + 1);
203
204   *url = res;
205
206   return GST_RTSP_OK;
207
208   /* ERRORS */
209 invalid:
210   {
211     gst_rtsp_url_free (res);
212     return GST_RTSP_EINVAL;
213   }
214 }
215
216 /**
217  * gst_rtsp_url_copy:
218  * @url: a #GstRTSPUrl
219  *
220  * Make a copy of @url.
221  *
222  * Returns: a copy of @url. Free with gst_rtsp_url_free () after usage.
223  *
224  * Since: 0.10.22
225  */
226 GstRTSPUrl *
227 gst_rtsp_url_copy (const GstRTSPUrl * url)
228 {
229   GstRTSPUrl *res;
230
231   g_return_val_if_fail (url != NULL, NULL);
232
233   res = g_new0 (GstRTSPUrl, 1);
234
235   res->transports = url->transports;
236   res->family = url->family;
237   res->user = g_strdup (url->user);
238   res->passwd = g_strdup (url->passwd);
239   res->host = g_strdup (url->host);
240   res->port = url->port;
241   res->abspath = g_strdup (url->abspath);
242   res->query = g_strdup (url->query);
243
244   return res;
245 }
246
247 /**
248  * gst_rtsp_url_free:
249  * @url: a #GstRTSPUrl
250  *
251  * Free the memory used by @url.
252  */
253 void
254 gst_rtsp_url_free (GstRTSPUrl * url)
255 {
256   if (url == NULL)
257     return;
258
259   g_free (url->user);
260   g_free (url->passwd);
261   g_free (url->host);
262   g_free (url->abspath);
263   g_free (url->query);
264   g_free (url);
265 }
266
267 /**
268  * gst_rtsp_url_set_port:
269  * @url: a #GstRTSPUrl
270  * @port: the port
271  *
272  * Set the port number in @url to @port.
273  *
274  * Returns: #GST_RTSP_OK.
275  */
276 GstRTSPResult
277 gst_rtsp_url_set_port (GstRTSPUrl * url, guint16 port)
278 {
279   g_return_val_if_fail (url != NULL, GST_RTSP_EINVAL);
280
281   url->port = port;
282
283   return GST_RTSP_OK;
284 }
285
286 /**
287  * gst_rtsp_url_get_port:
288  * @url: a #GstRTSPUrl
289  * @port: location to hold the port
290  *
291  * Get the port number of @url.
292  *
293  * Returns: #GST_RTSP_OK.
294  */
295 GstRTSPResult
296 gst_rtsp_url_get_port (const GstRTSPUrl * url, guint16 * port)
297 {
298   g_return_val_if_fail (url != NULL, GST_RTSP_EINVAL);
299   g_return_val_if_fail (port != NULL, GST_RTSP_EINVAL);
300
301   /* if a port was specified, use that else use the default port. */
302   if (url->port != 0)
303     *port = url->port;
304   else
305     *port = GST_RTSP_DEFAULT_PORT;
306
307   return GST_RTSP_OK;
308 }
309
310 /**
311  * gst_rtsp_url_get_request_uri:
312  * @url: a #GstRTSPUrl
313  *
314  * Get a newly allocated string describing the request URI for @url. 
315  *
316  * Returns: a string with the request URI. g_free() after usage.
317  */
318 gchar *
319 gst_rtsp_url_get_request_uri (const GstRTSPUrl * url)
320 {
321   gchar *uri;
322   const gchar *pre_host;
323   const gchar *post_host;
324   const gchar *pre_query;
325   const gchar *query;
326
327   g_return_val_if_fail (url != NULL, NULL);
328
329   pre_host = url->family == GST_RTSP_FAM_INET6 ? "[" : "";
330   post_host = url->family == GST_RTSP_FAM_INET6 ? "]" : "";
331   pre_query = url->query ? "?" : "";
332   query = url->query ? url->query : "";
333
334   if (url->port != 0) {
335     uri = g_strdup_printf ("rtsp://%s%s%s:%u%s%s%s", pre_host, url->host,
336         post_host, url->port, url->abspath, pre_query, query);
337   } else {
338     uri = g_strdup_printf ("rtsp://%s%s%s%s%s%s", pre_host, url->host,
339         post_host, url->abspath, pre_query, query);
340   }
341
342   return uri;
343 }
344
345 static int
346 hex_to_int (gchar c)
347 {
348   if (c >= '0' && c <= '9')
349     return c - '0';
350   else if (c >= 'a' && c <= 'f')
351     return c - 'a' + 10;
352   else if (c >= 'A' && c <= 'F')
353     return c - 'A' + 10;
354   else
355     return -1;
356 }
357
358 static void
359 unescape_path_component (gchar * comp)
360 {
361   guint len = strlen (comp);
362   guint i;
363
364   for (i = 0; i + 2 < len; i++)
365     if (comp[i] == '%') {
366       int a, b;
367
368       a = hex_to_int (comp[i + 1]);
369       b = hex_to_int (comp[i + 2]);
370
371       /* The a||b check is to ensure that the byte is not '\0' */
372       if (a >= 0 && b >= 0 && (a || b)) {
373         comp[i] = (gchar) (a * 16 + b);
374         memmove (comp + i + 1, comp + i + 3, len - i - 3);
375         len -= 2;
376         comp[len] = '\0';
377       }
378     }
379 }
380
381 /**
382  * gst_rtsp_url_decode_path_components:
383  * @url: a #GstRTSPUrl
384  *
385  * Splits the path of @url on '/' boundaries, decoding the resulting components,
386  *
387  * The decoding performed by this routine is "URI decoding", as defined in RFC
388  * 3986, commonly known as percent-decoding. For example, a string "foo%2fbar"
389  * will decode to "foo/bar" -- the %2f being replaced by the corresponding byte
390  * with hex value 0x2f. Note that there is no guarantee that the resulting byte
391  * sequence is valid in any given encoding. As a special case, %00 is not
392  * unescaped to NUL, as that would prematurely terminate the string.
393  *
394  * Also note that since paths usually start with a slash, the first component
395  * will usually be the empty string.
396  *
397  * Returns: a string vector. g_strfreev() after usage.
398  *
399  * Since: 0.10.32
400  */
401 gchar **
402 gst_rtsp_url_decode_path_components (const GstRTSPUrl * url)
403 {
404   gchar **ret;
405   guint i;
406
407   g_return_val_if_fail (url != NULL, NULL);
408   g_return_val_if_fail (url->abspath != NULL, NULL);
409
410   ret = g_strsplit (url->abspath, "/", -1);
411
412   for (i = 0; ret[i]; i++)
413     unescape_path_component (ret[i]);
414
415   return ret;
416 }