Add initial version of new cookie manager.

This commit is contained in:
Michael Drake 2013-07-25 16:07:46 +01:00
parent 4697d1ccc1
commit 219f5dac32
2 changed files with 860 additions and 0 deletions

758
desktop/cookie_manager.c Normal file
View File

@ -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);
}

102
desktop/cookie_manager.h Normal file
View File

@ -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