summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2013-07-25 16:07:46 +0100
committerMichael Drake <tlsa@netsurf-browser.org>2013-07-25 16:07:46 +0100
commit219f5dac32ac2c4ca158785514cdcc35cb3f18bf (patch)
treea117cf95db393232730e6d011ee0c15720150fb2
parent4697d1ccc1d6190788b931352b1e3a931dd0a0f7 (diff)
downloadnetsurf-219f5dac32ac2c4ca158785514cdcc35cb3f18bf.tar.gz
netsurf-219f5dac32ac2c4ca158785514cdcc35cb3f18bf.tar.bz2
Add initial version of new cookie manager.
-rw-r--r--desktop/cookie_manager.c758
-rw-r--r--desktop/cookie_manager.h102
2 files changed, 860 insertions, 0 deletions
diff --git a/desktop/cookie_manager.c b/desktop/cookie_manager.c
new file mode 100644
index 000000000..0ec32b4e1
--- /dev/null
+++ b/desktop/cookie_manager.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Cookie Manager (implementation).
+ */
+
+
+#include <stdlib.h>
+
+#include "content/urldb.h"
+#include "desktop/browser.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/treeview.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/log.h"
+
+enum cookie_manager_fields {
+ CM_NAME,
+ CM_CONTENT,
+ CM_DOMAIN,
+ CM_PATH,
+ CM_EXPIRES,
+ CM_LAST_USED,
+ CM_RESTRICTIONS,
+ CM_VERSION,
+ CM_PERSISTENT,
+ CM_DOMAIN_FOLDER,
+ N_FIELDS
+};
+
+struct cookie_manager_folder {
+ treeview_node *folder;
+ struct treeview_field_data data;
+};
+
+struct cookie_manager_ctx {
+ treeview *tree;
+ struct treeview_field_desc fields[N_FIELDS];
+ bool built;
+};
+struct cookie_manager_ctx cm_ctx;
+
+struct cookie_manager_entry {
+ bool user_delete;
+
+ treeview_node *entry;
+
+ struct treeview_field_data data[N_FIELDS - 1];
+};
+
+
+struct treeview_walk_ctx {
+ const char *title;
+ struct cookie_manager_folder *folder;
+ struct cookie_manager_entry *entry;
+};
+/** Callback for treeview_walk */
+static nserror cookie_manager_walk_cb(void *ctx, void *node_data,
+ enum treeview_node_type type, bool *abort)
+{
+ struct treeview_walk_ctx *tw = ctx;
+
+ if (type == TREE_NODE_ENTRY) {
+ struct cookie_manager_entry *entry = node_data;
+
+ if (strcmp(tw->title, entry->data[CM_NAME].value) == 0) {
+ /* Found what we're looking for */
+ tw->entry = entry;
+ *abort = true;
+ }
+
+ } else if (type == TREE_NODE_FOLDER) {
+ struct cookie_manager_folder *folder = node_data;
+
+ if (strcmp(tw->title, folder->data.value) == 0) {
+ /* Found what we're looking for */
+ tw->folder = folder;
+ *abort = true;
+ }
+ }
+
+ return NSERROR_OK;
+}
+/**
+ * Find a cookie entry in the cookie manager's treeview
+ *
+ * \param root Search root node, or NULL to search from tree's root
+ * \param title ID of the node to look for
+ * \param found Updated to the matching node's cookie maanger entry
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+static nserror cookie_manager_find_entry(treeview_node *root,
+ const char *title, struct cookie_manager_entry **found)
+{
+ nserror err;
+ struct treeview_walk_ctx tw = {
+ .title = title,
+ .folder = NULL,
+ .entry = NULL
+ };
+
+ err = treeview_walk(cm_ctx.tree, root, cookie_manager_walk_cb, &tw,
+ TREE_NODE_ENTRY);
+ if (err != NSERROR_OK)
+ return err;
+
+ *found = tw.entry;
+
+ return NSERROR_OK;
+}
+/**
+ * Find a cookie domain folder in the cookie manager's treeview
+ *
+ * \param root Search root node, or NULL to search from tree's root
+ * \param title ID of the node to look for
+ * \param found Updated to the matching node's cookie maanger folder
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+static nserror cookie_manager_find_folder(treeview_node *root,
+ const char *title, struct cookie_manager_folder **found)
+{
+ nserror err;
+ struct treeview_walk_ctx tw = {
+ .title = title,
+ .folder = NULL,
+ .entry = NULL
+ };
+
+ err = treeview_walk(cm_ctx.tree, root, cookie_manager_walk_cb, &tw,
+ TREE_NODE_FOLDER);
+ if (err != NSERROR_OK)
+ return err;
+
+ *found = tw.folder;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Free a cookie manager entry's treeview field data.
+ *
+ * \param e Cookie manager entry to free data from
+ */
+static void cookie_manager_free_treeview_field_data(
+ struct cookie_manager_entry *e)
+{
+ /* Eww */
+ free((void *)e->data[CM_NAME].value);
+ free((void *)e->data[CM_CONTENT].value);
+ free((void *)e->data[CM_DOMAIN].value);
+ free((void *)e->data[CM_PATH].value);
+ free((void *)e->data[CM_EXPIRES].value);
+ free((void *)e->data[CM_LAST_USED].value);
+ free((void *)e->data[CM_RESTRICTIONS].value);
+ free((void *)e->data[CM_VERSION].value);
+ free((void *)e->data[CM_PERSISTENT].value);
+}
+
+
+/**
+ * Set a cookie manager entry's data from the cookie_data.
+ *
+ * \param e Cookie manager entry to set up
+ * \param data Data associated with entry's cookie
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+static nserror cookie_manager_set_treeview_field_data(
+ struct cookie_manager_entry *e,
+ const struct cookie_data *data)
+{
+ const char *temp;
+ const char *date;
+ char *date2;
+ char buffer[32];
+
+ assert(e != NULL);
+ assert(data != NULL);
+
+ /* Set the Name text */
+ temp = data->name;
+ e->data[CM_NAME].field = cm_ctx.fields[CM_NAME].field;
+ e->data[CM_NAME].value = strdup(temp);
+ e->data[CM_NAME].value_len = (e->data[CM_NAME].value != NULL) ?
+ strlen(temp) : 0;
+
+ /* Set the Content text */
+ temp = data->value;
+ e->data[CM_CONTENT].field = cm_ctx.fields[CM_CONTENT].field;
+ e->data[CM_CONTENT].value = strdup(temp);
+ e->data[CM_CONTENT].value_len = (e->data[CM_CONTENT].value != NULL) ?
+ strlen(temp) : 0;
+
+ /* Set the Domain text */
+ temp = data->domain;
+ e->data[CM_DOMAIN].field = cm_ctx.fields[CM_DOMAIN].field;
+ e->data[CM_DOMAIN].value = strdup(temp);
+ e->data[CM_DOMAIN].value_len = (e->data[CM_DOMAIN].value != NULL) ?
+ strlen(temp) : 0;
+
+ /* Set the Path text */
+ temp = data->path;
+ e->data[CM_PATH].field = cm_ctx.fields[CM_PATH].field;
+ e->data[CM_PATH].value = strdup(temp);
+ e->data[CM_PATH].value_len = (e->data[CM_PATH].value != NULL) ?
+ strlen(temp) : 0;
+
+ /* Set the Expires date text */
+ date = ctime(&data->expires);
+ date2 = strdup(date);
+ if (date2 != NULL) {
+ assert(date2[24] == '\n');
+ date2[24] = '\0';
+ }
+
+ e->data[CM_EXPIRES].field = cm_ctx.fields[CM_EXPIRES].field;
+ e->data[CM_EXPIRES].value = date2;
+ e->data[CM_EXPIRES].value_len = (date2 != NULL) ? 24 : 0;
+
+ /* Set the Last used date text */
+ date = ctime(&data->last_used);
+ date2 = strdup(date);
+ if (date2 != NULL) {
+ assert(date2[24] == '\n');
+ date2[24] = '\0';
+ }
+
+ e->data[CM_LAST_USED].field = cm_ctx.fields[CM_LAST_USED].field;
+ e->data[CM_LAST_USED].value = date2;
+ e->data[CM_LAST_USED].value_len = (date2 != NULL) ? 24 : 0;
+
+ /* Set the Restrictions text */
+ if (data->secure && data->http_only)
+ temp = "Secure hosts via https only";
+ else if (data->secure)
+ temp = "Secure hosts only";
+ else if (data->http_only)
+ temp = "HTTP connections only";
+ else
+ temp = "None";
+
+ e->data[CM_RESTRICTIONS].field = cm_ctx.fields[CM_RESTRICTIONS].field;
+ e->data[CM_RESTRICTIONS].value = strdup(temp);
+ e->data[CM_RESTRICTIONS].value_len =
+ (e->data[CM_RESTRICTIONS].value != NULL) ?
+ strlen(temp) : 0;
+
+ /* Set the Version text */
+ snprintf(buffer, sizeof(buffer), "TreeVersion%i", data->version);
+ temp = messages_get(buffer);
+ e->data[CM_VERSION].field = cm_ctx.fields[CM_VERSION].field;
+ e->data[CM_VERSION].value = strdup(temp);
+ e->data[CM_VERSION].value_len = (e->data[CM_VERSION].value != NULL) ?
+ strlen(temp) : 0;
+
+ /* Set the Persistent text */
+ temp = data->no_destroy ? messages_get("Yes") : messages_get("No");
+ e->data[CM_PERSISTENT].field = cm_ctx.fields[CM_PERSISTENT].field;
+ e->data[CM_PERSISTENT].value = strdup(temp);
+ e->data[CM_PERSISTENT].value_len =
+ (e->data[CM_PERSISTENT].value != NULL) ?
+ strlen(temp) : 0;
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Creates an empty tree entry for a cookie, and links it into the tree.
+ *
+ * All information is copied from the cookie_data, and as such can
+ * be edited and should be freed.
+ *
+ * \param parent the node to link to
+ * \param data the cookie data to use
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+static nserror cookie_manager_create_cookie_node(
+ struct cookie_manager_folder *parent,
+ const struct cookie_data *data)
+{
+ nserror err;
+ struct cookie_manager_entry *cookie;
+
+ /* Create new cookie manager entry */
+ cookie = malloc(sizeof(struct cookie_manager_entry));
+ if (cookie == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ cookie->user_delete = false;
+
+ err = cookie_manager_set_treeview_field_data(cookie, data);
+ if (err != NSERROR_OK) {
+ free(cookie);
+ return err;
+ }
+
+ err = treeview_create_node_entry(cm_ctx.tree, &(cookie->entry),
+ parent->folder, TREE_REL_FIRST_CHILD,
+ cookie->data, cookie,
+ cm_ctx.built ? TREE_CREATE_NONE :
+ TREE_CREATE_SUPPRESS_RESIZE);
+ if (err != NSERROR_OK) {
+ cookie_manager_free_treeview_field_data(cookie);
+ free(cookie);
+ return err;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Updates a cookie manager entry for updated cookie_data.
+ *
+ * All information is copied from the cookie_data, and as such can
+ * be edited and should be freed.
+ *
+ * \param e the entry to update
+ * \param data the cookie data to use
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+static nserror cookie_manager_update_cookie_node(
+ struct cookie_manager_entry *e,
+ const struct cookie_data *data)
+{
+ nserror err;
+
+ assert(e != NULL);
+
+ /* Reset to defaults */
+ e->user_delete = false;
+ cookie_manager_free_treeview_field_data(e);
+
+ /* Set new field values from the cookie_data */
+ err = cookie_manager_set_treeview_field_data(e, data);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ /* Update the treeview */
+ err = treeview_update_node_entry(cm_ctx.tree, e->entry, e->data, e);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Creates an empty tree folder for a cookie domain, and links it into the tree.
+ *
+ * All information is copied from the cookie_data, and as such can
+ * be edited and should be freed.
+ *
+ * \param folder updated to the new folder
+ * \param data the cookie data to use
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+static nserror cookie_manager_create_domain_folder(
+ struct cookie_manager_folder **folder,
+ const struct cookie_data *data)
+{
+ nserror err;
+ struct cookie_manager_folder *f;
+
+ /* Create new cookie manager entry */
+ f = malloc(sizeof(struct cookie_manager_folder));
+ if (f == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ f->data.field = cm_ctx.fields[N_FIELDS - 1].field;
+ f->data.value = strdup(data->domain);
+ f->data.value_len = (f->data.value != NULL) ?
+ strlen(data->domain) : 0;
+
+ err = treeview_create_node_folder(cm_ctx.tree, &(f->folder),
+ NULL, TREE_REL_FIRST_CHILD, &f->data, f,
+ cm_ctx.built ? TREE_CREATE_NONE :
+ TREE_CREATE_SUPPRESS_RESIZE);
+ if (err != NSERROR_OK) {
+ free((void *)f->data.value);
+ free(f);
+ return err;
+ }
+
+ *folder = f;
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface documented in cookie_manager.h */
+bool cookie_manager_add(const struct cookie_data *data)
+{
+ struct cookie_manager_folder *parent = NULL;
+ struct cookie_manager_entry *cookie = NULL;
+ nserror err;
+
+ assert(data != NULL);
+
+ /* If we don't have a cookie manager at the moment, just return true */
+ if (cm_ctx.tree == NULL)
+ return true;
+
+ err = cookie_manager_find_folder(NULL, data->domain, &parent);
+ if (err != NSERROR_OK) {
+ return false;
+ }
+
+ if (parent == NULL) {
+ /* Need to create domain directory */
+ err = cookie_manager_create_domain_folder(&parent, data);
+ if (err != NSERROR_OK || parent == NULL)
+ return false;
+ }
+
+ /* Create cookie node */
+ err = cookie_manager_find_entry(parent->folder, data->name, &cookie);
+ if (err != NSERROR_OK)
+ return false;
+
+ if (cookie == NULL) {
+ err = cookie_manager_create_cookie_node(parent, data);
+ } else {
+ err = cookie_manager_update_cookie_node(cookie, data);
+ }
+ if (err != NSERROR_OK)
+ return false;
+
+ return true;
+}
+
+
+/* exported interface documented in cookie_manager.h */
+void cookie_manager_remove(const struct cookie_data *data)
+{
+ struct cookie_manager_folder *parent = NULL;
+ struct cookie_manager_entry *cookie = NULL;
+ nserror err;
+
+ assert(data != NULL);
+
+ /* If we don't have a cookie manager at the moment, just return */
+ if (cm_ctx.tree == NULL)
+ return;
+
+ err = cookie_manager_find_folder(NULL, data->domain, &parent);
+ if (err != NSERROR_OK || parent == NULL) {
+ /* Nothing to delete */
+ return;
+ }
+
+ err = cookie_manager_find_entry(parent->folder, data->name, &cookie);
+ if (err != NSERROR_OK || cookie == NULL) {
+ /* Nothing to delete */
+ return;
+ }
+
+ /* Delete the node */
+ treeview_delete_node(cm_ctx.tree, cookie->entry);
+}
+
+
+/**
+ * Initialise the treeview entry feilds
+ *
+ * \return true on success, false on memory exhaustion
+ */
+static nserror cookie_manager_init_entry_fields(void)
+{
+ int i;
+ const char *label;
+
+ for (i = 0; i < N_FIELDS; i++)
+ cm_ctx.fields[i].field = NULL;
+
+ cm_ctx.fields[CM_NAME].flags = TREE_FLAG_DEFAULT;
+ label = "TreeviewLabelName";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_NAME].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_CONTENT].flags = TREE_FLAG_SHOW_NAME;
+ label = "TreeviewLabelContent";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_CONTENT].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_DOMAIN].flags = TREE_FLAG_SHOW_NAME;
+ label = "TreeviewLabelDomain";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_DOMAIN].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_PATH].flags = TREE_FLAG_SHOW_NAME;
+ label = "TreeviewLabelPath";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_PATH].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_EXPIRES].flags = TREE_FLAG_SHOW_NAME;
+ label = "TreeviewLabelExpires";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_EXPIRES].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_LAST_USED].flags = TREE_FLAG_SHOW_NAME;
+ label = "TreeviewLabelLastUsed";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_LAST_USED].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_RESTRICTIONS].flags = TREE_FLAG_SHOW_NAME;
+ label = "TreeviewLabelRestrictions";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_RESTRICTIONS].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_VERSION].flags = TREE_FLAG_SHOW_NAME;
+ label = "TreeviewLabelVersion";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_VERSION].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_PERSISTENT].flags = TREE_FLAG_SHOW_NAME;
+ label = "TreeviewLabelPersistent";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_PERSISTENT].field) !=
+ lwc_error_ok) {
+ goto error;
+ }
+
+ cm_ctx.fields[CM_DOMAIN_FOLDER].flags = TREE_FLAG_DEFAULT;
+ label = "TreeviewLabelDomainFolder";
+ label = messages_get(label);
+ if (lwc_intern_string(label, strlen(label),
+ &cm_ctx.fields[CM_DOMAIN_FOLDER].field) !=
+ lwc_error_ok) {
+ return false;
+ }
+
+ return NSERROR_OK;
+
+error:
+ for (i = 0; i < N_FIELDS; i++)
+ if (cm_ctx.fields[i].field != NULL)
+ lwc_string_unref(cm_ctx.fields[i].field);
+
+ return NSERROR_UNKNOWN;
+}
+
+/**
+ * Delete cookie manager entries (and optionally delete from urldb)
+ *
+ * \param e Cookie manager entry to delete.
+ */
+static void cookie_manager_delete_entry(struct cookie_manager_entry *e)
+{
+ const char *domain;
+ const char *path;
+ const char *name;
+
+ if (e->user_delete) {
+ /* Delete the cookie from URLdb */
+ domain = e->data[CM_DOMAIN].value;
+ path = e->data[CM_PATH].value;
+ name = e->data[CM_NAME].value;
+
+ if ((domain != NULL) && (path != NULL) && (name != NULL)) {
+
+ urldb_delete_cookie(domain, path, name);
+ } else {
+ LOG(("Delete cookie fail: "
+ "need domain, path, and name."));
+ }
+ }
+
+ /* Delete the cookie manager entry */
+ cookie_manager_free_treeview_field_data(e);
+ free(e);
+}
+
+
+static nserror cookie_manager_tree_node_folder_cb(
+ struct treeview_node_msg msg, void *data)
+{
+ struct cookie_manager_folder *f = data;
+
+ switch (msg.msg) {
+ case TREE_MSG_NODE_DELETE:
+ free(f);
+ break;
+
+ case TREE_MSG_NODE_EDIT:
+ break;
+
+ case TREE_MSG_NODE_LAUNCH:
+ break;
+ }
+
+ return NSERROR_OK;
+}
+static nserror cookie_manager_tree_node_entry_cb(
+ struct treeview_node_msg msg, void *data)
+{
+ struct cookie_manager_entry *e = data;
+
+ switch (msg.msg) {
+ case TREE_MSG_NODE_DELETE:
+ e->entry = NULL;
+ e->user_delete = msg.data.delete.user;
+ cookie_manager_delete_entry(e);
+ break;
+
+ case TREE_MSG_NODE_EDIT:
+ break;
+
+ case TREE_MSG_NODE_LAUNCH:
+ break;
+ }
+ return NSERROR_OK;
+}
+struct treeview_callback_table cm_tree_cb_t = {
+ .folder = cookie_manager_tree_node_folder_cb,
+ .entry = cookie_manager_tree_node_entry_cb
+};
+
+
+/* Exported interface, documented in cookie_manager.h */
+nserror cookie_manager_init(struct core_window_callback_table *cw_t,
+ void *core_window_handle)
+{
+ nserror err;
+
+ LOG(("Generating cookie manager data"));
+
+ /* Init. cookie manager treeview entry fields */
+ err = cookie_manager_init_entry_fields();
+ if (err != NSERROR_OK) {
+ cm_ctx.tree = NULL;
+ return err;
+ }
+
+ /* Create the cookie manager treeview */
+ err = treeview_create(&cm_ctx.tree, &cm_tree_cb_t,
+ N_FIELDS, cm_ctx.fields,
+ cw_t, core_window_handle,
+ TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS);
+ if (err != NSERROR_OK) {
+ cm_ctx.tree = NULL;
+ return err;
+ }
+
+ /* Load the cookies */
+ urldb_iterate_cookies(cookie_manager_add);
+
+ /* Cookie manager is built
+ * We suppress the treeview height callback on entry insertion before
+ * the treeview is built. */
+ cm_ctx.built = true;
+
+ LOG(("Generated cookie manager data"));
+
+ return NSERROR_OK;
+}
+
+
+/* Exported interface, documented in cookie_manager.h */
+nserror cookie_manager_fini(void)
+{
+ int i;
+ nserror err;
+
+ LOG(("Finalising cookie manager"));
+
+ cm_ctx.built = false;
+
+ /* Destroy the cookie manager treeview */
+ err = treeview_destroy(cm_ctx.tree);
+
+ /* Free cookie manager treeview entry fields */
+ for (i = 0; i < N_FIELDS; i++)
+ if (cm_ctx.fields[i].field != NULL)
+ lwc_string_unref(cm_ctx.fields[i].field);
+
+ LOG(("Finalised cookie manager"));
+
+ return err;
+}
+
+
+/* Exported interface, documented in cookie_manager.h */
+void cookie_manager_redraw(int x, int y, struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ treeview_redraw(cm_ctx.tree, x, y, clip, ctx);
+}
+
+
+/* Exported interface, documented in cookie_manager.h */
+void cookie_manager_mouse_action(browser_mouse_state mouse, int x, int y)
+{
+ treeview_mouse_action(cm_ctx.tree, mouse, x, y);
+}
+
+
+/* Exported interface, documented in cookie_manager.h */
+void cookie_manager_keypress(uint32_t key)
+{
+ treeview_keypress(cm_ctx.tree, key);
+}
+
diff --git a/desktop/cookie_manager.h b/desktop/cookie_manager.h
new file mode 100644
index 000000000..724923677
--- /dev/null
+++ b/desktop/cookie_manager.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Cookie Manager (interface).
+ */
+
+#ifndef _NETSURF_DESKTOP_COOKIE_MANAGER_H_
+#define _NETSURF_DESKTOP_COOKIE_MANAGER_H_
+
+#include <stdbool.h>
+
+#include "desktop/core_window.h"
+
+struct cookie_data;
+
+/**
+ * Initialise the cookie manager.
+ *
+ * This iterates through the URL database, enumerating the cookies and
+ * creates a treeview.
+ *
+ * This must be called before any other cookie_manager_* function.
+ *
+ * \param cw_t Callback table for core_window containing the treeview
+ * \param cw The core_window in which the treeview is shown
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+nserror cookie_manager_init(struct core_window_callback_table *cw_t,
+ void *core_window_handle);
+
+/**
+ * Finalise the cookie manager.
+ *
+ * This destroys the cookie manager treeview and the cookie manager module's
+ * internal data. After calling this if the cookie manager is required again,
+ * cookie_manager_init must be called.
+ *
+ * \return NSERROR_OK on success, appropriate error otherwise
+ */
+nserror cookie_manager_fini(void);
+
+/**
+ * Add/update a cookie to the viewer. (Called by urldb.)
+ *
+ * \param data Data of cookie being added/updated.
+ * \return true (for urldb_iterate_entries)
+ */
+bool cookie_manager_add(const struct cookie_data *data);
+
+/**
+ * Remove a cookie from viewer. (Called by urldb.)
+ *
+ * \param data Data of cookie being removed.
+ */
+void cookie_manager_remove(const struct cookie_data *data);
+
+/**
+ * Redraw the cookies manager.
+ *
+ * \param x X coordinate to render treeview at
+ * \param x Y coordinate to render treeview at
+ * \param clip Current clip rectangle (wrt tree origin)
+ * \param ctx Current redraw context
+ */
+void cookie_manager_redraw(int x, int y, struct rect *clip,
+ const struct redraw_context *ctx);
+
+/**
+ * Handles all kinds of mouse action
+ *
+ * \param mouse The current mouse state
+ * \param x X coordinate
+ * \param y Y coordinate
+ */
+void cookie_manager_mouse_action(browser_mouse_state mouse, int x, int y);
+
+
+/**
+ * Key press handling.
+ *
+ * \param key The ucs4 character codepoint
+ * \return true if the keypress is dealt with, false otherwise.
+ */
+void cookie_manager_keypress(uint32_t key);
+
+#endif