implementation of SoupCookieJar that persists to a text file in the old
authorDan Winship <danw@src.gnome.org>
Tue, 4 Nov 2008 20:30:37 +0000 (20:30 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 4 Nov 2008 20:30:37 +0000 (20:30 +0000)
* 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

svn path=/trunk/; revision=1200

14 files changed:
ChangeLog
configure.in
libsoup/Makefile.am
libsoup/soup-cookie-jar-sqlite.c [new file with mode: 0644]
libsoup/soup-cookie-jar-sqlite.h [new file with mode: 0644]
libsoup/soup-cookie-jar-text.c [new file with mode: 0644]
libsoup/soup-cookie-jar-text.h [new file with mode: 0644]
libsoup/soup-cookie-jar.c
libsoup/soup-cookie-jar.h
libsoup/soup-cookie.c
libsoup/soup-cookie.h
libsoup/soup-gnome.h
libsoup/soup-marshal.list
libsoup/soup.h

index 5074a95..67f5d59 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2008-11-04  Dan Winship  <danw@gnome.org>
+
+       * 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
+
 2008-11-03  Dan Winship  <danw@gnome.org>
 
        * libsoup/soup-session-sync.c (process_queue_item): don't remove
index 865f598..730b123 100644 (file)
@@ -199,6 +199,13 @@ $GCONF_PKG_ERRORS
 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(HAVE_LIBPROXY)
 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 ***************
index 0037c02..94a3c2f 100644 (file)
@@ -13,6 +13,7 @@ INCLUDES =                            \
        $(XML_CFLAGS)                   \
        $(GCONF_CFLAGS)                 \
        $(LIBPROXY_CFLAGS)              \
+       $(SQLITE_CFLAGS)                \
        $(LIBGCRYPT_CFLAGS)             \
        $(LIBGNUTLS_CFLAGS)
 
@@ -56,6 +57,7 @@ soup_headers =                        \
        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 @@ libsoup_2_4_la_SOURCES =            \
        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 @@ if BUILD_LIBSOUP_GNOME
 libsoupgnomeincludedir = $(libsoupincludedir)
 
 libsoupgnomeinclude_HEADERS =  \
+       soup-cookie-jar-sqlite.h\
        soup-gnome.h            \
        soup-gnome-features.h
 
@@ -168,9 +172,12 @@ lib_LTLIBRARIES += libsoup-gnome-2.4.la
 
 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
diff --git a/libsoup/soup-cookie-jar-sqlite.c b/libsoup/soup-cookie-jar-sqlite.c
new file mode 100644 (file)
index 0000000..55c3bc9
--- /dev/null
@@ -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);
+}
diff --git a/libsoup/soup-cookie-jar-sqlite.h b/libsoup/soup-cookie-jar-sqlite.h
new file mode 100644 (file)
index 0000000..2a65988
--- /dev/null
@@ -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 */
diff --git a/libsoup/soup-cookie-jar-text.c b/libsoup/soup-cookie-jar-text.c
new file mode 100644 (file)
index 0000000..a1d62ea
--- /dev/null
@@ -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;
+               }
+       }
+}
diff --git a/libsoup/soup-cookie-jar-text.h b/libsoup/soup-cookie-jar-text.h
new file mode 100644 (file)
index 0000000..e23c90e
--- /dev/null
@@ -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 */
index 9b717c5..29a2de1 100644 (file)
@@ -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_DEFINE_TYPE_WITH_CODE (SoupCookieJar, soup_cookie_jar, G_TYPE_OBJECT,
                         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 @@ soup_cookie_jar_init (SoupCookieJar *jar)
 }
 
 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 @@ soup_cookie_jar_class_init (SoupCookieJarClass *jar_class)
 
        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 @@ soup_cookie_jar_session_feature_init (SoupSessionFeatureInterface *feature_inter
        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 @@ soup_cookie_jar_new (void)
        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 @@ soup_cookie_jar_get_cookies (SoupCookieJar *jar, SoupURI *uri,
        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 @@ soup_cookie_jar_get_cookies (SoupCookieJar *jar, SoupURI *uri,
        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 @@ soup_cookie_jar_get_cookies (SoupCookieJar *jar, SoupURI *uri,
        } 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 @@ soup_cookie_jar_get_cookies (SoupCookieJar *jar, SoupURI *uri,
                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_jar_set_cookie (SoupCookieJar *jar, SoupURI *uri,
 
        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 @@ process_set_cookie_header (SoupMessage *msg, gpointer user_data)
 
        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 @@ request_unqueued (SoupSessionFeature *feature, SoupSession *session,
        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;
+               }
+       }
+}
index 6e8b7d7..acab070 100644 (file)
@@ -25,27 +25,43 @@ typedef struct {
 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
 
index b53502a..a002563 100644 (file)
  * 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 @@ domain_matches (const char *domain, const char *host)
                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 @@ soup_cookie_applies_to_uri (SoupCookie *cookie, SoupURI *uri)
        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));
 }
index 2c1bad4..34a2aa4 100644 (file)
@@ -20,6 +20,9 @@ struct _SoupCookie {
        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 @@ SoupCookie *soup_cookie_new                     (const char  *name,
                                                 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 @@ void        soup_cookie_set_http_only           (SoupCookie  *cookie,
 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 @@ void        soup_cookies_free                   (GSList      *cookies);
 
 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 */
index 6747f3a..a2134a4 100644 (file)
@@ -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 */
index 4e1b2bc..1a43570 100644 (file)
@@ -4,4 +4,5 @@ NONE:NONE
 NONE:OBJECT
 NONE:OBJECT,OBJECT
 NONE:OBJECT,POINTER
+NONE:BOXED,BOXED
 NONE:OBJECT,OBJECT,BOOLEAN
index afb4892..e973fe8 100644 (file)
@@ -17,6 +17,7 @@ extern "C" {
 #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>