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