libsoup r1200 - in trunk: . libsoup



Author: danw
Date: Tue Nov  4 20:30:37 2008
New Revision: 1200
URL: http://svn.gnome.org/viewvc/libsoup?rev=1200&view=rev

Log:
	* libsoup/soup-cookie-jar-text.c: implementation of SoupCookieJar
	that persists to a text file in the old Mozilla cookies.txt
	format.

	* libsoup/soup-cookie-jar-sqlite.c: implementation of
	SoupCookieJar that persists to an sqlite database in the new
	Mozilla cookies.sqlite format. (Part of libsoup-gnome.)

	* libsoup/soup-cookie-jar.c: add various functionality needed by
	the two new subclasses. Does not break API/ABI compat with 2.24.

	* libsoup/soup-cookie.c (soup_cookie_get_type): register
	SoupCookie as a boxed type.
	(domain_matches): fix a bug here that meant "foo.com" couldn't set
	a cookie for domain=.foo.com
	(soup_cookie_applies_to_uri): fix path checking

	* configure.in: if building --with-gnome, require sqlite3


Added:
   trunk/libsoup/soup-cookie-jar-sqlite.c
   trunk/libsoup/soup-cookie-jar-sqlite.h
   trunk/libsoup/soup-cookie-jar-text.c
   trunk/libsoup/soup-cookie-jar-text.h
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/libsoup/Makefile.am
   trunk/libsoup/soup-cookie-jar.c
   trunk/libsoup/soup-cookie-jar.h
   trunk/libsoup/soup-cookie.c
   trunk/libsoup/soup-cookie.h
   trunk/libsoup/soup-gnome.h
   trunk/libsoup/soup-marshal.list
   trunk/libsoup/soup.h

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Tue Nov  4 20:30:37 2008
@@ -199,6 +199,13 @@
 One or the other is required for GNOME proxy support.
 Pass "--without-gnome" to configure if you want to build without GNOME support.])])
 	fi
+
+	PKG_CHECK_MODULES(SQLITE, sqlite3, :, [AC_MSG_ERROR(dnl
+[Could not find sqlite3 devel files:
+
+$SQLITE_PKG_ERRORS
+
+Pass "--without-gnome" to configure if you want to build without GNOME support.])])
 fi
 AC_SUBST(HAVE_GNOME)
 AC_SUBST(GCONF_CFLAGS)
@@ -207,6 +214,8 @@
 AC_SUBST(LIBPROXY_CFLAGS)
 AC_SUBST(LIBPROXY_LIBS)
 AM_CONDITIONAL(WITH_LIBPROXY, [test -n "$LIBPROXY_LIBS"])
+AC_SUBST(SQLITE_CFLAGS)
+AC_SUBST(SQLITE_LIBS)
 
 
 dnl ***************

Modified: trunk/libsoup/Makefile.am
==============================================================================
--- trunk/libsoup/Makefile.am	(original)
+++ trunk/libsoup/Makefile.am	Tue Nov  4 20:30:37 2008
@@ -13,6 +13,7 @@
 	$(XML_CFLAGS)			\
 	$(GCONF_CFLAGS)			\
 	$(LIBPROXY_CFLAGS)		\
+	$(SQLITE_CFLAGS)		\
 	$(LIBGCRYPT_CFLAGS)		\
 	$(LIBGNUTLS_CFLAGS)
 
@@ -56,6 +57,7 @@
 	soup-auth-domain-digest.h \
 	soup-cookie.h		\
 	soup-cookie-jar.h	\
+	soup-cookie-jar-text.h	\
 	soup-date.h		\
 	soup-form.h		\
 	soup-headers.h		\
@@ -118,6 +120,7 @@
 	soup-connection.c		\
 	soup-cookie.c			\
 	soup-cookie-jar.c		\
+	soup-cookie-jar-text.c		\
 	soup-date.c			\
 	soup-dns.h			\
 	soup-dns.c			\
@@ -161,6 +164,7 @@
 libsoupgnomeincludedir = $(libsoupincludedir)
 
 libsoupgnomeinclude_HEADERS =	\
+	soup-cookie-jar-sqlite.h\
 	soup-gnome.h		\
 	soup-gnome-features.h
 
@@ -168,9 +172,12 @@
 
 libsoup_gnome_2_4_la_LDFLAGS = $(libsoup_2_4_la_LDFLAGS)
 
-libsoup_gnome_2_4_la_LIBADD = libsoup-2.4.la
+libsoup_gnome_2_4_la_LIBADD =		\
+	libsoup-2.4.la			\
+	$(SQLITE_LIBS)
 
 libsoup_gnome_2_4_la_SOURCES =		\
+	soup-cookie-jar-sqlite.c	\
 	soup-gnome-features.c
 
 if WITH_LIBPROXY

Added: trunk/libsoup/soup-cookie-jar-sqlite.c
==============================================================================
--- (empty file)
+++ trunk/libsoup/soup-cookie-jar-sqlite.c	Tue Nov  4 20:30:37 2008
@@ -0,0 +1,286 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cookie-jar-sqlite.c: ff sqlite-based cookie storage
+ *
+ * Using danw's soup-cookie-jar-text as template
+ * Copyright (C) 2008 Diego Escalante Urrelo
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sqlite3.h>
+
+#include "soup-cookie-jar-sqlite.h"
+#include "soup-cookie.h"
+#include "soup-date.h"
+
+/**
+ * SECTION:soup-cookie-jar-sqlite
+ * @short_description: SQLite-based Cookie Jar
+ *
+ * #SoupCookieJarSqlite is a #SoupCookieJar that reads cookies from and
+ * writes them to an SQLite file in the new Mozilla format.
+ **/
+
+enum {
+	PROP_0,
+
+	PROP_FILENAME,
+
+	LAST_PROP
+};
+
+typedef struct {
+	char *filename;
+
+} SoupCookieJarSqlitePrivate;
+
+#define SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqlitePrivate))
+
+G_DEFINE_TYPE (SoupCookieJarSqlite, soup_cookie_jar_sqlite, SOUP_TYPE_COOKIE_JAR)
+
+static void load (SoupCookieJar *jar);
+static void changed (SoupCookieJar *jar,
+		     SoupCookie    *old_cookie,
+		     SoupCookie    *new_cookie);
+
+static void set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec);
+
+static void
+soup_cookie_jar_sqlite_init (SoupCookieJarSqlite *sqlite)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+	SoupCookieJarSqlitePrivate *priv =
+		SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
+
+	g_free (priv->filename);
+
+	G_OBJECT_CLASS (soup_cookie_jar_sqlite_parent_class)->finalize (object);
+}
+
+static void
+soup_cookie_jar_sqlite_class_init (SoupCookieJarSqliteClass *sqlite_class)
+{
+	SoupCookieJarClass *cookie_jar_class =
+		SOUP_COOKIE_JAR_CLASS (sqlite_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (sqlite_class);
+
+	g_type_class_add_private (sqlite_class, sizeof (SoupCookieJarSqlitePrivate));
+
+	cookie_jar_class->changed = changed;
+
+	object_class->finalize     = finalize;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+
+	g_object_class_install_property (
+		object_class, PROP_FILENAME,
+		g_param_spec_string (SOUP_COOKIE_JAR_SQLITE_FILENAME,
+				     "Filename",
+				     "Cookie-storage filename",
+				     NULL,
+				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+	      const GValue *value, GParamSpec *pspec)
+{
+	SoupCookieJarSqlitePrivate *priv =
+		SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		priv->filename = g_value_dup_string (value);
+		load (SOUP_COOKIE_JAR (object));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+	      GValue *value, GParamSpec *pspec)
+{
+	SoupCookieJarSqlitePrivate *priv =
+		SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		g_value_set_string (value, priv->filename);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
+ * soup_cookie_jar_sqlite_new:
+ * @filename: the filename to read to/write from, or %NULL
+ * @read_only: %TRUE if @filename is read-only
+ *
+ * Creates a #SoupCookieJarSqlite.
+ *
+ * @filename will be read in at startup to create an initial set of
+ * cookies. If @read_only is %FALSE, then the non-session cookies will
+ * be written to @filename when the 'changed' signal is emitted from
+ * the jar. (If @read_only is %TRUE, then the cookie jar will only be
+ * used for this session, and changes made to it will be lost when the
+ * jar is destroyed.)
+ *
+ * Return value: the new #SoupCookieJar
+ **/
+SoupCookieJar *
+soup_cookie_jar_sqlite_new (const char *filename, gboolean read_only)
+{
+	g_return_val_if_fail (filename != NULL, NULL);
+
+	return g_object_new (SOUP_TYPE_COOKIE_JAR_SQLITE,
+			     SOUP_COOKIE_JAR_SQLITE_FILENAME, filename,
+			     SOUP_COOKIE_JAR_READ_ONLY, read_only,
+			     NULL);
+}
+
+#define QUERY_ALL "SELECT * FROM moz_cookies;"
+#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
+#define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND domain=%Q;"
+
+enum {
+	COL_ID,
+	COL_NAME,
+	COL_VALUE,
+	COL_HOST,
+	COL_PATH,
+	COL_EXPIRY,
+	COL_LAST_ACCESS,
+	COL_SECURE,
+	COL_HTTP_ONLY,
+	N_COL,
+};
+
+static int
+callback (void *data, int argc, char **argv, char **colname)
+{
+	SoupCookie *cookie = NULL;
+	SoupCookieJar *jar = SOUP_COOKIE_JAR (data);
+
+	char *name, *value, *host, *path;
+	time_t max_age, now;
+	gboolean http_only = FALSE, secure = FALSE;
+
+	now = time (NULL);
+
+	name = argv[COL_NAME];
+	value = argv[COL_VALUE];
+	host = argv[COL_HOST];
+	path = argv[COL_PATH];
+	max_age = strtoul (argv[COL_EXPIRY], NULL, 10) - now;
+
+	if (max_age <= 0)
+		return 0;
+
+	http_only = (strcmp (argv[COL_HTTP_ONLY], "FALSE") != 0);
+	secure = (strcmp (argv[COL_SECURE], "FALSE") != 0);
+
+	cookie = soup_cookie_new (name, value, host, path, max_age);
+
+	if (secure)
+		soup_cookie_set_secure (cookie, TRUE);
+	if (http_only)
+		soup_cookie_set_http_only (cookie, TRUE);
+
+	soup_cookie_jar_add_cookie (jar, cookie);
+
+	return 0;
+}
+
+static void
+load (SoupCookieJar *jar)
+{
+	SoupCookieJarSqlitePrivate *priv =
+		SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
+
+	sqlite3 *db;
+	char *error = 0;
+
+	if (sqlite3_open (priv->filename, &db)) {
+		sqlite3_close (db);
+		g_debug ("Can't open %s", priv->filename);
+	}
+
+	if (sqlite3_exec (db, QUERY_ALL, callback, (void *)jar, &error)) {
+		g_debug ("Failed to execute query: %s", error);
+		sqlite3_free (error);
+	}
+
+	sqlite3_close (db);
+}
+
+static void
+changed (SoupCookieJar *jar,
+	 SoupCookie    *old_cookie,
+	 SoupCookie    *new_cookie)
+{
+	SoupCookieJarSqlitePrivate *priv =
+		SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
+	sqlite3 *db;
+	char *error = NULL;
+	char *query;
+
+	if (sqlite3_open (priv->filename, &db)) {
+		sqlite3_close (db);
+		g_warning ("Can't open %s", priv->filename);
+		return;
+	}
+
+	if (old_cookie) {
+		query = sqlite3_mprintf (QUERY_DELETE,
+					 old_cookie->name,
+					 old_cookie->domain);
+		if (sqlite3_exec (db, query, NULL, NULL, &error)) {
+			g_warning ("Failed to execute query: %s", error);
+			sqlite3_free (error);
+		}
+		sqlite3_free (query);
+	}
+
+	if (new_cookie) {
+		int expires;
+
+		if (new_cookie->expires)
+			expires = soup_date_to_time_t (new_cookie->expires);
+		else
+			expires = 0;
+
+		query = sqlite3_mprintf (QUERY_INSERT, 
+					 new_cookie->name,
+					 new_cookie->value,
+					 new_cookie->domain,
+					 new_cookie->path,
+					 expires,
+					 new_cookie->secure,
+					 new_cookie->http_only);
+		if (sqlite3_exec (db, query, NULL, NULL, &error)) {
+			g_warning ("Failed to execute query: %s", error);
+			sqlite3_free (error);
+		}
+		sqlite3_free (query);
+	}
+
+	sqlite3_close (db);
+}

Added: trunk/libsoup/soup-cookie-jar-sqlite.h
==============================================================================
--- (empty file)
+++ trunk/libsoup/soup-cookie-jar-sqlite.h	Tue Nov  4 20:30:37 2008
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Diego Escalante Urrelo
+ */
+
+#ifndef SOUP_COOKIE_JAR_SQLITE_H
+#define SOUP_COOKIE_JAR_SQLITE_H 1
+
+#include <libsoup/soup-cookie-jar.h>
+
+#define SOUP_TYPE_COOKIE_JAR_SQLITE            (soup_cookie_jar_sqlite_get_type ())
+#define SOUP_COOKIE_JAR_SQLITE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqlite))
+#define SOUP_COOKIE_JAR_SQLITE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqliteClass))
+#define SOUP_IS_COOKIE_JAR_SQLITE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE))
+#define SOUP_IS_COOKIE_JAR_SQLITE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE))
+#define SOUP_COOKIE_JAR_SQLITE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqliteClass))
+
+typedef struct {
+	SoupCookieJar parent;
+
+} SoupCookieJarSqlite;
+
+typedef struct {
+	SoupCookieJarClass parent_class;
+
+	/* Padding for future expansion */
+	void (*_libsoup_reserved1) (void);
+	void (*_libsoup_reserved2) (void);
+	void (*_libsoup_reserved3) (void);
+	void (*_libsoup_reserved4) (void);
+} SoupCookieJarSqliteClass;
+
+#define SOUP_COOKIE_JAR_SQLITE_FILENAME  "filename"
+
+GType soup_cookie_jar_sqlite_get_type (void);
+
+SoupCookieJar *soup_cookie_jar_sqlite_new (const char *filename,
+					 gboolean    read_only);
+
+#endif /* SOUP_COOKIE_JAR_SQLITE_H */

Added: trunk/libsoup/soup-cookie-jar-text.c
==============================================================================
--- (empty file)
+++ trunk/libsoup/soup-cookie-jar-text.c	Tue Nov  4 20:30:37 2008
@@ -0,0 +1,349 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cookie-jar-text.c: cookies.txt-based cookie storage
+ *
+ * Copyright (C) 2007, 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-cookie-jar-text.h"
+#include "soup-cookie.h"
+#include "soup-date.h"
+
+/**
+ * SECTION:soup-cookie-jar-text
+ * @short_description: Text-file-based ("cookies.txt") Cookie Jar
+ *
+ * #SoupCookieJarText is a #SoupCookieJar that reads cookies from and
+ * writes them to a text file in the Mozilla "cookies.txt" format.
+ **/
+
+enum {
+	PROP_0,
+
+	PROP_FILENAME,
+
+	LAST_PROP
+};
+
+typedef struct {
+	char *filename;
+
+} SoupCookieJarTextPrivate;
+#define SOUP_COOKIE_JAR_TEXT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarTextPrivate))
+
+G_DEFINE_TYPE (SoupCookieJarText, soup_cookie_jar_text, SOUP_TYPE_COOKIE_JAR)
+
+static void load    (SoupCookieJar *jar);
+static void changed (SoupCookieJar *jar,
+		     SoupCookie    *old_cookie,
+		     SoupCookie    *new_cookie);
+
+static void set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec);
+
+static void
+soup_cookie_jar_text_init (SoupCookieJarText *text)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+	SoupCookieJarTextPrivate *priv =
+		SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
+
+	g_free (priv->filename);
+
+	G_OBJECT_CLASS (soup_cookie_jar_text_parent_class)->finalize (object);
+}
+
+static void
+soup_cookie_jar_text_class_init (SoupCookieJarTextClass *text_class)
+{
+	SoupCookieJarClass *cookie_jar_class =
+		SOUP_COOKIE_JAR_CLASS (text_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (text_class);
+
+	g_type_class_add_private (text_class, sizeof (SoupCookieJarTextPrivate));
+
+	cookie_jar_class->changed = changed;
+
+	object_class->finalize     = finalize;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+
+	g_object_class_install_property (
+		object_class, PROP_FILENAME,
+		g_param_spec_string (SOUP_COOKIE_JAR_TEXT_FILENAME,
+				     "Filename",
+				     "Cookie-storage filename",
+				     NULL,
+				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+	      const GValue *value, GParamSpec *pspec)
+{
+	SoupCookieJarTextPrivate *priv =
+		SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		priv->filename = g_value_dup_string (value);
+		load (SOUP_COOKIE_JAR (object));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+	      GValue *value, GParamSpec *pspec)
+{
+	SoupCookieJarTextPrivate *priv =
+		SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FILENAME:
+		g_value_set_string (value, priv->filename);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
+ * soup_cookie_jar_text_new:
+ * @filename: the filename to read to/write from
+ * @read_only: %TRUE if @filename is read-only
+ *
+ * Creates a #SoupCookieJarText.
+ *
+ * @filename will be read in at startup to create an initial set of
+ * cookies. If @read_only is %FALSE, then the non-session cookies will
+ * be written to @filename when the 'changed' signal is emitted from
+ * the jar. (If @read_only is %TRUE, then the cookie jar will only be
+ * used for this session, and changes made to it will be lost when the
+ * jar is destroyed.)
+ *
+ * Return value: the new #SoupCookieJar
+ **/
+SoupCookieJar *
+soup_cookie_jar_text_new (const char *filename, gboolean read_only)
+{
+	g_return_val_if_fail (filename != NULL, NULL);
+
+	return g_object_new (SOUP_TYPE_COOKIE_JAR_TEXT,
+			     SOUP_COOKIE_JAR_TEXT_FILENAME, filename,
+			     SOUP_COOKIE_JAR_READ_ONLY, read_only,
+			     NULL);
+}
+
+static SoupCookie*
+parse_cookie (char *line, time_t now)
+{
+	char **result;
+	SoupCookie *cookie = NULL;
+	gboolean http_only;
+	time_t max_age;
+	char *host, *is_domain, *path, *secure, *expires, *name, *value;
+
+	if (g_str_has_prefix (line, "#HttpOnly_")) {
+		http_only = TRUE;
+		line += strlen ("#HttpOnly_");
+	} else if (*line == '#' || g_ascii_isspace (*line))
+		return cookie;
+	else
+		http_only = FALSE;
+
+	result = g_strsplit (line, "\t", -1);
+	if (g_strv_length (result) != 7)
+		goto out;
+
+	/* Check this first */
+	expires = result[4];
+	max_age = strtoul (expires, NULL, 10) - now;
+	if (max_age <= 0)
+		goto out;
+
+	host = result[0];
+	is_domain = result[1];
+	path = result[2];
+	secure = result[3];
+
+	name = result[5];
+	value = result[6];
+
+	cookie = soup_cookie_new (name, value, host, path, max_age);
+
+	if (strcmp (secure, "FALSE") != 0)
+		soup_cookie_set_secure (cookie, TRUE);
+	if (http_only)
+		soup_cookie_set_http_only (cookie, TRUE);
+
+ out:
+	g_strfreev (result);
+
+	return cookie;
+}
+
+static void
+parse_line (SoupCookieJar *jar, char *line, time_t now)
+{
+	SoupCookie *cookie;
+
+	cookie = parse_cookie (line, now);
+	if (cookie)
+		soup_cookie_jar_add_cookie (jar, cookie);
+}
+
+static void
+load (SoupCookieJar *jar)
+{
+	SoupCookieJarTextPrivate *priv =
+		SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
+	char *contents = NULL, *line, *p;
+	gsize length = 0;
+	time_t now = time (NULL);
+
+	/* FIXME: error? */
+	if (!g_file_get_contents (priv->filename, &contents, &length, NULL))
+		return;
+
+	line = contents;
+	for (p = contents; *p; p++) {
+		/* \r\n comes out as an extra empty line and gets ignored */
+		if (*p == '\r' || *p == '\n') {
+			*p = '\0';
+			parse_line (jar, line, now);
+			line = p + 1;
+		}
+	}
+	parse_line (jar, line, now);
+
+	g_free (contents);
+}
+
+static void
+write_cookie (FILE *out, SoupCookie *cookie)
+{
+	fseek (out, 0, SEEK_END);
+
+	fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
+		 cookie->http_only ? "#HttpOnly_" : "",
+		 cookie->domain,
+		 *cookie->domain == '.' ? "TRUE" : "FALSE",
+		 cookie->path,
+		 cookie->secure ? "TRUE" : "FALSE",
+		 (gulong)soup_date_to_time_t (cookie->expires),
+		 cookie->name,
+		 cookie->value);
+}
+
+static void
+delete_cookie (const char *filename, SoupCookie *cookie)
+{
+	char *contents = NULL, *line, *p;
+	gsize length = 0;
+	FILE *f;
+	SoupCookie *c;
+	time_t now = time (NULL);
+
+	if (!g_file_get_contents (filename, &contents, &length, NULL))
+		return;
+
+	f = fopen (filename, "w");
+	if (!f) {
+		g_free (contents);
+		return;
+	}
+
+	line = contents;
+	for (p = contents; *p; p++) {
+		/* \r\n comes out as an extra empty line and gets ignored */
+		if (*p == '\r' || *p == '\n') {
+			*p = '\0';
+			c = parse_cookie (line, now);
+			if (!c)
+				continue;
+			if (!soup_cookie_equal (cookie, c))
+				write_cookie (f, c);
+			line = p + 1;
+			soup_cookie_free (c);
+		}
+	}
+	c = parse_cookie (line, now);
+	if (c) {
+		if (!soup_cookie_equal (cookie, c))
+			write_cookie (f, c);
+		soup_cookie_free (c);
+	}
+
+	g_free (contents);
+	fclose (f);
+}
+
+static void
+changed (SoupCookieJar *jar,
+	 SoupCookie    *old_cookie,
+	 SoupCookie    *new_cookie)
+{
+	FILE *out;
+	SoupCookieJarTextPrivate *priv =
+		SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
+
+	/* We can sort of ignore the semantics of the 'changed'
+	 * signal here and simply delete the old cookie if present
+	 * and write the new cookie if present. That will do the
+	 * right thing for all 'added', 'deleted' and 'modified'
+	 * meanings.
+	 */
+	/* Also, delete_cookie takes the filename and write_cookie
+	 * a FILE pointer. Seems more convenient that way considering
+	 * the implementations of the functions
+	 */
+	if (old_cookie)
+		delete_cookie (priv->filename, old_cookie);
+
+	if (new_cookie) {
+		gboolean write_header = FALSE;
+
+		if (!g_file_test (priv->filename, G_FILE_TEST_EXISTS))
+			write_header = TRUE;
+
+		out = fopen (priv->filename, "a");
+		if (!out) {
+			/* FIXME: error? */
+			return;
+		}
+
+		if (write_header) {
+			fprintf (out, "# HTTP Cookie File\n");
+			fprintf (out, "# http://www.netscape.com/newsref/std/cookie_spec.html\n";);
+			fprintf (out, "# This is a generated file!  Do not edit.\n");
+			fprintf (out, "# To delete cookies, use the Cookie Manager.\n\n");
+		}
+
+		if (new_cookie->expires)
+			write_cookie (out, new_cookie);
+
+		if (fclose (out) != 0) {
+			/* FIXME: error? */
+			return;
+		}
+	}
+}

Added: trunk/libsoup/soup-cookie-jar-text.h
==============================================================================
--- (empty file)
+++ trunk/libsoup/soup-cookie-jar-text.h	Tue Nov  4 20:30:37 2008
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_COOKIE_JAR_TEXT_H
+#define SOUP_COOKIE_JAR_TEXT_H 1
+
+#include <libsoup/soup-cookie-jar.h>
+
+#define SOUP_TYPE_COOKIE_JAR_TEXT            (soup_cookie_jar_text_get_type ())
+#define SOUP_COOKIE_JAR_TEXT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarText))
+#define SOUP_COOKIE_JAR_TEXT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarTextClass))
+#define SOUP_IS_COOKIE_JAR_TEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_TEXT))
+#define SOUP_IS_COOKIE_JAR_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_TEXT))
+#define SOUP_COOKIE_JAR_TEXT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarTextClass))
+
+typedef struct {
+	SoupCookieJar parent;
+
+} SoupCookieJarText;
+
+typedef struct {
+	SoupCookieJarClass parent_class;
+
+	/* Padding for future expansion */
+	void (*_libsoup_reserved1) (void);
+	void (*_libsoup_reserved2) (void);
+	void (*_libsoup_reserved3) (void);
+	void (*_libsoup_reserved4) (void);
+} SoupCookieJarTextClass;
+
+#define SOUP_COOKIE_JAR_TEXT_FILENAME  "filename"
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+#define SOUP_COOKIE_JAR_TEXT_READ_ONLY SOUP_COOKIE_JAR_READ_ONLY
+#endif
+
+GType soup_cookie_jar_text_get_type (void);
+
+SoupCookieJar *soup_cookie_jar_text_new (const char *filename,
+					 gboolean    read_only);
+
+#endif /* SOUP_COOKIE_JAR_TEXT_H */

Modified: trunk/libsoup/soup-cookie-jar.c
==============================================================================
--- trunk/libsoup/soup-cookie-jar.c	(original)
+++ trunk/libsoup/soup-cookie-jar.c	Tue Nov  4 20:30:37 2008
@@ -15,6 +15,7 @@
 #include "soup-cookie.h"
 #include "soup-cookie-jar.h"
 #include "soup-date.h"
+#include "soup-marshal.h"
 #include "soup-message.h"
 #include "soup-session-feature.h"
 #include "soup-uri.h"
@@ -45,11 +46,32 @@
 			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
 						soup_cookie_jar_session_feature_init))
 
+enum {
+	CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+	PROP_0,
+
+	PROP_READ_ONLY,
+
+	LAST_PROP
+};
+
 typedef struct {
+	gboolean constructed, read_only;
 	GHashTable *domains;
 } SoupCookieJarPrivate;
 #define SOUP_COOKIE_JAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR, SoupCookieJarPrivate))
 
+static void set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec);
+
 static void
 soup_cookie_jar_init (SoupCookieJar *jar)
 {
@@ -60,6 +82,14 @@
 }
 
 static void
+constructed (GObject *object)
+{
+	SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (object);
+
+	priv->constructed = TRUE;
+}
+
+static void
 finalize (GObject *object)
 {
 	SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (object);
@@ -81,7 +111,29 @@
 
 	g_type_class_add_private (jar_class, sizeof (SoupCookieJarPrivate));
 
+	object_class->constructed = constructed;
 	object_class->finalize = finalize;
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+
+	signals[CHANGED] =
+		g_signal_new ("changed",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_FIRST,
+			      G_STRUCT_OFFSET (SoupCookieJarClass, changed),
+			      NULL, NULL,
+			      soup_marshal_NONE__BOXED_BOXED,
+			      G_TYPE_NONE, 2, 
+			      SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE,
+			      SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+	g_object_class_install_property (
+		object_class, PROP_READ_ONLY,
+		g_param_spec_boolean (SOUP_COOKIE_JAR_READ_ONLY,
+				      "Read-only",
+				      "Whether or not the cookie jar is read-only",
+				      FALSE,
+				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
@@ -93,10 +145,45 @@
 	feature_interface->request_unqueued = request_unqueued;
 }
 
+static void
+set_property (GObject *object, guint prop_id,
+	      const GValue *value, GParamSpec *pspec)
+{
+	SoupCookieJarPrivate *priv =
+		SOUP_COOKIE_JAR_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_READ_ONLY:
+		priv->read_only = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+	      GValue *value, GParamSpec *pspec)
+{
+	SoupCookieJarPrivate *priv =
+		SOUP_COOKIE_JAR_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_READ_ONLY:
+		g_value_set_boolean (value, priv->read_only);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
 /**
  * soup_cookie_jar_new:
  *
- * Creates a new #SoupCookieJar.
+ * Creates a new #SoupCookieJar. The base #SoupCookieJar class does
+ * not support persistent storage of cookies; use a subclass for that.
  *
  * Returns: a new #SoupCookieJar
  **/
@@ -106,18 +193,22 @@
 	return g_object_new (SOUP_TYPE_COOKIE_JAR, NULL);
 }
 
-/**
- * soup_cookie_jar_save:
- * @jar: a SoupCookieJar
- *
- * Tells @jar to save the state of its (non-session) cookies to some
- * sort of permanent storage.
- **/
 void
 soup_cookie_jar_save (SoupCookieJar *jar)
 {
-	if (SOUP_COOKIE_JAR_GET_CLASS (jar)->save)
-		SOUP_COOKIE_JAR_GET_CLASS (jar)->save (jar);
+	/* Does nothing, obsolete */
+}
+
+static void
+soup_cookie_jar_changed (SoupCookieJar *jar,
+			 SoupCookie *old, SoupCookie *new)
+{
+	SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+	if (priv->read_only || !priv->constructed)
+		return;
+
+	g_signal_emit (jar, signals[CHANGED], 0, old, new);
 }
 
 /**
@@ -148,6 +239,7 @@
 	SoupCookieJarPrivate *priv;
 	GSList *cookies, *domain_cookies;
 	char *domain, *cur, *next, *result;
+	GSList *new_head, *cookies_to_remove = NULL, *p;
 
 	g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
 	priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
@@ -161,14 +253,23 @@
 	domain = cur = g_strdup_printf (".%s", uri->host);
 	next = domain + 1;
 	do {
-		domain_cookies = g_hash_table_lookup (priv->domains, cur);
+		new_head = domain_cookies = g_hash_table_lookup (priv->domains, cur);
 		while (domain_cookies) {
+			GSList *next = domain_cookies->next;
 			SoupCookie *cookie = domain_cookies->data;
 
-			if (soup_cookie_applies_to_uri (cookie, uri) &&
-			    (for_http || !cookie->http_only))
+			if (cookie->expires && soup_date_is_past (cookie->expires)) {
+				cookies_to_remove = g_slist_append (cookies_to_remove,
+								    cookie);
+				new_head = g_slist_delete_link (new_head, domain_cookies);
+				g_hash_table_insert (priv->domains,
+						     g_strdup (cur),
+						     new_head);
+			} else if (soup_cookie_applies_to_uri (cookie, uri) &&
+				   (for_http || !cookie->http_only))
 				cookies = g_slist_append (cookies, cookie);
-			domain_cookies = domain_cookies->next;
+
+			domain_cookies = next;
 		}
 		cur = next;
 		if (cur)
@@ -176,6 +277,14 @@
 	} while (cur);
 	g_free (domain);
 
+	for (p = cookies_to_remove; p; p = p->next) {
+		SoupCookie *cookie = p->data;
+
+		soup_cookie_jar_changed (jar, cookie, NULL);
+		soup_cookie_free (cookie);
+	}
+	g_slist_free (cookies_to_remove);
+
 	if (cookies) {
 		/* FIXME: sort? */
 		result = soup_cookies_to_cookie_header (cookies);
@@ -185,63 +294,71 @@
 		return NULL;
 }
 
-static GSList *
-get_cookies_for_domain (SoupCookieJar *jar, const char *domain)
-{
-	SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
-	GSList *cookies, *orig_cookies, *c;
-	SoupCookie *cookie;
-
-	cookies = g_hash_table_lookup (priv->domains, domain);
-	c = orig_cookies = cookies;
-	while (c) {
-		cookie = c->data;
-		c = c->next;
-		if (cookie->expires && soup_date_is_past (cookie->expires)) {
-			cookies = g_slist_remove (cookies, cookie);
-			soup_cookie_free (cookie);
-		}
-	}
-
-	if (cookies != orig_cookies)
-		g_hash_table_insert (priv->domains, g_strdup (domain), cookies);
-	return cookies;
-}
-
-static void
-set_cookie (SoupCookieJar *jar, SoupCookie *cookie)
+/**
+ * soup_cookie_jar_add_cookie:
+ * @jar: a #SoupCookieJar
+ * @cookie: a #SoupCookie
+ *
+ * Adds @cookie to @jar, emitting the 'changed' signal if we are modifying
+ * an existing cookie or adding a valid new cookie ('valid' means
+ * that the cookie's expire date is not in the past).
+ *
+ * @cookie will be 'stolen' by the jar, so don't free it afterwards.
+ **/
+void
+soup_cookie_jar_add_cookie (SoupCookieJar *jar, SoupCookie *cookie)
 {
-	SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+	SoupCookieJarPrivate *priv;
 	GSList *old_cookies, *oc, *prev = NULL;
 	SoupCookie *old_cookie;
 
-	old_cookies = get_cookies_for_domain (jar, cookie->domain);
+	g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+	g_return_if_fail (cookie != NULL);
+
+	priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+	old_cookies = g_hash_table_lookup (priv->domains, cookie->domain);
 	for (oc = old_cookies; oc; oc = oc->next) {
 		old_cookie = oc->data;
 		if (!strcmp (cookie->name, old_cookie->name)) {
-			/* The new cookie is a replacement for an old
-			 * cookie. It might be pre-expired, but we
-			 * don't worry about that here;
-			 * get_cookies_for_domain() will delete it
-			 * later.
-			 */
-			soup_cookie_free (old_cookie);
-			oc->data = cookie;
+			if (cookie->expires && soup_date_is_past (cookie->expires)) {
+				/* The new cookie has an expired date,
+				 * this is the way the the server has
+				 * of telling us that we have to
+				 * remove the cookie.
+				 */
+				old_cookies = g_slist_delete_link (old_cookies, oc);
+				g_hash_table_insert (priv->domains,
+						     g_strdup (cookie->domain),
+						     old_cookies);
+				soup_cookie_jar_changed (jar, old_cookie, NULL);
+				soup_cookie_free (old_cookie);
+				soup_cookie_free (cookie);
+			} else {
+				oc->data = cookie;
+				soup_cookie_jar_changed (jar, old_cookie, cookie);
+				soup_cookie_free (old_cookie);
+			}
+
 			return;
 		}
 		prev = oc;
 	}
 
 	/* The new cookie is... a new cookie */
-	if (cookie->expires && soup_date_is_past (cookie->expires))
+	if (cookie->expires && soup_date_is_past (cookie->expires)) {
 		soup_cookie_free (cookie);
-	else if (prev)
+		return;
+	}
+
+	if (prev)
 		prev = g_slist_append (prev, cookie);
 	else {
 		old_cookies = g_slist_append (NULL, cookie);
 		g_hash_table_insert (priv->domains, g_strdup (cookie->domain),
 				     old_cookies);
 	}
+
+	soup_cookie_jar_changed (jar, NULL, cookie);
 }
 
 /**
@@ -264,8 +381,8 @@
 
 	soup_cookie = soup_cookie_parse (cookie, uri);
 	if (soup_cookie) {
-		set_cookie (jar, soup_cookie);
-		/* set_cookie will steal or free soup_cookie */
+		/* will steal or free soup_cookie */
+		soup_cookie_jar_add_cookie (jar, soup_cookie);
 	}
 }
 
@@ -277,7 +394,7 @@
 
 	new_cookies = soup_cookies_from_response (msg);
 	for (nc = new_cookies; nc; nc = nc->next)
-		set_cookie (jar, nc->data);
+		soup_cookie_jar_add_cookie (jar, nc->data);
 	g_slist_free (new_cookies);
 }
 
@@ -311,3 +428,75 @@
 	g_signal_handlers_disconnect_by_func (msg, process_set_cookie_header, feature);
 }
 
+/**
+ * soup_cookie_jar_all_cookies:
+ * @jar: a #SoupCookieJar
+ *
+ * Constructs a #GSList with every cookie inside the @jar.
+ * The cookies in the list are a copy of the original, so
+ * you have to free them when you are done with them.
+ *
+ * Return value: a #GSList with all the cookies in the @jar.
+ **/
+GSList *
+soup_cookie_jar_all_cookies (SoupCookieJar *jar)
+{
+	SoupCookieJarPrivate *priv;
+	GHashTableIter iter;
+	GSList *l = NULL;
+	gpointer key, value;
+
+	g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
+
+	priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+	g_hash_table_iter_init (&iter, priv->domains);
+
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		GSList *p, *cookies = value;
+		for (p = cookies; p; p = p->next)
+			l = g_slist_prepend (l, soup_cookie_copy (p->data));
+	}
+
+	return l;
+}
+
+/**
+ * soup_cookie_jar_delete_cookie:
+ * @jar: a #SoupCookieJar
+ * @cookie: a #SoupCookie
+ *
+ * Deletes @cookie from @jar, emitting the 'changed' signal.
+ **/
+void
+soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
+			       SoupCookie    *cookie)
+{
+	SoupCookieJarPrivate *priv;
+	GSList *cookies, *p;
+	char *domain;
+
+	g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
+	g_return_if_fail (cookie != NULL);
+
+	priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
+
+	domain = g_strdup (cookie->domain);
+
+	cookies = g_hash_table_lookup (priv->domains, domain);
+	if (cookies == NULL)
+		return;
+
+	for (p = cookies; p; p = p->next ) {
+		SoupCookie *c = (SoupCookie*)p->data;
+		if (soup_cookie_equal (cookie, c)) {
+			cookies = g_slist_delete_link (cookies, p);
+			g_hash_table_insert (priv->domains,
+					     domain,
+					     cookies);
+			soup_cookie_jar_changed (jar, c, NULL);
+			soup_cookie_free (c);
+			return;
+		}
+	}
+}

Modified: trunk/libsoup/soup-cookie-jar.h
==============================================================================
--- trunk/libsoup/soup-cookie-jar.h	(original)
+++ trunk/libsoup/soup-cookie-jar.h	Tue Nov  4 20:30:37 2008
@@ -25,27 +25,43 @@
 typedef struct {
 	GObjectClass parent_class;
 
-	void (*save) (SoupCookieJar *jar);
+	void (*save)    (SoupCookieJar *jar);
+
+	/* signals */
+	void (*changed) (SoupCookieJar *jar,
+			 SoupCookie    *old_cookie,
+			 SoupCookie    *new_cookie);
 
 	/* Padding for future expansion */
 	void (*_libsoup_reserved1) (void);
 	void (*_libsoup_reserved2) (void);
 	void (*_libsoup_reserved3) (void);
-	void (*_libsoup_reserved4) (void);
 } SoupCookieJarClass;
 
-GType          soup_cookie_jar_get_type    (void);
+#define SOUP_COOKIE_JAR_READ_ONLY "read-only"
+
+GType          soup_cookie_jar_get_type      (void);
+
+SoupCookieJar *soup_cookie_jar_new           (void);
+
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+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);
 
-SoupCookieJar *soup_cookie_jar_new         (void);
+void           soup_cookie_jar_add_cookie    (SoupCookieJar *jar,
+					      SoupCookie    *cookie);
+void           soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
+					      SoupCookie    *cookie);
 
-void           soup_cookie_jar_save        (SoupCookieJar *jar);
+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);
 
 G_END_DECLS
 

Modified: trunk/libsoup/soup-cookie.c
==============================================================================
--- trunk/libsoup/soup-cookie.c	(original)
+++ trunk/libsoup/soup-cookie.c	Tue Nov  4 20:30:37 2008
@@ -122,6 +122,44 @@
  * have a trailing ";".
  */
 
+GType
+soup_cookie_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 ("SoupCookie"),
+			(GBoxedCopyFunc) soup_cookie_copy,
+			(GBoxedFreeFunc) soup_cookie_free);
+		g_once_init_leave (&type_volatile, type);
+	}
+	return type_volatile;
+}
+
+/**
+ * soup_cookie_copy:
+ * @cookie: a #SoupCookie
+ *
+ * Copies @cookie.
+ **/
+SoupCookie *
+soup_cookie_copy (SoupCookie *cookie)
+{
+	SoupCookie *copy = g_slice_new0 (SoupCookie);
+
+	copy->name = g_strdup (cookie->name);
+	copy->value = g_strdup (cookie->value);
+	copy->domain = g_strdup (cookie->domain);
+	copy->path = g_strdup (cookie->path);
+	if (cookie->expires)
+		copy->expires = soup_date_copy(cookie->expires);
+	copy->secure = cookie->secure;
+	copy->http_only = cookie->http_only;
+
+	return copy;
+}
+
 static gboolean
 domain_matches (const char *domain, const char *host)
 {
@@ -132,6 +170,8 @@
 		return TRUE;
 	if (*domain != '.')
 		return FALSE;
+	if (!g_ascii_strcasecmp (domain + 1, host))
+		return TRUE;
 	dlen = strlen (domain);
 	while ((match = strstr (host, domain))) {
 		if (!match[dlen])
@@ -883,5 +923,16 @@
 	if (uri->path[plen] && uri->path[plen] != '/')
 		return FALSE;
 
-	return TRUE;
+	return !strncmp (cookie->path, uri->path, plen);
+}
+
+gboolean
+soup_cookie_equal (SoupCookie *cookie1, SoupCookie *cookie2)
+{
+	g_return_val_if_fail (cookie1, FALSE);
+	g_return_val_if_fail (cookie2, FALSE);
+
+	return (!strcmp (cookie1->name, cookie2->name) &&
+		!strcmp (cookie1->value, cookie2->value) &&
+		!strcmp (cookie1->path, cookie2->path));
 }

Modified: trunk/libsoup/soup-cookie.h
==============================================================================
--- trunk/libsoup/soup-cookie.h	(original)
+++ trunk/libsoup/soup-cookie.h	Tue Nov  4 20:30:37 2008
@@ -20,6 +20,9 @@
 	gboolean  http_only;
 };
 
+GType soup_cookie_get_type (void);
+#define SOUP_TYPE_COOKIE (soup_cookie_get_type())
+
 #define SOUP_COOKIE_MAX_AGE_ONE_HOUR (60 * 60)
 #define SOUP_COOKIE_MAX_AGE_ONE_DAY  (SOUP_COOKIE_MAX_AGE_ONE_HOUR * 24)
 #define SOUP_COOKIE_MAX_AGE_ONE_WEEK (SOUP_COOKIE_MAX_AGE_ONE_DAY * 7)
@@ -32,6 +35,7 @@
 						 int          max_age);
 SoupCookie *soup_cookie_parse                   (const char  *header,
 						 SoupURI     *origin);
+SoupCookie *soup_cookie_copy                    (SoupCookie  *cookie);
 
 void        soup_cookie_set_name                (SoupCookie  *cookie,
 						 const char  *name);
@@ -53,6 +57,11 @@
 char       *soup_cookie_to_set_cookie_header    (SoupCookie  *cookie);
 char       *soup_cookie_to_cookie_header        (SoupCookie  *cookie);
 
+gboolean    soup_cookie_applies_to_uri          (SoupCookie  *cookie,
+						 SoupURI     *uri);
+gboolean    soup_cookie_equal                   (SoupCookie  *cookie1,
+						 SoupCookie  *cookie2);
+
 void        soup_cookie_free                    (SoupCookie  *cookie);
 
 GSList     *soup_cookies_from_response          (SoupMessage *msg);
@@ -67,9 +76,6 @@
 
 char       *soup_cookies_to_cookie_header       (GSList      *cookies);
 
-gboolean    soup_cookie_applies_to_uri          (SoupCookie  *cookie,
-						 SoupURI     *uri);
-
 G_END_DECLS
 
 #endif /* SOUP_COOKIE_H */

Modified: trunk/libsoup/soup-gnome.h
==============================================================================
--- trunk/libsoup/soup-gnome.h	(original)
+++ trunk/libsoup/soup-gnome.h	Tue Nov  4 20:30:37 2008
@@ -8,6 +8,7 @@
 
 #include <libsoup/soup.h>
 
+#include <libsoup/soup-cookie-jar-sqlite.h>
 #include <libsoup/soup-gnome-features.h>
 
 #endif /* SOUP_GNOME_H */

Modified: trunk/libsoup/soup-marshal.list
==============================================================================
--- trunk/libsoup/soup-marshal.list	(original)
+++ trunk/libsoup/soup-marshal.list	Tue Nov  4 20:30:37 2008
@@ -4,4 +4,5 @@
 NONE:OBJECT
 NONE:OBJECT,OBJECT
 NONE:OBJECT,POINTER
+NONE:BOXED,BOXED
 NONE:OBJECT,OBJECT,BOOLEAN

Modified: trunk/libsoup/soup.h
==============================================================================
--- trunk/libsoup/soup.h	(original)
+++ trunk/libsoup/soup.h	Tue Nov  4 20:30:37 2008
@@ -17,6 +17,7 @@
 #include <libsoup/soup-auth-domain-digest.h>
 #include <libsoup/soup-cookie.h>
 #include <libsoup/soup-cookie-jar.h>
+#include <libsoup/soup-cookie-jar-text.h>
 #include <libsoup/soup-date.h>
 #include <libsoup/soup-enum-types.h>
 #include <libsoup/soup-form.h>



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