netsurf/desktop/cookies.c
2013-05-28 14:16:10 +01:00

526 lines
13 KiB
C

/*
* Copyright 2006 Richard Wilson <info@tinct.net>
* Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
*
* 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
* Cookies (implementation).
*/
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "content/content.h"
#include "content/hlcache.h"
#include "content/urldb.h"
#include "desktop/cookies.h"
#include "utils/nsoption.h"
#include "desktop/tree.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/schedule.h"
#include "utils/url.h"
#include "utils/utils.h"
/** Flags for each type of cookie tree node. */
enum tree_element_cookie {
TREE_ELEMENT_PERSISTENT = 0x01,
TREE_ELEMENT_VERSION = 0x02,
TREE_ELEMENT_SECURE = 0x03,
TREE_ELEMENT_LAST_USED = 0x04,
TREE_ELEMENT_EXPIRES = 0x05,
TREE_ELEMENT_PATH = 0x06,
TREE_ELEMENT_DOMAIN = 0x07,
TREE_ELEMENT_COMMENT = 0x08,
TREE_ELEMENT_VALUE = 0x09,
};
static struct tree *cookies_tree;
static struct node *cookies_tree_root;
static bool user_delete;
static hlcache_handle *folder_icon;
static hlcache_handle *cookie_icon;
/**
* Find an entry in the cookie tree
*
* \param node the node to check the children of
* \param title The title to find
* \return Pointer to node, or NULL if not found
*/
static struct node *cookies_find(struct node *node, const char *title)
{
struct node *search;
struct node_element *element;
assert(node !=NULL);
for (search = tree_node_get_child(node); search;
search = tree_node_get_next(search)) {
element = tree_node_find_element(search, TREE_ELEMENT_TITLE,
NULL);
if (strcmp(title, tree_node_element_get_text(element)) == 0)
return search;
}
return NULL;
}
/**
* Callback for all cookie tree nodes.
*/
static node_callback_resp cookies_node_callback(void *user_data, struct node_msg_data *msg_data)
{
struct node *node = msg_data->node;
struct node_element *domain, *path;
const char *domain_t, *path_t, *name_t;
char *space;
bool is_folder = tree_node_is_folder(node);
/* we don't remove any icons here */
if (msg_data->msg == NODE_DELETE_ELEMENT_IMG)
return NODE_CALLBACK_HANDLED;
/* let the tree handle events other than text data removal */
if (msg_data->msg != NODE_DELETE_ELEMENT_TXT)
return NODE_CALLBACK_NOT_HANDLED;
/* check if it's a domain folder */
if (is_folder)
return NODE_CALLBACK_NOT_HANDLED;
switch (msg_data->flag) {
case TREE_ELEMENT_TITLE:
if (!user_delete)
break;
/* get the rest of the cookie data */
domain = tree_node_find_element(node,
TREE_ELEMENT_DOMAIN, NULL);
path = tree_node_find_element(node, TREE_ELEMENT_PATH,
NULL);
if ((domain != NULL) &&
(path != NULL)) {
domain_t = tree_node_element_get_text(domain) +
strlen(messages_get(
"TreeDomain")) - 4;
space = strchr(domain_t, ' ');
if (space != NULL)
*space = '\0';
path_t = tree_node_element_get_text(path) +
strlen(messages_get("TreePath"))
- 4;
space = strchr(path_t, ' ');
if (space != NULL)
*space = '\0';
name_t = msg_data->data.text;
urldb_delete_cookie(domain_t, path_t, name_t);
}
break;
default:
break;
}
free(msg_data->data.text);
return NODE_CALLBACK_HANDLED;
}
/**
* Updates a tree entry for a cookie.
*
* All information is copied from the cookie_data, and as such can
* be edited and should be freed.
*
* \param node The node to update
* \param data The cookie data to use
* \return true if node updated, or false for failure
*/
static bool cookies_update_cookie_node(struct node *node,
const struct cookie_data *data)
{
struct node_element *element;
char buffer[32];
assert(data != NULL);
/* update the value text */
element = tree_node_find_element(node, TREE_ELEMENT_VALUE, NULL);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreeValue",
data->value != NULL ?
data->value :
messages_get("TreeUnused")));
/* update the comment text */
if ((data->comment != NULL) &&
(strcmp(data->comment, "") != 0)) {
element = tree_node_find_element(node, TREE_ELEMENT_COMMENT, NULL);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreeComment",
data->comment));
}
/* update domain text */
element = tree_node_find_element(node, TREE_ELEMENT_DOMAIN, element);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreeDomain",
data->domain,
data->domain_from_set ?
messages_get("TreeHeaders") :
""));
/* update path text */
element = tree_node_find_element(node, TREE_ELEMENT_PATH, element);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreePath", data->path,
data->path_from_set ?
messages_get("TreeHeaders") :
""));
/* update expiry text */
element = tree_node_find_element(node, TREE_ELEMENT_EXPIRES, element);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreeExpires",
(data->expires > 0)
? (data->expires == 1)
? messages_get("TreeSession")
: ctime(&data->expires)
: messages_get("TreeUnknown")));
/* update last used text */
element = tree_node_find_element(node, TREE_ELEMENT_LAST_USED, element);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreeLastUsed",
(data->last_used > 0) ?
ctime(&data->last_used) :
messages_get("TreeUnknown")));
/* update secure text */
element = tree_node_find_element(node, TREE_ELEMENT_SECURE, element);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreeSecure",
data->secure ?
messages_get("Yes") :
messages_get("No")));
/* update version text */
element = tree_node_find_element(node, TREE_ELEMENT_VERSION, element);
snprintf(buffer, sizeof(buffer), "TreeVersion%i", data->version);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreeVersion",
messages_get(buffer)));
/* update persistant text */
element = tree_node_find_element(node, TREE_ELEMENT_PERSISTENT, element);
tree_update_element_text(cookies_tree,
element,
messages_get_buff("TreePersistent",
data->no_destroy ?
messages_get("Yes") :
messages_get("No")));
return true;
}
/**
* 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 the node created, or NULL for failure
*/
static struct node *cookies_create_cookie_node(struct node *parent,
const struct cookie_data *data)
{
struct node *node;
node = tree_create_leaf_node(cookies_tree,
NULL,
data->name,
false, false, false);
if (node == NULL) {
return NULL;
}
tree_set_node_user_callback(node, cookies_node_callback, NULL);
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_PERSISTENT, false);
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_VERSION, false);
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_SECURE, false);
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_LAST_USED, false);
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_EXPIRES, false);
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_PATH, false);
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_DOMAIN, false);
if ((data->comment) && (strcmp(data->comment, "")))
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_COMMENT, false);
tree_create_node_element(node, NODE_ELEMENT_TEXT,
TREE_ELEMENT_VALUE, false);
tree_set_node_icon(cookies_tree, node, cookie_icon);
if (!cookies_update_cookie_node(node, data))
{
tree_delete_node(NULL, node, false);
return NULL;
}
tree_link_node(cookies_tree, parent, node, false);
return node;
}
/**
* Called when scheduled event gets fired. Actually performs the update.
*/
static void cookies_schedule_callback(const void *scheduled_data)
{
const struct cookie_data *data = scheduled_data;
struct node *node = NULL;
struct node *cookie_node = NULL;
assert(data != NULL);
node = cookies_find(cookies_tree_root, data->domain);
if (node == NULL) {
node = tree_create_folder_node(cookies_tree,
cookies_tree_root,
data->domain,
false, false, false);
if (node != NULL) {
tree_set_node_user_callback(node,
cookies_node_callback,
NULL);
tree_set_node_icon(cookies_tree, node, folder_icon);
}
}
if (node != NULL) {
cookie_node = cookies_find(node, data->name);
if (cookie_node == NULL) {
cookies_create_cookie_node(node, data);
} else {
cookies_update_cookie_node(cookie_node, data);
}
}
}
/**
* Initialises cookies tree.
*
* \param data user data for the callbacks
* \param start_redraw callback function called before every redraw
* \param end_redraw callback function called after every redraw
* \return true on success, false on memory exhaustion
*/
bool cookies_initialise(struct tree *tree, const char* folder_icon_name, const char* cookie_icon_name)
{
if (tree == NULL)
return false;
folder_icon = tree_load_icon(folder_icon_name);
cookie_icon = tree_load_icon(cookie_icon_name);
/* Create an empty tree */
cookies_tree = tree;
cookies_tree_root = tree_get_root(cookies_tree);
user_delete = false;
urldb_iterate_cookies(cookies_schedule_update);
tree_set_node_expanded(cookies_tree, cookies_tree_root,
false, true, true);
return true;
}
/**
* Get flags with which the cookies tree should be created;
*
* \return the flags
*/
unsigned int cookies_get_tree_flags(void)
{
return TREE_DELETE_EMPTY_DIRS;
}
/* exported interface documented in cookies.h */
bool cookies_schedule_update(const struct cookie_data *data)
{
assert(data != NULL);
assert(user_delete == false);
if (cookies_tree_root != NULL)
cookies_schedule_callback(data);
return true;
}
/* exported interface documented in cookies.h */
void cookies_remove(const struct cookie_data *data)
{
assert(data != NULL);
if (cookies_tree_root != NULL)
cookies_schedule_callback(data);
}
/**
* Free memory and release all other resources.
*/
void cookies_cleanup(void)
{
hlcache_handle_release(folder_icon);
hlcache_handle_release(cookie_icon);
}
/* Actions to be connected to front end specific toolbars */
/**
* Delete nodes which are currently selected.
*/
void cookies_delete_selected(void)
{
user_delete = true;
tree_delete_selected_nodes(cookies_tree, cookies_tree_root);
user_delete = false;
}
/**
* Delete all nodes.
*/
void cookies_delete_all(void)
{
bool needs_redraw = tree_get_redraw(cookies_tree);
if (needs_redraw)
tree_set_redraw(cookies_tree, false);
user_delete = true;
tree_set_node_selected(cookies_tree, cookies_tree_root, true, true);
tree_delete_selected_nodes(cookies_tree, cookies_tree_root);
user_delete = false;
if (needs_redraw)
tree_set_redraw(cookies_tree, true);
}
/**
* Select all nodes in the tree.
*/
void cookies_select_all(void)
{
tree_set_node_selected(cookies_tree, cookies_tree_root, true, true);
}
/**
* Unselect all nodes.
*/
void cookies_clear_selection(void)
{
tree_set_node_selected(cookies_tree, cookies_tree_root, true, false);
}
/**
* Expand both domain and cookie nodes.
*/
void cookies_expand_all(void)
{
tree_set_node_expanded(cookies_tree, cookies_tree_root,
true, true, true);
}
/**
* Expand domain nodes only.
*/
void cookies_expand_domains(void)
{
tree_set_node_expanded(cookies_tree, cookies_tree_root,
true, true, false);
}
/**
* Expand cookie nodes only.
*/
void cookies_expand_cookies(void)
{
tree_set_node_expanded(cookies_tree, cookies_tree_root,
true, false, true);
}
/**
* Collapse both domain and cookie nodes.
*/
void cookies_collapse_all(void)
{
tree_set_node_expanded(cookies_tree, cookies_tree_root,
false, true, true);
}
/**
* Collapse domain nodes only.
*/
void cookies_collapse_domains(void)
{
tree_set_node_expanded(cookies_tree, cookies_tree_root,
false, true, false);
}
/**
* Collapse cookie nodes only.
*/
void cookies_collapse_cookies(void)
{
tree_set_node_expanded(cookies_tree, cookies_tree_root,
false, false, true);
}