[libsoup] Implement acceptance policies in SoupCookieJar



commit 9deb863e27177ad47586b01be8b4e2ad91a62cc7
Author: Xan Lopez <xan gnome org>
Date:   Thu Jan 28 18:31:08 2010 +0200

    Implement acceptance policies in SoupCookieJar
    
    Through the "accept-policy" property in the jar we can now set one
    among three predetermined policies: accept all cookies
    (SOUP_COOKIE_JAR_ACCEPT_ALWAYS), accept none
    (SOUP_COOKIE_JAR_ACCEPT_NEVER) and only accept cookies set by the main
    document we are loading (SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY).
    
    A new property, "first-party", is added to SoupMessage so that the
    application using libsoup can inform us of what is the main document
    URI for each message that is requested.

 libsoup/soup-cookie-jar.c      |  166 +++++++++++++++++++++++++++++++++++++++-
 libsoup/soup-cookie-jar.h      |   46 +++++++-----
 libsoup/soup-cookie.c          |   25 +++++-
 libsoup/soup-cookie.h          |    3 +
 libsoup/soup-message-private.h |    2 +
 libsoup/soup-message.c         |   76 ++++++++++++++++++
 libsoup/soup-message.h         |    4 +
 tests/Makefile.am              |    7 +-
 tests/cookies-test.c           |  119 ++++++++++++++++++++++++++++
 9 files changed, 422 insertions(+), 26 deletions(-)
---
diff --git a/libsoup/soup-cookie-jar.c b/libsoup/soup-cookie-jar.c
index e5a80a6..8f0e62a 100644
--- a/libsoup/soup-cookie-jar.c
+++ b/libsoup/soup-cookie-jar.c
@@ -15,6 +15,7 @@
 #include "soup-cookie.h"
 #include "soup-cookie-jar.h"
 #include "soup-date.h"
+#include "soup-enum-types.h"
 #include "soup-marshal.h"
 #include "soup-message.h"
 #include "soup-session-feature.h"
@@ -57,6 +58,7 @@ enum {
 	PROP_0,
 
 	PROP_READ_ONLY,
+	PROP_ACCEPT_POLICY,
 
 	LAST_PROP
 };
@@ -65,6 +67,7 @@ typedef struct {
 	gboolean constructed, read_only;
 	GHashTable *domains, *serials;
 	guint serial;
+	SoupCookieJarAcceptPolicy accept_policy;
 } SoupCookieJarPrivate;
 #define SOUP_COOKIE_JAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR, SoupCookieJarPrivate))
 
@@ -82,6 +85,7 @@ soup_cookie_jar_init (SoupCookieJar *jar)
 					       soup_str_case_equal,
 					       g_free, NULL);
 	priv->serials = g_hash_table_new (NULL, NULL);
+	priv->accept_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
 }
 
 static void
@@ -158,6 +162,20 @@ soup_cookie_jar_class_init (SoupCookieJarClass *jar_class)
 				      "Whether or not the cookie jar is read-only",
 				      FALSE,
 				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * SOUP_COOKIE_JAR_ACCEPT_POLICY:
+	 *
+	 * Alias for the #SoupCookieJar:accept-policy property.
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_ACCEPT_POLICY,
+		g_param_spec_enum (SOUP_COOKIE_JAR_ACCEPT_POLICY,
+				   "Accept-policy",
+				   "The policy the jar should follow to accept or reject cookies",
+				   SOUP_TYPE_COOKIE_JAR_ACCEPT_POLICY,
+				   SOUP_COOKIE_JAR_ACCEPT_ALWAYS,
+				   G_PARAM_READWRITE));
 }
 
 static void
@@ -180,6 +198,9 @@ set_property (GObject *object, guint prop_id,
 	case PROP_READ_ONLY:
 		priv->read_only = g_value_get_boolean (value);
 		break;
+	case PROP_ACCEPT_POLICY:
+		priv->accept_policy = g_value_get_enum (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -197,6 +218,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_READ_ONLY:
 		g_value_set_boolean (value, priv->read_only);
 		break;
+	case PROP_ACCEPT_POLICY:
+		g_value_set_enum (value, priv->accept_policy);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -443,6 +467,12 @@ soup_cookie_jar_add_cookie (SoupCookieJar *jar, SoupCookie *cookie)
  * Adds @cookie to @jar, exactly as though it had appeared in a
  * Set-Cookie header returned from a request to @uri.
  *
+ * Keep in mind that if the #SoupCookieJarAcceptPolicy
+ * %SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY is set you'll need to use
+ * soup_cookie_jar_set_cookie_with_first_party(), otherwise the jar
+ * will have no way of knowing if the cookie is being set by a third
+ * party or not.
+ *
  * Since: 2.24
  **/
 void
@@ -450,6 +480,7 @@ soup_cookie_jar_set_cookie (SoupCookieJar *jar, SoupURI *uri,
 			    const char *cookie)
 {
 	SoupCookie *soup_cookie;
+	SoupCookieJarPrivate *priv;
 
 	g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
 	g_return_if_fail (uri != NULL);
@@ -458,6 +489,12 @@ soup_cookie_jar_set_cookie (SoupCookieJar *jar, SoupURI *uri,
 	if (!SOUP_URI_VALID_FOR_HTTP (uri))
 		return;
 
+	priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+	if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
+		return;
+
+	g_return_if_fail (priv->accept_policy != SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY);
+
 	soup_cookie = soup_cookie_parse (cookie, uri);
 	if (soup_cookie) {
 		/* will steal or free soup_cookie */
@@ -465,15 +502,78 @@ soup_cookie_jar_set_cookie (SoupCookieJar *jar, SoupURI *uri,
 	}
 }
 
+/**
+ * soup_cookie_jar_set_cookie_with_first_party:
+ * @jar: a #SoupCookieJar
+ * @uri: the URI setting the cookie
+ * @first_party: the URI for the main document
+ * @cookie: the stringified cookie to set
+ *
+ * Adds @cookie to @jar, exactly as though it had appeared in a
+ * Set-Cookie header returned from a request to @uri. @first_party
+ * will be used to reject cookies coming from third party resources in
+ * case such a security policy is set in the @jar.
+ *
+ * Since: 2.30
+ **/
+void
+soup_cookie_jar_set_cookie_with_first_party (SoupCookieJar *jar,
+					     SoupURI *uri,
+					     SoupURI *first_party,
+					     const char *cookie)
+{
+	SoupCookie *soup_cookie;
+	SoupCookieJarPrivate *priv;
+
+	g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+	g_return_if_fail (uri != NULL);
+	g_return_if_fail (first_party != NULL);
+	g_return_if_fail (cookie != NULL);
+
+	if (!SOUP_URI_VALID_FOR_HTTP (uri))
+		return;
+
+	priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+	if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
+		return;
+
+	soup_cookie = soup_cookie_parse (cookie, uri);
+	if (soup_cookie) {
+		if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS ||
+		    soup_cookie_domain_matches (soup_cookie, first_party->host)) {
+			/* will steal or free soup_cookie */
+			soup_cookie_jar_add_cookie (jar, soup_cookie);
+		} else {
+			soup_cookie_free (soup_cookie);
+		}
+	}
+}
+
 static void
 process_set_cookie_header (SoupMessage *msg, gpointer user_data)
 {
 	SoupCookieJar *jar = user_data;
+	SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
 	GSList *new_cookies, *nc;
 
+	if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
+		return;
+
 	new_cookies = soup_cookies_from_response (msg);
-	for (nc = new_cookies; nc; nc = nc->next)
-		soup_cookie_jar_add_cookie (jar, nc->data);
+	for (nc = new_cookies; nc; nc = nc->next) {
+		SoupURI *first_party = soup_message_get_first_party (msg);
+		
+		if (first_party == NULL &&
+		    priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY)
+			continue; /* Can't check anything */
+
+		if ((priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY &&
+		     soup_cookie_domain_matches (nc->data, first_party->host)) ||
+		    priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS)
+			soup_cookie_jar_add_cookie (jar, nc->data);
+		else
+			soup_cookie_free (nc->data);
+	}
 	g_slist_free (new_cookies);
 }
 
@@ -586,3 +686,65 @@ soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
 		}
 	}
 }
+
+/**
+ * SoupCookieJarAcceptPolicy:
+ * @SOUP_COOKIE_JAR_ACCEPT_ALWAYS: accept all cookies unconditionally.
+ * @SOUP_COOKIE_JAR_ACCEPT_NEVER: reject all cookies unconditionally.
+ * @SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY: accept all cookies set by
+ * the main document loaded in the application using libsoup. An
+ * example of the most common case, web browsers, would be: If
+ * http://www.example.com is the page loaded, accept all cookies set
+ * by example.com, but if a resource from http://www.third-party.com
+ * is loaded from that page reject any cookie that it could try to
+ * set. For libsoup to be able to tell apart first party cookies from
+ * the rest, the application must call soup_message_set_first_party()
+ * on each outgoing #SoupMessage, setting the #SoupURI of the main
+ * document. If no first party is set in a message when this policy is
+ * in effect, cookies will be assumed to be third party by default.
+ *
+**/
+
+/**
+ * soup_cookie_jar_get_accept_policy:
+ * @jar: a #SoupCookieJar
+ * 
+ * Returns: the #SoupCookieJarAcceptPolicy set in the @jar
+ *
+ * Since: 2.30
+ **/
+SoupCookieJarAcceptPolicy
+soup_cookie_jar_get_accept_policy (SoupCookieJar *jar)
+{
+	SoupCookieJarPrivate *priv;
+
+	g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), SOUP_COOKIE_JAR_ACCEPT_ALWAYS);
+
+	priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+	return priv->accept_policy;
+}
+
+/**
+ * soup_cookie_jar_set_accept_policy:
+ * @jar: a #SoupCookieJar
+ * @policy: a #SoupCookieJarAcceptPolicy
+ * 
+ * Sets @policy as the cookie acceptance policy for @jar.
+ *
+ * Since: 2.30
+ **/
+void
+soup_cookie_jar_set_accept_policy (SoupCookieJar *jar,
+				   SoupCookieJarAcceptPolicy policy)
+{
+	SoupCookieJarPrivate *priv;
+
+	g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+
+	priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+	if (priv->accept_policy != policy) {
+		priv->accept_policy = policy;
+		g_object_notify (G_OBJECT (jar), SOUP_COOKIE_JAR_ACCEPT_POLICY);
+	}
+}
diff --git a/libsoup/soup-cookie-jar.h b/libsoup/soup-cookie-jar.h
index 3b0c3c6..eab64bf 100644
--- a/libsoup/soup-cookie-jar.h
+++ b/libsoup/soup-cookie-jar.h
@@ -39,29 +39,37 @@ typedef struct {
 } SoupCookieJarClass;
 
 #define SOUP_COOKIE_JAR_READ_ONLY "read-only"
+#define SOUP_COOKIE_JAR_ACCEPT_POLICY "accept-policy"
 
-GType          soup_cookie_jar_get_type      (void);
-
-SoupCookieJar *soup_cookie_jar_new           (void);
+typedef enum {
+	SOUP_COOKIE_JAR_ACCEPT_ALWAYS,
+	SOUP_COOKIE_JAR_ACCEPT_NEVER,
+	SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
+} SoupCookieJarAcceptPolicy;
 
+GType                     soup_cookie_jar_get_type                    (void);
+SoupCookieJar *           soup_cookie_jar_new                         (void);
 #ifndef LIBSOUP_DISABLE_DEPRECATED
-void           soup_cookie_jar_save          (SoupCookieJar *jar);
+void                      soup_cookie_jar_save                        (SoupCookieJar             *jar);
 #endif
-
-char          *soup_cookie_jar_get_cookies   (SoupCookieJar *jar,
-					      SoupURI       *uri,
-					      gboolean       for_http);
-void           soup_cookie_jar_set_cookie    (SoupCookieJar *jar,
-					      SoupURI       *uri,
-					      const char    *cookie);
-
-void           soup_cookie_jar_add_cookie    (SoupCookieJar *jar,
-					      SoupCookie    *cookie);
-void           soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
-					      SoupCookie    *cookie);
-
-GSList        *soup_cookie_jar_all_cookies   (SoupCookieJar *jar);
-
+char          *           soup_cookie_jar_get_cookies                 (SoupCookieJar             *jar,
+								       SoupURI                   *uri,
+								       gboolean                   for_http);
+void                      soup_cookie_jar_set_cookie                  (SoupCookieJar             *jar,
+								       SoupURI                   *uri,
+								       const char                *cookie);
+void                      soup_cookie_jar_set_cookie_with_first_party (SoupCookieJar             *jar,
+								       SoupURI                   *uri,
+								       SoupURI                   *first_party,
+								       const char                *cookie);
+void                      soup_cookie_jar_add_cookie                  (SoupCookieJar             *jar,
+								       SoupCookie                *cookie);
+void                      soup_cookie_jar_delete_cookie               (SoupCookieJar             *jar,
+								       SoupCookie                *cookie);
+GSList        *           soup_cookie_jar_all_cookies                 (SoupCookieJar             *jar);
+void                      soup_cookie_jar_set_accept_policy           (SoupCookieJar             *jar,
+								       SoupCookieJarAcceptPolicy  policy);
+SoupCookieJarAcceptPolicy soup_cookie_jar_get_accept_policy           (SoupCookieJar             *jar);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-cookie.c b/libsoup/soup-cookie.c
index 8ac39dc..7f51496 100644
--- a/libsoup/soup-cookie.c
+++ b/libsoup/soup-cookie.c
@@ -116,11 +116,30 @@ soup_cookie_copy (SoupCookie *cookie)
 	return copy;
 }
 
-static gboolean
-domain_matches (const char *domain, const char *host)
+/**
+ * soup_cookie_domain_matches:
+ * @cookie: a #SoupCookie
+ * @host: a URI
+ *
+ * Checks if the @cookie's domain and @host match in the sense that
+ * @cookie should be sent when making a request to @host, or that
+ * @cookie should be accepted when receiving a response from @host.
+ * 
+ * Return value: %TRUE if the domains match, %FALSE otherwise
+ *
+ * Since: 2.30
+ **/
+gboolean
+soup_cookie_domain_matches (SoupCookie *cookie, const char *host)
 {
 	char *match;
 	int dlen;
+	const char *domain;
+
+	g_return_val_if_fail (cookie != NULL, FALSE);
+	g_return_val_if_fail (host != NULL, FALSE);
+
+	domain = cookie->domain;
 
 	if (!g_ascii_strcasecmp (domain, host))
 		return TRUE;
@@ -288,7 +307,7 @@ parse_one_cookie (const char *header, SoupURI *origin)
 	if (origin) {
 		/* Sanity-check domain */
 		if (cookie->domain) {
-			if (!domain_matches (cookie->domain, origin->host)) {
+			if (!soup_cookie_domain_matches (cookie, origin->host)) {
 				soup_cookie_free (cookie);
 				return NULL;
 			}
diff --git a/libsoup/soup-cookie.h b/libsoup/soup-cookie.h
index 34a2aa4..8bccd90 100644
--- a/libsoup/soup-cookie.h
+++ b/libsoup/soup-cookie.h
@@ -76,6 +76,9 @@ void        soup_cookies_free                   (GSList      *cookies);
 
 char       *soup_cookies_to_cookie_header       (GSList      *cookies);
 
+gboolean    soup_cookie_domain_matches          (SoupCookie  *cookie,
+						 const char  *host);
+
 G_END_DECLS
 
 #endif /* SOUP_COOKIE_H */
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index ee6221d..f279303 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -41,6 +41,8 @@ typedef struct {
 
 	GSList            *disabled_features;
 	GSList            *decoders;
+
+	SoupURI           *first_party;
 } SoupMessagePrivate;
 #define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE, SoupMessagePrivate))
 
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 4545e5c..5a0035f 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -119,6 +119,7 @@ enum {
 	PROP_SERVER_SIDE,
 	PROP_STATUS_CODE,
 	PROP_REASON_PHRASE,
+	PROP_FIRST_PARTY,
 
 	LAST_PROP
 };
@@ -158,6 +159,8 @@ finalize (GObject *object)
 
 	if (priv->uri)
 		soup_uri_free (priv->uri);
+	if (priv->first_party)
+		soup_uri_free (priv->first_party);
 	if (priv->addr)
 		g_object_unref (priv->addr);
 
@@ -576,6 +579,21 @@ soup_message_class_init (SoupMessageClass *message_class)
 				     "The HTTP response reason phrase",
 				     NULL,
 				     G_PARAM_READWRITE));
+
+	/**
+	 * SOUP_MESSAGE_FIRST_PARTY:
+	 *
+	 * Alias for the #SoupMessage:first-party property. (The
+	 * #SoupURI loaded in the application when the message was
+	 * queued.)
+	 **/
+	g_object_class_install_property (
+		object_class, PROP_FIRST_PARTY,
+		g_param_spec_boxed (SOUP_MESSAGE_FIRST_PARTY,
+				    "First party",
+				    "The URI loaded in the application when the message was requested.",
+				    SOUP_TYPE_URI,
+				    G_PARAM_READWRITE));
 }
 
 static void
@@ -612,6 +630,9 @@ set_property (GObject *object, guint prop_id,
 		soup_message_set_status_full (msg, msg->status_code,
 					      g_value_get_string (value));
 		break;
+	case PROP_FIRST_PARTY:
+		soup_message_set_first_party (msg, g_value_get_boxed (value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -647,6 +668,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_REASON_PHRASE:
 		g_value_set_string (value, msg->reason_phrase);
 		break;
+	case PROP_FIRST_PARTY:
+		g_value_set_boxed (value, priv->first_party);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1661,3 +1685,55 @@ soup_message_disables_feature (SoupMessage *msg, gpointer feature)
 	}
 	return FALSE;
 }
+
+/**
+ * soup_message_get_first_party:
+ * @msg: a #SoupMessage
+ * 
+ * Returns: the @msg's first party #SoupURI
+ * 
+ * Since: 2.30
+ **/
+SoupURI*
+soup_message_get_first_party (SoupMessage *msg)
+{
+	SoupMessagePrivate *priv;
+
+	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+	priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+	return priv->first_party;
+}
+
+/**
+ * soup_message_set_first_party:
+ * @msg: a #SoupMessage
+ * @first_party: the #SoupURI for the @msg's first party
+ * 
+ * Sets @first_party as the main document #SoupURI for @msg. For
+ * details of when and how this is used refer to the documentation for
+ * #SoupCookieJarAcceptPolicy.
+ *
+ * Since: 2.30
+ **/
+void
+soup_message_set_first_party (SoupMessage *msg,
+			      SoupURI     *first_party)
+{
+	SoupMessagePrivate *priv;
+
+	g_return_if_fail (SOUP_IS_MESSAGE (msg));
+	g_return_if_fail (first_party != NULL);
+
+	priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+	if (priv->first_party) {
+		if (soup_uri_equal (priv->first_party, first_party))
+			return;
+
+		soup_uri_free (priv->first_party);
+	}
+
+	priv->first_party = soup_uri_copy (first_party);
+	g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FIRST_PARTY);
+}
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 4fc9122..2638620 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -67,6 +67,7 @@ GType soup_message_get_type (void);
 #define SOUP_MESSAGE_SERVER_SIDE   "server-side"
 #define SOUP_MESSAGE_STATUS_CODE   "status-code"
 #define SOUP_MESSAGE_REASON_PHRASE "reason-phrase"
+#define SOUP_MESSAGE_FIRST_PARTY   "first-party"
 
 SoupMessage   *soup_message_new                 (const char        *method,
 						 const char        *uri_string);
@@ -100,6 +101,9 @@ void             soup_message_set_uri             (SoupMessage       *msg,
 						   SoupURI           *uri);
 SoupAddress     *soup_message_get_address         (SoupMessage       *msg);
 
+SoupURI         *soup_message_get_first_party     (SoupMessage       *msg);
+void             soup_message_set_first_party     (SoupMessage       *msg,
+						   SoupURI           *first_party);
 typedef enum {
 	SOUP_MESSAGE_NO_REDIRECT      = (1 << 1),
 #ifndef LIBSOUP_DISABLE_DEPRECATED
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 194d151..b69d27a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -17,6 +17,7 @@ noinst_PROGRAMS =	\
 	coding-test	\
 	context-test	\
 	continue-test	\
+	cookies-test    \
 	date		\
 	dns		\
 	forms-test	\
@@ -33,7 +34,7 @@ noinst_PROGRAMS =	\
 	timeout-test	\
 	uri-parsing	\
 	$(CURL_TESTS)	\
-	$(APACHE_TESTS)	\
+	$(APACHE_TESTS) \
 	$(SSL_TESTS)	\
 	$(XMLRPC_TESTS)
 
@@ -44,6 +45,7 @@ chunk_test_SOURCES = chunk-test.c $(TEST_SRCS)
 coding_test_SOURCES = coding-test.c $(TEST_SRCS)
 context_test_SOURCES = context-test.c $(TEST_SRCS)
 continue_test_SOURCES = continue-test.c $(TEST_SRCS)
+cookies_test_SOURCES = cookies-test.c $(TEST_SRCS)
 date_SOURCES = date.c $(TEST_SRCS)
 dns_SOURCES = dns.c
 forms_test_SOURCES = forms-test.c $(TEST_SRCS)
@@ -88,6 +90,7 @@ TESTS =			\
 	coding-test	\
 	context-test	\
 	continue-test	\
+	cookies-test	\
 	date		\
 	header-parsing	\
 	misc-test	\
@@ -97,7 +100,7 @@ TESTS =			\
 	streaming-test	\
 	timeout-test	\
 	uri-parsing	\
-	$(APACHE_TESTS)	\
+	$(APACHE_TESTS) \
 	$(CURL_TESTS)	\
 	$(SSL_TESTS)	\
 	$(XMLRPC_TESTS)
diff --git a/tests/cookies-test.c b/tests/cookies-test.c
new file mode 100644
index 0000000..4e0c4d1
--- /dev/null
+++ b/tests/cookies-test.c
@@ -0,0 +1,119 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ */
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+SoupServer *server;
+SoupURI *first_party_uri, *third_party_uri;
+const char *first_party = "http://127.0.0.1/";;
+const char *third_party = "http://localhost/";;
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+		 const char *path, GHashTable *query,
+		 SoupClientContext *context, gpointer data)
+{
+    if (g_str_equal(path, "/index.html"))
+	soup_message_headers_replace (msg->response_headers,
+				      "Set-Cookie",
+				      "foo=bar");
+    else if (g_str_equal (path, "/foo.jpg"))
+	soup_message_headers_replace (msg->response_headers,
+				      "Set-Cookie",
+				      "baz=qux");
+    else
+	g_return_if_reached ();
+
+    soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+typedef struct {
+    SoupCookieJarAcceptPolicy policy;
+    int n_cookies;
+} CookiesForPolicy;
+
+static const CookiesForPolicy validResults[] = {
+    { SOUP_COOKIE_JAR_ACCEPT_ALWAYS, 2 },
+    { SOUP_COOKIE_JAR_ACCEPT_NEVER, 0 },
+    { SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, 1 }
+};
+
+static void
+do_cookies_accept_policy_test (void)
+{
+	SoupSession *session;
+	SoupMessage *msg;
+	SoupURI *uri;
+	SoupCookieJar *jar;
+	GSList *l, *p;
+	int i;
+
+	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+	soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
+	jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR));
+
+	for (i = 0; i < G_N_ELEMENTS (validResults); i++) {
+	    soup_cookie_jar_set_accept_policy (jar, validResults[i].policy);
+
+	    uri = soup_uri_new_with_base (first_party_uri, "/index.html");
+	    msg = soup_message_new_from_uri ("GET", uri);
+	    soup_message_set_first_party (msg, first_party_uri);
+	    soup_session_send_message (session, msg);
+	    soup_uri_free (uri);
+	    g_object_unref (msg);
+
+	    /* We can't use to servers due to limitations in
+	     * test_server, so let's swap first and third party here
+	     * to simulate a cookie coming from a third party.
+	     */
+	    uri = soup_uri_new_with_base (first_party_uri, "/foo.jpg");
+	    msg = soup_message_new_from_uri ("GET", uri);
+	    soup_message_set_first_party (msg, third_party_uri);
+	    soup_session_send_message (session, msg);
+	    soup_uri_free (uri);
+	    g_object_unref (msg);
+
+	    l = soup_cookie_jar_all_cookies (jar);
+	    if (g_slist_length (l) < validResults[i].n_cookies) {
+		    debug_printf (1, " accepted less cookies than it should have\n");
+		    errors++;
+	    } else if (g_slist_length (l) > validResults[i].n_cookies) {
+		    debug_printf (1, " accepted more cookies than it should have\n");
+		    errors++;
+	    }
+
+	    for (p = l; p; p = p->next)
+		soup_cookie_jar_delete_cookie (jar, p->data);
+
+	    g_slist_free (l);
+	}
+
+	soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+	test_init (argc, argv, NULL);
+
+	server = soup_test_server_new (TRUE);
+	soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+	first_party_uri = soup_uri_new (first_party);
+	third_party_uri = soup_uri_new (third_party);
+	soup_uri_set_port (first_party_uri, soup_server_get_port (server));
+	soup_uri_set_port (third_party_uri, soup_server_get_port (server));
+
+	do_cookies_accept_policy_test ();
+
+	soup_uri_free (first_party_uri);
+	soup_uri_free (third_party_uri);
+
+	test_cleanup ();
+
+	return errors != 0;
+}



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