/** * soup_uri_equal: * @uri1: a #SoupURI * @uri2: another #SoupURI * * Tests whether or not @uri1 and @uri2 are equal in all parts * * Return value: %TRUE or %FALSE **/ gboolean soup_uri_equal (SoupURI *uri1, SoupURI *uri2) { g_return_val_if_fail (uri1 != NULL, FALSE); g_return_val_if_fail (uri2 != NULL, FALSE); g_warn_if_fail (SOUP_URI_IS_VALID (uri1)); g_warn_if_fail (SOUP_URI_IS_VALID (uri2)); if (uri1->scheme != uri2->scheme || uri1->port != uri2->port || !parts_equal (uri1->user, uri2->user, FALSE) || !parts_equal (uri1->password, uri2->password, FALSE) || !parts_equal (uri1->host, uri2->host, TRUE) || !parts_equal (uri1->path, uri2->path, FALSE) || !parts_equal (uri1->query, uri2->query, FALSE) || !parts_equal (uri1->fragment, uri2->fragment, FALSE)) return FALSE; return TRUE; }
/** * soup_uri_host_hash: * @key: (type Soup.URI): a #SoupURI with a non-%NULL @host member * * Hashes @key, considering only the scheme, host, and port. * * Return value: a hash * * Since: 2.28 **/ guint soup_uri_host_hash (gconstpointer key) { const SoupURI *uri = key; g_return_val_if_fail (uri != NULL && uri->host != NULL, 0); g_warn_if_fail (SOUP_URI_IS_VALID (uri)); return GPOINTER_TO_UINT (uri->scheme) + uri->port + soup_str_case_hash (uri->host); }
/** * soup_uri_to_string: * @uri: a #SoupURI * @just_path_and_query: if %TRUE, output just the path and query portions * * Returns a string representing @uri. * * If @just_path_and_query is %TRUE, this concatenates the path and query * together. That is, it constructs the string that would be needed in * the Request-Line of an HTTP request for @uri. * * Return value: a string representing @uri, which the caller must free. **/ char * soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query) { GString *str; char *return_result; g_return_val_if_fail (uri != NULL, NULL); g_warn_if_fail (SOUP_URI_IS_VALID (uri)); str = g_string_sized_new (20); if (uri->scheme && !just_path_and_query) g_string_append_printf (str, "%s:", uri->scheme); if (uri->host && !just_path_and_query) { g_string_append (str, "//"); if (uri->user) { append_uri_encoded (str, uri->user, ":;@?/"); g_string_append_c (str, '@'); } if (strchr (uri->host, ':')) { g_string_append_c (str, '['); g_string_append (str, uri->host); g_string_append_c (str, ']'); } else append_uri_encoded (str, uri->host, ":/"); if (uri->port && uri->port != soup_scheme_default_port (uri->scheme)) g_string_append_printf (str, ":%u", uri->port); if (!uri->path && (uri->query || uri->fragment)) g_string_append_c (str, '/'); else if ((!uri->path || !*uri->path) && (uri->scheme == SOUP_URI_SCHEME_HTTP || uri->scheme == SOUP_URI_SCHEME_HTTPS)) g_string_append_c (str, '/'); } if (uri->path && *uri->path) g_string_append (str, uri->path); else if (just_path_and_query) g_string_append_c (str, '/'); if (uri->query) { g_string_append_c (str, '?'); g_string_append (str, uri->query); } if (uri->fragment && !just_path_and_query) { g_string_append_c (str, '#'); g_string_append (str, uri->fragment); } return_result = str->str; g_string_free (str, FALSE); return return_result; }
/** * soup_uri_copy_host: * @uri: a #SoupURI * * Makes a copy of @uri, considering only the protocol, host, and port * * Return value: the new #SoupURI * * Since: 2.28 **/ SoupURI * soup_uri_copy_host (SoupURI *uri) { SoupURI *dup; g_return_val_if_fail (uri != NULL, NULL); g_warn_if_fail (SOUP_URI_IS_VALID (uri)); dup = soup_uri_new (NULL); dup->scheme = uri->scheme; dup->host = g_strdup (uri->host); dup->port = uri->port; dup->path = g_strdup (""); return dup; }
/** * soup_uri_new: * @uri_string: (allow-none): a URI * * Parses an absolute URI. * * You can also pass %NULL for @uri_string if you want to get back an * "empty" #SoupURI that you can fill in by hand. (You will need to * call at least soup_uri_set_scheme() and soup_uri_set_path(), since * those fields are required.) * * Return value: (nullable): a #SoupURI, or %NULL if the given string * was found to be invalid. **/ SoupURI * soup_uri_new (const char *uri_string) { SoupURI *uri; if (!uri_string) return g_slice_new0 (SoupURI); uri = soup_uri_new_with_base (NULL, uri_string); if (!uri) return NULL; if (!SOUP_URI_IS_VALID (uri)) { soup_uri_free (uri); return NULL; } return uri; }
/** * soup_uri_copy: * @uri: a #SoupURI * * Copies @uri * * Return value: a copy of @uri, which must be freed with soup_uri_free() **/ SoupURI * soup_uri_copy (SoupURI *uri) { SoupURI *dup; g_return_val_if_fail (uri != NULL, NULL); g_warn_if_fail (SOUP_URI_IS_VALID (uri)); dup = g_slice_new0 (SoupURI); dup->scheme = uri->scheme; dup->user = g_strdup (uri->user); dup->password = g_strdup (uri->password); dup->host = g_strdup (uri->host); dup->port = uri->port; dup->path = g_strdup (uri->path); dup->query = g_strdup (uri->query); dup->fragment = g_strdup (uri->fragment); return dup; }
static void network_set_soup_session_proxy (SoupSession *session, ProxyDetectMode mode, const gchar *host, guint port, const gchar *user, const gchar *password) { SoupURI *uri = NULL; switch (mode) { case PROXY_DETECT_MODE_AUTO: /* Sets proxy-resolver to the default resolver, this unsets proxy-uri. */ g_object_set (G_OBJECT (session), SOUP_SESSION_PROXY_RESOLVER, g_proxy_resolver_get_default (), NULL); break; case PROXY_DETECT_MODE_NONE: /* Sets proxy-resolver to NULL, this unsets proxy-uri. */ g_object_set (G_OBJECT (session), SOUP_SESSION_PROXY_RESOLVER, NULL, NULL); break; case PROXY_DETECT_MODE_MANUAL: uri = soup_uri_new (NULL); soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP); soup_uri_set_host (uri, host); soup_uri_set_port (uri, port); soup_uri_set_user (uri, user); soup_uri_set_password (uri, password); soup_uri_set_path (uri, "/"); if (SOUP_URI_IS_VALID (uri)) { /* Sets proxy-uri, this unsets proxy-resolver. */ g_object_set (G_OBJECT (session), SOUP_SESSION_PROXY_URI, uri, NULL); } soup_uri_free (uri); break; } }
char * soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query, gboolean include_password, gboolean force_port) { GString *str; char *return_result; g_return_val_if_fail (uri != NULL, NULL); g_warn_if_fail (SOUP_URI_IS_VALID (uri)); str = g_string_sized_new (40); if (uri->scheme && !just_path_and_query) g_string_append_printf (str, "%s:", uri->scheme); if (uri->host && !just_path_and_query) { g_string_append (str, "//"); if (uri->user) { append_uri_encoded (str, uri->user, ":;@?/"); if (uri->password && include_password) { g_string_append_c (str, ':'); append_uri_encoded (str, uri->password, ";@?/"); } g_string_append_c (str, '@'); } if (strchr (uri->host, ':')) { const char *pct; g_string_append_c (str, '['); pct = strchr (uri->host, '%'); if (pct) { g_string_append_printf (str, "%.*s%%25%s", (int) (pct - uri->host), uri->host, pct + 1); } else g_string_append (str, uri->host); g_string_append_c (str, ']'); } else append_uri_encoded (str, uri->host, ":/"); if (uri->port && (force_port || uri->port != soup_scheme_default_port (uri->scheme))) g_string_append_printf (str, ":%u", uri->port); if (!uri->path && (uri->query || uri->fragment)) g_string_append_c (str, '/'); else if ((!uri->path || !*uri->path) && (uri->scheme == SOUP_URI_SCHEME_HTTP || uri->scheme == SOUP_URI_SCHEME_HTTPS)) g_string_append_c (str, '/'); } if (uri->path && *uri->path) g_string_append (str, uri->path); else if (just_path_and_query) g_string_append_c (str, '/'); if (uri->query) { g_string_append_c (str, '?'); g_string_append (str, uri->query); } if (uri->fragment && !just_path_and_query) { g_string_append_c (str, '#'); g_string_append (str, uri->fragment); } return_result = str->str; g_string_free (str, FALSE); return return_result; }
/** * soup_uri_new_with_base: * @base: a base URI * @uri_string: the URI * * Parses @uri_string relative to @base. * * Return value: a parsed #SoupURI. **/ SoupURI * soup_uri_new_with_base (SoupURI *base, const char *uri_string) { SoupURI *uri, fixed_base; const char *end, *hash, *colon, *at, *path, *question; const char *p, *hostend; gboolean remove_dot_segments = TRUE; int len; g_return_val_if_fail (uri_string != NULL, NULL); /* Allow a %NULL path in @base, for compatibility */ if (base && base->scheme && !base->path) { g_warn_if_fail (SOUP_URI_IS_VALID (base)); memcpy (&fixed_base, base, sizeof (SoupURI)); fixed_base.path = ""; base = &fixed_base; } g_return_val_if_fail (base == NULL || SOUP_URI_IS_VALID (base), NULL); /* First some cleanup steps (which are supposed to all be no-ops, * but...). Skip initial whitespace, strip out internal tabs and * line breaks, and ignore trailing whitespace. */ while (g_ascii_isspace (*uri_string)) uri_string++; len = strcspn (uri_string, "\t\n\r"); if (uri_string[len]) { char *clean = g_malloc (strlen (uri_string) + 1), *d; const char *s; for (s = uri_string, d = clean; *s; s++) { if (*s != '\t' && *s != '\n' && *s != '\r') *d++ = *s; } *d = '\0'; uri = soup_uri_new_with_base (base, clean); g_free (clean); return uri; } end = uri_string + len; while (end > uri_string && g_ascii_isspace (end[-1])) end--; uri = g_slice_new0 (SoupURI); /* Find fragment. */ hash = strchr (uri_string, '#'); if (hash) { uri->fragment = uri_normalized_copy (hash + 1, end - hash + 1, NULL); end = hash; } /* Find scheme */ p = uri_string; while (p < end && (g_ascii_isalpha (*p) || (p > uri_string && (g_ascii_isdigit (*p) || *p == '.' || *p == '+' || *p == '-')))) p++; if (p > uri_string && *p == ':') { uri->scheme = soup_uri_parse_scheme (uri_string, p - uri_string); uri_string = p + 1; } if (uri_string == end && !base && !uri->fragment) { uri->path = g_strdup (""); return uri; } /* Check for authority */ if (strncmp (uri_string, "//", 2) == 0) { uri_string += 2; path = uri_string + strcspn (uri_string, "/?#"); if (path > end) path = end; at = strchr (uri_string, '@'); if (at && at < path) { colon = strchr (uri_string, ':'); if (colon && colon < at) { uri->password = soup_uri_decoded_copy (colon + 1, at - colon - 1, NULL); } else { uri->password = NULL; colon = at; } uri->user = soup_uri_decoded_copy (uri_string, colon - uri_string, NULL); uri_string = at + 1; } else uri->user = uri->password = NULL; /* Find host and port. */ if (*uri_string == '[') { const char *pct; uri_string++; hostend = strchr (uri_string, ']'); if (!hostend || hostend > path) { soup_uri_free (uri); return NULL; } if (*(hostend + 1) == ':') colon = hostend + 1; else colon = NULL; pct = memchr (uri_string, '%', hostend - uri_string); if (!pct || (pct[1] == '2' && pct[2] == '5')) { uri->host = soup_uri_decoded_copy (uri_string, hostend - uri_string, NULL); } else uri->host = g_strndup (uri_string, hostend - uri_string); } else { colon = memchr (uri_string, ':', path - uri_string); hostend = colon ? colon : path; uri->host = soup_uri_decoded_copy (uri_string, hostend - uri_string, NULL); } if (colon && colon != path - 1) { char *portend; uri->port = strtoul (colon + 1, &portend, 10); if (portend != (char *)path) { soup_uri_free (uri); return NULL; } } uri_string = path; } /* Find query */ question = memchr (uri_string, '?', end - uri_string); if (question) { uri->query = uri_normalized_copy (question + 1, end - (question + 1), NULL); end = question; } if (end != uri_string) { uri->path = uri_normalized_copy (uri_string, end - uri_string, NULL); } /* Apply base URI. This is spelled out in RFC 3986. */ if (base && !uri->scheme && uri->host) uri->scheme = base->scheme; else if (base && !uri->scheme) { uri->scheme = base->scheme; uri->user = g_strdup (base->user); uri->password = g_strdup (base->password); uri->host = g_strdup (base->host); uri->port = base->port; if (!uri->path) { uri->path = g_strdup (base->path); if (!uri->query) uri->query = g_strdup (base->query); remove_dot_segments = FALSE; } else if (*uri->path != '/') { char *newpath, *last; last = strrchr (base->path, '/'); if (last) { newpath = g_strdup_printf ("%.*s%s", (int)(last + 1 - base->path), base->path, uri->path); } else newpath = g_strdup_printf ("/%s", uri->path); g_free (uri->path); uri->path = newpath; } } if (remove_dot_segments && uri->path && *uri->path) { char *p, *q; /* Remove "./" where "." is a complete segment. */ for (p = uri->path + 1; *p; ) { if (*(p - 1) == '/' && *p == '.' && *(p + 1) == '/') memmove (p, p + 2, strlen (p + 2) + 1); else p++; } /* Remove "." at end. */ if (p > uri->path + 2 && *(p - 1) == '.' && *(p - 2) == '/') *(p - 1) = '\0'; /* Remove "<segment>/../" where <segment> != ".." */ for (p = uri->path + 1; *p; ) { if (!strncmp (p, "../", 3)) { p += 3; continue; } q = strchr (p + 1, '/'); if (!q) break; if (strncmp (q, "/../", 4) != 0) { p = q + 1; continue; } memmove (p, q + 4, strlen (q + 4) + 1); p = uri->path + 1; } /* Remove "<segment>/.." at end where <segment> != ".." */ q = strrchr (uri->path, '/'); if (q && !strcmp (q, "/..")) { p = q - 1; while (p > uri->path && *p != '/') p--; if (strncmp (p, "/../", 4) != 0) *(p + 1) = 0; } /* Remove extraneous initial "/.."s */ while (!strncmp (uri->path, "/../", 4)) memmove (uri->path, uri->path + 3, strlen (uri->path) - 2); if (!strcmp (uri->path, "/..")) uri->path[1] = '\0'; } /* HTTP-specific stuff */ if (uri->scheme == SOUP_URI_SCHEME_HTTP || uri->scheme == SOUP_URI_SCHEME_HTTPS) { if (!uri->path) uri->path = g_strdup ("/"); if (!SOUP_URI_VALID_FOR_HTTP (uri)) { soup_uri_free (uri); return NULL; } } if (uri->scheme == SOUP_URI_SCHEME_FTP) { if (!uri->host) { soup_uri_free (uri); return NULL; } } if (!uri->port) uri->port = soup_scheme_default_port (uri->scheme); if (!uri->path) uri->path = g_strdup (""); return uri; }
static void do_soup_uri_null_tests (void) { SoupURI *uri, *uri2; char *uri_string; debug_printf (1, "\nsoup_uri_new (NULL)\n"); uri = soup_uri_new (NULL); if (SOUP_URI_IS_VALID (uri) || SOUP_URI_VALID_FOR_HTTP (uri)) { debug_printf (1, " ERROR: soup_uri_new(NULL) returns valid URI?\n"); errors++; } /* This implicitly also verifies that none of these methods g_warn */ if (soup_uri_get_scheme (uri) || soup_uri_get_user (uri) || soup_uri_get_password (uri) || soup_uri_get_host (uri) || soup_uri_get_port (uri) || soup_uri_get_path (uri) || soup_uri_get_query (uri) || soup_uri_get_fragment (uri)) { debug_printf (1, " ERROR: soup_uri_new(NULL) returns non-empty URI?\n"); errors++; } expect_warning = TRUE; uri2 = soup_uri_new_with_base (uri, "/path"); if (uri2 || expect_warning) { debug_printf (1, " ERROR: soup_uri_new_with_base didn't fail on NULL URI?\n"); errors++; expect_warning = FALSE; } expect_warning = TRUE; uri_string = soup_uri_to_string (uri, FALSE); if (expect_warning) { debug_printf (1, " ERROR: soup_uri_to_string didn't fail on NULL URI?\n"); errors++; expect_warning = FALSE; } else if (*uri_string) { debug_printf (1, " ERROR: soup_uri_to_string on NULL URI returned '%s'\n", uri_string); errors++; } g_free (uri_string); soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP); if (SOUP_URI_IS_VALID (uri) || SOUP_URI_VALID_FOR_HTTP (uri)) { debug_printf (1, " ERROR: setting scheme on NULL URI makes it valid?\n"); errors++; } expect_warning = TRUE; uri_string = soup_uri_to_string (uri, FALSE); if (expect_warning) { debug_printf (1, " ERROR: soup_uri_to_string didn't fail on scheme-only URI?\n"); errors++; expect_warning = FALSE; } else if (strcmp (uri_string, "http:") != 0) { debug_printf (1, " ERROR: soup_uri_to_string returned '%s' instead of 'http:'\n", uri_string); errors++; } g_free (uri_string); soup_uri_set_host (uri, "localhost"); if (SOUP_URI_IS_VALID (uri)) { debug_printf (1, " ERROR: setting scheme+host on NULL URI makes it valid?\n"); errors++; } if (SOUP_URI_VALID_FOR_HTTP (uri)) { debug_printf (1, " ERROR: setting scheme+host on NULL URI makes it valid for http?\n"); errors++; } expect_warning = TRUE; uri_string = soup_uri_to_string (uri, FALSE); if (expect_warning) { debug_printf (1, " ERROR: soup_uri_to_string didn't fail on scheme+host URI?\n"); errors++; expect_warning = FALSE; } else if (strcmp (uri_string, "http://localhost/") != 0) { debug_printf (1, " ERROR: soup_uri_to_string with NULL path returned '%s' instead of 'http://localhost/'\n", uri_string); errors++; } g_free (uri_string); expect_warning = TRUE; uri2 = soup_uri_new_with_base (uri, "/path"); if (expect_warning) { debug_printf (1, " ERROR: soup_uri_new_with_base didn't warn on NULL+scheme URI?\n"); errors++; expect_warning = FALSE; } else if (!uri2) { debug_printf (1, " ERROR: soup_uri_new_with_base didn't fix path on NULL+scheme URI\n"); errors++; } if (uri2) { uri_string = soup_uri_to_string (uri2, FALSE); if (!uri_string) { debug_printf (1, " ERROR: soup_uri_to_string failed on uri2?\n"); errors++; } else if (strcmp (uri_string, "http://localhost/path") != 0) { debug_printf (1, " ERROR: soup_uri_to_string returned '%s' instead of 'http://localhost/path'\n", uri_string); errors++; } g_free (uri_string); soup_uri_free (uri2); } expect_warning = TRUE; soup_uri_set_path (uri, NULL); if (expect_warning) { debug_printf (1, " ERROR: setting path to NULL doesn't warn\n"); errors++; expect_warning = FALSE; } if (!uri->path || *uri->path) { debug_printf (1, " ERROR: setting path to NULL != \"\"\n"); errors++; soup_uri_set_path (uri, ""); } uri_string = soup_uri_to_string (uri, FALSE); if (!uri_string) { debug_printf (1, " ERROR: soup_uri_to_string failed on complete URI?\n"); errors++; } else if (strcmp (uri_string, "http://localhost/") != 0) { debug_printf (1, " ERROR: soup_uri_to_string with empty path returned '%s' instead of 'http://localhost/'\n", uri_string); errors++; } g_free (uri_string); if (!SOUP_URI_IS_VALID (uri)) { debug_printf (1, " ERROR: setting scheme+path on NULL URI doesn't make it valid?\n"); errors++; } if (!SOUP_URI_VALID_FOR_HTTP (uri)) { debug_printf (1, " ERROR: setting scheme+host+path on NULL URI doesn't make it valid for http?\n"); errors++; } soup_uri_free (uri); }