summaryrefslogtreecommitdiff
path: root/libsoup/soup-cookie-jar-db.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup/soup-cookie-jar-db.c')
-rw-r--r--libsoup/soup-cookie-jar-db.c337
1 files changed, 337 insertions, 0 deletions
diff --git a/libsoup/soup-cookie-jar-db.c b/libsoup/soup-cookie-jar-db.c
new file mode 100644
index 00000000..8f21baab
--- /dev/null
+++ b/libsoup/soup-cookie-jar-db.c
@@ -0,0 +1,337 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cookie-jar-db.c: database-based cookie storage
+ *
+ * Using danw's soup-cookie-jar-text as template
+ * Copyright (C) 2008 Diego Escalante Urrelo
+ * Copyright (C) 2009 Collabora Ltd.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <sqlite3.h>
+
+#include "soup-cookie-jar-db.h"
+#include "soup.h"
+
+/**
+ * SECTION:soup-cookie-jar-db
+ * @short_description: Database-based Cookie Jar
+ *
+ * #SoupCookieJarDB is a #SoupCookieJar that reads cookies from and
+ * writes them to a sqlite database in the new Mozilla format.
+ *
+ * (This is identical to <literal>SoupCookieJarSqlite</literal> in
+ * libsoup-gnome; it has just been moved into libsoup proper, and
+ * renamed to avoid conflicting.)
+ **/
+
+enum {
+ PROP_0,
+
+ PROP_FILENAME,
+
+ LAST_PROP
+};
+
+typedef struct {
+ char *filename;
+ sqlite3 *db;
+} SoupCookieJarDBPrivate;
+
+#define SOUP_COOKIE_JAR_DB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_DB, SoupCookieJarDBPrivate))
+
+G_DEFINE_TYPE (SoupCookieJarDB, soup_cookie_jar_db, SOUP_TYPE_COOKIE_JAR)
+
+static void load (SoupCookieJar *jar);
+
+static void
+soup_cookie_jar_db_init (SoupCookieJarDB *db)
+{
+}
+
+static void
+soup_cookie_jar_db_finalize (GObject *object)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (object);
+
+ g_free (priv->filename);
+ g_clear_pointer (&priv->db, sqlite3_close);
+
+ G_OBJECT_CLASS (soup_cookie_jar_db_parent_class)->finalize (object);
+}
+
+static void
+soup_cookie_jar_db_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_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
+soup_cookie_jar_db_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_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_db_new:
+ * @filename: the filename to read to/write from, or %NULL
+ * @read_only: %TRUE if @filename is read-only
+ *
+ * Creates a #SoupCookieJarDB.
+ *
+ * @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
+ *
+ * Since: 2.42
+ **/
+SoupCookieJar *
+soup_cookie_jar_db_new (const char *filename, gboolean read_only)
+{
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ return g_object_new (SOUP_TYPE_COOKIE_JAR_DB,
+ SOUP_COOKIE_JAR_DB_FILENAME, filename,
+ SOUP_COOKIE_JAR_READ_ONLY, read_only,
+ NULL);
+}
+
+#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;"
+#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
+#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 host=%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;
+ gulong expire_time;
+ time_t now;
+ int max_age;
+ 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];
+ expire_time = strtoul (argv[COL_EXPIRY], NULL, 10);
+
+ if (now >= expire_time)
+ return 0;
+ max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
+
+ http_only = (g_strcmp0 (argv[COL_HTTP_ONLY], "1") == 0);
+ secure = (g_strcmp0 (argv[COL_SECURE], "1") == 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
+try_create_table (sqlite3 *db)
+{
+ char *error = NULL;
+
+ if (sqlite3_exec (db, CREATE_TABLE, NULL, NULL, &error)) {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+}
+
+static void
+exec_query_with_try_create_table (sqlite3 *db,
+ const char *sql,
+ int (*callback)(void*,int,char**,char**),
+ void *argument)
+{
+ char *error = NULL;
+ gboolean try_create = TRUE;
+
+try_exec:
+ if (sqlite3_exec (db, sql, callback, argument, &error)) {
+ if (try_create) {
+ try_create = FALSE;
+ try_create_table (db);
+ sqlite3_free (error);
+ error = NULL;
+ goto try_exec;
+ } else {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+ }
+}
+
+/* Follows sqlite3 convention; returns TRUE on error */
+static gboolean
+open_db (SoupCookieJar *jar)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (jar);
+
+ char *error = NULL;
+
+ if (sqlite3_open (priv->filename, &priv->db)) {
+ sqlite3_close (priv->db);
+ priv->db = NULL;
+ g_warning ("Can't open %s", priv->filename);
+ return TRUE;
+ }
+
+ if (sqlite3_exec (priv->db, "PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;", NULL, NULL, &error)) {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+
+ return FALSE;
+}
+
+static void
+load (SoupCookieJar *jar)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (jar);
+
+ if (priv->db == NULL) {
+ if (open_db (jar))
+ return;
+ }
+
+ exec_query_with_try_create_table (priv->db, QUERY_ALL, callback, jar);
+}
+
+static void
+soup_cookie_jar_db_changed (SoupCookieJar *jar,
+ SoupCookie *old_cookie,
+ SoupCookie *new_cookie)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (jar);
+ char *query;
+
+ if (priv->db == NULL) {
+ if (open_db (jar))
+ return;
+ }
+
+ if (old_cookie) {
+ query = sqlite3_mprintf (QUERY_DELETE,
+ old_cookie->name,
+ old_cookie->domain);
+ exec_query_with_try_create_table (priv->db, query, NULL, NULL);
+ sqlite3_free (query);
+ }
+
+ if (new_cookie && new_cookie->expires) {
+ gulong expires;
+
+ expires = (gulong)soup_date_to_time_t (new_cookie->expires);
+ 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);
+ exec_query_with_try_create_table (priv->db, query, NULL, NULL);
+ sqlite3_free (query);
+ }
+}
+
+static gboolean
+soup_cookie_jar_db_is_persistent (SoupCookieJar *jar)
+{
+ return TRUE;
+}
+
+static void
+soup_cookie_jar_db_class_init (SoupCookieJarDBClass *db_class)
+{
+ SoupCookieJarClass *cookie_jar_class =
+ SOUP_COOKIE_JAR_CLASS (db_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (db_class);
+
+ g_type_class_add_private (db_class, sizeof (SoupCookieJarDBPrivate));
+
+ cookie_jar_class->is_persistent = soup_cookie_jar_db_is_persistent;
+ cookie_jar_class->changed = soup_cookie_jar_db_changed;
+
+ object_class->finalize = soup_cookie_jar_db_finalize;
+ object_class->set_property = soup_cookie_jar_db_set_property;
+ object_class->get_property = soup_cookie_jar_db_get_property;
+
+ /**
+ * SOUP_COOKIE_JAR_DB_FILENAME:
+ *
+ * Alias for the #SoupCookieJarDB:filename property. (The
+ * cookie-storage filename.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_FILENAME,
+ g_param_spec_string (SOUP_COOKIE_JAR_DB_FILENAME,
+ "Filename",
+ "Cookie-storage filename",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}