/* * Copyright 2006 Richard Wilson * Copyright 2009 Paul Blokus * * 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 . */ /** \file * Cookies (implementation). */ #include #include #include #include #include #include #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/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 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(const 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, 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); }