midori/midori/midori-privatedata.c

469 lines
18 KiB
C

/*
Copyright (C) 2008-2013 Christian Dywan <christian@twotoasts.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See the file COPYING for the full license text.
*/
#include "midori-privatedata.h"
#include "marshal.h"
#include "midori-platform.h"
#include "midori-core.h"
#include "config.h"
#include <string.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>
#include <sqlite3.h>
#define LIBSOUP_USE_UNSTABLE_REQUEST_API
#include <libsoup/soup-cache.h>
static void
midori_private_data_dialog_response_cb (GtkWidget* dialog,
gint response_id,
MidoriBrowser* browser)
{
if (response_id == GTK_RESPONSE_ACCEPT)
{
GtkToggleButton* button;
gint clear_prefs = MIDORI_CLEAR_NONE;
gint saved_prefs = MIDORI_CLEAR_NONE;
GList* data_items = midori_private_data_register_item (NULL, NULL, NULL);
GString* clear_data = g_string_new (NULL);
MidoriWebSettings* settings = midori_browser_get_settings (browser);
g_object_get (settings, "clear-private-data", &saved_prefs, NULL);
button = g_object_get_data (G_OBJECT (dialog), "session");
if (gtk_toggle_button_get_active (button))
{
GList* tabs = midori_browser_get_tabs (browser);
for (; tabs != NULL; tabs = g_list_next (tabs))
midori_browser_close_tab (browser, tabs->data);
g_list_free (tabs);
clear_prefs |= MIDORI_CLEAR_SESSION;
}
button = g_object_get_data (G_OBJECT (dialog), "history");
if (gtk_toggle_button_get_active (button))
{
KatzeArray* history = katze_object_get_object (browser, "history");
KatzeArray* trash = katze_object_get_object (browser, "trash");
katze_array_clear (history);
katze_array_clear (trash);
g_object_ref (history);
g_object_ref (trash);
clear_prefs |= MIDORI_CLEAR_HISTORY;
clear_prefs |= MIDORI_CLEAR_TRASH; /* For backward-compatibility */
}
if (clear_prefs != saved_prefs)
{
clear_prefs |= (saved_prefs & MIDORI_CLEAR_ON_QUIT);
g_object_set (settings, "clear-private-data", clear_prefs, NULL);
}
for (; data_items != NULL; data_items = g_list_next (data_items))
{
MidoriPrivateDataItem* privacy = data_items->data;
button = g_object_get_data (G_OBJECT (dialog), privacy->name);
g_return_if_fail (button != NULL && GTK_IS_TOGGLE_BUTTON (button));
if (gtk_toggle_button_get_active (button))
{
privacy->clear ();
g_string_append (clear_data, privacy->name);
g_string_append_c (clear_data, ',');
}
}
g_object_set (settings, "clear-data", clear_data->str, NULL);
g_string_free (clear_data, TRUE);
}
if (response_id != GTK_RESPONSE_DELETE_EVENT)
gtk_widget_destroy (dialog);
}
static void
midori_private_data_clear_on_quit_toggled_cb (GtkToggleButton* button,
MidoriWebSettings* settings)
{
gint clear_prefs = MIDORI_CLEAR_NONE;
g_object_get (settings, "clear-private-data", &clear_prefs, NULL);
clear_prefs ^= MIDORI_CLEAR_ON_QUIT;
g_object_set (settings, "clear-private-data", clear_prefs, NULL);
}
/**
* midori_private_data_dialog_is_empty:
* @dialog: the dialog
*
* The dialog is "empty" when none of the relevant checkboxes are activated.
*
* This function returns true if the dialog is empty.
**/
static bool
midori_private_data_dialog_is_empty (GtkDialog* dialog)
{
GtkToggleButton* button;
gint count = 0; // Counts the total number of checked boxes
GList* data_items = midori_private_data_register_item (NULL, NULL, NULL);
// Count these first two special ones
button = g_object_get_data (G_OBJECT (dialog), "session");
if (gtk_toggle_button_get_active (button))
count++;
button = g_object_get_data (G_OBJECT (dialog), "history");
if (gtk_toggle_button_get_active (button))
count++;
// Count each other one
for (; data_items != NULL; data_items = g_list_next (data_items))
{
MidoriPrivateDataItem* privacy = data_items->data;
button = g_object_get_data (G_OBJECT (dialog), privacy->name);
g_return_val_if_fail (button != NULL && GTK_IS_TOGGLE_BUTTON (button), false);
if (gtk_toggle_button_get_active (button))
count++;
}
// No checked boxes means the dialog is empty
if (count == 0)
return true;
return false;
}
/**
* midori_private_data_clear_button_check_sensitive:
* @dialog: the dialog to clear
*
* When called, sets the sensitivity of the clear private data button depending
* on whether the dialog is empty (see: midori_private_data_dialog_is_empty())
**/
static void
midori_private_data_clear_button_check_sensitive (GtkDialog* dialog)
{
GtkWidget* clear_button;
clear_button = gtk_dialog_get_widget_for_response (dialog, GTK_RESPONSE_ACCEPT);
if (midori_private_data_dialog_is_empty (dialog))
gtk_widget_set_sensitive (clear_button, FALSE);
else
gtk_widget_set_sensitive (clear_button, TRUE);
}
static void
midori_private_data_checkbox_toggled_cb (GtkToggleButton* button,
GtkWidget* dialog)
{
// This is a separate function so I can invoke it on start too
midori_private_data_clear_button_check_sensitive (GTK_DIALOG (dialog));
}
/**
* midori_private_data_get_dialog:
* @browser: the browser for which to create a dialog
*
* Shows a dialog for the user to configure private data settings
* and clear some items.
*
* Return value: (transfer full): the dialog
**/
GtkWidget*
midori_private_data_get_dialog (MidoriBrowser* browser)
{
GtkWidget* dialog;
GtkWidget* content_area;
GdkScreen* screen;
GtkSizeGroup* sizegroup;
GtkWidget* hbox;
GtkWidget* alignment;
GtkWidget* vbox;
GtkWidget* icon;
GtkWidget* label;
GtkWidget* button;
GList* data_items;
MidoriWebSettings* settings = midori_browser_get_settings (browser);
gchar* clear_data = katze_object_get_string (settings, "clear-data");
gint clear_prefs = MIDORI_CLEAR_NONE;
g_object_get (settings, "clear-private-data", &clear_prefs, NULL);
/* i18n: Dialog: Clear Private Data, in the Tools menu */
dialog = gtk_dialog_new_with_buttons (_("Clear Private Data"),
GTK_WINDOW (browser),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
_("_Clear private data"), GTK_RESPONSE_ACCEPT, NULL);
button = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE);
g_signal_connect (dialog, "response",
G_CALLBACK (midori_private_data_dialog_response_cb), browser);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
katze_widget_add_class (button, "destructive-action");
screen = gtk_widget_get_screen (GTK_WIDGET (browser));
if (screen)
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_CLEAR);
sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
hbox = gtk_hbox_new (FALSE, 4);
icon = gtk_image_new_from_icon_name ("edit-clear", GTK_ICON_SIZE_DIALOG);
gtk_size_group_add_widget (sizegroup, icon);
gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
label = gtk_label_new (_("Clear the following data:"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, FALSE, 0);
hbox = gtk_hbox_new (FALSE, 4);
icon = gtk_image_new ();
gtk_size_group_add_widget (sizegroup, icon);
gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
vbox = gtk_vbox_new (TRUE, 4);
alignment = gtk_alignment_new (0, 0, 1, 1);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 6, 12, 0);
button = gtk_check_button_new_with_mnemonic (_("Last open _tabs"));
if ((clear_prefs & MIDORI_CLEAR_SESSION) == MIDORI_CLEAR_SESSION)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
g_object_set_data (G_OBJECT (dialog), "session", button);
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
g_signal_connect (button, "toggled",
G_CALLBACK (midori_private_data_checkbox_toggled_cb), dialog);
/* i18n: Browsing history, visited web pages, closed tabs */
button = gtk_check_button_new_with_mnemonic (_("_History"));
if ((clear_prefs & MIDORI_CLEAR_HISTORY) == MIDORI_CLEAR_HISTORY)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
g_object_set_data (G_OBJECT (dialog), "history", button);
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
g_signal_connect (button, "toggled",
G_CALLBACK (midori_private_data_checkbox_toggled_cb), dialog);
data_items = midori_private_data_register_item (NULL, NULL, NULL);
for (; data_items != NULL; data_items = g_list_next (data_items))
{
MidoriPrivateDataItem* privacy = data_items->data;
button = gtk_check_button_new_with_mnemonic (privacy->label);
gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
g_object_set_data (G_OBJECT (dialog), privacy->name, button);
if (clear_data && strstr (clear_data, privacy->name))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
g_signal_connect (button, "toggled",
G_CALLBACK (midori_private_data_checkbox_toggled_cb), dialog);
}
midori_private_data_clear_button_check_sensitive (GTK_DIALOG (dialog));
g_free (clear_data);
gtk_container_add (GTK_CONTAINER (alignment), vbox);
gtk_box_pack_start (GTK_BOX (hbox), alignment, TRUE, TRUE, 4);
gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, FALSE, 8);
button = gtk_check_button_new_with_mnemonic (_("Clear private data when _quitting Midori"));
if ((clear_prefs & MIDORI_CLEAR_ON_QUIT) == MIDORI_CLEAR_ON_QUIT)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
g_signal_connect (button, "toggled",
G_CALLBACK (midori_private_data_clear_on_quit_toggled_cb), settings);
alignment = gtk_alignment_new (0, 0, 1, 1);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 2, 0);
gtk_container_add (GTK_CONTAINER (alignment), button);
gtk_box_pack_start (GTK_BOX (content_area), alignment, FALSE, FALSE, 0);
gtk_widget_show_all (content_area);
return dialog;
}
static void
midori_remove_config_file (gint clear_prefs,
gint flag,
const gchar* filename)
{
if ((clear_prefs & flag) == flag)
{
gchar* config_file = midori_paths_get_config_filename_for_writing (filename);
g_unlink (config_file);
g_free (config_file);
}
}
static void
midori_clear_web_cookies_cb (void)
{
#ifdef HAVE_WEBKIT2
WebKitWebContext* context = webkit_web_context_get_default ();
WebKitCookieManager* cookie_manager = webkit_web_context_get_cookie_manager (context);
webkit_cookie_manager_delete_all_cookies (cookie_manager);
/* FIXME: site data policy */
#else
SoupSession* session = webkit_get_default_session ();
MidoriWebSettings* settings = g_object_get_data (G_OBJECT (session), "midori-settings");
SoupSessionFeature* jar = soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
GSList* cookies = soup_cookie_jar_all_cookies (SOUP_COOKIE_JAR (jar));
/* HTTP Cookies/ Web Cookies */
for (; cookies != NULL; cookies = g_slist_next (cookies))
{
const gchar* domain = ((SoupCookie*)cookies->data)->domain;
if (midori_web_settings_get_site_data_policy (settings, domain)
== MIDORI_SITE_DATA_PRESERVE)
continue;
soup_cookie_jar_delete_cookie ((SoupCookieJar*)jar, cookies->data);
}
soup_cookies_free (cookies);
#endif
/* Local shared objects/ Flash cookies */
if (midori_web_settings_has_plugin_support ())
{
gchar* cache;
#ifdef GDK_WINDOWING_X11
cache = g_build_filename (g_get_home_dir (), ".macromedia", "Flash_Player", NULL);
midori_paths_remove_path (cache);
g_free (cache);
#elif defined(GDK_WINDOWING_WIN32)
cache = g_build_filename (g_get_user_data_dir (), "Macromedia", "Flash Player", NULL);
midori_paths_remove_path (cache);
g_free (cache);
#elif defined(GDK_WINDOWING_QUARTZ)
cache = g_build_filename (g_get_home_dir (), "Library", "Preferences",
"Macromedia", "Flash Player", NULL);
midori_paths_remove_path (cache);
g_free (cache);
#endif
}
#ifdef HAVE_WEBKIT2
/* TODO: clear databases and offline app caches */
#else
/* HTML5 databases */
webkit_remove_all_web_databases ();
/* HTML5 offline application caches */
/* Changing the size implies clearing the cache */
webkit_application_cache_set_maximum_size (
webkit_application_cache_get_maximum_size () - 1);
#endif
}
static void
midori_clear_saved_logins_cb (void)
{
sqlite3* db;
gchar* filename = midori_paths_get_config_filename_for_writing ("logins");
g_unlink (filename);
/* Form History database, written by the extension */
gchar* path = midori_paths_get_extension_config_dir ("formhistory");
katze_assign (filename, g_build_filename (path, "forms.db", NULL));
g_free (path);
if (sqlite3_open (filename, &db) == SQLITE_OK)
{
sqlite3_exec (db, "DELETE FROM forms", NULL, NULL, NULL);
sqlite3_close (db);
}
g_free (filename);
}
static void
midori_clear_web_cache_cb (void)
{
#ifdef HAVE_WEBKIT2
webkit_web_context_clear_cache (webkit_web_context_get_default ());
#else
SoupSession* session = webkit_get_default_session ();
SoupSessionFeature* feature = soup_session_get_feature (session, SOUP_TYPE_CACHE);
gchar* cache = g_build_filename (midori_paths_get_cache_dir (), "web", NULL);
soup_cache_clear (SOUP_CACHE (feature));
soup_cache_flush (SOUP_CACHE (feature));
midori_paths_remove_path (cache);
g_free (cache);
#endif
}
void
midori_private_data_register_built_ins ()
{
/* i18n: Logins and passwords in websites and web forms */
midori_private_data_register_item ("formhistory", _("Saved logins and _passwords"),
G_CALLBACK (midori_clear_saved_logins_cb));
midori_private_data_register_item ("web-cookies", _("Cookies and Website data"),
G_CALLBACK (midori_clear_web_cookies_cb));
/* TODO: Preserve page icons of search engines and merge privacy items */
midori_private_data_register_item ("web-cache", _("Web Cache"),
G_CALLBACK (midori_clear_web_cache_cb));
midori_private_data_register_item ("page-icons", _("Website icons"),
G_CALLBACK (midori_paths_clear_icons));
}
void
midori_private_data_clear_all (MidoriBrowser* browser)
{
KatzeArray* history = katze_object_get_object (browser, "history");
KatzeArray* trash = katze_object_get_object (browser, "trash");
GList* data_items = midori_private_data_register_item (NULL, NULL, NULL);
GList* tabs = midori_browser_get_tabs (browser);
for (; tabs; tabs = g_list_next (tabs))
midori_browser_close_tab (browser, tabs->data);
g_list_free (tabs);
if (history != NULL)
katze_array_clear (history);
if (trash != NULL)
katze_array_clear (trash);
g_object_unref (history);
g_object_unref (trash);
for (; data_items != NULL; data_items = g_list_next (data_items))
((MidoriPrivateDataItem*)(data_items->data))->clear ();
}
void
midori_private_data_on_quit (MidoriWebSettings* settings)
{
gint clear_prefs = MIDORI_CLEAR_NONE;
g_object_get (settings, "clear-private-data", &clear_prefs, NULL);
if (clear_prefs & MIDORI_CLEAR_ON_QUIT)
{
GList* data_items = midori_private_data_register_item (NULL, NULL, NULL);
gchar* clear_data = katze_object_get_string (settings, "clear-data");
midori_remove_config_file (clear_prefs, MIDORI_CLEAR_SESSION, "session.xbel");
midori_remove_config_file (clear_prefs, MIDORI_CLEAR_HISTORY, "history.db");
midori_remove_config_file (clear_prefs, MIDORI_CLEAR_HISTORY, "tabtrash.xbel");
for (; data_items != NULL; data_items = g_list_next (data_items))
{
MidoriPrivateDataItem* privacy = data_items->data;
if (clear_data && strstr (clear_data, privacy->name))
privacy->clear ();
}
g_free (clear_data);
}
}
/**
* midori_private_data_register_item:
* @name: the name of the privacy item
* @label: a user visible, localized label
* @clear: (scope async): a callback clearing data
*
* Registers an item to clear data, either via the
* Clear Private Data dialogue or when Midori quits.
*
* Return value: (element-type MidoriPrivateDataItem) (transfer none):
* a #GList of all previously-registered items if all arguments are
* given as %NULL, %NULL otherwise
**/
GList*
midori_private_data_register_item (const gchar* name,
const gchar* label,
GCallback clear)
{
static GList* items = NULL;
MidoriPrivateDataItem* item;
if (name == NULL && label == NULL && clear == NULL)
return items;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (label != NULL, NULL);
g_return_val_if_fail (clear != NULL, NULL);
item = g_new (MidoriPrivateDataItem, 1);
item->name = g_strdup (name);
item->label = g_strdup (label);
item->clear = clear;
items = g_list_append (items, item);
return NULL;
}