2010-10-05 23:14:46 +04:00
|
|
|
/*
|
|
|
|
* 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 "desktop/options.h"
|
|
|
|
#include "desktop/tree.h"
|
|
|
|
#include "utils/messages.h"
|
|
|
|
#include "utils/log.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;
|
|
|
|
|
2010-10-07 01:46:06 +04:00
|
|
|
assert(node !=NULL);
|
|
|
|
|
2010-10-05 23:14:46 +04:00
|
|
|
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 node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
name = strdup(data->name);
|
|
|
|
if (name == NULL) {
|
|
|
|
LOG(("malloc failed"));
|
|
|
|
warn_user("NoMemory", 0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
node = tree_create_leaf_node(cookies_tree, NULL, name,
|
|
|
|
false, false, false);
|
|
|
|
if (node == NULL) {
|
|
|
|
free(name);
|
|
|
|
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(void *scheduled_data)
|
|
|
|
{
|
|
|
|
const struct cookie_data *data = scheduled_data;
|
|
|
|
struct node *node = NULL;
|
|
|
|
struct node *cookie_node = NULL;
|
|
|
|
char *domain_cp;
|
|
|
|
|
|
|
|
assert(data != NULL);
|
|
|
|
|
|
|
|
node = cookies_find(cookies_tree_root, data->domain);
|
|
|
|
|
|
|
|
if (node == NULL) {
|
|
|
|
domain_cp = strdup(data->domain);
|
|
|
|
if (domain_cp == NULL) {
|
|
|
|
LOG(("malloc failed"));
|
|
|
|
warn_user("NoMemory", 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
node = tree_create_folder_node(cookies_tree,
|
|
|
|
cookies_tree_root, domain_cp,
|
|
|
|
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)
|
|
|
|
return;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (tree == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
folder_icon = tree_load_icon(tree_directory_icon_name);
|
|
|
|
cookie_icon = tree_load_icon(tree_content_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,
|
|
|
|
true, 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);
|
|
|
|
|
2010-10-07 01:46:06 +04:00
|
|
|
if (cookies_tree_root != NULL)
|
|
|
|
schedule(100, cookies_schedule_callback, (void *)data);
|
2010-10-05 23:14:46 +04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* exported interface documented in cookies.h */
|
|
|
|
void cookies_remove(const struct cookie_data *data)
|
|
|
|
{
|
|
|
|
assert(data != NULL);
|
|
|
|
|
2010-10-07 01:46:06 +04:00
|
|
|
if (cookies_tree_root != NULL)
|
|
|
|
schedule_remove(cookies_schedule_callback, (void *)data);
|
2010-10-05 23:14:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free memory and release all other resources.
|
|
|
|
*/
|
|
|
|
void cookies_cleanup(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|