From c68bff78ac88efb0abc5bf2aa50866d67ed64d76 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Sun, 26 Jul 2009 10:43:14 -0400 Subject: [PATCH] SoupSession: Add http-aliases and https-aliases properties Currently SoupSession treats all URI schemes except "https" as aliases for "http", and some apps depend on this. (Eg, iTunes sometimes returns redirects involving "daap" URIs, which Rhythmbox needs to treat as "http".) Unfortunately, this also means that it mishandles redirects to, eg, ftp. The http-aliases and https-aliases properties allow an app to explicitly indicate which URI schemes should be considered aliases for http and https, with other schemes considered to be unknown and unhandled. --- docs/reference/libsoup-2.4-sections.txt | 2 + libsoup/soup-session.c | 166 +++++++++++++++++++++++++++++++- libsoup/soup-session.h | 2 + 3 files changed, 168 insertions(+), 2 deletions(-) diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt index 3061351..7d462c8 100644 --- a/docs/reference/libsoup-2.4-sections.txt +++ b/docs/reference/libsoup-2.4-sections.txt @@ -419,6 +419,8 @@ SOUP_SESSION_REMOVE_FEATURE_BY_TYPE SOUP_SESSION_ACCEPT_LANGUAGE SOUP_SESSION_ACCEPT_LANGUAGE_AUTO SOUP_SESSION_SSL_STRICT +SOUP_SESSION_HTTP_ALIASES +SOUP_SESSION_HTTPS_ALIASES SOUP_IS_SESSION SOUP_IS_SESSION_CLASS diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index e46b19d..0015dba 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -103,6 +103,8 @@ typedef struct { GMainContext *async_context; GResolver *resolver; + + char **http_aliases, **https_aliases; } SoupSessionPrivate; #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate)) @@ -162,6 +164,8 @@ enum { PROP_ADD_FEATURE, PROP_ADD_FEATURE_BY_TYPE, PROP_REMOVE_FEATURE_BY_TYPE, + PROP_HTTP_ALIASES, + PROP_HTTPS_ALIASES, LAST_PROP }; @@ -206,6 +210,10 @@ soup_session_init (SoupSession *session) priv->resolver = g_resolver_get_default (); priv->ssl_strict = TRUE; + + priv->http_aliases = g_new (char *, 2); + priv->http_aliases[0] = (char *)g_intern_string ("*"); + priv->http_aliases[1] = NULL; } static void @@ -248,6 +256,9 @@ finalize (GObject *object) g_object_unref (priv->resolver); + g_free (priv->http_aliases); + g_free (priv->https_aliases); + G_OBJECT_CLASS (soup_session_parent_class)->finalize (object); } @@ -826,6 +837,69 @@ soup_session_class_init (SoupSessionClass *session_class) "Remove features of the given type from the session", SOUP_TYPE_SESSION_FEATURE, G_PARAM_READWRITE)); + /** + * SoupSession:http-aliases: + * + * A %NULL-terminated array of URI schemes that should be + * considered to be aliases for "http". Eg, if this included + * "dav", than a URI of + * dav://example.com/path would be treated + * identically to http://example.com/path. + * If the value is %NULL, then only "http" is recognized as + * meaning "http". + * + * For backward-compatibility reasons, the default value for + * this property is an array containing the single element + * "*", a special value which means that + * any scheme except "https" is considered to be an alias for + * "http". + * + * See also #SoupSession:https-aliases. + * + * Since: 2.38 + */ + /** + * SOUP_SESSION_HTTP_ALIASES: + * + * Alias for the #SoupSession:http-aliases property. (URI + * schemes that will be considered aliases for "http".) + * + * Since: 2.38 + */ + g_object_class_install_property ( + object_class, PROP_HTTP_ALIASES, + g_param_spec_boxed (SOUP_SESSION_HTTP_ALIASES, + "http aliases", + "URI schemes that are considered aliases for 'http'", + G_TYPE_STRV, + G_PARAM_READWRITE)); + /** + * SoupSession:https-aliases: + * + * A comma-delimited list of URI schemes that should be + * considered to be aliases for "https". See + * #SoupSession:http-aliases for more information. + * + * The default value is %NULL, meaning that no URI schemes + * are considered aliases for "https". + * + * Since: 2.38 + */ + /** + * SOUP_SESSION_HTTPS_ALIASES: + * + * Alias for the #SoupSession:https-aliases property. (URI + * schemes that will be considered aliases for "https".) + * + * Since: 2.38 + **/ + g_object_class_install_property ( + object_class, PROP_HTTPS_ALIASES, + g_param_spec_boxed (SOUP_SESSION_HTTPS_ALIASES, + "https aliases", + "URI schemes that are considered aliases for 'https'", + G_TYPE_STRV, + G_PARAM_READWRITE)); } /* Converts a language in POSIX format and to be RFC2616 compliant */ @@ -939,6 +1013,23 @@ load_ssl_ca_file (SoupSessionPrivate *priv) g_error_free (error); } +/* priv->http_aliases and priv->https_aliases are stored as arrays of + * *interned* strings, so we can't just use g_strdupv() to set them. + */ +static void +set_aliases (char ***variable, char **value) +{ + int len = g_strv_length (value), i; + + if (*variable) + g_free (*variable); + + *variable = g_new (char *, len); + for (i = 0; i < len; i++) + (*variable)[i] = (char *)g_intern_string (value[i]); + (*variable)[i] = NULL; +} + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -1064,6 +1155,12 @@ set_property (GObject *object, guint prop_id, case PROP_REMOVE_FEATURE_BY_TYPE: soup_session_remove_feature_by_type (session, g_value_get_gtype (value)); break; + case PROP_HTTP_ALIASES: + set_aliases (&priv->http_aliases, g_value_get_boxed (value)); + break; + case PROP_HTTPS_ALIASES: + set_aliases (&priv->https_aliases, g_value_get_boxed (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1134,12 +1231,60 @@ get_property (GObject *object, guint prop_id, case PROP_IDLE_TIMEOUT: g_value_set_uint (value, priv->idle_timeout); break; + case PROP_HTTP_ALIASES: + g_value_set_boxed (value, priv->http_aliases); + break; + case PROP_HTTPS_ALIASES: + g_value_set_boxed (value, priv->https_aliases); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +static gboolean +uri_is_http (SoupSessionPrivate *priv, SoupURI *uri) +{ + int i; + + if (uri->scheme == SOUP_URI_SCHEME_HTTP) + return TRUE; + else if (uri->scheme == SOUP_URI_SCHEME_HTTPS) + return FALSE; + else if (!priv->http_aliases) + return FALSE; + + for (i = 0; priv->http_aliases[i]; i++) { + if (uri->scheme == priv->http_aliases[i]) + return TRUE; + } + + if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*")) + return TRUE; + else + return FALSE; +} + +static gboolean +uri_is_https (SoupSessionPrivate *priv, SoupURI *uri) +{ + int i; + + if (uri->scheme == SOUP_URI_SCHEME_HTTPS) + return TRUE; + else if (uri->scheme == SOUP_URI_SCHEME_HTTP) + return FALSE; + else if (!priv->https_aliases) + return FALSE; + + for (i = 0; priv->https_aliases[i]; i++) { + if (uri->scheme == priv->https_aliases[i]) + return TRUE; + } + + return FALSE; +} /** * soup_session_get_async_context: @@ -1252,6 +1397,7 @@ redirect_handler (SoupMessage *msg, gpointer user_data) { SoupMessageQueueItem *item = user_data; SoupSession *session = item->session; + SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session); const char *new_loc; SoupURI *new_uri; @@ -1320,6 +1466,22 @@ redirect_handler (SoupMessage *msg, gpointer user_data) return; } + /* If the URI is not "http" or "https" or a recognized alias, + * then we let the redirect response be returned to the caller. + */ + if (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri)) { + soup_uri_free (new_uri); + return; + } + + if (!new_uri->host) { + soup_uri_free (new_uri); + soup_message_set_status_full (msg, + SOUP_STATUS_MALFORMED, + "Invalid Redirect URL"); + return; + } + soup_message_set_uri (msg, new_uri); soup_uri_free (new_uri); @@ -1544,14 +1706,14 @@ soup_session_get_connection (SoupSession *session, } uri = soup_message_get_uri (item->msg); - if (uri->scheme == SOUP_URI_SCHEME_HTTPS && item->proxy_addr) + if (uri_is_https (priv, uri) && item->proxy_addr) tunnel_addr = host->addr; conn = soup_connection_new ( SOUP_CONNECTION_REMOTE_ADDRESS, remote_addr, SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr, SOUP_CONNECTION_PROXY_URI, item->proxy_uri, - SOUP_CONNECTION_SSL, uri->scheme == SOUP_URI_SCHEME_HTTPS, + SOUP_CONNECTION_SSL, uri_is_https (priv, uri), SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb, SOUP_CONNECTION_SSL_STRICT, (priv->tlsdb != NULL) && priv->ssl_strict, SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context, diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h index a7aab0e..6afbbd1 100644 --- a/libsoup/soup-session.h +++ b/libsoup/soup-session.h @@ -76,6 +76,8 @@ GType soup_session_get_type (void); #define SOUP_SESSION_ADD_FEATURE "add-feature" #define SOUP_SESSION_ADD_FEATURE_BY_TYPE "add-feature-by-type" #define SOUP_SESSION_REMOVE_FEATURE_BY_TYPE "remove-feature-by-type" +#define SOUP_SESSION_HTTP_ALIASES "http-aliases" +#define SOUP_SESSION_HTTPS_ALIASES "https-aliases" GMainContext *soup_session_get_async_context(SoupSession *session); -- 2.7.4