1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  * e-passwords.c
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  */
8 
9 /*
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17  * for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * This looks a lot more complicated than it is, and than you'd think
25  * it would need to be.  There is however, method to the madness.
26  *
27  * The code must cope with being called from any thread at any time,
28  * recursively from the main thread, and then serialising every
29  * request so that sane and correct values are always returned, and
30  * duplicate requests are never made.
31  *
32  * To this end, every call is marshalled and queued and a dispatch
33  * method invoked until that request is satisfied.  If mainloop
34  * recursion occurs, then the sub-call will necessarily return out of
35  * order, but will not be processed out of order.
36  */
37 
38 #include "evolution-config.h"
39 
40 #include <limits.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <gtk/gtk.h>
44 #include <glib/gi18n-lib.h>
45 
46 #include <libsoup/soup.h>
47 
48 /* XXX Yeah, yeah... */
49 #define SECRET_API_SUBJECT_TO_CHANGE
50 
51 #include <libsecret/secret.h>
52 
53 #include <libedataserver/libedataserver.h>
54 
55 #include "e-passwords.h"
56 
57 #define d(x)
58 
59 typedef struct _EPassMsg EPassMsg;
60 
61 struct _EPassMsg {
62 	void (*dispatch) (EPassMsg *);
63 	EFlag *done;
64 
65 	/* input */
66 	GtkWindow *parent;
67 	const gchar *key;
68 	const gchar *title;
69 	const gchar *prompt;
70 	const gchar *oldpass;
71 	guint32 flags;
72 
73 	/* output */
74 	gboolean *remember;
75 	gchar *password;
76 	GError *error;
77 
78 	/* work variables */
79 	GtkWidget *entry;
80 	GtkWidget *check;
81 	guint ismain : 1;
82 	guint noreply:1;	/* supress replies; when calling
83 				 * dispatch functions from others */
84 };
85 
86 /* XXX probably want to share this with evalution-source-registry-migrate-sources.c */
87 static const SecretSchema e_passwords_schema = {
88 	"org.gnome.Evolution.Password",
89 	SECRET_SCHEMA_DONT_MATCH_NAME,
90 	{
91 		{ "application", SECRET_SCHEMA_ATTRIBUTE_STRING, },
92 		{ "user", SECRET_SCHEMA_ATTRIBUTE_STRING, },
93 		{ "server", SECRET_SCHEMA_ATTRIBUTE_STRING, },
94 		{ "protocol", SECRET_SCHEMA_ATTRIBUTE_STRING, },
95 	}
96 };
97 
98 G_LOCK_DEFINE_STATIC (passwords);
99 static GThread *main_thread = NULL;
100 static GHashTable *password_cache = NULL;
101 static GtkDialog *password_dialog = NULL;
102 static GQueue message_queue = G_QUEUE_INIT;
103 static gint idle_id;
104 static gint ep_online_state = TRUE;
105 
106 static SoupURI *
ep_keyring_uri_new(const gchar * string,GError ** error)107 ep_keyring_uri_new (const gchar *string,
108                     GError **error)
109 {
110 	SoupURI *uri;
111 
112 	uri = soup_uri_new (string);
113 	g_return_val_if_fail (uri != NULL, NULL);
114 
115 	/* LDAP URIs do not have usernames, so use the URI as the username. */
116 	if (uri->user == NULL && uri->scheme != NULL &&
117 			(strcmp (uri->scheme, "ldap") == 0|| strcmp (uri->scheme, "google") == 0))
118 		uri->user = g_strdelimit (g_strdup (string), "/=", '_');
119 
120 	/* Make sure the URI has the required components. */
121 	if (uri->user == NULL && uri->host == NULL) {
122 		g_set_error_literal (
123 			error, G_IO_ERROR,
124 			G_IO_ERROR_INVALID_ARGUMENT,
125 			_("Keyring key is unusable: no user or host name"));
126 		soup_uri_free (uri);
127 		uri = NULL;
128 	}
129 
130 	return uri;
131 }
132 
133 static gboolean
ep_idle_dispatch(gpointer data)134 ep_idle_dispatch (gpointer data)
135 {
136 	EPassMsg *msg;
137 
138 	/* As soon as a password window is up we stop; it will
139 	 * re - invoke us when it has been closed down */
140 	G_LOCK (passwords);
141 	while (password_dialog == NULL && (msg = g_queue_pop_head (&message_queue)) != NULL) {
142 		G_UNLOCK (passwords);
143 
144 		msg->dispatch (msg);
145 
146 		G_LOCK (passwords);
147 	}
148 
149 	idle_id = 0;
150 	G_UNLOCK (passwords);
151 
152 	return FALSE;
153 }
154 
155 static EPassMsg *
ep_msg_new(void (* dispatch)(EPassMsg *))156 ep_msg_new (void (*dispatch) (EPassMsg *))
157 {
158 	EPassMsg *msg;
159 
160 	e_passwords_init ();
161 
162 	msg = g_malloc0 (sizeof (*msg));
163 	msg->dispatch = dispatch;
164 	msg->done = e_flag_new ();
165 	msg->ismain = (g_thread_self () == main_thread);
166 
167 	return msg;
168 }
169 
170 static void
ep_msg_free(EPassMsg * msg)171 ep_msg_free (EPassMsg *msg)
172 {
173 	/* XXX We really should be passing this back to the caller, but
174 	 *     doing so will require breaking the password API. */
175 	if (msg->error != NULL) {
176 		g_warning ("%s", msg->error->message);
177 		g_error_free (msg->error);
178 	}
179 
180 	e_flag_free (msg->done);
181 	g_free (msg->password);
182 	g_free (msg);
183 }
184 
185 static void
ep_msg_send(EPassMsg * msg)186 ep_msg_send (EPassMsg *msg)
187 {
188 	gint needidle = 0;
189 
190 	G_LOCK (passwords);
191 	g_queue_push_tail (&message_queue, msg);
192 	if (!idle_id) {
193 		if (!msg->ismain)
194 			idle_id = g_idle_add (ep_idle_dispatch, NULL);
195 		else
196 			needidle = 1;
197 	}
198 	G_UNLOCK (passwords);
199 
200 	if (msg->ismain) {
201 		if (needidle)
202 			ep_idle_dispatch (NULL);
203 		while (!e_flag_is_set (msg->done))
204 			g_main_context_iteration (NULL, TRUE);
205 	} else
206 		e_flag_wait (msg->done);
207 }
208 
209 /* the functions that actually do the work */
210 
211 static void
ep_remember_password(EPassMsg * msg)212 ep_remember_password (EPassMsg *msg)
213 {
214 	gchar *password;
215 	SoupURI *uri;
216 	GError *error = NULL;
217 
218 	password = g_hash_table_lookup (password_cache, msg->key);
219 	if (password == NULL) {
220 		g_warning ("Password for key \"%s\" not found", msg->key);
221 		goto exit;
222 	}
223 
224 	uri = ep_keyring_uri_new (msg->key, &msg->error);
225 	if (uri == NULL)
226 		goto exit;
227 
228 	secret_password_store_sync (
229 		&e_passwords_schema,
230 		SECRET_COLLECTION_DEFAULT,
231 		msg->key, password,
232 		NULL, &error,
233 		"application", "Evolution",
234 		"user", uri->user,
235 		"server", uri->host,
236 		"protocol", uri->scheme,
237 		NULL);
238 
239 	/* Only remove the password from the session hash
240 	 * if the keyring insertion was successful. */
241 	if (error == NULL)
242 		g_hash_table_remove (password_cache, msg->key);
243 	else
244 		g_propagate_error (&msg->error, error);
245 
246 	soup_uri_free (uri);
247 
248 exit:
249 	if (!msg->noreply)
250 		e_flag_set (msg->done);
251 }
252 
253 static void
ep_forget_password(EPassMsg * msg)254 ep_forget_password (EPassMsg *msg)
255 {
256 	SoupURI *uri;
257 	GError *error = NULL;
258 
259 	g_hash_table_remove (password_cache, msg->key);
260 
261 	uri = ep_keyring_uri_new (msg->key, &msg->error);
262 	if (uri == NULL)
263 		goto exit;
264 
265 	/* Find all Evolution passwords matching the URI and delete them.
266 	 *
267 	 * XXX We didn't always store protocols in the keyring, so for
268 	 *     backward-compatibility we need to lookup passwords by user
269 	 *     and host only (no protocol).  But we do send the protocol
270 	 *     to ep_keyring_delete_passwords(), which also knows about
271 	 *     the backward-compatibility issue and will filter the list
272 	 *     appropriately. */
273 	secret_password_clear_sync (
274 		&e_passwords_schema, NULL, &error,
275 		"application", "Evolution",
276 		"user", uri->user,
277 		"server", uri->host,
278 		NULL);
279 
280 	if (error != NULL)
281 		g_propagate_error (&msg->error, error);
282 
283 	soup_uri_free (uri);
284 
285 exit:
286 	if (!msg->noreply)
287 		e_flag_set (msg->done);
288 }
289 
290 static void
ep_get_password(EPassMsg * msg)291 ep_get_password (EPassMsg *msg)
292 {
293 	SoupURI *uri;
294 	gchar *password;
295 	GError *error = NULL;
296 
297 	/* Check the in-memory cache first. */
298 	password = g_hash_table_lookup (password_cache, msg->key);
299 	if (password != NULL) {
300 		msg->password = g_strdup (password);
301 		goto exit;
302 	}
303 
304 	uri = ep_keyring_uri_new (msg->key, &msg->error);
305 	if (uri == NULL)
306 		goto exit;
307 
308 	msg->password = secret_password_lookup_sync (
309 		&e_passwords_schema, NULL, &error,
310 		"application", "Evolution",
311 		"user", uri->user,
312 		"server", uri->host,
313 		"protocol", uri->scheme,
314 		NULL);
315 
316 	if (msg->password != NULL)
317 		goto done;
318 
319 	/* Clear the previous error, if there was one.
320 	 * It's likely to occur again. */
321 	if (error != NULL)
322 		g_clear_error (&error);
323 
324 	/* XXX We didn't always store protocols in the keyring, so for
325 	 *     backward-compatibility we also need to lookup passwords
326 	 *     by user and host only (no protocol). */
327 	msg->password = secret_password_lookup_sync (
328 		&e_passwords_schema, NULL, &error,
329 		"application", "Evolution",
330 		"user", uri->user,
331 		"server", uri->host,
332 		NULL);
333 
334 done:
335 	if (error != NULL)
336 		g_propagate_error (&msg->error, error);
337 
338 	soup_uri_free (uri);
339 
340 exit:
341 	if (!msg->noreply)
342 		e_flag_set (msg->done);
343 }
344 
345 static void
ep_add_password(EPassMsg * msg)346 ep_add_password (EPassMsg *msg)
347 {
348 	g_hash_table_insert (
349 		password_cache, g_strdup (msg->key),
350 		g_strdup (msg->oldpass));
351 
352 	if (!msg->noreply)
353 		e_flag_set (msg->done);
354 }
355 
356 static void ep_ask_password (EPassMsg *msg);
357 
358 static void
pass_response(GtkDialog * dialog,gint response,gpointer data)359 pass_response (GtkDialog *dialog,
360                gint response,
361                gpointer data)
362 {
363 	EPassMsg *msg = data;
364 	gint type = msg->flags & E_PASSWORDS_REMEMBER_MASK;
365 	GList *iter, *trash = NULL;
366 
367 	if (response == GTK_RESPONSE_OK) {
368 		msg->password = g_strdup (gtk_entry_get_text ((GtkEntry *) msg->entry));
369 
370 		if (type != E_PASSWORDS_REMEMBER_NEVER) {
371 			gint noreply = msg->noreply;
372 
373 			*msg->remember = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (msg->check));
374 
375 			msg->noreply = 1;
376 
377 			if (*msg->remember || type == E_PASSWORDS_REMEMBER_FOREVER) {
378 				msg->oldpass = msg->password;
379 				ep_add_password (msg);
380 			}
381 			if (*msg->remember && type == E_PASSWORDS_REMEMBER_FOREVER)
382 				ep_remember_password (msg);
383 
384 			msg->noreply = noreply;
385 		}
386 	}
387 
388 	gtk_widget_destroy ((GtkWidget *) dialog);
389 	password_dialog = NULL;
390 
391 	/* ok, here things get interesting, we suck up any pending
392 	 * operations on this specific password, and return the same
393 	 * result or ignore other operations */
394 
395 	G_LOCK (passwords);
396 	for (iter = g_queue_peek_head_link (&message_queue); iter != NULL; iter = iter->next) {
397 		EPassMsg *pending = iter->data;
398 
399 		if ((pending->dispatch == ep_forget_password
400 		     || pending->dispatch == ep_get_password
401 		     || pending->dispatch == ep_ask_password)
402 			&& strcmp (pending->key, msg->key) == 0) {
403 
404 			/* Satisfy the pending operation. */
405 			pending->password = g_strdup (msg->password);
406 			e_flag_set (pending->done);
407 
408 			/* Mark the queue node for deletion. */
409 			trash = g_list_prepend (trash, iter);
410 		}
411 	}
412 
413 	/* Expunge the message queue. */
414 	for (iter = trash; iter != NULL; iter = iter->next)
415 		g_queue_delete_link (&message_queue, iter->data);
416 	g_list_free (trash);
417 
418 	G_UNLOCK (passwords);
419 
420 	if (!msg->noreply)
421 		e_flag_set (msg->done);
422 
423 	ep_idle_dispatch (NULL);
424 }
425 
426 static gboolean
update_capslock_state(GtkDialog * dialog,GdkEvent * event,GtkWidget * label)427 update_capslock_state (GtkDialog *dialog,
428                        GdkEvent *event,
429                        GtkWidget *label)
430 {
431 	GdkModifierType mask = 0;
432 	GdkWindow *window;
433 	gchar *markup = NULL;
434 	GdkDeviceManager *device_manager;
435 	GdkDevice *device;
436 
437 	device_manager = gdk_display_get_device_manager (gtk_widget_get_display (label));
438 	device = gdk_device_manager_get_client_pointer (device_manager);
439 	window = gtk_widget_get_window (GTK_WIDGET (dialog));
440 	gdk_window_get_device_position (window, device, NULL, NULL, &mask);
441 
442 	/* The space acts as a vertical placeholder. */
443 	markup = g_markup_printf_escaped (
444 		"<small>%s</small>", (mask & GDK_LOCK_MASK) ?
445 		_("You have the Caps Lock key on.") : " ");
446 	gtk_label_set_markup (GTK_LABEL (label), markup);
447 	g_free (markup);
448 
449 	return FALSE;
450 }
451 
452 static void
ep_ask_password(EPassMsg * msg)453 ep_ask_password (EPassMsg *msg)
454 {
455 	GtkWidget *widget;
456 	GtkWidget *container;
457 	GtkWidget *action_area;
458 	GtkWidget *content_area;
459 	gint type = msg->flags & E_PASSWORDS_REMEMBER_MASK;
460 	guint noreply = msg->noreply;
461 	gboolean visible;
462 	AtkObject *a11y;
463 
464 	msg->noreply = 1;
465 
466 	widget = gtk_dialog_new_with_buttons (
467 		msg->title, msg->parent, 0,
468 		_("_Cancel"), GTK_RESPONSE_CANCEL,
469 		_("_OK"), GTK_RESPONSE_OK,
470 		NULL);
471 	gtk_dialog_set_default_response (
472 		GTK_DIALOG (widget), GTK_RESPONSE_OK);
473 	gtk_window_set_resizable (GTK_WINDOW (widget), FALSE);
474 	gtk_window_set_transient_for (GTK_WINDOW (widget), msg->parent);
475 	gtk_window_set_position (GTK_WINDOW (widget), GTK_WIN_POS_CENTER_ON_PARENT);
476 	gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
477 	password_dialog = GTK_DIALOG (widget);
478 
479 	action_area = gtk_dialog_get_action_area (password_dialog);
480 	content_area = gtk_dialog_get_content_area (password_dialog);
481 
482 	/* Override GtkDialog defaults */
483 	gtk_box_set_spacing (GTK_BOX (action_area), 12);
484 	gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
485 	gtk_box_set_spacing (GTK_BOX (content_area), 12);
486 	gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
487 
488 	/* Grid */
489 	container = gtk_grid_new ();
490 	gtk_grid_set_column_spacing (GTK_GRID (container), 12);
491 	gtk_grid_set_row_spacing (GTK_GRID (container), 6);
492 	gtk_widget_show (container);
493 
494 	gtk_box_pack_start (
495 		GTK_BOX (content_area), container, FALSE, TRUE, 0);
496 
497 	/* Password Image */
498 	widget = gtk_image_new_from_icon_name (
499 		"dialog-password", GTK_ICON_SIZE_DIALOG);
500 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
501 	g_object_set (
502 		G_OBJECT (widget),
503 		"halign", GTK_ALIGN_FILL,
504 		"vexpand", TRUE,
505 		"valign", GTK_ALIGN_FILL,
506 		NULL);
507 	gtk_widget_show (widget);
508 
509 	gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 3);
510 
511 	/* Password Label */
512 	widget = gtk_label_new (NULL);
513 	gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
514 	gtk_label_set_width_chars (GTK_LABEL (widget), 20);
515 	gtk_label_set_markup (GTK_LABEL (widget), msg->prompt);
516 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
517 	g_object_set (
518 		G_OBJECT (widget),
519 		"hexpand", TRUE,
520 		"halign", GTK_ALIGN_FILL,
521 		NULL);
522 	gtk_widget_show (widget);
523 
524 	gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1);
525 
526 	/* Password Entry */
527 	widget = gtk_entry_new ();
528 	a11y = gtk_widget_get_accessible (widget);
529 	visible = !(msg->flags & E_PASSWORDS_SECRET);
530 	atk_object_set_description (a11y, msg->prompt);
531 	gtk_entry_set_visibility (GTK_ENTRY (widget), visible);
532 	gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
533 	gtk_widget_grab_focus (widget);
534 	g_object_set (
535 		G_OBJECT (widget),
536 		"hexpand", TRUE,
537 		"halign", GTK_ALIGN_FILL,
538 		NULL);
539 	gtk_widget_show (widget);
540 	msg->entry = widget;
541 
542 	if ((msg->flags & E_PASSWORDS_REPROMPT)) {
543 		ep_get_password (msg);
544 		if (msg->password != NULL) {
545 			gtk_entry_set_text (GTK_ENTRY (widget), msg->password);
546 			g_free (msg->password);
547 			msg->password = NULL;
548 		}
549 	}
550 
551 	gtk_grid_attach (GTK_GRID (container), widget, 1, 1, 1, 1);
552 
553 	/* Caps Lock Label */
554 	widget = gtk_label_new (NULL);
555 	g_object_set (
556 		G_OBJECT (widget),
557 		"hexpand", TRUE,
558 		"halign", GTK_ALIGN_FILL,
559 		NULL);
560 	gtk_widget_show (widget);
561 
562 	gtk_grid_attach (GTK_GRID (container), widget, 1, 2, 1, 1);
563 
564 	g_signal_connect (
565 		password_dialog, "key-release-event",
566 		G_CALLBACK (update_capslock_state), widget);
567 	g_signal_connect (
568 		password_dialog, "focus-in-event",
569 		G_CALLBACK (update_capslock_state), widget);
570 
571 	/* static password, shouldn't be remembered between sessions,
572 	 * but will be remembered within the session beyond our control */
573 	if (type != E_PASSWORDS_REMEMBER_NEVER) {
574 		if (msg->flags & E_PASSWORDS_PASSPHRASE) {
575 			widget = gtk_check_button_new_with_mnemonic (
576 				(type == E_PASSWORDS_REMEMBER_FOREVER)
577 				? _("_Remember this passphrase")
578 				: _("_Remember this passphrase for"
579 				" the remainder of this session"));
580 		} else {
581 			widget = gtk_check_button_new_with_mnemonic (
582 				(type == E_PASSWORDS_REMEMBER_FOREVER)
583 				? _("_Remember this password")
584 				: _("_Remember this password for"
585 				" the remainder of this session"));
586 		}
587 
588 		gtk_toggle_button_set_active (
589 			GTK_TOGGLE_BUTTON (widget), *msg->remember);
590 		if (msg->flags & E_PASSWORDS_DISABLE_REMEMBER)
591 			gtk_widget_set_sensitive (widget, FALSE);
592 		g_object_set (
593 			G_OBJECT (widget),
594 			"hexpand", TRUE,
595 			"halign", GTK_ALIGN_FILL,
596 			"valign", GTK_ALIGN_FILL,
597 			NULL);
598 		gtk_widget_show (widget);
599 		msg->check = widget;
600 
601 		gtk_grid_attach (GTK_GRID (container), widget, 1, 3, 1, 1);
602 	}
603 
604 	msg->noreply = noreply;
605 
606 	g_signal_connect (
607 		password_dialog, "response",
608 		G_CALLBACK (pass_response), msg);
609 
610 	if (msg->parent) {
611 		gtk_dialog_run (GTK_DIALOG (password_dialog));
612 	} else {
613 		gtk_window_present (GTK_WINDOW (password_dialog));
614 		/* workaround GTK+ bug (see Gnome's bugzilla bug #624229) */
615 		gtk_grab_add (GTK_WIDGET (password_dialog));
616 	}
617 }
618 
619 /**
620  * e_passwords_init:
621  *
622  * Initializes the e_passwords routines. Must be called before any other
623  * e_passwords_* function.
624  **/
625 void
e_passwords_init(void)626 e_passwords_init (void)
627 {
628 	G_LOCK (passwords);
629 
630 	if (password_cache == NULL) {
631 		password_cache = g_hash_table_new_full (
632 			g_str_hash, g_str_equal,
633 			(GDestroyNotify) g_free,
634 			(GDestroyNotify) g_free);
635 		main_thread = g_thread_self ();
636 	}
637 
638 	G_UNLOCK (passwords);
639 }
640 
641 /**
642  * e_passwords_set_online:
643  * @state:
644  *
645  * Set the offline-state of the application.  This is a work-around
646  * for having the backends fully offline aware, and returns a
647  * cancellation response instead of prompting for passwords.
648  *
649  * FIXME: This is not a permanent api, review post 2.0.
650  **/
651 void
e_passwords_set_online(gint state)652 e_passwords_set_online (gint state)
653 {
654 	ep_online_state = state;
655 	/* TODO: we could check that a request is open and close it, or maybe who cares */
656 }
657 
658 /**
659  * e_passwords_remember_password:
660  * @key: the key
661  *
662  * Saves the password associated with @key to disk.
663  **/
664 void
e_passwords_remember_password(const gchar * key)665 e_passwords_remember_password (const gchar *key)
666 {
667 	EPassMsg *msg;
668 
669 	g_return_if_fail (key != NULL);
670 
671 	msg = ep_msg_new (ep_remember_password);
672 	msg->key = key;
673 
674 	ep_msg_send (msg);
675 	ep_msg_free (msg);
676 }
677 
678 /**
679  * e_passwords_forget_password:
680  * @key: the key
681  *
682  * Forgets the password associated with @key, in memory and on disk.
683  **/
684 void
e_passwords_forget_password(const gchar * key)685 e_passwords_forget_password (const gchar *key)
686 {
687 	EPassMsg *msg;
688 
689 	g_return_if_fail (key != NULL);
690 
691 	msg = ep_msg_new (ep_forget_password);
692 	msg->key = key;
693 
694 	ep_msg_send (msg);
695 	ep_msg_free (msg);
696 }
697 
698 /**
699  * e_passwords_get_password:
700  * @key: the key
701  *
702  * Returns: the password associated with @key, or %NULL.  Caller
703  * must free the returned password.
704  **/
705 gchar *
e_passwords_get_password(const gchar * key)706 e_passwords_get_password (const gchar *key)
707 {
708 	EPassMsg *msg;
709 	gchar *passwd;
710 
711 	g_return_val_if_fail (key != NULL, NULL);
712 
713 	msg = ep_msg_new (ep_get_password);
714 	msg->key = key;
715 
716 	ep_msg_send (msg);
717 
718 	passwd = msg->password;
719 	msg->password = NULL;
720 	ep_msg_free (msg);
721 
722 	return passwd;
723 }
724 
725 /**
726  * e_passwords_add_password:
727  * @key: a key
728  * @passwd: the password for @key
729  *
730  * This stores the @key/@passwd pair in the current session's password
731  * hash.
732  **/
733 void
e_passwords_add_password(const gchar * key,const gchar * passwd)734 e_passwords_add_password (const gchar *key,
735                           const gchar *passwd)
736 {
737 	EPassMsg *msg;
738 
739 	g_return_if_fail (key != NULL);
740 	g_return_if_fail (passwd != NULL);
741 
742 	msg = ep_msg_new (ep_add_password);
743 	msg->key = key;
744 	msg->oldpass = passwd;
745 
746 	ep_msg_send (msg);
747 	ep_msg_free (msg);
748 }
749 
750 /**
751  * e_passwords_ask_password:
752  * @title: title for the password dialog
753  * @key: key to store the password under
754  * @prompt: prompt string
755  * @remember_type: whether or not to offer to remember the password,
756  * and for how long.
757  * @remember: on input, the default state of the remember checkbox.
758  * on output, the state of the checkbox when the dialog was closed.
759  * @parent: parent window of the dialog, or %NULL
760  *
761  * Asks the user for a password.
762  *
763  * Returns: the password, which the caller must free, or %NULL if
764  * the user cancelled the operation. *@remember will be set if the
765  * return value is non-%NULL and @remember_type is not
766  * E_PASSWORDS_DO_NOT_REMEMBER.
767  **/
768 gchar *
e_passwords_ask_password(const gchar * title,const gchar * key,const gchar * prompt,EPasswordsRememberType remember_type,gboolean * remember,GtkWindow * parent)769 e_passwords_ask_password (const gchar *title,
770                           const gchar *key,
771                           const gchar *prompt,
772                           EPasswordsRememberType remember_type,
773                           gboolean *remember,
774                           GtkWindow *parent)
775 {
776 	gchar *passwd;
777 	EPassMsg *msg;
778 
779 	g_return_val_if_fail (key != NULL, NULL);
780 
781 	if ((remember_type & E_PASSWORDS_ONLINE) && !ep_online_state)
782 		return NULL;
783 
784 	msg = ep_msg_new (ep_ask_password);
785 	msg->title = title;
786 	msg->key = key;
787 	msg->prompt = prompt;
788 	msg->flags = remember_type;
789 	msg->remember = remember;
790 	msg->parent = parent;
791 
792 	ep_msg_send (msg);
793 	passwd = msg->password;
794 	msg->password = NULL;
795 	ep_msg_free (msg);
796 
797 	return passwd;
798 }
799