[libsoup] New accept-language-auto property for SoupSession



commit b3c60a3706682226c9ae10079ba58b7284757711
Author: Mario Sanchez Prada <msanchez igalia com>
Date:   Wed Dec 16 15:17:01 2009 +0100

    New accept-language-auto property for SoupSession
    
    Provide a new way for SoupSession to assume an 'Accept-Language'
    string automatically from the return value of g_get_language_names(),
    properly formatted according to RFC2616.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=597004

 libsoup/soup-session.c |  145 ++++++++++++++++++++++++++++++++++++++++++++++-
 libsoup/soup-session.h |   21 ++++---
 2 files changed, 152 insertions(+), 14 deletions(-)
---
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 48f336e..2dc6d1f 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -76,6 +76,7 @@ typedef struct {
 
 	char *user_agent;
 	char *accept_language;
+	gboolean accept_language_auto;
 
 	GSList *features;
 	GHashTable *features_cache;
@@ -143,6 +144,7 @@ enum {
 	PROP_TIMEOUT,
 	PROP_USER_AGENT,
 	PROP_ACCEPT_LANGUAGE,
+	PROP_ACCEPT_LANGUAGE_AUTO,
 	PROP_IDLE_TIMEOUT,
 	PROP_ADD_FEATURE,
 	PROP_ADD_FEATURE_BY_TYPE,
@@ -592,6 +594,27 @@ soup_session_class_init (SoupSessionClass *session_class)
 				     G_PARAM_READWRITE));
 
 	/**
+	 * SoupSession:accept-language-auto:
+	 *
+	 * If %TRUE, #SoupSession will automatically set the string
+	 * for the "Accept-Language" header on every #SoupMessage
+	 * sent, based on the return value of g_get_language_names().
+	 *
+	 **/
+	/**
+	 * SOUP_SESSION_ACCEPT_LANGUAGE_AUTO:
+	 *
+	 * Alias for the #SoupSession:accept-language-auto property, qv.
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_ACCEPT_LANGUAGE_AUTO,
+		g_param_spec_boolean (SOUP_SESSION_ACCEPT_LANGUAGE_AUTO,
+				      "Accept-Language automatic mode",
+				      "Accept-Language automatic mode",
+				      FALSE,
+				      G_PARAM_READWRITE));
+
+	/**
 	 * SoupSession:add-feature:
 	 *
 	 * Add a feature object to the session. (Shortcut for calling
@@ -675,6 +698,103 @@ safe_str_equal (const char *a, const char *b)
 	return strcmp (a, b) == 0;
 }
 
+/* Converts a language in POSIX format and to be RFC2616 compliant    */
+/* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
+static gchar *
+posix_lang_to_rfc2616 (const gchar *language)
+{
+	char *lang = NULL;
+
+	if (strstr (language, ".") == 0 &&
+	    strstr (language, "@") == 0 &&
+	    strcmp (language, "C") != 0)
+	{
+		/* change to lowercase and '_' to '-' */
+		lang = g_strdelimit (g_ascii_strdown
+				     (language, -1), "_", '-');
+	}
+
+	return lang;
+}
+
+/* Adds a quality value to a string (any value between 0 and 1). */
+static gchar *
+add_quality_value (const gchar *str, float qvalue)
+{
+	char *qv_str = NULL;
+
+	g_return_val_if_fail (str != NULL, NULL);
+
+	if ((qvalue >= 0.0) && (qvalue <= 1.0)) {
+		int qv_int = (qvalue * 1000 + 0.5);
+		qv_str = g_strdup_printf ("%s;q=%d.%d",
+					  str,
+					  (int) (qv_int / 1000),
+					  qv_int % 1000);
+	} else {
+		/* Just dup the string in this case */
+		qv_str = g_strdup (str);
+	}
+
+	return qv_str;
+}
+
+/* Returns a RFC2616 compliant languages list from system locales */
+static gchar *
+accept_languages_from_system (void)
+{
+	const char * const * lang_names;
+	GArray *langs_garray = NULL;
+	char *cur_lang = NULL;
+	char *prev_lang = NULL;
+	char **langs_array;
+	char *langs_str;
+	float delta;
+	int i, n_lang_names;
+
+	lang_names = g_get_language_names ();
+	g_return_val_if_fail (lang_names != NULL, NULL);
+
+	/* Calculate delta for setting the quality values */
+	n_lang_names = g_strv_length ((gchar **)lang_names);
+	delta = 0.999 / (n_lang_names - 1);
+
+	/* Build the array of languages */
+	langs_garray = g_array_new (TRUE, FALSE, sizeof (char*));
+	for (i = 0; lang_names[i] != NULL; i++) {
+		cur_lang = posix_lang_to_rfc2616 (lang_names[i]);
+
+		/* Apart from getting a valid RFC2616 compliant
+		 * language, also get rid of extra variants
+		 */
+		if ((cur_lang != NULL) &&
+		    (!prev_lang ||
+		     (!g_strcmp0 (prev_lang, cur_lang) ||
+		      !g_strstr_len (prev_lang, -1, cur_lang)))) {
+
+			gchar *qv_lang = NULL;
+
+			/* Save reference for further comparison */
+			prev_lang = cur_lang;
+
+			/* Add the quality value and append it */
+			qv_lang = add_quality_value (cur_lang, 1 - i * delta);
+			g_array_append_val (langs_garray, qv_lang);
+		}
+	}
+
+	/* Fallback: add "en" if list is empty */
+	if (langs_garray->len == 0) {
+		char *default_lang = g_strdup ("en");
+		g_array_append_val (langs_garray, default_lang);
+	}
+
+	langs_array = (char **) g_array_free (langs_garray, FALSE);
+	langs_str = g_strjoinv (", ", langs_array);
+
+	return langs_str;
+}
+
 static void
 set_property (GObject *object, guint prop_id,
 	      const GValue *value, GParamSpec *pspec)
@@ -756,6 +876,18 @@ set_property (GObject *object, guint prop_id,
 	case PROP_ACCEPT_LANGUAGE:
 		g_free (priv->accept_language);
 		priv->accept_language = g_strdup (g_value_get_string (value));
+		priv->accept_language_auto = FALSE;
+		break;
+	case PROP_ACCEPT_LANGUAGE_AUTO:
+		priv->accept_language_auto = g_value_get_boolean (value);
+		if (priv->accept_language) {
+			g_free (priv->accept_language);
+			priv->accept_language = NULL;
+		}
+
+		/* Get languages from system if needed */
+		if (priv->accept_language_auto)
+			priv->accept_language = accept_languages_from_system ();
 		break;
 	case PROP_IDLE_TIMEOUT:
 		priv->idle_timeout = g_value_get_uint (value);
@@ -823,6 +955,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_ACCEPT_LANGUAGE:
 		g_value_set_string (value, priv->accept_language);
 		break;
+	case PROP_ACCEPT_LANGUAGE_AUTO:
+		g_value_set_boolean (value, priv->accept_language_auto);
+		break;
 	case PROP_IDLE_TIMEOUT:
 		g_value_set_uint (value, priv->idle_timeout);
 		break;
@@ -1021,10 +1156,12 @@ soup_session_send_queue_item (SoupSession *session,
 					      "User-Agent", priv->user_agent);
 	}
 
-	if (priv->accept_language) {
-		soup_message_headers_replace (item->msg->request_headers,
-					      "Accept-Language",
-					      priv->accept_language);
+	if (priv->accept_language &&
+	    !soup_message_headers_get_list (item->msg->request_headers,
+					    "Accept-Language")) {
+		soup_message_headers_append (item->msg->request_headers,
+					     "Accept-Language",
+					     priv->accept_language);
 	}
 
 	g_signal_emit (session, signals[REQUEST_STARTED], 0,
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index c7873e7..056799d 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -58,16 +58,17 @@ typedef struct {
 
 GType soup_session_get_type (void);
 
-#define SOUP_SESSION_PROXY_URI          "proxy-uri"
-#define SOUP_SESSION_MAX_CONNS          "max-conns"
-#define SOUP_SESSION_MAX_CONNS_PER_HOST "max-conns-per-host"
-#define SOUP_SESSION_USE_NTLM           "use-ntlm"
-#define SOUP_SESSION_SSL_CA_FILE        "ssl-ca-file"
-#define SOUP_SESSION_ASYNC_CONTEXT      "async-context"
-#define SOUP_SESSION_TIMEOUT            "timeout"
-#define SOUP_SESSION_USER_AGENT         "user-agent"
-#define SOUP_SESSION_ACCEPT_LANGUAGE    "accept-language"
-#define SOUP_SESSION_IDLE_TIMEOUT       "idle-timeout"
+#define SOUP_SESSION_PROXY_URI              "proxy-uri"
+#define SOUP_SESSION_MAX_CONNS              "max-conns"
+#define SOUP_SESSION_MAX_CONNS_PER_HOST     "max-conns-per-host"
+#define SOUP_SESSION_USE_NTLM               "use-ntlm"
+#define SOUP_SESSION_SSL_CA_FILE            "ssl-ca-file"
+#define SOUP_SESSION_ASYNC_CONTEXT          "async-context"
+#define SOUP_SESSION_TIMEOUT                "timeout"
+#define SOUP_SESSION_USER_AGENT             "user-agent"
+#define SOUP_SESSION_ACCEPT_LANGUAGE        "accept-language"
+#define SOUP_SESSION_ACCEPT_LANGUAGE_AUTO   "accept-language-auto"
+#define SOUP_SESSION_IDLE_TIMEOUT           "idle-timeout"
 #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"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]