[Xfce4-commits] <midori:master> Manage cookies accept policy per domain
Christian Dywan
noreply at xfce.org
Mon Feb 18 19:04:02 CET 2013
Updating branch refs/heads/master
to c395ad1bfba1e17d39cbf97344591fcc07638b1c (commit)
from 96ae0be82a9fd97f0f92ec3e92c1565c5633b1c6 (commit)
commit c395ad1bfba1e17d39cbf97344591fcc07638b1c
Author: Stephan Haller <nomad at froevel.de>
Date: Mon Feb 18 18:59:08 2013 +0100
Manage cookies accept policy per domain
Fixes: https://bugs.launchpad.net/midori/+bug/1114236
.../cookie-permission-manager-preferences-window.c | 768 +++++++++++++++
.../cookie-permission-manager-preferences-window.h | 55 ++
.../cookie-permissions/cookie-permission-manager.c | 995 ++++++++++++++++++++
.../cookie-permissions/cookie-permission-manager.h | 72 ++
extensions/cookie-permissions/main.c | 76 ++
po/POTFILES.in | 5 +
6 files changed, 1971 insertions(+), 0 deletions(-)
diff --git a/extensions/cookie-permissions/cookie-permission-manager-preferences-window.c b/extensions/cookie-permissions/cookie-permission-manager-preferences-window.c
new file mode 100644
index 0000000..c7ccc5b
--- /dev/null
+++ b/extensions/cookie-permissions/cookie-permission-manager-preferences-window.c
@@ -0,0 +1,768 @@
+/*
+ Copyright (C) 2013 Stephan Haller <nomad at froevel.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 "cookie-permission-manager-preferences-window.h"
+
+/* Define this class in GObject system */
+G_DEFINE_TYPE(CookiePermissionManagerPreferencesWindow,
+ cookie_permission_manager_preferences_window,
+ GTK_TYPE_DIALOG)
+
+/* Properties */
+enum
+{
+ PROP_0,
+
+ PROP_MANAGER,
+
+ PROP_LAST
+};
+
+static GParamSpec* CookiePermissionManagerPreferencesWindowProperties[PROP_LAST]={ 0, };
+
+/* Private structure - access only by public API if needed */
+#define COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW, CookiePermissionManagerPreferencesWindowPrivate))
+
+struct _CookiePermissionManagerPreferencesWindowPrivate
+{
+ /* Extension related */
+ CookiePermissionManager *manager;
+ sqlite3 *database;
+
+ /* Dialog related */
+ GtkWidget *contentArea;
+ GtkListStore *listStore;
+ GtkWidget *list;
+ GtkTreeSelection *listSelection;
+ GtkWidget *deleteButton;
+ GtkWidget *deleteAllButton;
+ GtkWidget *askForUnknownPolicyCheckbox;
+ GtkWidget *addDomainEntry;
+ GtkWidget *addDomainPolicyCombo;
+ GtkWidget *addDomainButton;
+
+ gint signalManagerChangedDatabaseID;
+ gint signalManagerAskForUnknownPolicyID;
+ gint signalAskForUnknownPolicyID;
+};
+
+enum
+{
+ DOMAIN_COLUMN,
+ POLICY_COLUMN,
+ N_COLUMN
+};
+
+
+/* IMPLEMENTATION: Private variables and methods */
+
+/* "Add domain"-button was pressed */
+static void _cookie_permission_manager_preferences_on_add_domain_clicked(CookiePermissionManagerPreferencesWindow *self,
+ gpointer *inUserData)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ gchar *domain;
+ const gchar *domainStart, *domainEnd;
+ gchar *realDomain;
+ GtkTreeIter policyIter;
+
+ g_return_if_fail(priv->database);
+
+ /* Get domain name entered */
+ domain=g_hostname_to_ascii(gtk_entry_get_text(GTK_ENTRY(priv->addDomainEntry)));
+
+ /* Trim whitespaces from start and end of entered domain name */
+ domainStart=domain;
+ while(*domainStart && g_ascii_isspace(*domainStart)) domainStart++;
+
+ domainEnd=domain+strlen(domain)-1;
+ while(*domainEnd && g_ascii_isspace(*domainEnd)) domainEnd--;
+ if(domainEnd<=domainStart) return;
+
+ /* Seperate domain name from whitespaces */
+ realDomain=g_strndup(domain, domainEnd-domainStart+1);
+ if(!realDomain) return;
+
+ /* Get policy from combo box */
+ if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(priv->addDomainPolicyCombo), &policyIter))
+ {
+ gchar *sql;
+ gchar *error=NULL;
+ gint success;
+ gint policy;
+ gchar *policyName;
+
+ /* Get policy value to set for domain */
+ gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(priv->addDomainPolicyCombo)),
+ &policyIter,
+ 0, &policy,
+ 1, &policyName,
+ -1);
+
+ /* Add domain name and the selected policy to database */
+ sql=sqlite3_mprintf("INSERT OR REPLACE INTO policies (domain, value) VALUES ('%q', %d);",
+ realDomain,
+ policy);
+ success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
+
+ /* Show error message if any */
+ if(success==SQLITE_OK)
+ {
+ gtk_list_store_append(priv->listStore, &policyIter);
+ gtk_list_store_set(priv->listStore,
+ &policyIter,
+ DOMAIN_COLUMN, realDomain,
+ POLICY_COLUMN, policyName,
+ -1);
+ }
+ else g_warning(_("SQL fails: %s"), error);
+
+
+ if(error) sqlite3_free(error);
+
+ /* Free allocated resources */
+ sqlite3_free(sql);
+ }
+
+ /* Free allocated resources */
+ g_free(realDomain);
+ g_free(domain);
+}
+
+/* Entry containing domain name which may be added to list has changed */
+static void _cookie_permission_manager_preferences_on_add_domain_entry_changed(CookiePermissionManagerPreferencesWindow *self,
+ GtkEditable *inEditable)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ gchar *asciiDomain, *checkAsciiDomain;
+ gchar *asciiDomainStart, *asciiDomainEnd;
+ gint dots;
+ gboolean isValid=FALSE;
+
+ /* Get ASCII representation of domain name entered */
+ asciiDomain=g_hostname_to_ascii(gtk_entry_get_text(GTK_ENTRY(priv->addDomainEntry)));
+
+ /* Trim whitespaces from start and end of entered domain name */
+ asciiDomainStart=asciiDomain;
+ while(*asciiDomainStart && g_ascii_isspace(*asciiDomainStart)) asciiDomainStart++;
+
+ asciiDomainEnd=asciiDomain+strlen(asciiDomain)-1;
+ while(*asciiDomainEnd && g_ascii_isspace(*asciiDomainEnd)) asciiDomainEnd--;
+
+ /* We allow only domain names and not cookie domain name so entered name
+ * must not start with a dot
+ */
+ checkAsciiDomain=asciiDomainStart;
+ isValid=(*asciiDomainStart!='.' && *asciiDomainEnd!='.');
+
+ /* Now check if ASCII domain name is valid (very very simple check)
+ * and contains a hostname besides TLD
+ */
+ dots=0;
+
+ while(*checkAsciiDomain &&
+ checkAsciiDomain<=asciiDomainEnd &&
+ isValid)
+ {
+ /* Check for dot as (at least the first one) seperates hostname from TLD */
+ if(*checkAsciiDomain=='.') dots++;
+ else
+ {
+ /* Check for valid characters in domain name.
+ * Valid domain name can only contain ASCII alphabetic letters,
+ * digits (0-9) and hyphens ('-')
+ */
+ isValid=(g_ascii_isalpha(*checkAsciiDomain) ||
+ g_ascii_isdigit(*checkAsciiDomain) ||
+ *checkAsciiDomain=='-');
+ }
+
+ checkAsciiDomain++;
+ }
+
+ /* If we have not reached the trimmed end of string something must have gone wrong
+ * and domain entered is invalid. If domain name entered excluding dots is longer
+ * than 255 character it is also invalid.
+ */
+ if(checkAsciiDomain<asciiDomainEnd) isValid=FALSE;
+ else if((checkAsciiDomain-asciiDomainStart-dots)>255) isValid=FALSE;
+
+ /* We need at least one dot in domain name (minimum number of dots to seperate
+ * hostname from TLD)
+ */
+ isValid=(isValid && dots>0);
+
+ /* Activate "add" button if hostname (equal to domain name here) is valid */
+ gtk_widget_set_sensitive(priv->addDomainButton, isValid);
+
+ /* Free allocated resources */
+ g_free(asciiDomain);
+}
+
+/* Fill domain list with stored policies */
+static void _cookie_permission_manager_preferences_window_fill(CookiePermissionManagerPreferencesWindow *self)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ gint success;
+ sqlite3_stmt *statement=NULL;
+
+ /* Clear tree/list view */
+ gtk_list_store_clear(priv->listStore);
+
+ /* If no database is present return here */
+ if(!priv->database) return;
+
+ /* Fill list store with policies from database */
+ success=sqlite3_prepare_v2(priv->database,
+ "SELECT domain, value FROM policies;",
+ -1,
+ &statement,
+ NULL);
+ if(statement && success==SQLITE_OK)
+ {
+ gchar *domain;
+ gint policy;
+ gchar *policyName;
+ GtkTreeIter iter;
+
+ while(sqlite3_step(statement)==SQLITE_ROW)
+ {
+ /* Get values */
+ domain=(gchar*)sqlite3_column_text(statement, 0);
+ policy=sqlite3_column_int(statement, 1);
+
+ switch(policy)
+ {
+ case COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT:
+ policyName=_("Accept");
+ break;
+
+ case COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION:
+ policyName=_("Accept for session");
+ break;
+
+ case COOKIE_PERMISSION_MANAGER_POLICY_BLOCK:
+ policyName=_("Block");
+ break;
+
+ default:
+ policyName=NULL;
+ break;
+ }
+
+ if(policyName)
+ {
+ gtk_list_store_append(priv->listStore, &iter);
+ gtk_list_store_set(priv->listStore,
+ &iter,
+ DOMAIN_COLUMN, domain,
+ POLICY_COLUMN, policyName,
+ -1);
+ }
+ }
+ }
+ else g_warning(_("SQL fails: %s"), sqlite3_errmsg(priv->database));
+
+ sqlite3_finalize(statement);
+}
+
+/* Database instance in manager changed */
+static void _cookie_permission_manager_preferences_window_manager_database_changed(CookiePermissionManagerPreferencesWindow *self,
+ GParamSpec *inSpec,
+ gpointer inUserData)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ CookiePermissionManager *manager=COOKIE_PERMISSION_MANAGER(inUserData);
+ sqlite3 *database;
+
+ /* Close connection to any open database */
+ if(priv->database) sqlite3_close(priv->database);
+ priv->database=NULL;
+
+ /* Get pointer to new database and open database */
+ g_object_get(manager, "database", &database, NULL);
+ if(database)
+ {
+ const gchar *databaseFile;
+ gint success;
+
+ databaseFile=sqlite3_db_filename(database, NULL);
+ success=sqlite3_open(databaseFile, &priv->database);
+ if(success!=SQLITE_OK)
+ {
+ g_warning(_("Could not open database of extenstion: %s"), sqlite3_errmsg(priv->database));
+
+ if(priv->database) sqlite3_close(priv->database);
+ priv->database=NULL;
+ }
+ }
+
+ /* Fill list with new database */
+ _cookie_permission_manager_preferences_window_fill(self);
+
+ /* Set up availability of management buttons */
+ gtk_widget_set_sensitive(priv->deleteAllButton, priv->database!=NULL);
+ gtk_widget_set_sensitive(priv->list, priv->database!=NULL);
+
+ return;
+}
+
+/* Ask-for-unknown-policy in manager changed or check-box changed */
+static void _cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed(CookiePermissionManagerPreferencesWindow *self,
+ GParamSpec *inSpec,
+ gpointer inUserData)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ CookiePermissionManager *manager=COOKIE_PERMISSION_MANAGER(inUserData);
+ gboolean doAsk;
+
+ /* Get new ask-for-unknown-policy value */
+ g_object_get(manager, "ask-for-unknown-policy", &doAsk, NULL);
+
+ /* Set toogle in widget (but block signal for toggle) */
+ g_signal_handler_block(priv->askForUnknownPolicyCheckbox, priv->signalAskForUnknownPolicyID);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->askForUnknownPolicyCheckbox), doAsk);
+ g_signal_handler_unblock(priv->askForUnknownPolicyCheckbox, priv->signalAskForUnknownPolicyID);
+}
+
+static void _cookie_permission_manager_preferences_window_ask_for_unknown_policy_changed(CookiePermissionManagerPreferencesWindow *self,
+ gpointer *inUserData)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ gboolean doAsk;
+
+ /* Get toogle state of widget (but block signal for manager) and set in manager */
+ g_signal_handler_block(priv->manager, priv->signalManagerAskForUnknownPolicyID);
+ doAsk=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->askForUnknownPolicyCheckbox));
+ g_object_set(priv->manager, "ask-for-unknown-policy", doAsk, NULL);
+ g_signal_handler_unblock(priv->manager, priv->signalManagerAskForUnknownPolicyID);
+}
+
+/* Selection in list changed */
+void _cookie_permission_manager_preferences_changed_selection(CookiePermissionManagerPreferencesWindow *self,
+ GtkTreeSelection *inSelection)
+{
+ gboolean selected=(gtk_tree_selection_count_selected_rows(inSelection)>0 ? TRUE: FALSE);
+
+ gtk_widget_set_sensitive(self->priv->deleteButton, selected);
+}
+
+/* Delete button was clicked on selection */
+void _cookie_permission_manager_preferences_on_delete_selection(CookiePermissionManagerPreferencesWindow *self,
+ GtkButton *inButton)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ GList *rows, *row, *refs=NULL;
+ GtkTreeRowReference *ref;
+ GtkTreeModel *model=GTK_TREE_MODEL(priv->listStore);
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gchar *domain;
+ gchar *sql;
+ gint success;
+ gchar *error;
+
+ /* Get selected rows in list and create a row reference because
+ * we will modify the model while iterating through selected rows
+ */
+ rows=gtk_tree_selection_get_selected_rows(priv->listSelection, &model);
+ for(row=rows; row; row=row->next)
+ {
+ ref=gtk_tree_row_reference_new(model, (GtkTreePath*)row->data);
+ refs=g_list_prepend(refs, ref);
+ }
+ g_list_foreach(rows,(GFunc)gtk_tree_path_free, NULL);
+ g_list_free(rows);
+
+ /* Delete each selected row by its reference */
+ for(row=refs; row; row=row->next)
+ {
+ /* Get domain from selected row */
+ path=gtk_tree_row_reference_get_path((GtkTreeRowReference*)row->data);
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_model_get(model, &iter, DOMAIN_COLUMN, &domain, -1);
+
+ /* Delete domain from database */
+ sql=sqlite3_mprintf("DELETE FROM policies WHERE domain='%q';", domain);
+ success=sqlite3_exec(priv->database,
+ sql,
+ NULL,
+ NULL,
+ &error);
+ if(success!=SQLITE_OK || error)
+ {
+ if(error)
+ {
+ g_critical(_("Failed to execute database statement: %s"), error);
+ sqlite3_free(error);
+ }
+ else g_critical(_("Failed to execute database statement: %s"), sqlite3_errmsg(priv->database));
+ }
+ sqlite3_free(sql);
+
+ /* Delete row from model */
+ gtk_list_store_remove(priv->listStore, &iter);
+ }
+ g_list_foreach(refs,(GFunc)gtk_tree_row_reference_free, NULL);
+ g_list_free(refs);
+}
+
+/* Delete all button was clicked */
+void _cookie_permission_manager_preferences_on_delete_all(CookiePermissionManagerPreferencesWindow *self,
+ GtkButton *inButton)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ gint success;
+ gchar *error=NULL;
+ GtkWidget *dialog;
+ gint dialogResponse;
+
+ /* Ask user if he really wants to delete all permissions */
+ dialog=gtk_message_dialog_new(GTK_WINDOW(self),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ _("Do you really want to delete all cookie permissions?"));
+
+ gtk_window_set_title(GTK_WINDOW(dialog), _("Delete all cookie permissions?"));
+ gtk_window_set_icon_name(GTK_WINDOW(dialog), GTK_STOCK_PROPERTIES);
+
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
+ _("This action will delete all cookie permissions. "
+ "You will be asked for permissions again for each web site visited."));
+
+ dialogResponse=gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ if(dialogResponse==GTK_RESPONSE_NO) return;
+
+ /* Delete all permission */
+ success=sqlite3_exec(priv->database,
+ "DELETE FROM policies;",
+ NULL,
+ NULL,
+ &error);
+
+ if(success!=SQLITE_OK || error)
+ {
+ if(error)
+ {
+ g_critical(_("Failed to execute database statement: %s"), error);
+ sqlite3_free(error);
+ }
+ }
+
+ /* Re-setup list */
+ _cookie_permission_manager_preferences_window_fill(self);
+}
+
+/* IMPLEMENTATION: GObject */
+
+/* Finalize this object */
+static void cookie_permission_manager_preferences_window_finalize(GObject *inObject)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv=COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(inObject)->priv;
+
+ /* Dispose allocated resources */
+ if(priv->database) sqlite3_close(priv->database);
+ priv->database=NULL;
+
+ if(priv->manager)
+ {
+ if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
+ priv->signalManagerChangedDatabaseID=0;
+
+ if(priv->signalManagerAskForUnknownPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerAskForUnknownPolicyID);
+ priv->signalManagerAskForUnknownPolicyID=0;
+
+ g_object_unref(priv->manager);
+ priv->manager=NULL;
+ }
+
+ /* Call parent's class finalize method */
+ G_OBJECT_CLASS(cookie_permission_manager_preferences_window_parent_class)->finalize(inObject);
+}
+
+/* Set/get properties */
+static void cookie_permission_manager_preferences_window_set_property(GObject *inObject,
+ guint inPropID,
+ const GValue *inValue,
+ GParamSpec *inSpec)
+{
+ CookiePermissionManagerPreferencesWindow *self=COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(inObject);
+ CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
+ GObject *manager;
+
+ switch(inPropID)
+ {
+ /* Construct-only properties */
+ case PROP_MANAGER:
+ /* Release old manager if available and disconnect signals */
+ if(priv->manager)
+ {
+ if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
+ priv->signalManagerChangedDatabaseID=0;
+
+ if(priv->signalManagerAskForUnknownPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerAskForUnknownPolicyID);
+ priv->signalManagerAskForUnknownPolicyID=0;
+
+ g_object_unref(priv->manager);
+ priv->manager=NULL;
+ }
+
+ /* Set new cookie permission manager and
+ * listen to changes in database property
+ */
+ manager=g_value_get_object(inValue);
+ if(manager)
+ {
+ priv->manager=g_object_ref(manager);
+
+ priv->signalManagerChangedDatabaseID=g_signal_connect_swapped(priv->manager,
+ "notify::database",
+ G_CALLBACK(_cookie_permission_manager_preferences_window_manager_database_changed),
+ self);
+ priv->signalManagerAskForUnknownPolicyID=g_signal_connect_swapped(priv->manager,
+ "notify::ask-for-unknown-policy",
+ G_CALLBACK(_cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed),
+ self);
+
+ _cookie_permission_manager_preferences_window_manager_database_changed(self, NULL, priv->manager);
+ _cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed(self, NULL, priv->manager);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+static void cookie_permission_manager_preferences_window_get_property(GObject *inObject,
+ guint inPropID,
+ GValue *outValue,
+ GParamSpec *inSpec)
+{
+ CookiePermissionManagerPreferencesWindow *self=COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(inObject);
+
+ switch(inPropID)
+ {
+ case PROP_MANAGER:
+ g_value_set_object(outValue, self->priv->manager);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+/* Class initialization
+ * Override functions in parent classes and define properties and signals
+ */
+static void cookie_permission_manager_preferences_window_class_init(CookiePermissionManagerPreferencesWindowClass *klass)
+{
+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
+
+ /* Override functions */
+ gobjectClass->finalize=cookie_permission_manager_preferences_window_finalize;
+ gobjectClass->set_property=cookie_permission_manager_preferences_window_set_property;
+ gobjectClass->get_property=cookie_permission_manager_preferences_window_get_property;
+
+ /* Set up private structure */
+ g_type_class_add_private(klass, sizeof(CookiePermissionManagerPreferencesWindowPrivate));
+
+ /* Define properties */
+ CookiePermissionManagerPreferencesWindowProperties[PROP_MANAGER]=
+ g_param_spec_object("manager",
+ _("Cookie permission manager"),
+ _("Instance of current cookie permission manager"),
+ TYPE_COOKIE_PERMISSION_MANAGER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties(gobjectClass, PROP_LAST, CookiePermissionManagerPreferencesWindowProperties);
+}
+
+/* Object initialization
+ * Create private structure and set up default values
+ */
+static void cookie_permission_manager_preferences_window_init(CookiePermissionManagerPreferencesWindow *self)
+{
+ CookiePermissionManagerPreferencesWindowPrivate *priv;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkWidget *widget;
+ gchar *text;
+ gchar *dialogTitle;
+ GtkWidget *scrolled;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ gint width, height;
+ GtkListStore *list;
+ GtkTreeIter listIter;
+
+ priv=self->priv=COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_GET_PRIVATE(self);
+
+ /* Set up default values */
+ priv->manager=NULL;
+
+ /* Get content area to add gui controls to */
+ priv->contentArea=gtk_dialog_get_content_area(GTK_DIALOG(self));
+#ifdef HAVE_GTK3
+ vbox=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
+#else
+ vbox=gtk_vbox_new(FALSE, 0);
+#endif
+
+ /* Set up dialog */
+ dialogTitle=_("Configure cookie permission");
+
+ gtk_window_set_title(GTK_WINDOW(self), dialogTitle);
+ gtk_window_set_icon_name(GTK_WINDOW(self), GTK_STOCK_PROPERTIES);
+
+ sokoke_widget_get_text_size(GTK_WIDGET(self), "M", &width, &height);
+ gtk_window_set_default_size(GTK_WINDOW(self), width*52, -1);
+
+ widget=sokoke_xfce_header_new(gtk_window_get_icon_name(GTK_WINDOW(self)), dialogTitle);
+ if(widget) gtk_box_pack_start(GTK_BOX(priv->contentArea), widget, FALSE, FALSE, 0);
+
+ gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
+
+ /* Set up description */
+ widget=gtk_label_new(NULL);
+ text=g_strdup_printf(_("Below is a list of all web sites and the policy set for them. "
+ "You can delete policies by marking the entries and clicking on <i>Delete</i>."
+ "You can also add a policy for a domain manually by entering the domain below, "
+ "choosing the policy and clicking on <i>Add</i>."));
+ gtk_label_set_markup(GTK_LABEL(widget), text);
+ g_free(text);
+ gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 4);
+
+ /* Set up model for cookie domain list */
+ priv->listStore=gtk_list_store_new(N_COLUMN,
+ G_TYPE_STRING, /* DOMAIN_COLUMN */
+ G_TYPE_STRING /* POLICY_COLUMN */);
+
+ /* Set up domain addition widgets */
+#ifdef HAVE_GTK3
+ hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
+#else
+ hbox=gtk_hbox_new(FALSE, 0);
+#endif
+
+ priv->addDomainEntry=gtk_entry_new();
+ gtk_entry_set_max_length(GTK_ENTRY(priv->addDomainEntry), 64);
+ gtk_container_add(GTK_CONTAINER(hbox), priv->addDomainEntry);
+ g_signal_connect_swapped(priv->addDomainEntry, "changed", G_CALLBACK(_cookie_permission_manager_preferences_on_add_domain_entry_changed), self);
+
+ list=gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
+ gtk_list_store_append(list, &listIter);
+ gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT, 1, _("Accept"), -1);
+ gtk_list_store_append(list, &listIter);
+ gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION, 1, _("Accept for session"), -1);
+ gtk_list_store_append(list, &listIter);
+ gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_BLOCK, 1, _("Block"), -1);
+
+ priv->addDomainPolicyCombo=gtk_combo_box_new_with_model(GTK_TREE_MODEL(list));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(priv->addDomainPolicyCombo), 0);
+ gtk_container_add(GTK_CONTAINER(hbox), priv->addDomainPolicyCombo);
+
+ renderer=gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->addDomainPolicyCombo), renderer, TRUE);
+ gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(priv->addDomainPolicyCombo), renderer, "text", 1);
+
+ priv->addDomainButton=gtk_button_new_from_stock(GTK_STOCK_ADD);
+ gtk_widget_set_sensitive(priv->addDomainButton, FALSE);
+ gtk_container_add(GTK_CONTAINER(hbox), priv->addDomainButton);
+ g_signal_connect_swapped(priv->addDomainButton, "clicked", G_CALLBACK(_cookie_permission_manager_preferences_on_add_domain_clicked), self);
+
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
+
+ /* Set up cookie domain list */
+ priv->list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(priv->listStore));
+
+#ifndef HAVE_GTK3
+ gtk_widget_set_size_request(priv->list, -1, 300);
+#endif
+
+ priv->listSelection=gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->list));
+ gtk_tree_selection_set_mode(priv->listSelection, GTK_SELECTION_MULTIPLE);
+ g_signal_connect_swapped(priv->listSelection, "changed", G_CALLBACK(_cookie_permission_manager_preferences_changed_selection), self);
+
+ renderer=gtk_cell_renderer_text_new();
+ column=gtk_tree_view_column_new_with_attributes(_("Domain"),
+ renderer,
+ "text", DOMAIN_COLUMN,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
+
+ renderer=gtk_cell_renderer_text_new();
+ column=gtk_tree_view_column_new_with_attributes(_("Policy"),
+ renderer,
+ "text", POLICY_COLUMN,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
+
+ scrolled=gtk_scrolled_window_new(NULL, NULL);
+#ifdef HAVE_GTK3
+ gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolled), height*10);
+#endif
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(scrolled), priv->list);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 5);
+
+ /* Set up cookie domain list management buttons */
+#ifdef HAVE_GTK3
+ hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
+#else
+ hbox=gtk_hbox_new(FALSE, 0);
+#endif
+
+ priv->deleteButton=gtk_button_new_from_stock(GTK_STOCK_DELETE);
+ gtk_widget_set_sensitive(priv->deleteButton, FALSE);
+ gtk_container_add(GTK_CONTAINER(hbox), priv->deleteButton);
+ g_signal_connect_swapped(priv->deleteButton, "clicked", G_CALLBACK(_cookie_permission_manager_preferences_on_delete_selection), self);
+
+ priv->deleteAllButton=gtk_button_new_with_mnemonic(_("Delete _all"));
+ gtk_button_set_image(GTK_BUTTON(priv->deleteAllButton), gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON));
+ gtk_widget_set_sensitive(priv->deleteAllButton, FALSE);
+ gtk_container_add(GTK_CONTAINER(hbox), priv->deleteAllButton);
+ g_signal_connect_swapped(priv->deleteAllButton, "clicked", G_CALLBACK(_cookie_permission_manager_preferences_on_delete_all), self);
+
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
+
+ /* Add "ask-for-unknown-policy" checkbox */
+ priv->askForUnknownPolicyCheckbox=gtk_check_button_new_with_mnemonic(_("A_sk for policy if unknown for a domain"));
+ priv->signalAskForUnknownPolicyID=g_signal_connect_swapped(priv->askForUnknownPolicyCheckbox,
+ "toggled",
+ G_CALLBACK(_cookie_permission_manager_preferences_window_ask_for_unknown_policy_changed),
+ self);
+ gtk_box_pack_start(GTK_BOX(vbox), priv->askForUnknownPolicyCheckbox, TRUE, TRUE, 5);
+
+ /* Finalize setup of content area */
+ gtk_container_add(GTK_CONTAINER(priv->contentArea), vbox);
+}
+
+/* Implementation: Public API */
+
+/* Create new object */
+GtkWidget* cookie_permission_manager_preferences_window_new(CookiePermissionManager *inManager)
+{
+ return(g_object_new(TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW,
+ "manager", inManager,
+ NULL));
+}
diff --git a/extensions/cookie-permissions/cookie-permission-manager-preferences-window.h b/extensions/cookie-permissions/cookie-permission-manager-preferences-window.h
new file mode 100644
index 0000000..155b890
--- /dev/null
+++ b/extensions/cookie-permissions/cookie-permission-manager-preferences-window.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2013 Stephan Haller <nomad at froevel.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.
+*/
+
+#ifndef __COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW__
+#define __COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW__
+
+#include "config.h"
+#include <midori/midori.h>
+
+#include "cookie-permission-manager.h"
+
+G_BEGIN_DECLS
+
+#define TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW (cookie_permission_manager_preferences_window_get_type())
+#define COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW, CookiePermissionManagerPreferencesWindow))
+#define IS_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW))
+#define COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW, CookiePermissionManagerPreferencesWindowClass))
+#define IS_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW))
+#define COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW, CookiePermissionManagerPreferencesWindowClass))
+
+typedef struct _CookiePermissionManagerPreferencesWindow CookiePermissionManagerPreferencesWindow;
+typedef struct _CookiePermissionManagerPreferencesWindowClass CookiePermissionManagerPreferencesWindowClass;
+typedef struct _CookiePermissionManagerPreferencesWindowPrivate CookiePermissionManagerPreferencesWindowPrivate;
+
+struct _CookiePermissionManagerPreferencesWindow
+{
+ /* Parent instance */
+ GtkDialog parent_instance;
+
+ /* Private structure */
+ CookiePermissionManagerPreferencesWindowPrivate *priv;
+};
+
+struct _CookiePermissionManagerPreferencesWindowClass
+{
+ /* Parent class */
+ GtkDialogClass parent_class;
+};
+
+/* Public API */
+GType cookie_permission_manager_preferences_window_get_type(void);
+
+GtkWidget* cookie_permission_manager_preferences_window_new(CookiePermissionManager *inManager);
+
+G_END_DECLS
+
+#endif /* __COOKIE_PERMISSION_MANAGER_PREFERENCES_WINDOW__ */
diff --git a/extensions/cookie-permissions/cookie-permission-manager.c b/extensions/cookie-permissions/cookie-permission-manager.c
new file mode 100644
index 0000000..98d6abd
--- /dev/null
+++ b/extensions/cookie-permissions/cookie-permission-manager.c
@@ -0,0 +1,995 @@
+/*
+ Copyright (C) 2013 Stephan Haller <nomad at froevel.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 "cookie-permission-manager.h"
+
+#include <errno.h>
+
+/* Define this class in GObject system */
+G_DEFINE_TYPE(CookiePermissionManager,
+ cookie_permission_manager,
+ G_TYPE_OBJECT)
+
+/* Properties */
+enum
+{
+ PROP_0,
+
+ PROP_EXTENSION,
+ PROP_APPLICATION,
+
+ PROP_DATABASE,
+ PROP_ASK_FOR_UNKNOWN_POLICY,
+
+
+ PROP_LAST
+};
+
+static GParamSpec* CookiePermissionManagerProperties[PROP_LAST]={ 0, };
+
+/* Private structure - access only by public API if needed */
+#define COOKIE_PERMISSION_MANAGER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_COOKIE_PERMISSION_MANAGER, CookiePermissionManagerPrivate))
+
+struct _CookiePermissionManagerPrivate
+{
+ /* Extension related */
+ MidoriExtension *extension;
+ MidoriApp *application;
+ sqlite3 *database;
+ gboolean askForUnknownPolicy;
+
+ /* Session related */
+ void(*oldRequestQueued)(SoupSessionFeature *inFeature, SoupSession *inSession, SoupMessage *inMessage);
+ void(*oldRequestUnqueued)(SoupSessionFeature *inFeature, SoupSession *inSession, SoupMessage *inMessage);
+
+ /* Cookie jar related */
+ SoupSession *session;
+ SoupCookieJar *cookieJar;
+ SoupSessionFeatureInterface *featureIface;
+ gint cookieJarChangedID;
+};
+
+enum
+{
+ DOMAIN_COLUMN,
+ PATH_COLUMN,
+ NAME_COLUMN,
+ VALUE_COLUMN,
+ EXPIRE_DATE_COLUMN,
+ N_COLUMN
+};
+
+/* IMPLEMENTATION: Private variables and methods */
+
+/* Show common error dialog */
+static void _cookie_permission_manager_error(CookiePermissionManager *self, const gchar *inReason)
+{
+ GtkWidget *dialog;
+
+ /* Show confirmation dialog for undetermined cookies */
+ dialog=gtk_message_dialog_new(NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("A fatal error occurred which prevents "
+ "the cookie permission manager extension "
+ "to continue. You should disable it."));
+
+ gtk_window_set_title(GTK_WINDOW(dialog), _("Error in cookie permission manager extension"));
+ gtk_window_set_icon_name(GTK_WINDOW (dialog), "midori");
+
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
+ "%s:\n%s",
+ _("Reason"),
+ inReason);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+
+ /* Free up allocated resources */
+ gtk_widget_destroy(dialog);
+}
+
+/* Open database containing policies for cookie domains.
+ * Create database and setup table structure if it does not exist yet.
+ */
+static void _cookie_permission_manager_open_database(CookiePermissionManager *self)
+{
+ CookiePermissionManagerPrivate *priv=self->priv;
+ const gchar *configDir;
+ gchar *databaseFile;
+ gchar *error=NULL;
+ gint success;
+ sqlite3_stmt *statement=NULL;
+
+ /* Close any open database */
+ if(priv->database)
+ {
+ sqlite3_close(priv->database);
+ priv->database=NULL;
+ g_object_notify_by_pspec(G_OBJECT(self), CookiePermissionManagerProperties[PROP_DATABASE]);
+ }
+
+ /* Build path to database file */
+ configDir=midori_extension_get_config_dir(priv->extension);
+ if(!configDir)
+ {
+ g_warning(_("Could not get path to configuration of extension: path is NULL"));
+
+ _cookie_permission_manager_error(self, _("Could not get path to configuration of extension."));
+ return;
+ }
+
+ if(katze_mkdir_with_parents(configDir, 0700))
+ {
+ g_warning(_("Could not create configuration folder for extension: %s"), g_strerror(errno));
+
+ _cookie_permission_manager_error(self, _("Could not create configuration folder for extension."));
+ return;
+ }
+
+ /* Open database */
+ databaseFile=g_build_filename(configDir, COOKIE_PERMISSION_DATABASE, NULL);
+ success=sqlite3_open(databaseFile, &priv->database);
+ g_free(databaseFile);
+ if(success!=SQLITE_OK)
+ {
+ g_warning(_("Could not open database of extenstion: %s"), sqlite3_errmsg(priv->database));
+
+ if(priv->database) sqlite3_close(priv->database);
+ priv->database=NULL;
+
+ _cookie_permission_manager_error(self, _("Could not open database of extension."));
+ return;
+ }
+
+ /* Create table structure if it does not exist */
+ success=sqlite3_exec(priv->database,
+ "CREATE TABLE IF NOT EXISTS "
+ "policies(domain text, value integer);",
+ NULL,
+ NULL,
+ &error);
+
+ if(success==SQLITE_OK)
+ {
+ success=sqlite3_exec(priv->database,
+ "CREATE UNIQUE INDEX IF NOT EXISTS "
+ "domain ON policies (domain);",
+ NULL,
+ NULL,
+ &error);
+ }
+
+ if(success==SQLITE_OK)
+ {
+ success=sqlite3_exec(priv->database,
+ "PRAGMA journal_mode=TRUNCATE;",
+ NULL,
+ NULL,
+ &error);
+ }
+
+ if(success!=SQLITE_OK || error)
+ {
+ _cookie_permission_manager_error(self, _("Could not set up database structure of extension."));
+
+ if(error)
+ {
+ g_critical(_("Failed to execute database statement: %s"), error);
+ sqlite3_free(error);
+ }
+
+ sqlite3_close(priv->database);
+ priv->database=NULL;
+ return;
+ }
+
+ // Delete all cookies allowed only in one session
+ success=sqlite3_prepare_v2(priv->database,
+ "SELECT domain FROM policies WHERE value=? ORDER BY domain DESC;",
+ -1,
+ &statement,
+ NULL);
+ if(statement && success==SQLITE_OK) success=sqlite3_bind_int(statement, 1, COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION);
+ if(statement && success==SQLITE_OK)
+ {
+ while(sqlite3_step(statement)==SQLITE_ROW)
+ {
+ gchar *domain=(gchar*)sqlite3_column_text(statement, 0);
+ GSList *cookies, *cookie;
+
+#ifdef HAVE_LIBSOUP_2_40_0
+ SoupURI *uri;
+
+ uri=soup_uri_new(NULL);
+ soup_uri_set_host(uri, domain);
+ cookies=soup_cookie_jar_get_cookie_list(priv->cookieJar, uri, TRUE);
+ for(cookie=cookies; cookie; cookie->next)
+ {
+ soup_cookie_jar_delete_cookie(priv->cookieJar, (SoupCookie*)cookie->data);
+ }
+ soup_cookies_free(cookies);
+ soup_uri_free(uri);
+#else
+ cookies=soup_cookie_jar_all_cookies(priv->cookieJar);
+ for(cookie=cookies; cookie; cookie=cookie->next)
+ {
+ if(soup_cookie_domain_matches((SoupCookie*)cookie->data, domain))
+ {
+ soup_cookie_jar_delete_cookie(priv->cookieJar, (SoupCookie*)cookie->data);
+ }
+ }
+ soup_cookies_free(cookies);
+#endif
+ }
+ }
+ else g_warning(_("SQL fails: %s"), sqlite3_errmsg(priv->database));
+
+ sqlite3_finalize(statement);
+
+ g_object_notify_by_pspec(G_OBJECT(self), CookiePermissionManagerProperties[PROP_DATABASE]);
+}
+
+/* Get policy for cookies from domain */
+static gint _cookie_permission_manager_get_policy(CookiePermissionManager *self, SoupCookie *inCookie)
+{
+ CookiePermissionManagerPrivate *priv=self->priv;
+ sqlite3_stmt *statement=NULL;
+ gchar *domain;
+ gint error;
+ gint policy=COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED;
+ gboolean foundPolicy=FALSE;
+
+ /* Check for open database */
+ g_return_val_if_fail(priv->database, COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED);
+
+ /* Lookup policy for cookie domain in database */
+ domain=g_strdup(soup_cookie_get_domain(inCookie));
+ if(*domain=='.') *domain='%';
+
+ error=sqlite3_prepare_v2(priv->database,
+ "SELECT domain, value FROM policies WHERE domain LIKE ? ORDER BY domain DESC;",
+ -1,
+ &statement,
+ NULL);
+ if(statement && error==SQLITE_OK) error=sqlite3_bind_text(statement, 1, domain, -1, NULL);
+ if(statement && error==SQLITE_OK)
+ {
+ while(policy==COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED &&
+ sqlite3_step(statement)==SQLITE_ROW)
+ {
+ gchar *policyDomain=(gchar*)sqlite3_column_text(statement, 0);
+
+ if(soup_cookie_domain_matches(inCookie, policyDomain))
+ {
+ policy=sqlite3_column_int(statement, 1);
+ foundPolicy=TRUE;
+ }
+ }
+ }
+ else g_warning(_("SQL fails: %s"), sqlite3_errmsg(priv->database));
+
+ sqlite3_finalize(statement);
+
+ /* Check if policy is undetermined. If it is then check if this policy was set by user.
+ * If it was not set by user check if we should ask user for his decision
+ */
+ if(!priv->askForUnknownPolicy && !foundPolicy)
+ {
+ switch(soup_cookie_jar_get_accept_policy(priv->cookieJar))
+ {
+ case SOUP_COOKIE_JAR_ACCEPT_ALWAYS:
+ case SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY:
+ policy=COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT;
+ break;
+
+ case SOUP_COOKIE_JAR_ACCEPT_NEVER:
+ policy=COOKIE_PERMISSION_MANAGER_POLICY_BLOCK;
+ break;
+
+ default:
+ g_critical(_("Could not determine global cookie policy to set for domain: %s"), domain);
+ break;
+ }
+ }
+
+ /* Release allocated resources */
+ g_free(domain);
+
+ return(policy);
+}
+
+/* Ask user what to do with cookies from domain(s) which were neither marked accepted nor blocked */
+static gint _cookie_permission_manager_sort_cookies_by_domain(SoupCookie *inLeft, SoupCookie *inRight)
+{
+ const gchar *domainLeft=soup_cookie_get_domain(inLeft);
+ const gchar *domainRight=soup_cookie_get_domain(inRight);
+
+ if(*domainLeft=='.') domainLeft++;
+ if(*domainRight=='.') domainRight++;
+
+ return(g_ascii_strcasecmp(domainLeft, domainRight));
+}
+
+static GSList* _cookie_permission_manager_get_number_domains_and_cookies(CookiePermissionManager *self,
+ GSList *inCookies,
+ gint *ioNumberDomains,
+ gint *ioNumberCookies)
+{
+ GSList *sortedList, *iter;
+ gint domains, cookies;
+ const gchar *lastDomain=NULL;
+ const gchar *cookieDomain;
+
+ /* Make copy and sort cookies in new list */
+ sortedList=g_slist_copy(inCookies);
+
+ /* Sort cookies by domain to prevent a doman counted multiple times */
+ sortedList=g_slist_sort(sortedList, (GCompareFunc)_cookie_permission_manager_sort_cookies_by_domain);
+
+ /* Iterate through list and count domains and cookies */
+ domains=cookies=0;
+ for(iter=sortedList; iter; iter=iter->next)
+ {
+ cookieDomain=soup_cookie_get_domain((SoupCookie*)iter->data);
+
+ if(!lastDomain || g_ascii_strcasecmp(lastDomain, cookieDomain)!=0)
+ {
+ domains++;
+ lastDomain=cookieDomain;
+ }
+
+ cookies++;
+ }
+
+ /* Store counted numbers to final variables */
+ if(ioNumberDomains) *ioNumberDomains=domains;
+ if(ioNumberCookies) *ioNumberCookies=cookies;
+
+ /* Return the copied but sorted cookie list. Caller is responsible to free
+ * this list with g_slist_free
+ */
+ return(sortedList);
+}
+
+static void _cookie_permission_manager_when_ask_expander_changed(CookiePermissionManager *self,
+ GParamSpec *inSpec,
+ gpointer inUserData)
+{
+ GtkExpander *expander=GTK_EXPANDER(inUserData);
+
+ midori_extension_set_boolean(self->priv->extension, "show-details-when-ask", gtk_expander_get_expanded(expander));
+}
+
+static gint _cookie_permission_manager_ask_for_policy(CookiePermissionManager *self,
+ GSList *inUnknownCookies)
+{
+ CookiePermissionManagerPrivate *priv=self->priv;
+ GtkWidget *dialog;
+ GtkWidget *widget;
+ GtkWidget *contentArea;
+ GtkWidget *vbox, *hbox;
+ GtkWidget *expander;
+ GtkListStore *listStore;
+ GtkTreeIter listIter;
+ GtkWidget *scrolled;
+ GtkWidget *list;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ gchar *text;
+ gint numberDomains, numberCookies;
+ gint response;
+ GSList *sortedCookies, *cookies;
+
+ /* Create a copy of cookies and sort them */
+ sortedCookies=_cookie_permission_manager_get_number_domains_and_cookies(self,
+ inUnknownCookies,
+ &numberDomains,
+ &numberCookies);
+
+ /* Create list model and fill in data */
+ listStore=gtk_list_store_new(N_COLUMN,
+ G_TYPE_STRING, /* DOMAIN_COLUMN */
+ G_TYPE_STRING, /* PATH_COLUMN */
+ G_TYPE_STRING, /* NAME_COLUMN */
+ G_TYPE_STRING, /* VALUE_COLUMN */
+ G_TYPE_STRING /* EXPIRE_DATE_COLUMN */);
+
+ for(cookies=sortedCookies; cookies; cookies=cookies->next)
+ {
+ SoupCookie *cookie=(SoupCookie*)cookies->data;
+ SoupDate *cookieDate=soup_cookie_get_expires(cookie);
+
+ text=soup_date_to_string(cookieDate, SOUP_DATE_HTTP);
+
+ gtk_list_store_append(listStore, &listIter);
+ gtk_list_store_set(listStore,
+ &listIter,
+ DOMAIN_COLUMN, soup_cookie_get_domain(cookie),
+ PATH_COLUMN, soup_cookie_get_path(cookie),
+ NAME_COLUMN, soup_cookie_get_name(cookie),
+ VALUE_COLUMN, soup_cookie_get_value(cookie),
+ EXPIRE_DATE_COLUMN, text,
+ -1);
+
+ g_free(text);
+ }
+
+ /* Create dialog with text, icon, title and so on */
+ dialog=gtk_dialog_new();
+
+ gtk_window_set_title(GTK_WINDOW(dialog), _("Confirm storing cookie"));
+ gtk_window_set_icon_name(GTK_WINDOW (dialog), "midori");
+
+ /* Get content area and layout widgets */
+ contentArea=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+#ifdef HAVE_GTK3
+ vbox=gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
+#else
+ vbox=gtk_vbox_new(FALSE, 0);
+#endif
+
+ /* Create description text */
+#ifdef HAVE_GTK3
+ hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
+#else
+ hbox=gtk_hbox_new(FALSE, 0);
+#endif
+
+ widget=gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 4);
+
+ if(numberDomains==1)
+ {
+ const gchar *cookieDomain=soup_cookie_get_domain((SoupCookie*)sortedCookies->data);
+
+ if(*cookieDomain=='.') cookieDomain++;
+
+ if(numberCookies>1)
+ text=g_strdup_printf(_("The website %s wants to store %d cookies."), cookieDomain, numberCookies);
+ else
+ text=g_strdup_printf(_("The website %s wants to store a cookie."), cookieDomain);
+ }
+ else
+ {
+ text=g_strdup_printf(_("Multiple websites want to store %d cookies in total."), numberCookies);
+ }
+
+ widget=gtk_label_new(NULL);
+ gtk_label_set_markup(GTK_LABEL(widget), text);
+ gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);
+ gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 4);
+ g_free(text);
+
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 4);
+
+ /* Create expander for details */
+ expander=gtk_expander_new_with_mnemonic("_Details");
+ gtk_box_pack_start(GTK_BOX(vbox), expander, TRUE, TRUE, 5);
+
+ /* Create list and set up columns of list */
+ list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(listStore));
+#ifndef HAVE_GTK3
+ gtk_widget_set_size_request(list, -1, 100);
+#endif
+
+ renderer=gtk_cell_renderer_text_new();
+ column=gtk_tree_view_column_new_with_attributes(_("Domain"),
+ renderer,
+ "text", DOMAIN_COLUMN,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
+
+ renderer=gtk_cell_renderer_text_new();
+ column=gtk_tree_view_column_new_with_attributes(_("Path"),
+ renderer,
+ "text", PATH_COLUMN,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
+
+ renderer=gtk_cell_renderer_text_new();
+ column=gtk_tree_view_column_new_with_attributes(_("Name"),
+ renderer,
+ "text", NAME_COLUMN,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
+
+ renderer=gtk_cell_renderer_text_new();
+ column=gtk_tree_view_column_new_with_attributes(_("Value"),
+ renderer,
+ "text", VALUE_COLUMN,
+ NULL);
+ g_object_set(G_OBJECT(renderer),
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ "width-chars", 30,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
+
+ renderer=gtk_cell_renderer_text_new();
+ column=gtk_tree_view_column_new_with_attributes(_("Expire date"),
+ renderer,
+ "text", EXPIRE_DATE_COLUMN,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
+
+ scrolled=gtk_scrolled_window_new(NULL, NULL);
+#ifdef HAVE_GTK3
+ gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolled), 100);
+#endif
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(scrolled), list);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(expander), scrolled);
+
+ gtk_widget_show_all(vbox);
+ gtk_container_add(GTK_CONTAINER(contentArea), vbox);
+
+ /* Set state of expander based on config 'show-details-when-ask' */
+ gtk_expander_set_expanded(GTK_EXPANDER(expander),
+ midori_extension_get_boolean(priv->extension, "show-details-when-ask"));
+ g_signal_connect_swapped(expander, "notify::expanded", G_CALLBACK(_cookie_permission_manager_when_ask_expander_changed), self);
+
+ /* Create buttons for dialog */
+ widget=gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Accept"), COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT);
+ gtk_button_set_image(GTK_BUTTON(widget), gtk_image_new_from_stock(GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON));
+
+ gtk_dialog_add_button(GTK_DIALOG(dialog), _("Accept for this _session"), COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION);
+
+ widget=gtk_dialog_add_button(GTK_DIALOG(dialog), _("De_ny"), COOKIE_PERMISSION_MANAGER_POLICY_BLOCK);
+ gtk_button_set_image(GTK_BUTTON(widget), gtk_image_new_from_stock(GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON));
+
+ /* Show confirmation dialog and wait for response of user */
+ response=gtk_dialog_run(GTK_DIALOG(dialog));
+
+ /* Store user's decision in database if it is not a temporary block.
+ * We use the already sorted list of cookies to prevent multiple
+ * updates of database for the same domain. This sorted list is a copy
+ * to avoid a reorder of cookies
+ */
+ if(response>=0)
+ {
+ const gchar *lastDomain=NULL;
+
+ /* Iterate through cookies and store decision for each domain once */
+ for(cookies=sortedCookies; cookies; cookies=cookies->next)
+ {
+ SoupCookie *cookie=(SoupCookie*)cookies->data;
+ const gchar *cookieDomain=soup_cookie_get_domain(cookie);
+
+ if(*cookieDomain=='.') cookieDomain++;
+
+ /* Store decision if new domain found while iterating through cookies */
+ if(!lastDomain || g_ascii_strcasecmp(lastDomain, cookieDomain)!=0)
+ {
+ gchar *sql;
+ gchar *error=NULL;
+ gint success;
+
+ sql=sqlite3_mprintf("INSERT OR REPLACE INTO policies (domain, value) VALUES ('%q', %d);",
+ cookieDomain,
+ response);
+ success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
+ if(success!=SQLITE_OK) g_warning(_("SQL fails: %s"), error);
+ if(error) sqlite3_free(error);
+ sqlite3_free(sql);
+
+ lastDomain=cookieDomain;
+ }
+ }
+ }
+
+ /* Free up allocated resources */
+ g_slist_free(sortedCookies);
+ gtk_widget_destroy(dialog);
+
+ /* Return user's selection */
+ return(response>=0 ? response : COOKIE_PERMISSION_MANAGER_POLICY_BLOCK);
+}
+
+/* A cookie was changed outside a request (e.g. Javascript) */
+static void _cookie_permission_manager_on_cookie_changed(CookiePermissionManager *self,
+ SoupCookie *inOldCookie,
+ SoupCookie *inNewCookie,
+ SoupCookieJar *inCookieJar)
+{
+ GSList *newCookies;
+ gint newCookiePolicy;
+
+ /* Do not check changed cookies because they must have been allowed before.
+ * Also do not check removed cookies because they are removed ;)
+ */
+ if(inNewCookie==NULL || inOldCookie) return;
+
+ /* New cookie is a new cookie so check */
+ switch(_cookie_permission_manager_get_policy(self, inNewCookie))
+ {
+ case COOKIE_PERMISSION_MANAGER_POLICY_BLOCK:
+ soup_cookie_jar_delete_cookie(inCookieJar, inNewCookie);
+ break;
+
+ case COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED:
+ newCookies=g_slist_prepend(NULL, inNewCookie);
+ newCookiePolicy=_cookie_permission_manager_ask_for_policy(self, newCookies);
+ if(newCookiePolicy==COOKIE_PERMISSION_MANAGER_POLICY_BLOCK)
+ {
+ /* Free cookie because it should be blocked */
+ soup_cookie_jar_delete_cookie(inCookieJar, inNewCookie);
+ }
+ else
+ {
+ /* Cookie was accept so do nothing (it is already added) */
+ }
+ g_slist_free(newCookies);
+ break;
+
+ case COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT:
+ case COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION:
+ break;
+ }
+}
+
+/* We received the HTTP headers of the request and it contains cookie-managing headers */
+static void _cookie_permission_manager_process_set_cookie_header(SoupMessage *inMessage, gpointer inUserData)
+{
+ g_return_if_fail(IS_COOKIE_PERMISSION_MANAGER(inUserData));
+
+ CookiePermissionManager *self=COOKIE_PERMISSION_MANAGER(inUserData);
+ CookiePermissionManagerPrivate *priv=self->priv;
+ GSList *newCookies, *cookie;
+ GSList *unknownCookies=NULL, *acceptedCookies=NULL;
+ SoupURI *firstParty;
+ SoupCookieJarAcceptPolicy cookiePolicy;
+ gint unknownCookiesPolicy;
+
+ /* If policy is to deny all cookies return immediately */
+ cookiePolicy=soup_cookie_jar_get_accept_policy(priv->cookieJar);
+ if(cookiePolicy==SOUP_COOKIE_JAR_ACCEPT_NEVER) return;
+
+ /* Iterate through cookies in response and check if they should be
+ * blocked (remove from cookies list) or accepted (added to cookie jar).
+ * If we could not determine what to do collect these cookies and
+ * ask user
+ */
+ newCookies=soup_cookies_from_response(inMessage);
+ firstParty=soup_message_get_first_party(inMessage);
+ for(cookie=newCookies; cookie; cookie=cookie->next)
+ {
+ switch(_cookie_permission_manager_get_policy(self, cookie->data))
+ {
+ case COOKIE_PERMISSION_MANAGER_POLICY_BLOCK:
+ soup_cookie_free(cookie->data);
+ break;
+
+ case COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT:
+ case COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION:
+ if((cookiePolicy==SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY &&
+ firstParty!=NULL &&
+ firstParty->host &&
+ soup_cookie_domain_matches(cookie->data, firstParty->host)) ||
+ cookiePolicy==SOUP_COOKIE_JAR_ACCEPT_ALWAYS)
+ {
+ acceptedCookies=g_slist_prepend(acceptedCookies, cookie->data);
+ }
+ else soup_cookie_free(cookie->data);
+ break;
+
+ case COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED:
+ default:
+ if((cookiePolicy==SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY &&
+ firstParty!=NULL &&
+ firstParty->host &&
+ soup_cookie_domain_matches(cookie->data, firstParty->host)) ||
+ cookiePolicy==SOUP_COOKIE_JAR_ACCEPT_ALWAYS)
+ {
+ unknownCookies=g_slist_prepend(unknownCookies, cookie->data);
+ }
+ else soup_cookie_free(cookie->data);
+ break;
+ }
+ }
+
+ /* Prepending an item to list is the fastest method but the order of cookies
+ * is reversed now and may be added to cookie jar in the wrong order. So we
+ * need to reverse list now of both - undetermined and accepted cookies
+ */
+ unknownCookies=g_slist_reverse(unknownCookies);
+ acceptedCookies=g_slist_reverse(acceptedCookies);
+
+ /* Ask user for his decision what to do with cookies whose policy is undetermined
+ * But only ask if there is any undetermined one
+ */
+ if(g_slist_length(unknownCookies)>0)
+ {
+ unknownCookiesPolicy=_cookie_permission_manager_ask_for_policy(self, unknownCookies);
+ if(unknownCookiesPolicy==COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT ||
+ unknownCookiesPolicy==COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION)
+ {
+ /* Add accepted undetermined cookies to cookie jar */
+ for(cookie=unknownCookies; cookie; cookie=cookie->next)
+ {
+ soup_cookie_jar_add_cookie(priv->cookieJar, (SoupCookie*)cookie->data);
+ }
+ }
+ else
+ {
+ /* Free cookies because they should be blocked */
+ for(cookie=unknownCookies; cookie; cookie=cookie->next)
+ {
+ soup_cookie_free((SoupCookie*)cookie->data);
+ }
+ }
+ }
+
+ /* Add accepted cookies to cookie jar */
+ for(cookie=acceptedCookies; cookie; cookie=cookie->next)
+ {
+ soup_cookie_jar_add_cookie(priv->cookieJar, (SoupCookie*)cookie->data);
+ }
+
+ /* Free list of cookies */
+ g_slist_free(unknownCookies);
+ g_slist_free(acceptedCookies);
+ g_slist_free(newCookies);
+}
+
+/* A request was started and is in queue now */
+static void _cookie_permission_manager_request_queued(SoupSessionFeature *inFeature, SoupSession *inSession, SoupMessage *inMessage)
+{
+ /* Get class instance */
+ CookiePermissionManager *manager=g_object_get_data(G_OBJECT(inFeature), "cookie-permission-manager");
+
+ /* Listen to "got-headers" signals and register handlers for
+ * checking cookie-managing headers in HTTP stream
+ */
+ soup_message_add_header_handler(inMessage,
+ "got-headers",
+ "Set-Cookie",
+ G_CALLBACK(_cookie_permission_manager_process_set_cookie_header),
+ manager);
+
+ soup_message_add_header_handler(inMessage,
+ "got-headers",
+ "Set-Cookie2",
+ G_CALLBACK(_cookie_permission_manager_process_set_cookie_header),
+ manager);
+}
+
+/* Request has loaded and was unqueued */
+static void _cookie_permission_manager_request_unqueued(SoupSessionFeature *inFeature, SoupSession *inSession, SoupMessage *inMessage)
+{
+ /* Stop listening to HTTP stream */
+ g_signal_handlers_disconnect_by_func(inMessage, _cookie_permission_manager_process_set_cookie_header, inFeature);
+}
+
+/* IMPLEMENTATION: GObject */
+
+/* Finalize this object */
+static void cookie_permission_manager_finalize(GObject *inObject)
+{
+ CookiePermissionManagerPrivate *priv=COOKIE_PERMISSION_MANAGER(inObject)->priv;
+
+ /* Dispose allocated resources */
+ if(priv->database)
+ {
+ sqlite3_close(priv->database);
+ priv->database=NULL;
+ g_object_notify_by_pspec(inObject, CookiePermissionManagerProperties[PROP_DATABASE]);
+ }
+
+ g_signal_handler_disconnect(priv->cookieJar, priv->cookieJarChangedID);
+
+ priv->featureIface->request_queued=priv->oldRequestQueued;
+ priv->featureIface->request_unqueued=priv->oldRequestUnqueued;
+
+ g_object_steal_data(G_OBJECT(priv->cookieJar), "cookie-permission-manager");
+
+ /* Call parent's class finalize method */
+ G_OBJECT_CLASS(cookie_permission_manager_parent_class)->finalize(inObject);
+}
+
+/* Set/get properties */
+static void cookie_permission_manager_set_property(GObject *inObject,
+ guint inPropID,
+ const GValue *inValue,
+ GParamSpec *inSpec)
+{
+ CookiePermissionManager *self=COOKIE_PERMISSION_MANAGER(inObject);
+
+ switch(inPropID)
+ {
+ /* Construct-only properties */
+ case PROP_EXTENSION:
+ self->priv->extension=g_value_get_object(inValue);
+ _cookie_permission_manager_open_database(self);
+ break;
+
+ case PROP_APPLICATION:
+ self->priv->application=g_value_get_object(inValue);
+ break;
+
+ case PROP_ASK_FOR_UNKNOWN_POLICY:
+ cookie_permission_manager_set_ask_for_unknown_policy(self, g_value_get_boolean(inValue));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+static void cookie_permission_manager_get_property(GObject *inObject,
+ guint inPropID,
+ GValue *outValue,
+ GParamSpec *inSpec)
+{
+ CookiePermissionManager *self=COOKIE_PERMISSION_MANAGER(inObject);
+
+ switch(inPropID)
+ {
+ case PROP_EXTENSION:
+ g_value_set_object(outValue, self->priv->extension);
+ break;
+
+ case PROP_APPLICATION:
+ g_value_set_object(outValue, self->priv->application);
+ break;
+
+ case PROP_DATABASE:
+ g_value_set_pointer(outValue, self->priv->database);
+ break;
+
+ case PROP_ASK_FOR_UNKNOWN_POLICY:
+ g_value_set_boolean(outValue, self->priv->askForUnknownPolicy);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+/* Class initialization
+ * Override functions in parent classes and define properties and signals
+ */
+static void cookie_permission_manager_class_init(CookiePermissionManagerClass *klass)
+{
+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
+
+ /* Override functions */
+ gobjectClass->finalize=cookie_permission_manager_finalize;
+ gobjectClass->set_property=cookie_permission_manager_set_property;
+ gobjectClass->get_property=cookie_permission_manager_get_property;
+
+ /* Set up private structure */
+ g_type_class_add_private(klass, sizeof(CookiePermissionManagerPrivate));
+
+ /* Define properties */
+ CookiePermissionManagerProperties[PROP_EXTENSION]=
+ g_param_spec_object("extension",
+ _("Extension instance"),
+ _("The Midori extension instance for this extension"),
+ MIDORI_TYPE_EXTENSION,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ CookiePermissionManagerProperties[PROP_APPLICATION]=
+ g_param_spec_object("application",
+ _("Application instance"),
+ _("The Midori application instance this extension belongs to"),
+ MIDORI_TYPE_APP,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ CookiePermissionManagerProperties[PROP_DATABASE]=
+ g_param_spec_pointer("database",
+ _("Database instance"),
+ _("Pointer to sqlite database instance used by this extension"),
+ G_PARAM_READABLE);
+
+ CookiePermissionManagerProperties[PROP_ASK_FOR_UNKNOWN_POLICY]=
+ g_param_spec_boolean("ask-for-unknown-policy",
+ _("Ask for unknown policy"),
+ _("If true this extension ask for policy for every unknown domain."
+ "If false this extension uses the global cookie policy set in midori settings."),
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+
+ g_object_class_install_properties(gobjectClass, PROP_LAST, CookiePermissionManagerProperties);
+}
+
+/* Object initialization
+ * Create private structure and set up default values
+ */
+static void cookie_permission_manager_init(CookiePermissionManager *self)
+{
+ CookiePermissionManagerPrivate *priv;
+
+ priv=self->priv=COOKIE_PERMISSION_MANAGER_GET_PRIVATE(self);
+
+ /* Set up default values */
+ priv->database=NULL;
+ priv->askForUnknownPolicy=TRUE;
+
+ /* Hijack session's cookie jar to handle cookies requests on our own in HTTP streams
+ * but remember old handlers to restore them on deactivation
+ */
+ priv->session=webkit_get_default_session();
+ priv->cookieJar=SOUP_COOKIE_JAR(soup_session_get_feature(priv->session, SOUP_TYPE_COOKIE_JAR));
+ priv->featureIface=SOUP_SESSION_FEATURE_GET_CLASS(priv->cookieJar);
+ g_object_set_data(G_OBJECT(priv->cookieJar), "cookie-permission-manager", self);
+
+ priv->oldRequestQueued=priv->featureIface->request_queued;
+ priv->oldRequestUnqueued=priv->featureIface->request_unqueued;
+
+ priv->featureIface->request_queued=_cookie_permission_manager_request_queued;
+ priv->featureIface->request_unqueued=_cookie_permission_manager_request_unqueued;
+
+ /* Listen to changed cookies set or changed by other sources like javascript */
+ priv->cookieJarChangedID=g_signal_connect_swapped(priv->cookieJar, "changed", G_CALLBACK(_cookie_permission_manager_on_cookie_changed), self);
+}
+
+/* Implementation: Public API */
+
+/* Create new object */
+CookiePermissionManager* cookie_permission_manager_new(MidoriExtension *inExtension, MidoriApp *inApp)
+{
+ return(g_object_new(TYPE_COOKIE_PERMISSION_MANAGER,
+ "extension", inExtension,
+ "application", inApp,
+ NULL));
+}
+
+/* Get/set policy to ask for policy if unknown for a domain */
+gboolean cookie_permission_manager_get_ask_for_unknown_policy(CookiePermissionManager *self)
+{
+ g_return_val_if_fail(IS_COOKIE_PERMISSION_MANAGER(self), FALSE);
+
+ return(self->priv->askForUnknownPolicy);
+}
+
+void cookie_permission_manager_set_ask_for_unknown_policy(CookiePermissionManager *self, gboolean inDoAsk)
+{
+ g_return_if_fail(IS_COOKIE_PERMISSION_MANAGER(self));
+
+ if(inDoAsk!=self->priv->askForUnknownPolicy)
+ {
+ self->priv->askForUnknownPolicy=inDoAsk;
+ midori_extension_set_boolean(self->priv->extension, "ask-for-unknown-policy", inDoAsk);
+ g_object_notify_by_pspec(G_OBJECT(self), CookiePermissionManagerProperties[PROP_ASK_FOR_UNKNOWN_POLICY]);
+ }
+}
+
+/************************************************************************************/
+
+/* Implementation: Enumeration */
+GType cookie_permission_manager_policy_get_type(void)
+{
+ static volatile gsize g_define_type_id__volatile=0;
+
+ if(g_once_init_enter(&g_define_type_id__volatile))
+ {
+ static const GEnumValue values[]=
+ {
+ { COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED, "COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED", N_("Undetermined") },
+ { COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT, "COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT", N_("Accept") },
+ { COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION, "COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION", N_("Accept for session") },
+ { COOKIE_PERMISSION_MANAGER_POLICY_BLOCK, "COOKIE_PERMISSION_MANAGER_POLICY_BLOCK", N_("Block") },
+ { 0, NULL, NULL }
+ };
+
+ GType g_define_type_id=g_enum_register_static(g_intern_static_string("CookiePermissionManagerPolicy"), values);
+ g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+ }
+
+ return(g_define_type_id__volatile);
+}
diff --git a/extensions/cookie-permissions/cookie-permission-manager.h b/extensions/cookie-permissions/cookie-permission-manager.h
new file mode 100644
index 0000000..6e0ec3a
--- /dev/null
+++ b/extensions/cookie-permissions/cookie-permission-manager.h
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 2013 Stephan Haller <nomad at froevel.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.
+*/
+
+#ifndef __COOKIE_PERMISSION_MANAGER__
+#define __COOKIE_PERMISSION_MANAGER__
+
+#include "config.h"
+#include <midori/midori.h>
+
+#define COOKIE_PERMISSION_DATABASE "domains.db"
+
+G_BEGIN_DECLS
+
+/* Cookie permission manager enums */
+typedef enum
+{
+ COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED,
+ COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT,
+ COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION,
+ COOKIE_PERMISSION_MANAGER_POLICY_BLOCK
+} CookiePermissionManagerPolicy;
+
+/* Cookie permission manager object */
+#define TYPE_COOKIE_PERMISSION_MANAGER (cookie_permission_manager_get_type())
+#define COOKIE_PERMISSION_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COOKIE_PERMISSION_MANAGER, CookiePermissionManager))
+#define IS_COOKIE_PERMISSION_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COOKIE_PERMISSION_MANAGER))
+#define COOKIE_PERMISSION_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COOKIE_PERMISSION_MANAGER, CookiePermissionManagerClass))
+#define IS_COOKIE_PERMISSION_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COOKIE_PERMISSION_MANAGER))
+#define COOKIE_PERMISSION_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_COOKIE_PERMISSION_MANAGER, CookiePermissionManagerClass))
+
+typedef struct _CookiePermissionManager CookiePermissionManager;
+typedef struct _CookiePermissionManagerClass CookiePermissionManagerClass;
+typedef struct _CookiePermissionManagerPrivate CookiePermissionManagerPrivate;
+
+struct _CookiePermissionManager
+{
+ /* Parent instance */
+ GObject parent_instance;
+
+ /* Private structure */
+ CookiePermissionManagerPrivate *priv;
+};
+
+struct _CookiePermissionManagerClass
+{
+ /* Parent class */
+ GObjectClass parent_class;
+};
+
+/* Public API */
+GType cookie_permission_manager_get_type(void);
+
+CookiePermissionManager* cookie_permission_manager_new(MidoriExtension *inExtension, MidoriApp *inApp);
+
+gboolean cookie_permission_manager_get_ask_for_unknown_policy(CookiePermissionManager *self);
+void cookie_permission_manager_set_ask_for_unknown_policy(CookiePermissionManager *self, gboolean inDoAsk);
+
+/* Enumeration */
+GType cookie_permission_manager_policy_get_type(void) G_GNUC_CONST;
+#define COOKIE_PERMISSION_MANAGER_TYPE_POLICY (cookie_permission_manager_policy_get_type())
+
+G_END_DECLS
+
+#endif /* __COOKIE_PERMISSION_MANAGER__ */
diff --git a/extensions/cookie-permissions/main.c b/extensions/cookie-permissions/main.c
new file mode 100644
index 0000000..fcf87bf
--- /dev/null
+++ b/extensions/cookie-permissions/main.c
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2013 Stephan Haller <nomad at froevel.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 "cookie-permission-manager.h"
+#include "cookie-permission-manager-preferences-window.h"
+
+/* Global instance */
+CookiePermissionManager *cpm=NULL;
+
+/* This extension was activated */
+static void _cpm_on_activate(MidoriExtension *inExtension, MidoriApp *inApp, gpointer inUserData)
+{
+ g_return_if_fail(cpm==NULL);
+
+ cpm=cookie_permission_manager_new(inExtension, inApp);
+ g_object_set(cpm, "ask-for-unknown-policy", midori_extension_get_boolean(inExtension, "ask-for-unknown-policy"), NULL);
+}
+
+/* This extension was deactivated */
+static void _cpm_on_deactivate(MidoriExtension *inExtension, gpointer inUserData)
+{
+ g_return_if_fail(cpm);
+
+ g_object_unref(cpm);
+ cpm=NULL;
+}
+
+/* Preferences of this extension should be opened */
+static void _cpm_on_open_preferences_response(GtkWidget* inDialog,
+ gint inResponse,
+ MidoriExtension* inExtension)
+{
+ gtk_widget_destroy(inDialog);
+}
+
+static void _cpm_on_open_preferences(MidoriExtension *inExtension)
+{
+ g_return_if_fail(cpm);
+
+ /* Show preferences window */
+ GtkWidget* dialog;
+
+ dialog=cookie_permission_manager_preferences_window_new(cpm);
+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+ g_signal_connect(dialog, "response", G_CALLBACK (_cpm_on_open_preferences_response), inExtension);
+ gtk_widget_show_all(dialog);
+}
+
+/* Main entry for extension */
+MidoriExtension *extension_init(void)
+{
+ /* Set up extension */
+ MidoriExtension *extension=g_object_new(MIDORI_TYPE_EXTENSION,
+ "name", _("Cookie Security Manager"),
+ "description", _("Manage cookie permission per site"),
+ "version", "0.1" MIDORI_VERSION_SUFFIX,
+ "authors", "Stephan Haller <nomad at froevel.de>",
+ NULL);
+
+ midori_extension_install_boolean(extension, "ask-for-unknown-policy", TRUE);
+ midori_extension_install_boolean(extension, "show-details-when-ask", FALSE);
+
+ g_signal_connect(extension, "activate", G_CALLBACK(_cpm_on_activate), NULL);
+ g_signal_connect(extension, "deactivate", G_CALLBACK(_cpm_on_deactivate), NULL);
+ g_signal_connect(extension, "open-preferences", G_CALLBACK(_cpm_on_open_preferences), NULL);
+
+ return(extension);
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5af5366..50b04bf 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -81,3 +81,8 @@ midori/midori-bookmarks.c
midori/midori-session.c
extensions/nsplugin-manager.vala
midori/midori-frontend.c
+extensions/cookie-permissions/cookie-permission-manager-preferences-window.c
+extensions/cookie-permissions/cookie-permission-manager-preferences-window.h
+extensions/cookie-permissions/cookie-permission-manager.c
+extensions/cookie-permissions/cookie-permission-manager.h
+extensions/cookie-permissions/main.c
More information about the Xfce4-commits
mailing list