From d09d8de870b6697c8a8b262e7e077b871a69b315 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 10 Dec 2012 08:09:59 -0500 Subject: 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. --- e-util/e-passwords.c | 890 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 890 insertions(+) create mode 100644 e-util/e-passwords.c (limited to 'e-util/e-passwords.c') 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 +#include +#include +#include +#include + +/* XXX Yeah, yeah... */ +#define SECRET_API_SUBJECT_TO_CHANGE + +#include + +#include + +#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 ( + "%s", (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; +} -- cgit v1.2.3