add a test for just-fixed crasher
[swfdec.git] / swfdec / swfdec_url.c
blob40af41aa6e1ede3a1f4942c7fd6156050c53c8aa
1 /* Swfdec
2 * Copyright (C) 2007 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 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 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #include "swfdec_url.h"
29 #include "swfdec_debug.h"
31 /**
32 * SECTION:SwfdecURL
33 * @title: SwfdecURL
34 * @short_description: URL handling in Swfdec
36 * SwfdecURL is Swfdec's way of handling URLs. You probably don't need to mess
37 * with this type unless you want to write a #SwfdecLoader. In that case you
38 * will want to use swfdec_loader_get_url() to get its url and then use the
39 * functions in this section to access it.
41 * Note that URLs inside Swfdec mirror the behavior of Flash and will therefore
42 * not miror standards behavior, but the behavior of Flash.
44 * @see_also: #SwfdecLoader
47 /**
48 * SwfdecURL:
50 * this is the structure used for URLs. It is a boxed type to glib's type system
51 * and it is not reference counted. It is also a static struct in that it cannot
52 * be modified after creation.
55 struct _SwfdecURL {
56 char * url; /* the complete url */
57 char * protocol; /* lowercase, http, file, rtmp, ... */
58 char * host; /* lowercase, can be NULL for files */
59 guint port; /* can be 0 */
60 char * path; /* can be NULL for root path */
61 char * query; /* can be NULL */
64 GType
65 swfdec_url_get_type (void)
67 static GType type = 0;
69 if (!type)
70 type = g_boxed_type_register_static ("SwfdecURL",
71 (GBoxedCopyFunc) swfdec_url_copy, (GBoxedFreeFunc) swfdec_url_free);
73 return type;
76 /**
77 * swfdec_url_new:
78 * @string: a valid utf-8 string possibly containing an URL
80 * Parses the given string into a URL for use in swfdec.
82 * Returns: a new #SwfdecURL or %NULL if the URL was invalid
83 **/
84 SwfdecURL *
85 swfdec_url_new (const char *string)
87 SwfdecURL *url;
88 char *s;
90 g_return_val_if_fail (string != NULL, NULL);
92 /* FIXME: error checking? */
93 SWFDEC_DEBUG ("new url: %s", string);
94 url = g_slice_new0 (SwfdecURL);
95 url->url = g_strdup (string);
96 s = strstr (string, "://");
97 if (s == NULL) {
98 SWFDEC_INFO ("URL %s has no protocol", string);
99 goto error;
101 url->protocol = g_utf8_strdown (string, s - string);
102 string = s + 3;
103 s = strchr (string, '/');
104 if (s != string) {
105 char *colon;
106 url->host = g_ascii_strdown (string, s ? s - string : -1);
107 colon = strrchr (url->host, ':');
108 if (colon) {
109 *colon = 0;
110 errno = 0;
111 url->port = strtoul (colon + 1, &colon, 10);
112 if (errno || *colon != 0) {
113 SWFDEC_INFO ("%s: invalid port number", string);
114 goto error;
117 if (s == NULL)
118 return url;
120 string = s + 1;
121 s = strchr (string, '?');
122 if (s == NULL) {
123 url->path = *string ? g_strdup (string) : NULL;
124 return url;
126 url->path = g_strndup (string, s - string);
127 s++;
128 if (*s)
129 url->query = g_strdup (s);
130 return url;
132 error:
133 swfdec_url_free (url);
134 return NULL;
138 * swfdec_url_new_components:
139 * @protocol: protocol to use
140 * @hostname: hostname or IP address or %NULL
141 * @port: port number or 0. Must be 0 if no hostname is given
142 * @path: a path or %NULL
143 * @query: the query string or %NULL
145 * Creates a new URL from the given components.
147 * Returns: a new url pointing to the url from the given components
149 SwfdecURL *
150 swfdec_url_new_components (const char *protocol, const char *hostname,
151 guint port, const char *path, const char *query)
153 GString *str;
154 SwfdecURL *url;
156 g_return_val_if_fail (protocol != NULL, NULL);
157 g_return_val_if_fail (hostname != NULL || port == 0, NULL);
158 g_return_val_if_fail (port < 65536, NULL);
160 url = g_slice_new0 (SwfdecURL);
161 str = g_string_new ("");
163 /* protocol */
164 url->protocol = g_ascii_strdown (protocol, -1);
165 g_string_append (str, url->protocol);
166 g_string_append (str, "://");
168 /* hostname + port */
169 if (hostname) {
170 url->host = g_ascii_strdown (hostname, -1);
171 url->port = port;
172 g_string_append (str, url->host);
173 if (port) {
174 g_string_append_printf (str, ":%u", port);
177 g_string_append (str, "/");
179 /* path */
180 if (path) {
181 url->path = g_strdup (path);
182 g_string_append (str, path);
185 /* query string */
186 if (query) {
187 url->query = g_strdup (query);
188 g_string_append (str, "?");
189 g_string_append (str, query);
192 url->url = g_string_free (str, FALSE);
193 return url;
196 static gboolean
197 swfdec_url_path_to_parent_path (char *path)
199 char *last = strrchr (path, '/');
201 if (last == NULL)
202 return FALSE;
204 if (last[1] == '\0') {
205 last[0] = '\0';
206 return swfdec_url_path_to_parent_path (path);
209 *last = '\0';
210 return TRUE;
214 * swfdec_url_new_parent:
215 * @url: a #SwfdecURL
217 * Creates a new url that is the parent of @url. If the given @url has no
218 * parent, a copy of itself is returned.
220 * Returns: a new url pointing to the parent of @url or %NULL on failure.
222 SwfdecURL *
223 swfdec_url_new_parent (const SwfdecURL *url)
225 char *path;
226 SwfdecURL *ret;
228 path = g_strdup (url->path);
229 if (path) {
230 if (!swfdec_url_path_to_parent_path (path)) {
231 g_free (path);
232 path = NULL;
235 ret = swfdec_url_new_components (url->protocol, url->host, url->port,
236 path, NULL);
237 g_free (path);
238 return ret;
242 * swfdec_url_new_relative:
243 * @url: a #SwfdecURL
244 * @string: a relative or absolute URL path
246 * Parses @string into a new URL. If the given @string is a relative URL, it
247 * uses @url to resolve it to an absolute url; @url must already contain a
248 * directory path.
250 * Returns: a new #SwfdecURL or %NULL if an error was detected.
252 SwfdecURL *
253 swfdec_url_new_relative (const SwfdecURL *url, const char *string)
255 SwfdecURL *ret;
256 char *path, *query;
258 g_return_val_if_fail (url != NULL, NULL);
259 g_return_val_if_fail (string != NULL, NULL);
261 /* check for full-qualified URL */
262 if (strstr (string, "://"))
263 return swfdec_url_new (string);
265 if (string[0] == '/') {
266 /* absolute URL */
267 string++;
268 query = strchr (string, '?');
269 if (query == NULL) {
270 path = *string ? g_strdup (string) : NULL;
271 } else {
272 path = g_strndup (string, query - string);
273 query = g_strdup (query + 1);
275 } else {
276 /* relative URL */
277 char *cur = g_strdup (url->path);
278 while (g_str_has_prefix (string, "../")) {
279 if (cur && !swfdec_url_path_to_parent_path (cur)) {
280 g_free (cur);
281 cur = NULL;
283 string += 3;
285 if (strstr (string, "/../")) {
286 g_free (cur);
287 return NULL;
289 if (cur) {
290 path = g_strconcat (cur, "/", string, NULL);
291 g_free (cur);
292 cur = path;
293 } else {
294 cur = g_strdup (string);
296 query = strchr (cur, '?');
297 if (query == NULL) {
298 path = *string ? g_strdup (cur) : NULL;
299 } else {
300 path = g_strndup (cur, query - cur);
301 query = g_strdup (query + 1);
303 g_free (cur);
305 ret = swfdec_url_new_components (url->protocol, url->host, url->port,
306 path, query);
307 g_free (path);
308 g_free (query);
309 return ret;
313 * swfdec_url_copy:
314 * @url: a #SwfdecURL
316 * copies the given url.
318 * Returns: a new #SwfdecURL
320 SwfdecURL *
321 swfdec_url_copy (const SwfdecURL *url)
323 SwfdecURL *copy;
325 g_return_val_if_fail (url != NULL, NULL);
327 copy = g_slice_new0 (SwfdecURL);
328 copy->url = g_strdup (url->url);
329 copy->protocol = g_strdup (url->protocol);
330 copy->host = g_strdup (url->host);
331 copy->port = url->port;
332 copy->path = g_strdup (url->path);
333 copy->query = g_strdup (url->query);
335 return copy;
339 * swfdec_url_free:
340 * @url: a #SwfdecURL
342 * Frees the URL and its associated ressources.
344 void
345 swfdec_url_free (SwfdecURL *url)
347 g_return_if_fail (url != NULL);
349 g_free (url->url);
350 g_free (url->protocol);
351 g_free (url->host);
352 g_free (url->path);
353 g_free (url->query);
354 g_slice_free (SwfdecURL, url);
358 * swfdec_url_get_url:
359 * @url: a #SwfdecURL
361 * Gets the whole URL.
363 * Returns: the complete URL as string
365 const char *
366 swfdec_url_get_url (const SwfdecURL *url)
368 g_return_val_if_fail (url != NULL, NULL);
370 return url->url;
374 * swfdec_url_get_protocol:
375 * @url: a #SwfdecURL
377 * Gets the protocol used by this URL, such as "http" or "file".
379 * Returns: the protocol used or "error" if the URL is broken
381 const char *
382 swfdec_url_get_protocol (const SwfdecURL *url)
384 g_return_val_if_fail (url != NULL, NULL);
386 if (url->protocol)
387 return url->protocol;
388 else
389 return "error";
393 * swfdec_url_has_protocol:
394 * @url: a url
395 * @protocol: protocol name to check for
397 * Checks if the given @url references the given @protocol
399 * Returns: %TRUE if both protocols match, %FALSE otherwise
401 gboolean
402 swfdec_url_has_protocol (const SwfdecURL *url, const char *protocol)
404 g_return_val_if_fail (url != NULL, FALSE);
405 g_return_val_if_fail (protocol != NULL, FALSE);
407 return g_str_equal (url->protocol, protocol);
411 * swfdec_url_get_host:
412 * @url: a #SwfdecURL
414 * Gets the host for @url as a lower case string.
416 * Returns: the host or %NULL if none (typically for file URLs).
418 const char *
419 swfdec_url_get_host (const SwfdecURL *url)
421 g_return_val_if_fail (url != NULL, NULL);
423 return url->host;
427 * swfdec_url_get_port:
428 * @url: a #SwfdecURL
430 * Gets the port number specified by the given @url. If the @url does not
431 * specify a port number, 0 will be returned.
433 * Returns: the specified port or 0 if none was given.
435 guint
436 swfdec_url_get_port (const SwfdecURL *url)
438 g_return_val_if_fail (url != NULL, 0);
440 return url->port;
444 * swfdec_url_get_path:
445 * @url: a #SwfdecURL
447 * Gets the path associated with @url. If it contains no path, %NULL is
448 * returned.
449 * <note>The returned path does not start with a slash. So in particular for
450 * files, you want to prepend the slash yourself.</note>
452 * Returns: the path or %NULL if none
454 const char *
455 swfdec_url_get_path (const SwfdecURL *url)
457 g_return_val_if_fail (url != NULL, NULL);
459 return url->path;
463 * swfdec_url_get_query:
464 * @url: a #SwfdecURL
466 * Gets the query string associated with @url. If the URL does not have a query
467 * string, %NULL is returned.
469 * Returns: Query string or %NULL
471 const char *
472 swfdec_url_get_query (const SwfdecURL *url)
474 g_return_val_if_fail (url != NULL, NULL);
476 return url->query;
480 * swfdec_url_is_parent:
481 * @parent: the supposed parent url
482 * @child: the supposed child url
484 * Checks if the given @parent url is a parent url of the given @child url. The
485 * algorithm used is the same as checking policy files if hey apply. If @parent
486 * equals @child, %TRUE is returned. This function does not compare query
487 * strings.
489 * Returns: %TRUE if @parent is a parent of @child, %FALSE otherwise.
491 gboolean
492 swfdec_url_is_parent (const SwfdecURL *parent, const SwfdecURL *child)
494 gsize len;
496 g_return_val_if_fail (parent != NULL, FALSE);
497 g_return_val_if_fail (child != NULL, FALSE);
499 if (!g_str_equal (parent->protocol, child->protocol))
500 return FALSE;
501 if (parent->host == NULL) {
502 if (child->host != NULL)
503 return FALSE;
504 } else {
505 if (child->host == NULL || !g_str_equal (parent->host, child->host))
506 return FALSE;
508 if (parent->port != child->port)
509 return FALSE;
510 if (parent->path == NULL)
511 return TRUE;
512 if (child->path == NULL)
513 return TRUE;
514 len = strlen (parent->path);
515 if (strncmp (parent->path, child->path, len) != 0)
516 return FALSE;
517 return child->path[len] == '\0' || child->path[len] == '/';
521 * swfdec_url_is_local:
522 * @url: the url to check
524 * Checks if the given @url references a local resource. Local resources are
525 * treated differently by Flash, since they get a higher degree of trust.
527 * Returns: %TRUE if the given url is local.
529 gboolean
530 swfdec_url_is_local (const SwfdecURL *url)
532 g_return_val_if_fail (url != NULL, FALSE);
534 /* FIXME: If we ever support gnome-vfs, this might become tricky */
535 return swfdec_url_has_protocol (url, "file");
539 * swfdec_url_host_equal:
540 * @a: a #SwfdecURL
541 * @b: a #SwfdecURL
543 * Compares the 2 given URLs for equality, ignoring path. 2 URLs are
544 * considered to have equal hosts when they have the same protocol,
545 * host, and port.
546 * Returns: %TRUE if the 2 given urls point to the same host, %FALSE
547 * otherwise.
549 gboolean
550 swfdec_url_host_equal (gconstpointer a, gconstpointer b)
552 const SwfdecURL *urla = a;
553 const SwfdecURL *urlb = b;
555 if (!swfdec_url_has_protocol (urla, urlb->protocol))
556 return FALSE;
558 if (urla->host == NULL) {
559 if (urlb->host != NULL)
560 return FALSE;
561 } else {
562 if (urlb->host == NULL ||
563 !g_str_equal (urla->host, urlb->host))
564 return FALSE;
567 if (urla->port != urlb->port)
568 return FALSE;
570 return TRUE;
574 * swfdec_url_equal:
575 * @a: a #SwfdecURL
576 * @b: a #SwfdecURL
578 * Compares the 2 given URLs for equality. 2 URLs are considered equal, when
579 * they point to the same resource. This function is intended to be
580 * used together with swfdec_url_hash() in a #GHashtable.
582 * Returns: %TRUE if the 2 given urls point to the same resource, %FALSE
583 * otherwise.
585 gboolean
586 swfdec_url_equal (gconstpointer a, gconstpointer b)
588 const SwfdecURL *urla = a;
589 const SwfdecURL *urlb = b;
591 if (!swfdec_url_host_equal (urla, urlb))
592 return FALSE;
594 if (urla->path == NULL) {
595 if (urlb->path != NULL)
596 return FALSE;
597 } else {
598 if (urlb->path == NULL ||
599 !g_str_equal (urla->path, urlb->path))
600 return FALSE;
603 /* FIXME: include query strings? */
604 if (urla->query == NULL) {
605 if (urlb->query != NULL)
606 return FALSE;
607 } else {
608 if (urlb->query == NULL ||
609 !g_str_equal (urla->query, urlb->query))
610 return FALSE;
613 return TRUE;
617 * swfdec_url_hash:
618 * @url: a #SwfdecURL
620 * Creates a hash value for the given @url. This function is intended to be
621 * used together with swfdec_url_equal() in a #GHashtable.
623 * Returns: a hash value
625 guint
626 swfdec_url_hash (gconstpointer url)
628 const SwfdecURL *u = url;
629 guint ret;
631 /* random hash function, feel free to improve it */
632 ret = g_str_hash (u->protocol);
633 if (u->host)
634 ret ^= g_str_hash (u->host);
635 ret ^= u->port;
636 if (u->path)
637 ret ^= g_str_hash (u->path);
638 /* queries aren't used often in hashed urls, so ignore them */
639 return ret;
643 * swfdec_url_path_is_relative:
644 * @path: a string used to specify a url
646 * Checks if the given URL is relative or absolute.
648 * Returns: %TRUE if the path is a relative path, %FALSE if it is absolute
650 gboolean
651 swfdec_url_path_is_relative (const char *path)
653 g_return_val_if_fail (path != NULL, FALSE);
655 return strstr (path, "://") == NULL;
659 * swfdec_url_new_from_input:
660 * @input: the input povided
662 * Tries to guess the right URL from the given @input. This function is meant
663 * as a utility function helping to convert user input (like command line
664 * arguments) to a URL without requiring the full URL.
666 * Returns: a new url best matching the given @input.
668 SwfdecURL *
669 swfdec_url_new_from_input (const char *input)
671 SwfdecURL *url;
673 g_return_val_if_fail (input != NULL, NULL);
675 /* if it's a full URL, return it */
676 if (!swfdec_url_path_is_relative (input) &&
677 (url = swfdec_url_new (input)))
678 return url;
680 /* FIXME: split at '?' for query? */
681 if (g_path_is_absolute (input)) {
682 char *escaped =
683 g_uri_escape_string (input[0] == '/' ? &input[1] : &input[0], "/", TRUE);
684 url = swfdec_url_new_components ("file", NULL, 0, escaped, NULL);
685 g_free (escaped);
686 } else {
687 char *absolute, *cur;
688 cur = g_get_current_dir ();
689 absolute = g_build_filename (cur, input, NULL);
690 g_free (cur);
691 cur = g_uri_escape_string (absolute, "/", TRUE);
692 g_free (absolute);
693 url = swfdec_url_new_components ("file", NULL, 0, cur, NULL);
694 g_free (cur);
697 g_return_val_if_fail (url != NULL, NULL);
698 return url;
702 * swfdec_url_format_for_display:
703 * @url: the url to display
705 * Creates a string suitable to display the given @url. An example for using
706 * this function is to identify a currently playing Flash URL. Use
707 * swfdec_player_get_url() to query the player's URL and then use this function
708 * to get a displayable string.
710 * Returns: A new string containig a short description for this URL. g_free()
711 * after use.
713 char *
714 swfdec_url_format_for_display (const SwfdecURL *url)
716 GString *str;
718 g_return_val_if_fail (url != NULL, NULL);
720 if (swfdec_url_is_local (url)) {
721 const char *slash;
723 if (url->path == NULL)
724 return g_strdup ("/");
725 slash = strrchr (url->path, '/');
726 if (slash && slash[1] != '\0') {
727 return g_strdup (slash + 1);
728 } else {
729 return g_strdup (url->path);
732 str = g_string_new (url->protocol);
733 g_string_append (str, "://");
734 if (url->host)
735 g_string_append (str, url->host);
736 g_string_append (str, "/");
737 if (url->path)
738 g_string_append (str, url->path);
740 return g_string_free (str, FALSE);