1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* soup-uri.c : utility functions to parse URLs */
5 * Copyright 1999-2003 Ximian, Inc.
13 #include "soup-misc-private.h"
17 * @short_description: URIs
19 * A #SoupURI represents a (parsed) URI.
21 * Many applications will not need to use #SoupURI directly at all; on
22 * the client side, soup_message_new() takes a stringified URI, and on
23 * the server side, the path and query components are provided for you
24 * in the server callback.
29 * @scheme: the URI scheme (eg, "http")
30 * @user: a username, or %NULL
31 * @password: a password, or %NULL
32 * @host: the hostname or IP address
33 * @port: the port number on @host
34 * @path: the path on @host
35 * @query: a query for @path, or %NULL
36 * @fragment: a fragment identifier within @path, or %NULL
38 * A #SoupURI represents a (parsed) URI. #SoupURI supports RFC 3986
39 * (URI Generic Syntax), and can parse any valid URI. However, libsoup
40 * only uses "http" and "https" URIs internally; You can use
41 * SOUP_URI_VALID_FOR_HTTP() to test if a #SoupURI is a valid HTTP
44 * @scheme will always be set in any URI. It is an interned string and
45 * is always all lowercase. (If you parse a URI with a non-lowercase
46 * scheme, it will be converted to lowercase.) The macros
47 * %SOUP_URI_SCHEME_HTTP and %SOUP_URI_SCHEME_HTTPS provide the
48 * interned values for "http" and "https" and can be compared against
51 * @user and @password are parsed as defined in the older URI specs
52 * (ie, separated by a colon; RFC 3986 only talks about a single
53 * "userinfo" field). Note that @password is not included in the
54 * output of soup_uri_to_string(). libsoup does not normally use these
55 * fields; authentication is handled via #SoupSession signals.
57 * @host contains the hostname, and @port the port specified in the
58 * URI. If the URI doesn't contain a hostname, @host will be %NULL,
59 * and if it doesn't specify a port, @port may be 0. However, for
60 * "http" and "https" URIs, @host is guaranteed to be non-%NULL
61 * (trying to parse an http URI with no @host will return %NULL), and
62 * @port will always be non-0 (because libsoup knows the default value
63 * to use when it is not specified in the URI).
65 * @path is always non-%NULL. For http/https URIs, @path will never be
66 * an empty string either; if the input URI has no path, the parsed
67 * #SoupURI will have a @path of "/".
69 * @query and @fragment are optional for all URI types.
70 * soup_form_decode() may be useful for parsing @query.
72 * Note that @path, @query, and @fragment may contain
73 * %<!-- -->-encoded characters. soup_uri_new() calls
74 * soup_uri_normalize() on them, but not soup_uri_decode(). This is
75 * necessary to ensure that soup_uri_to_string() will generate a URI
76 * that has exactly the same meaning as the original. (In theory,
77 * #SoupURI should leave @user, @password, and @host partially-encoded
78 * as well, but this would be more annoying than useful.)
85 * Tests whether @uri is a valid #SoupURI; that is, that it is non-%NULL
86 * and its @scheme and @path members are also non-%NULL.
88 * This macro does not check whether http and https URIs have a non-%NULL
91 * Return value: %TRUE if @uri is valid for use.
97 * SOUP_URI_VALID_FOR_HTTP:
100 * Tests if @uri is a valid #SoupURI for HTTP communication; that is, if
101 * it can be used to construct a #SoupMessage.
103 * Return value: %TRUE if @uri is a valid "http" or "https" URI.
109 * SOUP_URI_SCHEME_HTTP:
111 * "http" as an interned string; you can compare this directly to a
112 * #SoupURI's <literal>scheme</literal> field using
113 * <literal>==</literal>.
116 * SOUP_URI_SCHEME_HTTPS:
118 * "https" as an interned string; you can compare this directly to a
119 * #SoupURI's <literal>scheme</literal> field using
120 * <literal>==</literal>.
123 * SOUP_URI_SCHEME_FTP:
125 * "ftp" as an interned string; you can compare this directly to a
126 * #SoupURI's <literal>scheme</literal> field using
127 * <literal>==</literal>.
132 * SOUP_URI_SCHEME_FILE:
134 * "file" as an interned string; you can compare this directly to a
135 * #SoupURI's <literal>scheme</literal> field using
136 * <literal>==</literal>.
141 * SOUP_URI_SCHEME_DATA:
143 * "data" as an interned string; you can compare this directly to a
144 * #SoupURI's <literal>scheme</literal> field using
145 * <literal>==</literal>.
150 * SOUP_URI_SCHEME_RESOURCE:
152 * "data" as an interned string; you can compare this directly to a
153 * #SoupURI's <literal>scheme</literal> field using
154 * <literal>==</literal>.
159 static void append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars);
160 static char *uri_normalized_copy (const char *str, int length, const char *unescape_extra);
162 gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
163 gpointer _SOUP_URI_SCHEME_FTP;
164 gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA, _SOUP_URI_SCHEME_RESOURCE;
166 static inline const char *
167 soup_uri_parse_scheme (const char *scheme, int len)
169 if (len == 4 && !g_ascii_strncasecmp (scheme, "http", len)) {
170 return SOUP_URI_SCHEME_HTTP;
171 } else if (len == 5 && !g_ascii_strncasecmp (scheme, "https", len)) {
172 return SOUP_URI_SCHEME_HTTPS;
173 } else if (len == 8 && !g_ascii_strncasecmp (scheme, "resource", len)) {
174 return SOUP_URI_SCHEME_RESOURCE;
178 lower_scheme = g_ascii_strdown (scheme, len);
179 scheme = g_intern_static_string (lower_scheme);
180 if (scheme != (const char *)lower_scheme)
181 g_free (lower_scheme);
187 soup_scheme_default_port (const char *scheme)
189 if (scheme == SOUP_URI_SCHEME_HTTP)
191 else if (scheme == SOUP_URI_SCHEME_HTTPS)
193 else if (scheme == SOUP_URI_SCHEME_FTP)
200 * soup_uri_new_with_base:
202 * @uri_string: the URI
204 * Parses @uri_string relative to @base.
206 * Return value: a parsed #SoupURI.
209 soup_uri_new_with_base (SoupURI *base, const char *uri_string)
211 SoupURI *uri, fixed_base;
212 const char *end, *hash, *colon, *at, *path, *question;
213 const char *p, *hostend;
214 gboolean remove_dot_segments = TRUE;
217 g_return_val_if_fail (uri_string != NULL, NULL);
219 /* Allow a %NULL path in @base, for compatibility */
220 if (base && base->scheme && !base->path) {
221 g_warn_if_fail (SOUP_URI_IS_VALID (base));
223 memcpy (&fixed_base, base, sizeof (SoupURI));
224 fixed_base.path = "";
228 g_return_val_if_fail (base == NULL || SOUP_URI_IS_VALID (base), NULL);
230 /* First some cleanup steps (which are supposed to all be no-ops,
231 * but...). Skip initial whitespace, strip out internal tabs and
232 * line breaks, and ignore trailing whitespace.
234 while (g_ascii_isspace (*uri_string))
237 len = strcspn (uri_string, "\t\n\r");
238 if (uri_string[len]) {
239 char *clean = g_malloc (strlen (uri_string) + 1), *d;
242 for (s = uri_string, d = clean; *s; s++) {
243 if (*s != '\t' && *s != '\n' && *s != '\r')
248 uri = soup_uri_new_with_base (base, clean);
252 end = uri_string + len;
253 while (end > uri_string && g_ascii_isspace (end[-1]))
256 uri = g_slice_new0 (SoupURI);
259 hash = strchr (uri_string, '#');
261 uri->fragment = uri_normalized_copy (hash + 1, end - hash + 1,
268 while (p < end && (g_ascii_isalpha (*p) ||
269 (p > uri_string && (g_ascii_isdigit (*p) ||
275 if (p > uri_string && *p == ':') {
276 uri->scheme = soup_uri_parse_scheme (uri_string, p - uri_string);
280 if (uri_string == end && !base && !uri->fragment) {
281 uri->path = g_strdup ("");
285 /* Check for authority */
286 if (strncmp (uri_string, "//", 2) == 0) {
289 path = uri_string + strcspn (uri_string, "/?#");
292 at = strchr (uri_string, '@');
293 if (at && at < path) {
294 colon = strchr (uri_string, ':');
295 if (colon && colon < at) {
296 uri->password = soup_uri_decoded_copy (colon + 1,
297 at - colon - 1, NULL);
299 uri->password = NULL;
303 uri->user = soup_uri_decoded_copy (uri_string,
304 colon - uri_string, NULL);
307 uri->user = uri->password = NULL;
309 /* Find host and port. */
310 if (*uri_string == '[') {
314 hostend = strchr (uri_string, ']');
315 if (!hostend || hostend > path) {
319 if (*(hostend + 1) == ':')
324 pct = memchr (uri_string, '%', hostend - uri_string);
325 if (!pct || (pct[1] == '2' && pct[2] == '5')) {
326 uri->host = soup_uri_decoded_copy (uri_string,
327 hostend - uri_string, NULL);
329 uri->host = g_strndup (uri_string, hostend - uri_string);
331 colon = memchr (uri_string, ':', path - uri_string);
332 hostend = colon ? colon : path;
333 uri->host = soup_uri_decoded_copy (uri_string,
334 hostend - uri_string, NULL);
337 if (colon && colon != path - 1) {
339 uri->port = strtoul (colon + 1, &portend, 10);
340 if (portend != (char *)path) {
350 question = memchr (uri_string, '?', end - uri_string);
352 uri->query = uri_normalized_copy (question + 1,
353 end - (question + 1),
358 if (end != uri_string) {
359 uri->path = uri_normalized_copy (uri_string, end - uri_string,
363 /* Apply base URI. This is spelled out in RFC 3986. */
364 if (base && !uri->scheme && uri->host)
365 uri->scheme = base->scheme;
366 else if (base && !uri->scheme) {
367 uri->scheme = base->scheme;
368 uri->user = g_strdup (base->user);
369 uri->password = g_strdup (base->password);
370 uri->host = g_strdup (base->host);
371 uri->port = base->port;
374 uri->path = g_strdup (base->path);
376 uri->query = g_strdup (base->query);
377 remove_dot_segments = FALSE;
378 } else if (*uri->path != '/') {
379 char *newpath, *last;
381 last = strrchr (base->path, '/');
383 newpath = g_strdup_printf ("%.*s%s",
384 (int)(last + 1 - base->path),
388 newpath = g_strdup_printf ("/%s", uri->path);
395 if (remove_dot_segments && uri->path && *uri->path) {
398 /* Remove "./" where "." is a complete segment. */
399 for (p = uri->path + 1; *p; ) {
400 if (*(p - 1) == '/' &&
401 *p == '.' && *(p + 1) == '/')
402 memmove (p, p + 2, strlen (p + 2) + 1);
406 /* Remove "." at end. */
407 if (p > uri->path + 2 &&
408 *(p - 1) == '.' && *(p - 2) == '/')
411 /* Remove "<segment>/../" where <segment> != ".." */
412 for (p = uri->path + 1; *p; ) {
413 if (!strncmp (p, "../", 3)) {
417 q = strchr (p + 1, '/');
420 if (strncmp (q, "/../", 4) != 0) {
424 memmove (p, q + 4, strlen (q + 4) + 1);
427 /* Remove "<segment>/.." at end where <segment> != ".." */
428 q = strrchr (uri->path, '/');
429 if (q && !strcmp (q, "/..")) {
431 while (p > uri->path && *p != '/')
433 if (strncmp (p, "/../", 4) != 0)
437 /* Remove extraneous initial "/.."s */
438 while (!strncmp (uri->path, "/../", 4))
439 memmove (uri->path, uri->path + 3, strlen (uri->path) - 2);
440 if (!strcmp (uri->path, "/.."))
444 /* HTTP-specific stuff */
445 if (uri->scheme == SOUP_URI_SCHEME_HTTP ||
446 uri->scheme == SOUP_URI_SCHEME_HTTPS) {
448 uri->path = g_strdup ("/");
449 if (!SOUP_URI_VALID_FOR_HTTP (uri)) {
455 if (uri->scheme == SOUP_URI_SCHEME_FTP) {
463 uri->port = soup_scheme_default_port (uri->scheme);
465 uri->path = g_strdup ("");
472 * @uri_string: (allow-none): a URI
474 * Parses an absolute URI.
476 * You can also pass %NULL for @uri_string if you want to get back an
477 * "empty" #SoupURI that you can fill in by hand. (You will need to
478 * call at least soup_uri_set_scheme() and soup_uri_set_path(), since
479 * those fields are required.)
481 * Return value: a #SoupURI, or %NULL if the given string was found to be
485 soup_uri_new (const char *uri_string)
490 return g_slice_new0 (SoupURI);
492 uri = soup_uri_new_with_base (NULL, uri_string);
495 if (!SOUP_URI_IS_VALID (uri)) {
505 soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query,
511 g_return_val_if_fail (uri != NULL, NULL);
512 g_warn_if_fail (SOUP_URI_IS_VALID (uri));
514 str = g_string_sized_new (40);
516 if (uri->scheme && !just_path_and_query)
517 g_string_append_printf (str, "%s:", uri->scheme);
518 if (uri->host && !just_path_and_query) {
519 g_string_append (str, "//");
521 append_uri_encoded (str, uri->user, ":;@?/");
522 g_string_append_c (str, '@');
524 if (strchr (uri->host, ':')) {
527 g_string_append_c (str, '[');
528 pct = strchr (uri->host, '%');
530 g_string_append_printf (str, "%.*s%%25%s",
531 (int) (pct - uri->host),
534 g_string_append (str, uri->host);
535 g_string_append_c (str, ']');
537 append_uri_encoded (str, uri->host, ":/");
538 if (uri->port && (force_port || uri->port != soup_scheme_default_port (uri->scheme)))
539 g_string_append_printf (str, ":%u", uri->port);
540 if (!uri->path && (uri->query || uri->fragment))
541 g_string_append_c (str, '/');
542 else if ((!uri->path || !*uri->path) &&
543 (uri->scheme == SOUP_URI_SCHEME_HTTP ||
544 uri->scheme == SOUP_URI_SCHEME_HTTPS))
545 g_string_append_c (str, '/');
548 if (uri->path && *uri->path)
549 g_string_append (str, uri->path);
550 else if (just_path_and_query)
551 g_string_append_c (str, '/');
554 g_string_append_c (str, '?');
555 g_string_append (str, uri->query);
557 if (uri->fragment && !just_path_and_query) {
558 g_string_append_c (str, '#');
559 g_string_append (str, uri->fragment);
562 return_result = str->str;
563 g_string_free (str, FALSE);
565 return return_result;
569 * soup_uri_to_string:
571 * @just_path_and_query: if %TRUE, output just the path and query portions
573 * Returns a string representing @uri.
575 * If @just_path_and_query is %TRUE, this concatenates the path and query
576 * together. That is, it constructs the string that would be needed in
577 * the Request-Line of an HTTP request for @uri.
579 * Note that the output will never contain a password, even if @uri
582 * Return value: a string representing @uri, which the caller must free.
585 soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
587 return soup_uri_to_string_internal (uri, just_path_and_query, FALSE);
596 * Return value: a copy of @uri, which must be freed with soup_uri_free()
599 soup_uri_copy (SoupURI *uri)
603 g_return_val_if_fail (uri != NULL, NULL);
604 g_warn_if_fail (SOUP_URI_IS_VALID (uri));
606 dup = g_slice_new0 (SoupURI);
607 dup->scheme = uri->scheme;
608 dup->user = g_strdup (uri->user);
609 dup->password = g_strdup (uri->password);
610 dup->host = g_strdup (uri->host);
611 dup->port = uri->port;
612 dup->path = g_strdup (uri->path);
613 dup->query = g_strdup (uri->query);
614 dup->fragment = g_strdup (uri->fragment);
619 static inline gboolean
620 parts_equal (const char *one, const char *two, gboolean insensitive)
626 return insensitive ? !g_ascii_strcasecmp (one, two) : !strcmp (one, two);
632 * @uri2: another #SoupURI
634 * Tests whether or not @uri1 and @uri2 are equal in all parts
636 * Return value: %TRUE or %FALSE
639 soup_uri_equal (SoupURI *uri1, SoupURI *uri2)
641 g_return_val_if_fail (uri1 != NULL, FALSE);
642 g_return_val_if_fail (uri2 != NULL, FALSE);
643 g_warn_if_fail (SOUP_URI_IS_VALID (uri1));
644 g_warn_if_fail (SOUP_URI_IS_VALID (uri2));
646 if (uri1->scheme != uri2->scheme ||
647 uri1->port != uri2->port ||
648 !parts_equal (uri1->user, uri2->user, FALSE) ||
649 !parts_equal (uri1->password, uri2->password, FALSE) ||
650 !parts_equal (uri1->host, uri2->host, TRUE) ||
651 !parts_equal (uri1->path, uri2->path, FALSE) ||
652 !parts_equal (uri1->query, uri2->query, FALSE) ||
653 !parts_equal (uri1->fragment, uri2->fragment, FALSE))
666 soup_uri_free (SoupURI *uri)
668 g_return_if_fail (uri != NULL);
671 g_free (uri->password);
675 g_free (uri->fragment);
677 g_slice_free (SoupURI, uri);
681 append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars)
683 const unsigned char *s = (const unsigned char *)in;
686 if (soup_char_is_uri_percent_encoded (*s) ||
687 soup_char_is_uri_gen_delims (*s) ||
688 (extra_enc_chars && strchr (extra_enc_chars, *s)))
689 g_string_append_printf (str, "%%%02X", (int)*s++);
691 g_string_append_c (str, *s++);
698 * @escape_extra: (allow-none): additional reserved characters to
701 * This %<!-- -->-encodes the given URI part and returns the escaped
702 * version in allocated memory, which the caller must free when it is
705 * Return value: the encoded URI part
708 soup_uri_encode (const char *part, const char *escape_extra)
713 g_return_val_if_fail (part != NULL, NULL);
715 str = g_string_new (NULL);
716 append_uri_encoded (str, part, escape_extra);
718 g_string_free (str, FALSE);
723 #define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
724 #define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
727 soup_uri_decoded_copy (const char *part, int length, int *decoded_length)
729 unsigned char *s, *d;
732 g_return_val_if_fail (part != NULL, NULL);
734 decoded = g_strndup (part, length);
735 s = d = (unsigned char *)decoded;
738 if (!g_ascii_isxdigit (s[1]) ||
739 !g_ascii_isxdigit (s[2])) {
750 *decoded_length = d - (unsigned char *)decoded - 1;
759 * Fully %<!-- -->-decodes @part.
761 * In the past, this would return %NULL if @part contained invalid
762 * percent-encoding, but now it just ignores the problem (as
763 * soup_uri_new() already did).
765 * Return value: the decoded URI part.
768 soup_uri_decode (const char *part)
770 g_return_val_if_fail (part != NULL, NULL);
772 return soup_uri_decoded_copy (part, strlen (part), NULL);
776 uri_normalized_copy (const char *part, int length,
777 const char *unescape_extra)
779 unsigned char *s, *d, c;
780 char *normalized = g_strndup (part, length);
781 gboolean need_fixup = FALSE;
786 s = d = (unsigned char *)normalized;
789 if (!g_ascii_isxdigit (s[1]) ||
790 !g_ascii_isxdigit (s[2])) {
796 if (soup_char_is_uri_unreserved (c) ||
797 (c && strchr (unescape_extra, c))) {
801 /* We leave it unchanged. We used to uppercase percent-encoded
802 * triplets but we do not do it any more as RFC3986 Section 6.2.2.1
803 * says that they only SHOULD be case normalized.
810 if (!g_ascii_isgraph (*s) &&
811 !strchr (unescape_extra, *s))
821 fixed = g_string_new (NULL);
822 s = (guchar *)normalized;
824 if (g_ascii_isgraph (*s) ||
825 strchr (unescape_extra, *s))
826 g_string_append_c (fixed, *s);
828 g_string_append_printf (fixed, "%%%02X", (int)*s);
832 normalized = g_string_free (fixed, FALSE);
839 * soup_uri_normalize:
841 * @unescape_extra: reserved characters to unescape (or %NULL)
843 * %<!-- -->-decodes any "unreserved" characters (or characters in
844 * @unescape_extra) in @part.
846 * "Unreserved" characters are those that are not allowed to be used
847 * for punctuation according to the URI spec. For example, letters are
848 * unreserved, so soup_uri_normalize() will turn
849 * <literal>http://example.com/foo/b%<!-- -->61r</literal> into
850 * <literal>http://example.com/foo/bar</literal>, which is guaranteed
851 * to mean the same thing. However, "/" is "reserved", so
852 * <literal>http://example.com/foo%<!-- -->2Fbar</literal> would not
853 * be changed, because it might mean something different to the
856 * In the past, this would return %NULL if @part contained invalid
857 * percent-encoding, but now it just ignores the problem (as
858 * soup_uri_new() already did).
860 * Return value: the normalized URI part
863 soup_uri_normalize (const char *part, const char *unescape_extra)
865 g_return_val_if_fail (part != NULL, NULL);
867 return uri_normalized_copy (part, strlen (part), unescape_extra);
872 * soup_uri_uses_default_port:
875 * Tests if @uri uses the default port for its scheme. (Eg, 80 for
876 * http.) (This only works for http, https and ftp; libsoup does not know
877 * the default ports of other protocols.)
879 * Return value: %TRUE or %FALSE
882 soup_uri_uses_default_port (SoupURI *uri)
884 g_return_val_if_fail (uri != NULL, FALSE);
885 g_warn_if_fail (SOUP_URI_IS_VALID (uri));
887 return uri->port == soup_scheme_default_port (uri->scheme);
891 * soup_uri_get_scheme:
894 * Gets @uri's scheme.
896 * Return value: @uri's scheme.
901 soup_uri_get_scheme (SoupURI *uri)
903 g_return_val_if_fail (uri != NULL, NULL);
909 * soup_uri_set_scheme:
911 * @scheme: the URI scheme
913 * Sets @uri's scheme to @scheme. This will also set @uri's port to
914 * the default port for @scheme, if known.
917 soup_uri_set_scheme (SoupURI *uri, const char *scheme)
919 g_return_if_fail (uri != NULL);
920 g_return_if_fail (scheme != NULL);
922 uri->scheme = soup_uri_parse_scheme (scheme, strlen (scheme));
923 uri->port = soup_scheme_default_port (uri->scheme);
932 * Return value: @uri's user.
937 soup_uri_get_user (SoupURI *uri)
939 g_return_val_if_fail (uri != NULL, NULL);
947 * @user: (allow-none): the username, or %NULL
949 * Sets @uri's user to @user.
952 soup_uri_set_user (SoupURI *uri, const char *user)
954 g_return_if_fail (uri != NULL);
957 uri->user = g_strdup (user);
961 * soup_uri_get_password:
964 * Gets @uri's password.
966 * Return value: @uri's password.
971 soup_uri_get_password (SoupURI *uri)
973 g_return_val_if_fail (uri != NULL, NULL);
975 return uri->password;
979 * soup_uri_set_password:
981 * @password: (allow-none): the password, or %NULL
983 * Sets @uri's password to @password.
986 soup_uri_set_password (SoupURI *uri, const char *password)
988 g_return_if_fail (uri != NULL);
990 g_free (uri->password);
991 uri->password = g_strdup (password);
1000 * Return value: @uri's host.
1005 soup_uri_get_host (SoupURI *uri)
1007 g_return_val_if_fail (uri != NULL, NULL);
1013 * soup_uri_set_host:
1015 * @host: (allow-none): the hostname or IP address, or %NULL
1017 * Sets @uri's host to @host.
1019 * If @host is an IPv6 IP address, it should not include the brackets
1020 * required by the URI syntax; they will be added automatically when
1021 * converting @uri to a string.
1023 * http and https URIs should not have a %NULL @host.
1026 soup_uri_set_host (SoupURI *uri, const char *host)
1028 g_return_if_fail (uri != NULL);
1031 uri->host = g_strdup (host);
1035 * soup_uri_get_port:
1040 * Return value: @uri's port.
1045 soup_uri_get_port (SoupURI *uri)
1047 g_return_val_if_fail (uri != NULL, 0);
1053 * soup_uri_set_port:
1055 * @port: the port, or 0
1057 * Sets @uri's port to @port. If @port is 0, @uri will not have an
1058 * explicitly-specified port.
1061 soup_uri_set_port (SoupURI *uri, guint port)
1063 g_return_if_fail (uri != NULL);
1069 * soup_uri_get_path:
1074 * Return value: @uri's path.
1079 soup_uri_get_path (SoupURI *uri)
1081 g_return_val_if_fail (uri != NULL, NULL);
1087 * soup_uri_set_path:
1089 * @path: the non-%NULL path
1091 * Sets @uri's path to @path.
1094 soup_uri_set_path (SoupURI *uri, const char *path)
1096 g_return_if_fail (uri != NULL);
1098 /* We allow a NULL path for compatibility, but warn about it. */
1100 g_warn_if_fail (path != NULL);
1105 uri->path = g_strdup (path);
1109 * soup_uri_get_query:
1112 * Gets @uri's query.
1114 * Return value: @uri's query.
1119 soup_uri_get_query (SoupURI *uri)
1121 g_return_val_if_fail (uri != NULL, NULL);
1127 * soup_uri_set_query:
1129 * @query: (allow-none): the query
1131 * Sets @uri's query to @query.
1134 soup_uri_set_query (SoupURI *uri, const char *query)
1136 g_return_if_fail (uri != NULL);
1138 g_free (uri->query);
1139 uri->query = g_strdup (query);
1143 * soup_uri_set_query_from_form:
1145 * @form: (element-type utf8 utf8): a #GHashTable containing HTML form
1148 * Sets @uri's query to the result of encoding @form according to the
1149 * HTML form rules. See soup_form_encode_hash() for more information.
1152 soup_uri_set_query_from_form (SoupURI *uri, GHashTable *form)
1154 g_return_if_fail (uri != NULL);
1156 g_free (uri->query);
1157 uri->query = soup_form_encode_hash (form);
1161 * soup_uri_set_query_from_fields:
1163 * @first_field: name of the first form field to encode into query
1164 * @...: value of @first_field, followed by additional field names
1165 * and values, terminated by %NULL.
1167 * Sets @uri's query to the result of encoding the given form fields
1168 * and values according to the * HTML form rules. See
1169 * soup_form_encode() for more information.
1172 soup_uri_set_query_from_fields (SoupURI *uri,
1173 const char *first_field,
1178 g_return_if_fail (uri != NULL);
1180 g_free (uri->query);
1181 va_start (args, first_field);
1182 uri->query = soup_form_encode_valist (first_field, args);
1187 * soup_uri_get_fragment:
1190 * Gets @uri's fragment.
1192 * Return value: @uri's fragment.
1197 soup_uri_get_fragment (SoupURI *uri)
1199 g_return_val_if_fail (uri != NULL, NULL);
1201 return uri->fragment;
1205 * soup_uri_set_fragment:
1207 * @fragment: (allow-none): the fragment
1209 * Sets @uri's fragment to @fragment.
1212 soup_uri_set_fragment (SoupURI *uri, const char *fragment)
1214 g_return_if_fail (uri != NULL);
1216 g_free (uri->fragment);
1217 uri->fragment = g_strdup (fragment);
1221 * soup_uri_copy_host:
1224 * Makes a copy of @uri, considering only the protocol, host, and port
1226 * Return value: the new #SoupURI
1231 soup_uri_copy_host (SoupURI *uri)
1235 g_return_val_if_fail (uri != NULL, NULL);
1236 g_warn_if_fail (SOUP_URI_IS_VALID (uri));
1238 dup = soup_uri_new (NULL);
1239 dup->scheme = uri->scheme;
1240 dup->host = g_strdup (uri->host);
1241 dup->port = uri->port;
1242 dup->path = g_strdup ("");
1248 * soup_uri_host_hash:
1249 * @key: (type Soup.URI): a #SoupURI with a non-%NULL @host member
1251 * Hashes @key, considering only the scheme, host, and port.
1253 * Return value: a hash
1258 soup_uri_host_hash (gconstpointer key)
1260 const SoupURI *uri = key;
1262 g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
1263 g_warn_if_fail (SOUP_URI_IS_VALID (uri));
1265 return GPOINTER_TO_UINT (uri->scheme) + uri->port +
1266 soup_str_case_hash (uri->host);
1270 * soup_uri_host_equal:
1271 * @v1: (type Soup.URI): a #SoupURI with a non-%NULL @host member
1272 * @v2: (type Soup.URI): a #SoupURI with a non-%NULL @host member
1274 * Compares @v1 and @v2, considering only the scheme, host, and port.
1276 * Return value: whether or not the URIs are equal in scheme, host,
1282 soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
1284 const SoupURI *one = v1;
1285 const SoupURI *two = v2;
1287 g_return_val_if_fail (one != NULL && two != NULL, one == two);
1288 g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
1289 g_warn_if_fail (SOUP_URI_IS_VALID (one));
1290 g_warn_if_fail (SOUP_URI_IS_VALID (two));
1292 if (one->scheme != two->scheme)
1294 if (one->port != two->port)
1297 return g_ascii_strcasecmp (one->host, two->host) == 0;
1301 soup_uri_is_http (SoupURI *uri, char **aliases)
1305 if (uri->scheme == SOUP_URI_SCHEME_HTTP)
1307 else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
1312 for (i = 0; aliases[i]; i++) {
1313 if (uri->scheme == aliases[i])
1317 if (!aliases[1] && !strcmp (aliases[0], "*"))
1324 soup_uri_is_https (SoupURI *uri, char **aliases)
1328 if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
1330 else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
1335 for (i = 0; aliases[i]; i++) {
1336 if (uri->scheme == aliases[i])
1343 G_DEFINE_BOXED_TYPE (SoupURI, soup_uri, soup_uri_copy, soup_uri_free)