aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-passwords.c
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2012-12-10 21:09:59 +0800
committerMatthew Barnes <mbarnes@redhat.com>2012-12-13 03:33:43 +0800
commitd09d8de870b6697c8a8b262e7e077b871a69b315 (patch)
tree3b718882e7a0bb0a996daf2967a033d91714c9b5 /e-util/e-passwords.c
parentb61331ed03ac1c7a9b8614e25510040b9c60ae02 (diff)
downloadgsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.gz
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.tar.zst
gsoc2013-evolution-d09d8de870b6697c8a8b262e7e077b871a69b315.zip
Consolidate base utility libraries into libeutil.
Evolution consists of entirely too many small utility libraries, which increases linking and loading time, places a burden on higher layers of the application (e.g. modules) which has to remember to link to all the small in-tree utility libraries, and makes it difficult to generate API documentation for these utility libraries in one Gtk-Doc module. Merge the following utility libraries under the umbrella of libeutil, and enforce a single-include policy on libeutil so we can reorganize the files as desired without disrupting its pseudo-public API. libemail-utils/libemail-utils.la libevolution-utils/libevolution-utils.la filter/libfilter.la widgets/e-timezone-dialog/libetimezonedialog.la widgets/menus/libmenus.la widgets/misc/libemiscwidgets.la widgets/table/libetable.la widgets/text/libetext.la This also merges libedataserverui from the Evolution-Data-Server module, since Evolution is its only consumer nowadays, and I'd like to make some improvements to those APIs without concern for backward-compatibility. And finally, start a Gtk-Doc module for libeutil. It's going to be a project just getting all the symbols _listed_ much less _documented_. But the skeletal structure is in place and I'm off to a good start.
Diffstat (limited to 'e-util/e-passwords.c')
-rw-r--r--e-util/e-passwords.c890
1 files changed, 890 insertions, 0 deletions
diff --git a/e-util/e-passwords.c b/e-util/e-passwords.c
new file mode 100644
index 0000000000..bf4cfc1e7f
--- /dev/null
+++ b/e-util/e-passwords.c
@@ -0,0 +1,890 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * e-passwords.c
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA.
+ */
+
+/*
+ * This looks a lot more complicated than it is, and than you'd think
+ * it would need to be. There is however, method to the madness.
+ *
+ * The code must cope with being called from any thread at any time,
+ * recursively from the main thread, and then serialising every
+ * request so that sane and correct values are always returned, and
+ * duplicate requests are never made.
+ *
+ * To this end, every call is marshalled and queued and a dispatch
+ * method invoked until that request is satisfied. If mainloop
+ * recursion occurs, then the sub-call will necessarily return out of
+ * order, but will not be processed out of order.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n-lib.h>
+
+/* XXX Yeah, yeah... */
+#define SECRET_API_SUBJECT_TO_CHANGE
+
+#include <libsecret/secret.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-passwords.h"
+
+#define d(x)
+
+typedef struct _EPassMsg EPassMsg;
+
+struct _EPassMsg {
+ void (*dispatch) (EPassMsg *);
+ EFlag *done;
+
+ /* input */
+ GtkWindow *parent;
+ const gchar *key;
+ const gchar *title;
+ const gchar *prompt;
+ const gchar *oldpass;
+ guint32 flags;
+
+ /* output */
+ gboolean *remember;
+ gchar *password;
+ GError *error;
+
+ /* work variables */
+ GtkWidget *entry;
+ GtkWidget *check;
+ guint ismain : 1;
+ guint noreply:1; /* supress replies; when calling
+ * dispatch functions from others */
+};
+
+/* XXX probably want to share this with evalution-source-registry-migrate-sources.c */
+static const SecretSchema e_passwords_schema = {
+ "org.gnome.Evolution.Password",
+ SECRET_SCHEMA_DONT_MATCH_NAME,
+ {
+ { "application", SECRET_SCHEMA_ATTRIBUTE_STRING, },
+ { "user", SECRET_SCHEMA_ATTRIBUTE_STRING, },
+ { "server", SECRET_SCHEMA_ATTRIBUTE_STRING, },
+ { "protocol", SECRET_SCHEMA_ATTRIBUTE_STRING, },
+ }
+};
+
+G_LOCK_DEFINE_STATIC (passwords);
+static GThread *main_thread = NULL;
+static GHashTable *password_cache = NULL;
+static GtkDialog *password_dialog = NULL;
+static GQueue message_queue = G_QUEUE_INIT;
+static gint idle_id;
+static gint ep_online_state = TRUE;
+
+static EUri *
+ep_keyring_uri_new (const gchar *string,
+ GError **error)
+{
+ EUri *uri;
+
+ uri = e_uri_new (string);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ /* LDAP URIs do not have usernames, so use the URI as the username. */
+ if (uri->user == NULL && uri->protocol != NULL &&
+ (strcmp (uri->protocol, "ldap") == 0|| strcmp (uri->protocol, "google") == 0))
+ uri->user = g_strdelimit (g_strdup (string), "/=", '_');
+
+ /* Make sure the URI has the required components. */
+ if (uri->user == NULL && uri->host == NULL) {
+ g_set_error_literal (
+ error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Keyring key is unusable: no user or host name"));
+ e_uri_free (uri);
+ uri = NULL;
+ }
+
+ return uri;
+}
+
+static gboolean
+ep_idle_dispatch (gpointer data)
+{
+ EPassMsg *msg;
+
+ /* As soon as a password window is up we stop; it will
+ * re - invoke us when it has been closed down */
+ G_LOCK (passwords);
+ while (password_dialog == NULL && (msg = g_queue_pop_head (&message_queue)) != NULL) {
+ G_UNLOCK (passwords);
+
+ msg->dispatch (msg);
+
+ G_LOCK (passwords);
+ }
+
+ idle_id = 0;
+ G_UNLOCK (passwords);
+
+ return FALSE;
+}
+
+static EPassMsg *
+ep_msg_new (void (*dispatch) (EPassMsg *))
+{
+ EPassMsg *msg;
+
+ e_passwords_init ();
+
+ msg = g_malloc0 (sizeof (*msg));
+ msg->dispatch = dispatch;
+ msg->done = e_flag_new ();
+ msg->ismain = (g_thread_self () == main_thread);
+
+ return msg;
+}
+
+static void
+ep_msg_free (EPassMsg *msg)
+{
+ /* XXX We really should be passing this back to the caller, but
+ * doing so will require breaking the password API. */
+ if (msg->error != NULL) {
+ g_warning ("%s", msg->error->message);
+ g_error_free (msg->error);
+ }
+
+ e_flag_free (msg->done);
+ g_free (msg->password);
+ g_free (msg);
+}
+
+static void
+ep_msg_send (EPassMsg *msg)
+{
+ gint needidle = 0;
+
+ G_LOCK (passwords);
+ g_queue_push_tail (&message_queue, msg);
+ if (!idle_id) {
+ if (!msg->ismain)
+ idle_id = g_idle_add (ep_idle_dispatch, NULL);
+ else
+ needidle = 1;
+ }
+ G_UNLOCK (passwords);
+
+ if (msg->ismain) {
+ if (needidle)
+ ep_idle_dispatch (NULL);
+ while (!e_flag_is_set (msg->done))
+ g_main_context_iteration (NULL, TRUE);
+ } else
+ e_flag_wait (msg->done);
+}
+
+/* the functions that actually do the work */
+
+static void
+ep_clear_passwords (EPassMsg *msg)
+{
+ GError *error = NULL;
+
+ /* Find all Evolution passwords and delete them. */
+ secret_password_clear_sync (
+ &e_passwords_schema, NULL, &error,
+ "application", "Evolution", NULL);
+
+ if (error != NULL)
+ g_propagate_error (&msg->error, error);
+
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void
+ep_remember_password (EPassMsg *msg)
+{
+ gchar *password;
+ EUri *uri;
+ GError *error = NULL;
+
+ password = g_hash_table_lookup (password_cache, msg->key);
+ if (password == NULL) {
+ g_warning ("Password for key \"%s\" not found", msg->key);
+ goto exit;
+ }
+
+ uri = ep_keyring_uri_new (msg->key, &msg->error);
+ if (uri == NULL)
+ goto exit;
+
+ secret_password_store_sync (
+ &e_passwords_schema,
+ SECRET_COLLECTION_DEFAULT,
+ msg->key, password,
+ NULL, &error,
+ "application", "Evolution",
+ "user", uri->user,
+ "server", uri->host,
+ "protocol", uri->protocol,
+ NULL);
+
+ /* Only remove the password from the session hash
+ * if the keyring insertion was successful. */
+ if (error == NULL)
+ g_hash_table_remove (password_cache, msg->key);
+ else
+ g_propagate_error (&msg->error, error);
+
+ e_uri_free (uri);
+
+exit:
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void
+ep_forget_password (EPassMsg *msg)
+{
+ EUri *uri;
+ GError *error = NULL;
+
+ g_hash_table_remove (password_cache, msg->key);
+
+ uri = ep_keyring_uri_new (msg->key, &msg->error);
+ if (uri == NULL)
+ goto exit;
+
+ /* Find all Evolution passwords matching the URI and delete them.
+ *
+ * XXX We didn't always store protocols in the keyring, so for
+ * backward-compatibility we need to lookup passwords by user
+ * and host only (no protocol). But we do send the protocol
+ * to ep_keyring_delete_passwords(), which also knows about
+ * the backward-compatibility issue and will filter the list
+ * appropriately. */
+ secret_password_clear_sync (
+ &e_passwords_schema, NULL, &error,
+ "application", "Evolution",
+ "user", uri->user,
+ "server", uri->host,
+ NULL);
+
+ if (error != NULL)
+ g_propagate_error (&msg->error, error);
+
+ e_uri_free (uri);
+
+exit:
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void
+ep_get_password (EPassMsg *msg)
+{
+ EUri *uri;
+ gchar *password;
+ GError *error = NULL;
+
+ /* Check the in-memory cache first. */
+ password = g_hash_table_lookup (password_cache, msg->key);
+ if (password != NULL) {
+ msg->password = g_strdup (password);
+ goto exit;
+ }
+
+ uri = ep_keyring_uri_new (msg->key, &msg->error);
+ if (uri == NULL)
+ goto exit;
+
+ msg->password = secret_password_lookup_sync (
+ &e_passwords_schema, NULL, &error,
+ "application", "Evolution",
+ "user", uri->user,
+ "server", uri->host,
+ "protocol", uri->protocol,
+ NULL);
+
+ if (msg->password != NULL)
+ goto done;
+
+ /* Clear the previous error, if there was one.
+ * It's likely to occur again. */
+ if (error != NULL)
+ g_clear_error (&error);
+
+ /* XXX We didn't always store protocols in the keyring, so for
+ * backward-compatibility we also need to lookup passwords
+ * by user and host only (no protocol). */
+ msg->password = secret_password_lookup_sync (
+ &e_passwords_schema, NULL, &error,
+ "application", "Evolution",
+ "user", uri->user,
+ "server", uri->host,
+ NULL);
+
+done:
+ if (error != NULL)
+ g_propagate_error (&msg->error, error);
+
+ e_uri_free (uri);
+
+exit:
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void
+ep_add_password (EPassMsg *msg)
+{
+ g_hash_table_insert (
+ password_cache, g_strdup (msg->key),
+ g_strdup (msg->oldpass));
+
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void ep_ask_password (EPassMsg *msg);
+
+static void
+pass_response (GtkDialog *dialog,
+ gint response,
+ gpointer data)
+{
+ EPassMsg *msg = data;
+ gint type = msg->flags & E_PASSWORDS_REMEMBER_MASK;
+ GList *iter, *trash = NULL;
+
+ if (response == GTK_RESPONSE_OK) {
+ msg->password = g_strdup (gtk_entry_get_text ((GtkEntry *) msg->entry));
+
+ if (type != E_PASSWORDS_REMEMBER_NEVER) {
+ gint noreply = msg->noreply;
+
+ *msg->remember = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (msg->check));
+
+ msg->noreply = 1;
+
+ if (*msg->remember || type == E_PASSWORDS_REMEMBER_FOREVER) {
+ msg->oldpass = msg->password;
+ ep_add_password (msg);
+ }
+ if (*msg->remember && type == E_PASSWORDS_REMEMBER_FOREVER)
+ ep_remember_password (msg);
+
+ msg->noreply = noreply;
+ }
+ }
+
+ gtk_widget_destroy ((GtkWidget *) dialog);
+ password_dialog = NULL;
+
+ /* ok, here things get interesting, we suck up any pending
+ * operations on this specific password, and return the same
+ * result or ignore other operations */
+
+ G_LOCK (passwords);
+ for (iter = g_queue_peek_head_link (&message_queue); iter != NULL; iter = iter->next) {
+ EPassMsg *pending = iter->data;
+
+ if ((pending->dispatch == ep_forget_password
+ || pending->dispatch == ep_get_password
+ || pending->dispatch == ep_ask_password)
+ && strcmp (pending->key, msg->key) == 0) {
+
+ /* Satisfy the pending operation. */
+ pending->password = g_strdup (msg->password);
+ e_flag_set (pending->done);
+
+ /* Mark the queue node for deletion. */
+ trash = g_list_prepend (trash, iter);
+ }
+ }
+
+ /* Expunge the message queue. */
+ for (iter = trash; iter != NULL; iter = iter->next)
+ g_queue_delete_link (&message_queue, iter->data);
+ g_list_free (trash);
+
+ G_UNLOCK (passwords);
+
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+
+ ep_idle_dispatch (NULL);
+}
+
+static gboolean
+update_capslock_state (GtkDialog *dialog,
+ GdkEvent *event,
+ GtkWidget *label)
+{
+ GdkModifierType mask = 0;
+ GdkWindow *window;
+ gchar *markup = NULL;
+ GdkDeviceManager *device_manager;
+ GdkDevice *device;
+
+ device_manager = gdk_display_get_device_manager (gtk_widget_get_display (label));
+ device = gdk_device_manager_get_client_pointer (device_manager);
+ window = gtk_widget_get_window (GTK_WIDGET (dialog));
+ gdk_window_get_device_position (window, device, NULL, NULL, &mask);
+
+ /* The space acts as a vertical placeholder. */
+ markup = g_markup_printf_escaped (
+ "<small>%s</small>", (mask & GDK_LOCK_MASK) ?
+ _("You have the Caps Lock key on.") : " ");
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+
+ return FALSE;
+}
+
+static void
+ep_ask_password (EPassMsg *msg)
+{
+ GtkWidget *widget;
+ GtkWidget *container;
+ GtkWidget *action_area;
+ GtkWidget *content_area;
+ gint type = msg->flags & E_PASSWORDS_REMEMBER_MASK;
+ guint noreply = msg->noreply;
+ gboolean visible;
+ AtkObject *a11y;
+
+ msg->noreply = 1;
+
+ widget = gtk_dialog_new_with_buttons (
+ msg->title, msg->parent, 0,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (
+ GTK_DIALOG (widget), GTK_RESPONSE_OK);
+ gtk_window_set_resizable (GTK_WINDOW (widget), FALSE);
+ gtk_window_set_transient_for (GTK_WINDOW (widget), msg->parent);
+ gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
+ password_dialog = GTK_DIALOG (widget);
+
+ action_area = gtk_dialog_get_action_area (password_dialog);
+ content_area = gtk_dialog_get_content_area (password_dialog);
+
+ /* Override GtkDialog defaults */
+ gtk_box_set_spacing (GTK_BOX (action_area), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
+ gtk_box_set_spacing (GTK_BOX (content_area), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+ /* Grid */
+ container = gtk_grid_new ();
+ gtk_grid_set_column_spacing (GTK_GRID (container), 12);
+ gtk_grid_set_row_spacing (GTK_GRID (container), 6);
+ gtk_widget_show (container);
+
+ gtk_box_pack_start (
+ GTK_BOX (content_area), container, FALSE, TRUE, 0);
+
+ /* Password Image */
+ widget = gtk_image_new_from_icon_name (
+ "dialog-password", GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+ g_object_set (G_OBJECT (widget),
+ "halign", GTK_ALIGN_FILL,
+ "vexpand", TRUE,
+ "valign", GTK_ALIGN_FILL,
+ NULL);
+ gtk_widget_show (widget);
+
+ gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 3);
+
+ /* Password Label */
+ widget = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_label_set_markup (GTK_LABEL (widget), msg->prompt);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ g_object_set (G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ NULL);
+ gtk_widget_show (widget);
+
+ gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1);
+
+ /* Password Entry */
+ widget = gtk_entry_new ();
+ a11y = gtk_widget_get_accessible (widget);
+ visible = !(msg->flags & E_PASSWORDS_SECRET);
+ atk_object_set_description (a11y, msg->prompt);
+ gtk_entry_set_visibility (GTK_ENTRY (widget), visible);
+ gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+ gtk_widget_grab_focus (widget);
+ g_object_set (G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ NULL);
+ gtk_widget_show (widget);
+ msg->entry = widget;
+
+ if ((msg->flags & E_PASSWORDS_REPROMPT)) {
+ ep_get_password (msg);
+ if (msg->password != NULL) {
+ gtk_entry_set_text (GTK_ENTRY (widget), msg->password);
+ g_free (msg->password);
+ msg->password = NULL;
+ }
+ }
+
+ gtk_grid_attach (GTK_GRID (container), widget, 1, 1, 1, 1);
+
+ /* Caps Lock Label */
+ widget = gtk_label_new (NULL);
+ g_object_set (G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ NULL);
+ gtk_widget_show (widget);
+
+ gtk_grid_attach (GTK_GRID (container), widget, 1, 2, 1, 1);
+
+ g_signal_connect (
+ password_dialog, "key-release-event",
+ G_CALLBACK (update_capslock_state), widget);
+ g_signal_connect (
+ password_dialog, "focus-in-event",
+ G_CALLBACK (update_capslock_state), widget);
+
+ /* static password, shouldn't be remembered between sessions,
+ * but will be remembered within the session beyond our control */
+ if (type != E_PASSWORDS_REMEMBER_NEVER) {
+ if (msg->flags & E_PASSWORDS_PASSPHRASE) {
+ widget = gtk_check_button_new_with_mnemonic (
+ (type == E_PASSWORDS_REMEMBER_FOREVER)
+ ? _("_Remember this passphrase")
+ : _("_Remember this passphrase for"
+ " the remainder of this session"));
+ } else {
+ widget = gtk_check_button_new_with_mnemonic (
+ (type == E_PASSWORDS_REMEMBER_FOREVER)
+ ? _("_Remember this password")
+ : _("_Remember this password for"
+ " the remainder of this session"));
+ }
+
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (widget), *msg->remember);
+ if (msg->flags & E_PASSWORDS_DISABLE_REMEMBER)
+ gtk_widget_set_sensitive (widget, FALSE);
+ g_object_set (G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_FILL,
+ NULL);
+ gtk_widget_show (widget);
+ msg->check = widget;
+
+ gtk_grid_attach (GTK_GRID (container), widget, 1, 3, 1, 1);
+ }
+
+ msg->noreply = noreply;
+
+ g_signal_connect (
+ password_dialog, "response",
+ G_CALLBACK (pass_response), msg);
+
+ if (msg->parent) {
+ gtk_dialog_run (GTK_DIALOG (password_dialog));
+ } else {
+ gtk_window_present (GTK_WINDOW (password_dialog));
+ /* workaround GTK+ bug (see Gnome's bugzilla bug #624229) */
+ gtk_grab_add (GTK_WIDGET (password_dialog));
+ }
+}
+
+/**
+ * e_passwords_init:
+ *
+ * Initializes the e_passwords routines. Must be called before any other
+ * e_passwords_* function.
+ **/
+void
+e_passwords_init (void)
+{
+ G_LOCK (passwords);
+
+ if (password_cache == NULL) {
+ password_cache = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+ main_thread = g_thread_self ();
+ }
+
+ G_UNLOCK (passwords);
+}
+
+/**
+ * e_passwords_cancel:
+ *
+ * Cancel any outstanding password operations and close any dialogues
+ * currently being shown.
+ **/
+void
+e_passwords_cancel (void)
+{
+ EPassMsg *msg;
+
+ G_LOCK (passwords);
+ while ((msg = g_queue_pop_head (&message_queue)) != NULL)
+ e_flag_set (msg->done);
+ G_UNLOCK (passwords);
+
+ if (password_dialog)
+ gtk_dialog_response (password_dialog, GTK_RESPONSE_CANCEL);
+}
+
+/**
+ * e_passwords_shutdown:
+ *
+ * Cleanup routine to call before exiting.
+ **/
+void
+e_passwords_shutdown (void)
+{
+ EPassMsg *msg;
+
+ G_LOCK (passwords);
+
+ while ((msg = g_queue_pop_head (&message_queue)) != NULL)
+ e_flag_set (msg->done);
+
+ if (password_cache != NULL) {
+ g_hash_table_destroy (password_cache);
+ password_cache = NULL;
+ }
+
+ G_UNLOCK (passwords);
+
+ if (password_dialog != NULL)
+ gtk_dialog_response (password_dialog, GTK_RESPONSE_CANCEL);
+}
+
+/**
+ * e_passwords_set_online:
+ * @state:
+ *
+ * Set the offline-state of the application. This is a work-around
+ * for having the backends fully offline aware, and returns a
+ * cancellation response instead of prompting for passwords.
+ *
+ * FIXME: This is not a permanent api, review post 2.0.
+ **/
+void
+e_passwords_set_online (gint state)
+{
+ ep_online_state = state;
+ /* TODO: we could check that a request is open and close it, or maybe who cares */
+}
+
+/**
+ * e_passwords_forget_passwords:
+ *
+ * Forgets all cached passwords, in memory and on disk.
+ **/
+void
+e_passwords_forget_passwords (void)
+{
+ EPassMsg *msg = ep_msg_new (ep_clear_passwords);
+
+ ep_msg_send (msg);
+ ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_clear_passwords:
+ *
+ * Forgets all disk cached passwords for the component.
+ **/
+void
+e_passwords_clear_passwords (const gchar *unused)
+{
+ EPassMsg *msg = ep_msg_new (ep_clear_passwords);
+
+ ep_msg_send (msg);
+ ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_remember_password:
+ * @key: the key
+ *
+ * Saves the password associated with @key to disk.
+ **/
+void
+e_passwords_remember_password (const gchar *unused,
+ const gchar *key)
+{
+ EPassMsg *msg;
+
+ g_return_if_fail (key != NULL);
+
+ msg = ep_msg_new (ep_remember_password);
+ msg->key = key;
+
+ ep_msg_send (msg);
+ ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_forget_password:
+ * @key: the key
+ *
+ * Forgets the password associated with @key, in memory and on disk.
+ **/
+void
+e_passwords_forget_password (const gchar *unused,
+ const gchar *key)
+{
+ EPassMsg *msg;
+
+ g_return_if_fail (key != NULL);
+
+ msg = ep_msg_new (ep_forget_password);
+ msg->key = key;
+
+ ep_msg_send (msg);
+ ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_get_password:
+ * @key: the key
+ *
+ * Returns: the password associated with @key, or %NULL. Caller
+ * must free the returned password.
+ **/
+gchar *
+e_passwords_get_password (const gchar *unused,
+ const gchar *key)
+{
+ EPassMsg *msg;
+ gchar *passwd;
+
+ g_return_val_if_fail (key != NULL, NULL);
+
+ msg = ep_msg_new (ep_get_password);
+ msg->key = key;
+
+ ep_msg_send (msg);
+
+ passwd = msg->password;
+ msg->password = NULL;
+ ep_msg_free (msg);
+
+ return passwd;
+}
+
+/**
+ * e_passwords_add_password:
+ * @key: a key
+ * @passwd: the password for @key
+ *
+ * This stores the @key/@passwd pair in the current session's password
+ * hash.
+ **/
+void
+e_passwords_add_password (const gchar *key,
+ const gchar *passwd)
+{
+ EPassMsg *msg;
+
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (passwd != NULL);
+
+ msg = ep_msg_new (ep_add_password);
+ msg->key = key;
+ msg->oldpass = passwd;
+
+ ep_msg_send (msg);
+ ep_msg_free (msg);
+}
+
+/**
+ * e_passwords_ask_password:
+ * @title: title for the password dialog
+ * @unused: this argument is no longer used
+ * @key: key to store the password under
+ * @prompt: prompt string
+ * @remember_type: whether or not to offer to remember the password,
+ * and for how long.
+ * @remember: on input, the default state of the remember checkbox.
+ * on output, the state of the checkbox when the dialog was closed.
+ * @parent: parent window of the dialog, or %NULL
+ *
+ * Asks the user for a password.
+ *
+ * Returns: the password, which the caller must free, or %NULL if
+ * the user cancelled the operation. *@remember will be set if the
+ * return value is non-%NULL and @remember_type is not
+ * E_PASSWORDS_DO_NOT_REMEMBER.
+ **/
+gchar *
+e_passwords_ask_password (const gchar *title,
+ const gchar *unused,
+ const gchar *key,
+ const gchar *prompt,
+ EPasswordsRememberType remember_type,
+ gboolean *remember,
+ GtkWindow *parent)
+{
+ gchar *passwd;
+ EPassMsg *msg;
+
+ g_return_val_if_fail (key != NULL, NULL);
+
+ if ((remember_type & E_PASSWORDS_ONLINE) && !ep_online_state)
+ return NULL;
+
+ msg = ep_msg_new (ep_ask_password);
+ msg->title = title;
+ msg->key = key;
+ msg->prompt = prompt;
+ msg->flags = remember_type;
+ msg->remember = remember;
+ msg->parent = parent;
+
+ ep_msg_send (msg);
+ passwd = msg->password;
+ msg->password = NULL;
+ ep_msg_free (msg);
+
+ return passwd;
+}