diff options
Diffstat (limited to 'e-util/e-passwords-win32.c')
-rw-r--r-- | e-util/e-passwords-win32.c | 1064 |
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; +} |