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-form.h"
14 #include "soup-misc.h"
18 * @short_description: URIs
20 * A #SoupURI represents a (parsed) URI.
22 * Many applications will not need to use #SoupURI directly at all; on
23 * the client side, soup_message_new() takes a stringified URI, and on
24 * the server side, the path and query components are provided for you
25 * in the server callback.
30 * @scheme: the URI scheme (eg, "http")
31 * @user: a username, or %NULL
32 * @password: a password, or %NULL
33 * @host: the hostname or IP address
34 * @port: the port number on @host
35 * @path: the path on @host
36 * @query: a query for @path, or %NULL
37 * @fragment: a fragment identifier within @path, or %NULL
39 * A #SoupURI represents a (parsed) URI. #SoupURI supports RFC 3986
40 * (URI Generic Syntax), and can parse any valid URI. However, libsoup
41 * only uses "http" and "https" URIs internally; You can use
42 * SOUP_URI_VALID_FOR_HTTP() to test if a #SoupURI is a valid HTTP
45 * @scheme will always be set in any URI. It is an interned string and
46 * is always all lowercase. (If you parse a URI with a non-lowercase
47 * scheme, it will be converted to lowercase.) The macros
48 * %SOUP_URI_SCHEME_HTTP and %SOUP_URI_SCHEME_HTTPS provide the
49 * interned values for "http" and "https" and can be compared against
52 * @user and @password are parsed as defined in the older URI specs
53 * (ie, separated by a colon; RFC 3986 only talks about a single
54 * "userinfo" field). Note that @password is not included in the
55 * output of soup_uri_to_string(). libsoup does not normally use these
56 * fields; authentication is handled via #SoupSession signals.
58 * @host contains the hostname, and @port the port specified in the
59 * URI. If the URI doesn't contain a hostname, @host will be %NULL,
60 * and if it doesn't specify a port, @port may be 0. However, for
61 * "http" and "https" URIs, @host is guaranteed to be non-%NULL
62 * (trying to parse an http URI with no @host will return %NULL), and
63 * @port will always be non-0 (because libsoup knows the default value
64 * to use when it is not specified in the URI).
66 * @path is always non-%NULL. For http/https URIs, @path will never be
67 * an empty string either; if the input URI has no path, the parsed
68 * #SoupURI will have a @path of "/".
70 * @query and @fragment are optional for all URI types.
71 * soup_form_decode_urlencoded() may be useful for parsing @query.
73 * Note that @path, @query, and @fragment may contain
74 * %<!-- -->-encoded characters. soup_uri_new() calls
75 * soup_uri_normalize() on them, but not soup_uri_decode(). This is
76 * necessary to ensure that soup_uri_to_string() will generate a URI
77 * that has exactly the same meaning as the original. (In theory,
78 * #SoupURI should leave @user, @password, and @host partially-encoded
79 * as well, but this would be more annoying than useful.)
83 * SOUP_URI_VALID_FOR_HTTP:
86 * Tests if @uri is a valid #SoupURI for HTTP communication; that is, if
87 * it can be used to construct a #SoupMessage.
89 * Return value: %TRUE if @uri is a valid "http" or "https" URI.
94 static void append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars);
95 static char *uri_decoded_copy (const char *str, int length);
96 static char *uri_normalized_copy (const char *str, int length, const char *unescape_extra, gboolean fixup);
98 gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
99 gpointer _SOUP_URI_SCHEME_FTP;
100 gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA;
102 static inline const char *
103 soup_uri_get_scheme (const char *scheme, int len)
105 if (len == 4 && !g_ascii_strncasecmp (scheme, "http", len)) {
106 return SOUP_URI_SCHEME_HTTP;
107 } else if (len == 5 && !g_ascii_strncasecmp (scheme, "https", len)) {
108 return SOUP_URI_SCHEME_HTTPS;
112 lower_scheme = g_ascii_strdown (scheme, len);
113 scheme = g_intern_static_string (lower_scheme);
114 if (scheme != (const char *)lower_scheme)
115 g_free (lower_scheme);
121 soup_scheme_default_port (const char *scheme)
123 if (scheme == SOUP_URI_SCHEME_HTTP)
125 else if (scheme == SOUP_URI_SCHEME_HTTPS)
127 else if (scheme == SOUP_URI_SCHEME_FTP)
134 * soup_uri_new_with_base:
136 * @uri_string: the URI
138 * Parses @uri_string relative to @base.
140 * Return value: a parsed #SoupURI.
143 soup_uri_new_with_base (SoupURI *base, const char *uri_string)
146 const char *end, *hash, *colon, *at, *path, *question;
147 const char *p, *hostend;
148 gboolean remove_dot_segments = TRUE;
150 uri = g_slice_new0 (SoupURI);
152 /* See RFC 3986 for details. IF YOU CHANGE ANYTHING IN THIS
153 * FUNCTION, RUN tests/uri-parsing AFTERWARDS.
157 end = hash = strchr (uri_string, '#');
158 if (hash && hash[1]) {
159 uri->fragment = uri_normalized_copy (hash + 1, strlen (hash + 1),
161 if (!uri->fragment) {
166 end = uri_string + strlen (uri_string);
168 /* Find scheme: initial [a-z+.-]* substring until ":" */
170 while (p < end && (g_ascii_isalnum (*p) ||
171 *p == '.' || *p == '+' || *p == '-'))
174 if (p > uri_string && *p == ':') {
175 uri->scheme = soup_uri_get_scheme (uri_string, p - uri_string);
183 if (!*uri_string && !base)
186 /* Check for authority */
187 if (strncmp (uri_string, "//", 2) == 0) {
190 path = uri_string + strcspn (uri_string, "/?#");
191 at = strchr (uri_string, '@');
192 if (at && at < path) {
193 colon = strchr (uri_string, ':');
194 if (colon && colon < at) {
195 uri->password = uri_decoded_copy (colon + 1,
197 if (!uri->password) {
202 uri->password = NULL;
206 uri->user = uri_decoded_copy (uri_string,
214 uri->user = uri->password = NULL;
216 /* Find host and port. */
217 if (*uri_string == '[') {
219 hostend = strchr (uri_string, ']');
220 if (!hostend || hostend > path) {
224 if (*(hostend + 1) == ':')
229 colon = memchr (uri_string, ':', path - uri_string);
230 hostend = colon ? colon : path;
233 uri->host = uri_decoded_copy (uri_string, hostend - uri_string);
239 if (colon && colon != path - 1) {
241 uri->port = strtoul (colon + 1, &portend, 10);
242 if (portend != (char *)path) {
252 question = memchr (uri_string, '?', end - uri_string);
254 uri->query = uri_normalized_copy (question + 1,
255 end - (question + 1),
264 if (end != uri_string) {
265 uri->path = uri_normalized_copy (uri_string, end - uri_string,
273 /* Apply base URI. Again, this is spelled out in RFC 3986. */
274 if (base && !uri->scheme && uri->host)
275 uri->scheme = base->scheme;
276 else if (base && !uri->scheme) {
277 uri->scheme = base->scheme;
278 uri->user = g_strdup (base->user);
279 uri->password = g_strdup (base->password);
280 uri->host = g_strdup (base->host);
281 uri->port = base->port;
284 uri->path = g_strdup (base->path);
286 uri->query = g_strdup (base->query);
287 remove_dot_segments = FALSE;
288 } else if (*uri->path != '/') {
289 char *newpath, *last;
291 last = strrchr (base->path, '/');
293 newpath = g_strdup_printf ("%.*s/%s",
294 (int)(last - base->path),
298 newpath = g_strdup_printf ("/%s", uri->path);
305 if (remove_dot_segments && uri->path && *uri->path) {
306 char *p = uri->path, *q;
308 /* Remove "./" where "." is a complete segment. */
309 for (p = uri->path + 1; *p; ) {
310 if (*(p - 1) == '/' &&
311 *p == '.' && *(p + 1) == '/')
312 memmove (p, p + 2, strlen (p + 2) + 1);
316 /* Remove "." at end. */
317 if (p > uri->path + 2 &&
318 *(p - 1) == '.' && *(p - 2) == '/')
321 /* Remove "<segment>/../" where <segment> != ".." */
322 for (p = uri->path + 1; *p; ) {
323 if (!strncmp (p, "../", 3)) {
327 q = strchr (p + 1, '/');
330 if (strncmp (q, "/../", 4) != 0) {
334 memmove (p, q + 4, strlen (q + 4) + 1);
337 /* Remove "<segment>/.." at end where <segment> != ".." */
338 q = strrchr (uri->path, '/');
339 if (q && !strcmp (q, "/..")) {
341 while (p > uri->path && *p != '/')
343 if (strncmp (p, "/../", 4) != 0)
347 /* Remove extraneous initial "/.."s */
348 while (!strncmp (uri->path, "/../", 4))
349 memmove (uri->path, uri->path + 3, strlen (uri->path) - 2);
350 if (!strcmp (uri->path, "/.."))
354 /* HTTP-specific stuff */
355 if (uri->scheme == SOUP_URI_SCHEME_HTTP ||
356 uri->scheme == SOUP_URI_SCHEME_HTTPS) {
358 uri->path = g_strdup ("/");
359 if (!SOUP_URI_VALID_FOR_HTTP (uri)) {
365 if (uri->scheme == SOUP_URI_SCHEME_FTP) {
373 uri->port = soup_scheme_default_port (uri->scheme);
375 uri->path = g_strdup ("");
384 * Parses an absolute URI.
386 * You can also pass %NULL for @uri_string if you want to get back an
387 * "empty" #SoupURI that you can fill in by hand. (You will need to
388 * call at least soup_uri_set_scheme() and soup_uri_set_path(), since
389 * those fields are required.)
391 * Return value: a #SoupURI, or %NULL.
394 soup_uri_new (const char *uri_string)
399 return g_slice_new0 (SoupURI);
401 uri = soup_uri_new_with_base (NULL, uri_string);
414 * soup_uri_to_string:
416 * @just_path_and_query: if %TRUE, output just the path and query portions
418 * Returns a string representing @uri.
420 * If @just_path_and_query is %TRUE, this concatenates the path and query
421 * together. That is, it constructs the string that would be needed in
422 * the Request-Line of an HTTP request for @uri.
424 * Return value: a string representing @uri, which the caller must free.
427 soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
432 g_return_val_if_fail (uri != NULL, NULL);
434 /* IF YOU CHANGE ANYTHING IN THIS FUNCTION, RUN
435 * tests/uri-parsing AFTERWARD.
438 str = g_string_sized_new (20);
440 if (uri->scheme && !just_path_and_query)
441 g_string_append_printf (str, "%s:", uri->scheme);
442 if (uri->host && !just_path_and_query) {
443 g_string_append (str, "//");
445 append_uri_encoded (str, uri->user, ":;@?/");
446 g_string_append_c (str, '@');
448 if (strchr (uri->host, ':')) {
449 g_string_append_c (str, '[');
450 g_string_append (str, uri->host);
451 g_string_append_c (str, ']');
453 append_uri_encoded (str, uri->host, ":/");
454 if (uri->port && uri->port != soup_scheme_default_port (uri->scheme))
455 g_string_append_printf (str, ":%d", uri->port);
456 if (!uri->path && (uri->query || uri->fragment))
457 g_string_append_c (str, '/');
460 if (uri->path && *uri->path)
461 g_string_append (str, uri->path);
464 g_string_append_c (str, '?');
465 g_string_append (str, uri->query);
467 if (uri->fragment && !just_path_and_query) {
468 g_string_append_c (str, '#');
469 g_string_append (str, uri->fragment);
472 return_result = str->str;
473 g_string_free (str, FALSE);
475 return return_result;
484 * Return value: a copy of @uri, which must be freed with soup_uri_free()
487 soup_uri_copy (SoupURI *uri)
491 g_return_val_if_fail (uri != NULL, NULL);
493 dup = g_slice_new0 (SoupURI);
494 dup->scheme = uri->scheme;
495 dup->user = g_strdup (uri->user);
496 dup->password = g_strdup (uri->password);
497 dup->host = g_strdup (uri->host);
498 dup->port = uri->port;
499 dup->path = g_strdup (uri->path);
500 dup->query = g_strdup (uri->query);
501 dup->fragment = g_strdup (uri->fragment);
506 static inline gboolean
507 parts_equal (const char *one, const char *two, gboolean insensitive)
513 return insensitive ? !g_ascii_strcasecmp (one, two) : !strcmp (one, two);
519 * @uri2: another #SoupURI
521 * Tests whether or not @uri1 and @uri2 are equal in all parts
523 * Return value: %TRUE or %FALSE
526 soup_uri_equal (SoupURI *uri1, SoupURI *uri2)
528 if (uri1->scheme != uri2->scheme ||
529 uri1->port != uri2->port ||
530 !parts_equal (uri1->user, uri2->user, FALSE) ||
531 !parts_equal (uri1->password, uri2->password, FALSE) ||
532 !parts_equal (uri1->host, uri2->host, TRUE) ||
533 !parts_equal (uri1->path, uri2->path, FALSE) ||
534 !parts_equal (uri1->query, uri2->query, FALSE) ||
535 !parts_equal (uri1->fragment, uri2->fragment, FALSE))
548 soup_uri_free (SoupURI *uri)
550 g_return_if_fail (uri != NULL);
553 g_free (uri->password);
557 g_free (uri->fragment);
559 g_slice_free (SoupURI, uri);
563 #define SOUP_URI_UNRESERVED 0
564 #define SOUP_URI_PCT_ENCODED 1
565 #define SOUP_URI_GEN_DELIMS 2
566 #define SOUP_URI_SUB_DELIMS 4
567 static const char uri_encoded_char[] = {
568 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 - 0x0f */
569 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x10 - 0x1f */
570 1, 4, 1, 2, 4, 1, 4, 4, 4, 4, 4, 4, 4, 0, 0, 2, /* !"#$%&'()*+,-./ */
571 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 1, 4, 1, 2, /* 0123456789:;<=>? */
572 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ABCDEFGHIJKLMNO */
573 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 0, /* PQRSTUVWXYZ[\]^_ */
574 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* `abcdefghijklmno */
575 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* pqrstuvwxyz{|}~ */
576 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
577 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
578 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
579 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
580 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
581 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
582 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
583 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
587 append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars)
589 const unsigned char *s = (const unsigned char *)in;
592 if ((uri_encoded_char[*s] & (SOUP_URI_PCT_ENCODED | SOUP_URI_GEN_DELIMS)) ||
593 (extra_enc_chars && strchr (extra_enc_chars, *s)))
594 g_string_append_printf (str, "%%%02X", (int)*s++);
596 g_string_append_c (str, *s++);
603 * @escape_extra: additional reserved characters to escape (or %NULL)
605 * This %<!-- -->-encodes the given URI part and returns the escaped
606 * version in allocated memory, which the caller must free when it is
609 * Return value: the encoded URI part
612 soup_uri_encode (const char *part, const char *escape_extra)
617 str = g_string_new (NULL);
618 append_uri_encoded (str, part, escape_extra);
620 g_string_free (str, FALSE);
625 #define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
626 #define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
629 uri_decoded_copy (const char *part, int length)
631 unsigned char *s, *d;
632 char *decoded = g_strndup (part, length);
634 s = d = (unsigned char *)decoded;
637 if (!g_ascii_isxdigit (s[1]) ||
638 !g_ascii_isxdigit (s[2])) {
655 * Fully %<!-- -->-decodes @part.
657 * Return value: the decoded URI part, or %NULL if an invalid percent
658 * code was encountered.
661 soup_uri_decode (const char *part)
663 return uri_decoded_copy (part, strlen (part));
667 uri_normalized_copy (const char *part, int length,
668 const char *unescape_extra, gboolean fixup)
670 unsigned char *s, *d, c;
671 char *normalized = g_strndup (part, length);
672 gboolean need_fixup = FALSE;
674 s = d = (unsigned char *)normalized;
677 if (!g_ascii_isxdigit (s[1]) ||
678 !g_ascii_isxdigit (s[2])) {
684 if (uri_encoded_char[c] == SOUP_URI_UNRESERVED ||
685 (unescape_extra && strchr (unescape_extra, c))) {
690 *d++ = g_ascii_toupper (*s++);
691 *d++ = g_ascii_toupper (*s);
700 if (fixup && need_fixup) {
702 /* This code is lame, but so are people who put
703 * unencoded spaces in URLs!
705 while ((sp = strchr (normalized, ' '))) {
706 tmp = g_strdup_printf ("%.*s%%20%s",
707 (int)(sp - normalized),
718 * soup_uri_normalize:
720 * @unescape_extra: reserved characters to unescape (or %NULL)
722 * %<!-- -->-decodes any "unreserved" characters (or characters in
723 * @unescape_extra) in @part.
725 * "Unreserved" characters are those that are not allowed to be used
726 * for punctuation according to the URI spec. For example, letters are
727 * unreserved, so soup_uri_normalize() will turn
728 * <literal>http://example.com/foo/b%<!-- -->61r</literal> into
729 * <literal>http://example.com/foo/bar</literal>, which is guaranteed
730 * to mean the same thing. However, "/" is "reserved", so
731 * <literal>http://example.com/foo%<!-- -->2Fbar</literal> would not
732 * be changed, because it might mean something different to the
735 * Return value: the normalized URI part, or %NULL if an invalid percent
736 * code was encountered.
739 soup_uri_normalize (const char *part, const char *unescape_extra)
741 return uri_normalized_copy (part, strlen (part), unescape_extra, FALSE);
746 * soup_uri_uses_default_port:
749 * Tests if @uri uses the default port for its scheme. (Eg, 80 for
750 * http.) (This only works for http and https; libsoup does not know
751 * the default ports of other protocols.)
753 * Return value: %TRUE or %FALSE
756 soup_uri_uses_default_port (SoupURI *uri)
758 g_return_val_if_fail (uri->scheme == SOUP_URI_SCHEME_HTTP ||
759 uri->scheme == SOUP_URI_SCHEME_HTTPS ||
760 uri->scheme == SOUP_URI_SCHEME_FTP, FALSE);
762 return uri->port == soup_scheme_default_port (uri->scheme);
766 * SOUP_URI_SCHEME_HTTP:
768 * "http" as an interned string. This can be compared directly against
769 * the value of a #SoupURI's <structfield>scheme</structfield>
773 * SOUP_URI_SCHEME_HTTPS:
775 * "https" as an interned string. This can be compared directly
776 * against the value of a #SoupURI's <structfield>scheme</structfield>
780 * soup_uri_set_scheme:
782 * @scheme: the URI scheme
784 * Sets @uri's scheme to @scheme. This will also set @uri's port to
785 * the default port for @scheme, if known.
788 soup_uri_set_scheme (SoupURI *uri, const char *scheme)
790 uri->scheme = soup_uri_get_scheme (scheme, strlen (scheme));
791 uri->port = soup_scheme_default_port (uri->scheme);
797 * @user: the username, or %NULL
799 * Sets @uri's user to @user.
802 soup_uri_set_user (SoupURI *uri, const char *user)
805 uri->user = g_strdup (user);
809 * soup_uri_set_password:
811 * @password: the password, or %NULL
813 * Sets @uri's password to @password.
816 soup_uri_set_password (SoupURI *uri, const char *password)
818 g_free (uri->password);
819 uri->password = g_strdup (password);
825 * @host: the hostname or IP address, or %NULL
827 * Sets @uri's host to @host.
829 * If @host is an IPv6 IP address, it should not include the brackets
830 * required by the URI syntax; they will be added automatically when
831 * converting @uri to a string.
834 soup_uri_set_host (SoupURI *uri, const char *host)
837 uri->host = g_strdup (host);
843 * @port: the port, or 0
845 * Sets @uri's port to @port. If @port is 0, @uri will not have an
846 * explicitly-specified port.
849 soup_uri_set_port (SoupURI *uri, guint port)
859 * Sets @uri's path to @path.
862 soup_uri_set_path (SoupURI *uri, const char *path)
865 uri->path = g_strdup (path);
869 * soup_uri_set_query:
873 * Sets @uri's query to @query.
876 soup_uri_set_query (SoupURI *uri, const char *query)
879 uri->query = g_strdup (query);
883 * soup_uri_set_query_from_form:
885 * @form: a #GHashTable containing HTML form information
887 * Sets @uri's query to the result of encoding @form according to the
888 * HTML form rules. See soup_form_encode_hash() for more information.
891 soup_uri_set_query_from_form (SoupURI *uri, GHashTable *form)
894 uri->query = soup_form_encode_urlencoded (form);
898 * soup_uri_set_query_from_fields:
900 * @first_field: name of the first form field to encode into query
901 * @...: value of @first_field, followed by additional field names
902 * and values, terminated by %NULL.
904 * Sets @uri's query to the result of encoding the given form fields
905 * and values according to the * HTML form rules. See
906 * soup_form_encode() for more information.
909 soup_uri_set_query_from_fields (SoupURI *uri,
910 const char *first_field,
916 va_start (args, first_field);
917 uri->query = soup_form_encode_valist (first_field, args);
922 * soup_uri_set_fragment:
924 * @fragment: the fragment
926 * Sets @uri's fragment to @fragment.
929 soup_uri_set_fragment (SoupURI *uri, const char *fragment)
931 g_free (uri->fragment);
932 uri->fragment = g_strdup (fragment);
936 * soup_uri_copy_host:
939 * Makes a copy of @uri, considering only the protocol, host, and port
941 * Return value: the new #SoupUri
946 soup_uri_copy_host (SoupURI *uri)
950 g_return_val_if_fail (uri != NULL, NULL);
952 dup = soup_uri_new (NULL);
953 dup->scheme = uri->scheme;
954 dup->host = g_strdup (uri->host);
955 dup->port = uri->port;
956 if (dup->scheme == SOUP_URI_SCHEME_HTTP ||
957 dup->scheme == SOUP_URI_SCHEME_HTTPS)
958 dup->path = g_strdup ("");
964 * soup_uri_host_hash:
967 * Hashes @key, considering only the scheme, host, and port.
969 * Return value: a hash
974 soup_uri_host_hash (gconstpointer key)
976 const SoupURI *uri = key;
978 g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
980 return GPOINTER_TO_UINT (uri->scheme) + uri->port +
981 soup_str_case_hash (uri->host);
985 * soup_uri_host_equal:
989 * Compares @v1 and @v2, considering only the scheme, host, and port.
991 * Return value: whether or not the URIs are equal in scheme, host,
997 soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
999 const SoupURI *one = v1;
1000 const SoupURI *two = v2;
1002 g_return_val_if_fail (one != NULL && two != NULL, one == two);
1003 g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
1005 if (one->scheme != two->scheme)
1007 if (one->port != two->port)
1010 return g_ascii_strcasecmp (one->host, two->host) == 0;
1015 soup_uri_get_type (void)
1017 static volatile gsize type_volatile = 0;
1019 if (g_once_init_enter (&type_volatile)) {
1020 GType type = g_boxed_type_register_static (
1021 g_intern_static_string ("SoupURI"),
1022 (GBoxedCopyFunc) soup_uri_copy,
1023 (GBoxedFreeFunc) soup_uri_free);
1024 g_once_init_leave (&type_volatile, type);
1026 return type_volatile;