* Copyright 1999-2003 Ximian, Inc.
*/
-#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "soup-uri.h"
+#include "soup.h"
#include "soup-misc-private.h"
-#include "soup-form.h"
-#include "soup-misc.h"
/**
* SECTION:soup-uri
* Since: 2.24
**/
+/**
+ * SOUP_URI_SCHEME_HTTP:
+ *
+ * "http" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ */
+/**
+ * SOUP_URI_SCHEME_HTTPS:
+ *
+ * "https" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ */
+/**
+ * SOUP_URI_SCHEME_FTP:
+ *
+ * "ftp" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ *
+ * Since: 2.30
+ */
+/**
+ * SOUP_URI_SCHEME_FILE:
+ *
+ * "file" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ *
+ * Since: 2.30
+ */
+/**
+ * SOUP_URI_SCHEME_DATA:
+ *
+ * "data" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ *
+ * Since: 2.30
+ */
+/**
+ * SOUP_URI_SCHEME_RESOURCE:
+ *
+ * "data" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ *
+ * Since: 2.42
+ */
+
static void append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars);
static char *uri_normalized_copy (const char *str, int length, const char *unescape_extra);
gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
gpointer _SOUP_URI_SCHEME_FTP;
-gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA;
+gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA, _SOUP_URI_SCHEME_RESOURCE;
static inline const char *
soup_uri_parse_scheme (const char *scheme, int len)
return SOUP_URI_SCHEME_HTTP;
} else if (len == 5 && !g_ascii_strncasecmp (scheme, "https", len)) {
return SOUP_URI_SCHEME_HTTPS;
+ } else if (len == 8 && !g_ascii_strncasecmp (scheme, "resource", len)) {
+ return SOUP_URI_SCHEME_RESOURCE;
} else {
char *lower_scheme;
SoupURI *
soup_uri_new_with_base (SoupURI *base, const char *uri_string)
{
- SoupURI *uri;
+ 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 (base == NULL || SOUP_URI_IS_VALID (base), NULL);
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.
/* Find scheme: initial [a-z+.-]* substring until ":" */
p = uri_string;
- while (p < end && (g_ascii_isalnum (*p) ||
+ while (p < end && (g_ascii_isalpha (*p) ||
*p == '.' || *p == '+' || *p == '-'))
p++;
colon = strchr (uri_string, ':');
if (colon && colon < at) {
uri->password = uri_decoded_copy (colon + 1,
- at - colon - 1);
+ at - colon - 1, NULL);
} else {
uri->password = NULL;
colon = at;
}
uri->user = uri_decoded_copy (uri_string,
- colon - uri_string);
+ colon - uri_string, NULL);
uri_string = at + 1;
} else
uri->user = uri->password = NULL;
hostend = colon ? colon : path;
}
- uri->host = uri_decoded_copy (uri_string, hostend - uri_string);
+ uri->host = uri_decoded_copy (uri_string, hostend - uri_string, NULL);
if (colon && colon != path - 1) {
char *portend;
}
-/**
- * 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)
+soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query,
+ gboolean force_port)
{
GString *str;
char *return_result;
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
-
- /* IF YOU CHANGE ANYTHING IN THIS FUNCTION, RUN
- * tests/uri-parsing AFTERWARD.
- */
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_warn_if_fail (SOUP_URI_IS_VALID (uri));
- str = g_string_sized_new (20);
+ str = g_string_sized_new (40);
if (uri->scheme && !just_path_and_query)
g_string_append_printf (str, "%s:", uri->scheme);
g_string_append_c (str, ']');
} else
append_uri_encoded (str, uri->host, ":/");
- if (uri->port && uri->port != soup_scheme_default_port (uri->scheme))
+ 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, '?');
}
/**
+ * 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)
+{
+ return soup_uri_to_string_internal (uri, just_path_and_query, FALSE);
+}
+
+/**
* soup_uri_copy:
* @uri: a #SoupURI
*
{
SoupURI *dup;
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ 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;
gboolean
soup_uri_equal (SoupURI *uri1, SoupURI *uri2)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri1), FALSE);
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri2), FALSE);
+ 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 ||
#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
char *
-uri_decoded_copy (const char *part, int length)
+uri_decoded_copy (const char *part, int length, int *decoded_length)
{
unsigned char *s, *d;
char *decoded = g_strndup (part, length);
*d++ = *s;
} while (*s++);
+ if (decoded_length)
+ *decoded_length = d - (unsigned char *)decoded - 1;
+
return decoded;
}
{
g_return_val_if_fail (part != NULL, NULL);
- return uri_decoded_copy (part, strlen (part));
+ return uri_decoded_copy (part, strlen (part), NULL);
}
static char *
char *normalized = g_strndup (part, length);
gboolean need_fixup = FALSE;
+ if (!unescape_extra)
+ unescape_extra = "";
+
s = d = (unsigned char *)normalized;
- do {
+ while (*s) {
if (*s == '%') {
if (!g_ascii_isxdigit (s[1]) ||
!g_ascii_isxdigit (s[2])) {
- *d++ = *s;
+ *d++ = *s++;
continue;
}
c = HEXCHAR (s);
if (soup_char_is_uri_unreserved (c) ||
- (unescape_extra && strchr (unescape_extra, c))) {
+ (c && strchr (unescape_extra, c))) {
*d++ = c;
- s += 2;
+ s += 3;
} else {
/* We leave it unchanged. We used to uppercase percent-encoded
* triplets but we do not do it any more as RFC3986 Section 6.2.2.1
*/
*d++ = *s++;
*d++ = *s++;
- *d++ = *s;
+ *d++ = *s++;
}
} else {
- if (!g_ascii_isgraph (*s))
+ if (!g_ascii_isgraph (*s) &&
+ !strchr (unescape_extra, *s))
need_fixup = TRUE;
- *d++ = *s;
+ *d++ = *s++;
}
- } while (*s++);
+ }
+ *d = '\0';
if (need_fixup) {
GString *fixed;
fixed = g_string_new (NULL);
s = (guchar *)normalized;
while (*s) {
- if (g_ascii_isgraph (*s))
+ if (g_ascii_isgraph (*s) ||
+ strchr (unescape_extra, *s))
g_string_append_c (fixed, *s);
else
g_string_append_printf (fixed, "%%%02X", (int)*s);
soup_uri_uses_default_port (SoupURI *uri)
{
g_return_val_if_fail (uri != NULL, FALSE);
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), FALSE);
+ g_warn_if_fail (SOUP_URI_IS_VALID (uri));
return uri->port == soup_scheme_default_port (uri->scheme);
}
**/
/**
+ * SOUP_URI_SCHEME_RESOURCE:
+ *
+ * "resource" as an interned string. This can be compared directly
+ * against the value of a #SoupURI's <structfield>scheme</structfield>
+ *
+ * Since: 2.42
+ **/
+
+/**
* soup_uri_get_scheme:
* @uri: a #SoupURI
*
const char *
soup_uri_get_scheme (SoupURI *uri)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
return uri->scheme;
}
const char *
soup_uri_get_user (SoupURI *uri)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
return uri->user;
}
const char *
soup_uri_get_password (SoupURI *uri)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
return uri->password;
}
const char *
soup_uri_get_host (SoupURI *uri)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
return uri->host;
}
guint
soup_uri_get_port (SoupURI *uri)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), 0);
+ g_return_val_if_fail (uri != NULL, 0);
return uri->port;
}
const char *
soup_uri_get_path (SoupURI *uri)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
return uri->path;
}
soup_uri_set_path (SoupURI *uri, const char *path)
{
g_return_if_fail (uri != NULL);
- g_return_if_fail (path != NULL);
+
+ /* We allow a NULL path for compatibility, but warn about it. */
+ if (!path) {
+ g_warn_if_fail (path != NULL);
+ path = "";
+ }
g_free (uri->path);
uri->path = g_strdup (path);
const char *
soup_uri_get_query (SoupURI *uri)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
return uri->query;
}
const char *
soup_uri_get_fragment (SoupURI *uri)
{
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
return uri->fragment;
}
*
* Return value: the new #SoupURI
*
- * Since: 2.26.3
+ * Since: 2.28
**/
SoupURI *
soup_uri_copy_host (SoupURI *uri)
{
SoupURI *dup;
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), NULL);
+ 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 ("");
+ dup->path = g_strdup ("");
return dup;
}
*
* Return value: a hash
*
- * Since: 2.26.3
+ * Since: 2.28
**/
guint
soup_uri_host_hash (gconstpointer key)
{
const SoupURI *uri = key;
- g_return_val_if_fail (SOUP_URI_IS_VALID (uri), 0);
- g_return_val_if_fail (uri->host != NULL, 0);
+ 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);
* Return value: whether or not the URIs are equal in scheme, host,
* and port.
*
- * Since: 2.26.3
+ * Since: 2.28
**/
gboolean
soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
const SoupURI *two = v2;
g_return_val_if_fail (one != NULL && two != NULL, one == two);
- g_return_val_if_fail (SOUP_URI_IS_VALID (one), FALSE);
- g_return_val_if_fail (SOUP_URI_IS_VALID (two), FALSE);
g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
+ g_warn_if_fail (SOUP_URI_IS_VALID (one));
+ g_warn_if_fail (SOUP_URI_IS_VALID (two));
if (one->scheme != two->scheme)
return FALSE;
return g_ascii_strcasecmp (one->host, two->host) == 0;
}
-
-GType
-soup_uri_get_type (void)
-{
- static volatile gsize type_volatile = 0;
-
- if (g_once_init_enter (&type_volatile)) {
- GType type = g_boxed_type_register_static (
- g_intern_static_string ("SoupURI"),
- (GBoxedCopyFunc) soup_uri_copy,
- (GBoxedFreeFunc) soup_uri_free);
- g_once_init_leave (&type_volatile, type);
- }
- return type_volatile;
-}
+G_DEFINE_BOXED_TYPE (SoupURI, soup_uri, soup_uri_copy, soup_uri_free)