aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-passwords-win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-passwords-win32.c')
-rw-r--r--e-util/e-passwords-win32.c1064
1 files changed, 1064 insertions, 0 deletions
diff --git a/e-util/e-passwords-win32.c b/e-util/e-passwords-win32.c
new file mode 100644
index 0000000000..51c0cb21bb
--- /dev/null
+++ b/e-util/e-passwords-win32.c
@@ -0,0 +1,1064 @@
+/* -*- 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 most 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>
+
+#include "e-passwords.h"
+#include "libedataserver/e-data-server-util.h"
+#include "libedataserver/e-flag.h"
+#include "libedataserver/e-url.h"
+
+#define d(x)
+
+typedef struct _EPassMsg EPassMsg;
+
+struct _EPassMsg {
+ void (*dispatch) (EPassMsg *);
+ EFlag *done;
+
+ /* input */
+ GtkWindow *parent;
+ const gchar *component;
+ 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 */
+};
+
+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;
+
+#define KEY_FILE_GROUP_PREFIX "Passwords-"
+static GKeyFile *key_file = NULL;
+
+static gboolean
+check_key_file (const gchar *funcname)
+{
+ if (!key_file)
+ g_message ("%s: Failed to create key file!", funcname);
+
+ return key_file != NULL;
+}
+
+static gchar *
+ep_key_file_get_filename (void)
+{
+ return g_build_filename (
+ e_get_user_config_dir (), "credentials", "Passwords", NULL);
+}
+
+static gchar *
+ep_key_file_get_group (const gchar *component)
+{
+ return g_strconcat (KEY_FILE_GROUP_PREFIX, component, NULL);
+}
+
+static gchar *
+ep_key_file_normalize_key (const gchar *key)
+{
+ /* XXX Previous code converted all slashes and equal signs in the
+ * key to underscores for use with "gnome-config" functions. While
+ * it may not be necessary to convert slashes for use with GKeyFile,
+ * we continue to do the same for backward-compatibility. */
+
+ gchar *normalized_key, *cp;
+
+ normalized_key = g_strdup (key);
+ for (cp = normalized_key; *cp != '\0'; cp++)
+ if (*cp == '/' || *cp == '=')
+ *cp = '_';
+
+ return normalized_key;
+}
+
+static void
+ep_key_file_load (void)
+{
+ gchar *filename;
+ GError *error = NULL;
+
+ if (!check_key_file (G_STRFUNC))
+ return;
+
+ filename = ep_key_file_get_filename ();
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+ goto exit;
+
+ g_key_file_load_from_file (
+ key_file, filename, G_KEY_FILE_KEEP_COMMENTS |
+ G_KEY_FILE_KEEP_TRANSLATIONS, &error);
+
+ if (error != NULL) {
+ g_warning ("%s: %s", filename, error->message);
+ g_error_free (error);
+ }
+
+exit:
+ g_free (filename);
+}
+
+static void
+ep_key_file_save (void)
+{
+ gchar *contents;
+ gchar *filename;
+ gchar *pathname;
+ gsize length = 0;
+ GError *error = NULL;
+
+ if (!check_key_file (G_STRFUNC))
+ return;
+
+ filename = ep_key_file_get_filename ();
+ contents = g_key_file_to_data (key_file, &length, &error);
+ pathname = g_path_get_dirname (filename);
+
+ if (!error) {
+ g_mkdir_with_parents (pathname, 0700);
+ g_file_set_contents (filename, contents, length, &error);
+ }
+
+ g_free (pathname);
+
+ if (error != NULL) {
+ g_warning ("%s: %s", filename, error->message);
+ g_error_free (error);
+ }
+
+ g_free (contents);
+ g_free (filename);
+}
+
+static gchar *
+ep_password_encode (const gchar *password)
+{
+ /* XXX The previous Base64 encoding function did not encode the
+ * password's trailing nul byte. This makes decoding the Base64
+ * string into a nul-terminated password more difficult, but we
+ * continue to do it this way for backward-compatibility. */
+
+ gsize length = strlen (password);
+ return g_base64_encode ((const guchar *) password, length);
+}
+
+static gchar *
+ep_password_decode (const gchar *encoded_password)
+{
+ /* XXX The previous Base64 encoding function did not encode the
+ * password's trailing nul byte, so we have to append a nul byte
+ * to the decoded data to make it a nul-terminated string. */
+
+ gchar *password;
+ gsize length = 0;
+
+ password = (gchar *) g_base64_decode (encoded_password, &length);
+ password = g_realloc (password, length + 1);
+ password[length] = '\0';
+
+ return password;
+}
+
+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_keyfile (EPassMsg *msg)
+{
+ gchar *group;
+ GError *error = NULL;
+
+ if (!check_key_file (G_STRFUNC))
+ return;
+
+ group = ep_key_file_get_group (msg->component);
+
+ if (g_key_file_remove_group (key_file, group, &error))
+ ep_key_file_save ();
+
+ /* Not finding the requested group is acceptable, but we still
+ * want to leave an informational message on the terminal. */
+ else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+ g_message ("%s", error->message);
+ g_error_free (error);
+
+ } else if (error != NULL)
+ g_propagate_error (&msg->error, error);
+
+ g_free (group);
+}
+
+static void
+ep_clear_passwords (EPassMsg *msg)
+{
+ ep_clear_passwords_keyfile (msg);
+
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void
+ep_forget_passwords_keyfile (EPassMsg *msg)
+{
+ gchar **groups;
+ gsize length = 0, ii;
+
+ if (!check_key_file (G_STRFUNC))
+ return;
+
+ groups = g_key_file_get_groups (key_file, &length);
+
+ if (!groups)
+ return;
+
+ for (ii = 0; ii < length; ii++) {
+ GError *error = NULL;
+
+ if (!g_str_has_prefix (groups[ii], KEY_FILE_GROUP_PREFIX))
+ continue;
+
+ g_key_file_remove_group (key_file, groups[ii], &error);
+
+ /* Not finding the requested group is acceptable, but we still
+ * want to leave an informational message on the terminal. */
+ if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+ g_message ("%s", error->message);
+ g_error_free (error);
+
+ /* Issue a warning if anything else goes wrong. */
+ } else if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ }
+ ep_key_file_save ();
+ g_strfreev (groups);
+}
+
+static void
+ep_forget_passwords (EPassMsg *msg)
+{
+ g_hash_table_remove_all (password_cache);
+
+ ep_forget_passwords_keyfile (msg);
+
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void
+ep_remember_password_keyfile (EPassMsg *msg)
+{
+ gchar *group, *key, *password;
+
+ password = g_hash_table_lookup (password_cache, msg->key);
+ if (password == NULL) {
+ g_warning ("Password for key \"%s\" not found", msg->key);
+ return;
+ }
+
+ group = ep_key_file_get_group (msg->component);
+ key = ep_key_file_normalize_key (msg->key);
+ password = ep_password_encode (password);
+
+ g_hash_table_remove (password_cache, msg->key);
+ if (check_key_file (G_STRFUNC)) {
+ g_key_file_set_string (key_file, group, key, password);
+ ep_key_file_save ();
+ }
+
+ g_free (group);
+ g_free (key);
+ g_free (password);
+}
+
+static void
+ep_remember_password (EPassMsg *msg)
+{
+ ep_remember_password_keyfile (msg);
+
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void
+ep_forget_password_keyfile (EPassMsg *msg)
+{
+ gchar *group, *key;
+ GError *error = NULL;
+
+ if (!check_key_file (G_STRFUNC))
+ return;
+
+ group = ep_key_file_get_group (msg->component);
+ key = ep_key_file_normalize_key (msg->key);
+
+ if (g_key_file_remove_key (key_file, group, key, &error))
+ ep_key_file_save ();
+
+ /* Not finding the requested key is acceptable, but we still
+ * want to leave an informational message on the terminal. */
+ else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+ g_message ("%s", error->message);
+ g_error_free (error);
+
+ /* Not finding the requested group is also acceptable. */
+ } else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+ g_message ("%s", error->message);
+ g_error_free (error);
+
+ } else if (error != NULL)
+ g_propagate_error (&msg->error, error);
+
+ g_free (group);
+ g_free (key);
+}
+
+static void
+ep_forget_password (EPassMsg *msg)
+{
+ g_hash_table_remove (password_cache, msg->key);
+
+ ep_forget_password_keyfile (msg);
+
+ if (!msg->noreply)
+ e_flag_set (msg->done);
+}
+
+static void
+ep_get_password_keyfile (EPassMsg *msg)
+{
+ gchar *group, *key, *password;
+ GError *error = NULL;
+
+ if (!check_key_file (G_STRFUNC))
+ return;
+
+ group = ep_key_file_get_group (msg->component);
+ key = ep_key_file_normalize_key (msg->key);
+
+ password = g_key_file_get_string (key_file, group, key, &error);
+ if (password != NULL) {
+ msg->password = ep_password_decode (password);
+ g_free (password);
+
+ /* Not finding the requested key is acceptable, but we still
+ * want to leave an informational message on the terminal. */
+ } else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+ g_message ("%s", error->message);
+ g_error_free (error);
+
+ /* Not finding the requested group is also acceptable. */
+ } else if (g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+ g_message ("%s", error->message);
+ g_error_free (error);
+
+ } else if (error != NULL)
+ g_propagate_error (&msg->error, error);
+
+ g_free (group);
+ g_free (key);
+}
+
+static void
+ep_get_password (EPassMsg *msg)
+{
+ gchar *password;
+
+ /* Check the in-memory cache first. */
+ password = g_hash_table_lookup (password_cache, msg->key);
+ if (password != NULL) {
+ msg->password = g_strdup (password);
+
+ } else
+ ep_get_password_keyfile (msg);
+
+ 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->component, msg->component) == 0
+ && 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);
+#if !GTK_CHECK_VERSION(2,90,7)
+ g_object_set (widget, "has-separator", FALSE, NULL);
+#endif
+ 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 ();
+
+ /* Load the keyfile even if we're using the keyring.
+ * We might be able to extract passwords from it. */
+ key_file = g_key_file_new ();
+ ep_key_file_load ();
+
+ }
+
+ 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_forget_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 *component_name)
+{
+ EPassMsg *msg = ep_msg_new (ep_clear_passwords);
+
+ msg->component = component_name;
+ 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 *component_name,
+ const gchar *key)
+{
+ EPassMsg *msg;
+
+ g_return_if_fail (component_name != NULL);
+ g_return_if_fail (key != NULL);
+
+ msg = ep_msg_new (ep_remember_password);
+ msg->component = component_name;
+ 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 *component_name,
+ const gchar *key)
+{
+ EPassMsg *msg;
+
+ g_return_if_fail (component_name != NULL);
+ g_return_if_fail (key != NULL);
+
+ msg = ep_msg_new (ep_forget_password);
+ msg->component = component_name;
+ 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 *component_name,
+ const gchar *key)
+{
+ EPassMsg *msg;
+ gchar *passwd;
+
+ g_return_val_if_fail (component_name != NULL, NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ msg = ep_msg_new (ep_get_password);
+ msg->component = component_name;
+ 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
+ * @component_name: the name of the component for which we're storing
+ * the password (e.g. Mail, Addressbook, etc.)
+ * @key: key to store the password under
+ * @prompt: prompt string
+ * @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 *component_name,
+ const gchar *key,
+ const gchar *prompt,
+ EPasswordsRememberType type,
+ gboolean *remember,
+ GtkWindow *parent)
+{
+ gchar *passwd;
+ EPassMsg *msg;
+
+ g_return_val_if_fail (component_name != NULL, NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ if ((type & E_PASSWORDS_ONLINE) && !ep_online_state)
+ return NULL;
+
+ msg = ep_msg_new (ep_ask_password);
+ msg->title = title;
+ msg->component = component_name;
+ msg->key = key;
+ msg->prompt = prompt;
+ msg->flags = type;
+ msg->remember = remember;
+ msg->parent = parent;
+
+ ep_msg_send (msg);
+ passwd = msg->password;
+ msg->password = NULL;
+ ep_msg_free (msg);
+
+ return passwd;
+}