Make use of G_DEFINE_QUARK()
[platform/upstream/evolution-data-server.git] / libebackend / e-authentication-session.c
1 /*
2  * e-authentication-session.c
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
16  *
17  */
18
19 /**
20  * SECTION: e-authentication-session
21  * @include: libebackend/libebackend.h
22  * @short_description: Centralized authentication management
23  *
24  * #EAuthenticationSession provides centralized password management and
25  * password prompting for all clients of the registry D-Bus service.
26  *
27  * An #EAuthenticationSession is created within the registry D-Bus service
28  * when the service receives a request to authenticate some data source.
29  * Clients can issue requests by calling e_source_registry_authenticate().
30  * Requests can also come from any #ECollectionBackend running within the
31  * service itself.
32  *
33  * An #EAuthenticationSession requires some object implementing the
34  * #ESourceAuthenticator interface to verify stored or user-provided
35  * passwords.  #EAuthenticationMediator is used for client-issued
36  * authentication requests.  Custom collection backends derived from
37  * #ECollectionBackend usually implement the #ESourceAuthenticator
38  * interface themselves.
39  *
40  * The #EAuthenticationSession is then handed to #ESourceRegistryServer
41  * through e_source_registry_server_authenticate() where it waits in line
42  * behind other previously added authentication sessions.  When its turn
43  * comes, the server calls e_authentication_session_execute() to begin
44  * the interactive authentication session.
45  **/
46
47 #include "e-authentication-session.h"
48
49 /* XXX Yeah, yeah... */
50 #define GCR_API_SUBJECT_TO_CHANGE
51
52 #include <config.h>
53 #include <string.h>
54 #include <glib/gi18n-lib.h>
55 #include <gcr/gcr-base.h>
56 #include <libsecret/secret.h>
57
58 /* Private D-Bus classes. */
59 #include <e-dbus-authenticator.h>
60
61 #include <libebackend/e-authentication-mediator.h>
62 #include <libebackend/e-backend-enumtypes.h>
63 #include <libebackend/e-server-side-source.h>
64 #include <libebackend/e-source-registry-server.h>
65
66 #define E_AUTHENTICATION_SESSION_GET_PRIVATE(obj) \
67         (G_TYPE_INSTANCE_GET_PRIVATE \
68         ((obj), E_TYPE_AUTHENTICATION_SESSION, EAuthenticationSessionPrivate))
69
70 /* Wait forever for a system prompt. */
71 #define SYSTEM_PROMPT_TIMEOUT (-1)
72
73 #define KEYRING_ITEM_ATTRIBUTE_NAME     "e-source-uid"
74 #define KEYRING_ITEM_DISPLAY_FORMAT     "Evolution Data Source %s"
75
76 typedef struct _AsyncContext AsyncContext;
77
78 struct _EAuthenticationSessionPrivate {
79         ESourceRegistryServer *server;
80         ESourceAuthenticator *authenticator;
81         gchar *source_uid;
82
83         /* These are for configuring system prompts. */
84         GMutex property_lock;
85         gchar *prompt_title;
86         gchar *prompt_message;
87         gchar *prompt_description;
88 };
89
90 struct _AsyncContext {
91         EAuthenticationSessionResult auth_result;
92         gchar *password;
93         gboolean permanently;
94 };
95
96 enum {
97         PROP_0,
98         PROP_AUTHENTICATOR,
99         PROP_PROMPT_DESCRIPTION,
100         PROP_PROMPT_MESSAGE,
101         PROP_PROMPT_TITLE,
102         PROP_SERVER,
103         PROP_SOURCE_UID
104 };
105
106 static SecretSchema schema = {
107         "org.gnome.Evolution.Data.Source",
108         SECRET_SCHEMA_DONT_MATCH_NAME,
109         {
110                 { KEYRING_ITEM_ATTRIBUTE_NAME,
111                   SECRET_SCHEMA_ATTRIBUTE_STRING },
112                 { NULL, 0 }
113         }
114 };
115
116 /* Forward Declarations */
117 static void     authentication_session_msg
118                                         (EAuthenticationSession *session,
119                                          const gchar *format,
120                                          ...) G_GNUC_PRINTF (2, 3);
121
122 G_DEFINE_TYPE (
123         EAuthenticationSession,
124         e_authentication_session,
125         G_TYPE_OBJECT)
126
127 static void
128 async_context_free (AsyncContext *async_context)
129 {
130         g_free (async_context->password);
131         g_slice_free (AsyncContext, async_context);
132 }
133
134 static void
135 authentication_session_msg (EAuthenticationSession *session,
136                             const gchar *format,
137                             ...)
138 {
139         GString *buffer;
140         va_list args;
141
142         buffer = g_string_sized_new (256);
143
144         g_string_append_printf (
145                 buffer, "AUTH (%s): ",
146                 session->priv->source_uid);
147
148         va_start (args, format);
149         g_string_append_vprintf (buffer, format, args);
150         va_end (args);
151
152         g_print ("%s\n", buffer->str);
153
154         g_string_free (buffer, TRUE);
155 }
156
157 static void
158 authentication_session_set_authenticator (EAuthenticationSession *session,
159                                           ESourceAuthenticator *authenticator)
160 {
161         g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (authenticator));
162         g_return_if_fail (session->priv->authenticator == NULL);
163
164         session->priv->authenticator = g_object_ref (authenticator);
165 }
166
167 static void
168 authentication_session_set_server (EAuthenticationSession *session,
169                                    ESourceRegistryServer *server)
170 {
171         g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
172         g_return_if_fail (session->priv->server == NULL);
173
174         session->priv->server = g_object_ref (server);
175 }
176
177 static void
178 authentication_session_set_source_uid (EAuthenticationSession *session,
179                                        const gchar *source_uid)
180 {
181         g_return_if_fail (source_uid != NULL);
182         g_return_if_fail (session->priv->source_uid == NULL);
183
184         session->priv->source_uid = g_strdup (source_uid);
185 }
186
187 static void
188 authentication_session_set_property (GObject *object,
189                                      guint property_id,
190                                      const GValue *value,
191                                      GParamSpec *pspec)
192 {
193         switch (property_id) {
194                 case PROP_AUTHENTICATOR:
195                         authentication_session_set_authenticator (
196                                 E_AUTHENTICATION_SESSION (object),
197                                 g_value_get_object (value));
198                         return;
199
200                 case PROP_PROMPT_DESCRIPTION:
201                         e_authentication_session_set_prompt_description (
202                                 E_AUTHENTICATION_SESSION (object),
203                                 g_value_get_string (value));
204                         return;
205
206                 case PROP_PROMPT_MESSAGE:
207                         e_authentication_session_set_prompt_message (
208                                 E_AUTHENTICATION_SESSION (object),
209                                 g_value_get_string (value));
210                         return;
211
212                 case PROP_PROMPT_TITLE:
213                         e_authentication_session_set_prompt_title (
214                                 E_AUTHENTICATION_SESSION (object),
215                                 g_value_get_string (value));
216                         return;
217
218                 case PROP_SERVER:
219                         authentication_session_set_server (
220                                 E_AUTHENTICATION_SESSION (object),
221                                 g_value_get_object (value));
222                         return;
223
224                 case PROP_SOURCE_UID:
225                         authentication_session_set_source_uid (
226                                 E_AUTHENTICATION_SESSION (object),
227                                 g_value_get_string (value));
228                         return;
229         }
230
231         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
232 }
233
234 static void
235 authentication_session_get_property (GObject *object,
236                                      guint property_id,
237                                      GValue *value,
238                                      GParamSpec *pspec)
239 {
240         switch (property_id) {
241                 case PROP_AUTHENTICATOR:
242                         g_value_set_object (
243                                 value,
244                                 e_authentication_session_get_authenticator (
245                                 E_AUTHENTICATION_SESSION (object)));
246                         return;
247
248                 case PROP_PROMPT_DESCRIPTION:
249                         g_value_take_string (
250                                 value,
251                                 e_authentication_session_dup_prompt_description (
252                                 E_AUTHENTICATION_SESSION (object)));
253                         return;
254
255                 case PROP_PROMPT_MESSAGE:
256                         g_value_take_string (
257                                 value,
258                                 e_authentication_session_dup_prompt_message (
259                                 E_AUTHENTICATION_SESSION (object)));
260                         return;
261
262                 case PROP_PROMPT_TITLE:
263                         g_value_take_string (
264                                 value,
265                                 e_authentication_session_dup_prompt_title (
266                                 E_AUTHENTICATION_SESSION (object)));
267                         return;
268
269                 case PROP_SERVER:
270                         g_value_set_object (
271                                 value,
272                                 e_authentication_session_get_server (
273                                 E_AUTHENTICATION_SESSION (object)));
274                         return;
275
276                 case PROP_SOURCE_UID:
277                         g_value_set_string (
278                                 value,
279                                 e_authentication_session_get_source_uid (
280                                 E_AUTHENTICATION_SESSION (object)));
281                         return;
282         }
283
284         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
285 }
286
287 static void
288 authentication_session_dispose (GObject *object)
289 {
290         EAuthenticationSessionPrivate *priv;
291
292         priv = E_AUTHENTICATION_SESSION_GET_PRIVATE (object);
293
294         if (priv->server != NULL) {
295                 g_object_unref (priv->server);
296                 priv->server = NULL;
297         }
298
299         if (priv->authenticator != NULL) {
300                 g_object_unref (priv->authenticator);
301                 priv->authenticator = NULL;
302         }
303
304         /* Chain up to parent's dispose() method. */
305         G_OBJECT_CLASS (e_authentication_session_parent_class)->
306                 dispose (object);
307 }
308
309 static void
310 authentication_session_finalize (GObject *object)
311 {
312         EAuthenticationSessionPrivate *priv;
313
314         priv = E_AUTHENTICATION_SESSION_GET_PRIVATE (object);
315
316         g_mutex_clear (&priv->property_lock);
317
318         g_free (priv->source_uid);
319         g_free (priv->prompt_title);
320         g_free (priv->prompt_message);
321         g_free (priv->prompt_description);
322
323         /* Chain up to parent's finalize() method. */
324         G_OBJECT_CLASS (e_authentication_session_parent_class)->
325                 finalize (object);
326 }
327
328 static void
329 authentication_session_constructed (GObject *object)
330 {
331         EAuthenticationSession *session;
332         ESourceAuthenticator *authenticator;
333         ESourceRegistryServer *server;
334         ESource *source;
335         const gchar *source_uid;
336
337         session = E_AUTHENTICATION_SESSION (object);
338
339         /* Chain up to parent's constructed() method. */
340         G_OBJECT_CLASS (e_authentication_session_parent_class)->
341                 constructed (object);
342
343         /* If the server knows about the data source UID we've been
344          * given, then we can auto-configure our own prompt strings. */
345
346         server = e_authentication_session_get_server (session);
347         source_uid = e_authentication_session_get_source_uid (session);
348         authenticator = e_authentication_session_get_authenticator (session);
349
350         source = e_source_registry_server_ref_source (server, source_uid);
351         if (source != NULL) {
352                 gchar *prompt_title = NULL;
353                 gchar *prompt_message = NULL;
354                 gchar *prompt_description = NULL;
355
356                 e_source_authenticator_get_prompt_strings (
357                         authenticator, source,
358                         &prompt_title,
359                         &prompt_message,
360                         &prompt_description);
361
362                 g_object_set (
363                         session,
364                         "prompt-title", prompt_title,
365                         "prompt-message", prompt_message,
366                         "prompt-description", prompt_description,
367                         NULL);
368
369                 g_free (prompt_title);
370                 g_free (prompt_message);
371                 g_free (prompt_description);
372
373                 g_object_unref (source);
374         }
375 }
376
377 /* Helper for authentication_session_execute() */
378 static void
379 authentication_session_execute_thread (GSimpleAsyncResult *simple,
380                                        GObject *object,
381                                        GCancellable *cancellable)
382 {
383         AsyncContext *async_context;
384         GError *error = NULL;
385
386         async_context = g_simple_async_result_get_op_res_gpointer (simple);
387
388         async_context->auth_result =
389                 e_authentication_session_execute_sync (
390                         E_AUTHENTICATION_SESSION (object),
391                         cancellable, &error);
392
393         if (error != NULL)
394                 g_simple_async_result_take_error (simple, error);
395 }
396
397 static EAuthenticationSessionResult
398 authentication_session_execute_sync (EAuthenticationSession *session,
399                                      GCancellable *cancellable,
400                                      GError **error)
401 {
402         ESourceAuthenticator *authenticator;
403         EAuthenticationSessionResult session_result;
404         ESourceAuthenticationResult auth_result;
405         ESourceRegistryServer *server;
406         ESource *source;
407         GcrPrompt *prompt;
408         GString *password_string;
409         gboolean allow_auth_prompt;
410         const gchar *label;
411         const gchar *source_uid;
412         const gchar *prompt_password;
413         gchar *stored_password = NULL;
414         gboolean success;
415         GError *local_error = NULL;
416
417         /* XXX I moved the execute() operation into a class method thinking
418          *     we might someday want to subclass EAuthenticationSession and
419          *     override this method to make it behave differently.
420          *
421          *     It would be a little premature to do this now, but we might
422          *     also want to use the basic algorithm here as a template and
423          *     turn the password lookup/delete/store operations into class
424          *     methods that could also be overridden.  I reserved adequate
425          *     space in the class struct for this should the need arise.
426          *
427          *     For now though we'll keep it simple.  I don't want to over-
428          *     engineer this too much in trying to make it future-proof.
429          */
430
431         authentication_session_msg (session, "Initiated");
432
433         server = e_authentication_session_get_server (session);
434         source_uid = e_authentication_session_get_source_uid (session);
435         authenticator = e_authentication_session_get_authenticator (session);
436
437         success = e_authentication_session_lookup_password_sync (
438                 session, cancellable, &stored_password, error);
439
440         if (!success) {
441                 session_result = E_AUTHENTICATION_SESSION_ERROR;
442                 goto exit;
443         }
444
445         auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
446
447         /* If we found a stored password, signal the client without
448          * interrupting the user.  Note, if the client responds with
449          * REJECTED, we'll have to interrupt the user after all. */
450         if (stored_password != NULL) {
451                 password_string = g_string_new (stored_password);
452
453                 auth_result = e_source_authenticator_try_password_sync (
454                         authenticator, password_string, cancellable, error);
455
456                 g_string_free (password_string, TRUE);
457                 password_string = NULL;
458
459                 g_free (stored_password);
460                 stored_password = NULL;
461         }
462
463         if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) {
464                 session_result = E_AUTHENTICATION_SESSION_ERROR;
465                 goto exit;
466         }
467
468         if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
469                 session_result = E_AUTHENTICATION_SESSION_SUCCESS;
470                 goto exit;
471         }
472
473         g_warn_if_fail (auth_result == E_SOURCE_AUTHENTICATION_REJECTED);
474
475         /* The stored password is bad so delete it from the keyring.
476          * Failure here does not affect the outcome of this operation,
477          * but leave a breadcrumb as evidence that something went wrong. */
478
479         e_authentication_session_delete_password_sync (
480                 session, cancellable, &local_error);
481
482         if (local_error != NULL) {
483                 g_warning ("%s: %s", G_STRFUNC, local_error->message);
484                 g_clear_error (&local_error);
485         }
486
487         /* This will return NULL if the authenticating data source
488          * has not yet been submitted to the D-Bus registry service. */
489         source = e_source_registry_server_ref_source (server, source_uid);
490         if (source != NULL) {
491                 allow_auth_prompt =
492                         e_server_side_source_get_allow_auth_prompt (
493                         E_SERVER_SIDE_SOURCE (source));
494                 g_object_unref (source);
495         } else {
496                 allow_auth_prompt = TRUE;
497         }
498
499         /* Check if we're allowed to interrupt the user for a password.
500          * If not, we have no choice but to dismiss the authentication
501          * request. */
502         if (!allow_auth_prompt) {
503                 session_result = E_AUTHENTICATION_SESSION_DISMISSED;
504                 goto exit;
505         }
506
507         /* Configure a system prompt. */
508
509         prompt = gcr_system_prompt_open (
510                 SYSTEM_PROMPT_TIMEOUT, cancellable, error);
511
512         if (prompt == NULL) {
513                 session_result = E_AUTHENTICATION_SESSION_ERROR;
514                 goto exit;
515         }
516
517         g_object_bind_property (
518                 session, "prompt-title",
519                 prompt, "title",
520                 G_BINDING_SYNC_CREATE);
521
522         g_object_bind_property (
523                 session, "prompt-message",
524                 prompt, "message",
525                 G_BINDING_SYNC_CREATE);
526
527         g_object_bind_property (
528                 session, "prompt-description",
529                 prompt, "description",
530                 G_BINDING_SYNC_CREATE);
531
532         label = _("Add this password to your keyring");
533         gcr_prompt_set_choice_label (prompt, label);
534         gcr_prompt_set_choice_chosen (prompt, TRUE);
535
536 try_again:
537
538         /* Prompt the user for a password. */
539
540         prompt_password = gcr_prompt_password (
541                 prompt, cancellable, &local_error);
542
543         if (local_error != NULL) {
544                 session_result = E_AUTHENTICATION_SESSION_ERROR;
545                 g_propagate_error (error, local_error);
546                 local_error = NULL;
547                 goto close_prompt;
548         }
549
550         /* No password and no error indicates a dismissal. */
551         if (prompt_password == NULL) {
552                 session_result = E_AUTHENTICATION_SESSION_DISMISSED;
553                 goto close_prompt;
554         }
555
556         /* Attempt authentication with the provided password. */
557
558         password_string = g_string_new (prompt_password);
559
560         auth_result = e_source_authenticator_try_password_sync (
561                 authenticator, password_string, cancellable, error);
562
563         g_string_free (password_string, TRUE);
564         password_string = NULL;
565
566         if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) {
567                 session_result = E_AUTHENTICATION_SESSION_ERROR;
568                 goto close_prompt;
569         }
570
571         if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
572                 gboolean permanently;
573                 gchar *password_copy;
574
575                 permanently = gcr_prompt_get_choice_chosen (prompt);
576                 session_result = E_AUTHENTICATION_SESSION_SUCCESS;
577
578                 /* Close our prompt before storing the password in
579                  * the keyring.  If the keyring is locked, it will
580                  * need to prompt the user for a keyring password,
581                  * but it can't do that if our password prompt is
582                  * still open since both prompts are system-modal.
583                  * Not sure what would happen next; probably the
584                  * store operation would either fail or deadlock. */
585
586                 /* XXX Not sure if it's safe to use the prompt's
587                  *     password string after closing the prompt,
588                  *     so make a copy here just to be safe. */
589                 password_copy = gcr_secure_memory_strdup (prompt_password);
590
591                 /* Failure here does not affect the outcome of this
592                  * operation, but leave a breadcrumb as evidence that
593                  * something went wrong. */
594
595                 gcr_system_prompt_close (
596                         GCR_SYSTEM_PROMPT (prompt),
597                         cancellable, &local_error);
598
599                 if (local_error != NULL) {
600                         g_warning ("%s: %s", G_STRFUNC, local_error->message);
601                         g_clear_error (&local_error);
602                 }
603
604                 g_object_unref (prompt);
605
606                 e_authentication_session_store_password_sync (
607                         session, password_copy, permanently,
608                         cancellable, &local_error);
609
610                 if (local_error != NULL) {
611                         g_warning ("%s: %s", G_STRFUNC, local_error->message);
612                         g_clear_error (&local_error);
613                 }
614
615                 gcr_secure_memory_strfree (password_copy);
616
617                 goto exit;
618         }
619
620         g_warn_if_fail (auth_result == E_SOURCE_AUTHENTICATION_REJECTED);
621
622         gcr_prompt_set_warning (prompt, _("Password was incorrect"));
623
624         goto try_again;
625
626 close_prompt:
627
628         /* Failure here does not affect the outcome of this operation,
629          * but leave a breadcrumb as evidence that something went wrong. */
630
631         gcr_system_prompt_close (
632                 GCR_SYSTEM_PROMPT (prompt),
633                 cancellable, &local_error);
634
635         if (local_error != NULL) {
636                 g_warning ("%s: %s", G_STRFUNC, local_error->message);
637                 g_clear_error (&local_error);
638         }
639
640         g_object_unref (prompt);
641
642 exit:
643
644         switch (session_result) {
645                 case E_AUTHENTICATION_SESSION_ERROR:
646                         if (error != NULL && *error != NULL)
647                                 authentication_session_msg (
648                                         session, "Complete (ERROR - %s)",
649                                         (*error)->message);
650                         else
651                                 authentication_session_msg (
652                                         session, "Complete (ERROR)");
653                         break;
654                 case E_AUTHENTICATION_SESSION_SUCCESS:
655                         authentication_session_msg (
656                                 session, "Complete (SUCCESS)");
657                         break;
658                 case E_AUTHENTICATION_SESSION_DISMISSED:
659                         authentication_session_msg (
660                                 session, "Complete (DISMISSED)");
661                         break;
662                 default:
663                         g_warn_if_reached ();
664         }
665
666         return session_result;
667 }
668
669 static void
670 authentication_session_execute (EAuthenticationSession *session,
671                                 gint io_priority,
672                                 GCancellable *cancellable,
673                                 GAsyncReadyCallback callback,
674                                 gpointer user_data)
675 {
676         GSimpleAsyncResult *simple;
677         AsyncContext *async_context;
678
679         g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
680
681         async_context = g_slice_new0 (AsyncContext);
682
683         simple = g_simple_async_result_new (
684                 G_OBJECT (session), callback, user_data,
685                 e_authentication_session_execute);
686
687         g_simple_async_result_set_check_cancellable (simple, cancellable);
688
689         g_simple_async_result_set_op_res_gpointer (
690                 simple, async_context, (GDestroyNotify) async_context_free);
691
692         g_simple_async_result_run_in_thread (
693                 simple, authentication_session_execute_thread,
694                 io_priority, cancellable);
695
696         g_object_unref (simple);
697 }
698
699 static EAuthenticationSessionResult
700 authentication_session_execute_finish (EAuthenticationSession *session,
701                                        GAsyncResult *result,
702                                        GError **error)
703 {
704         GSimpleAsyncResult *simple;
705         AsyncContext *async_context;
706
707         g_return_val_if_fail (
708                 g_simple_async_result_is_valid (
709                 result, G_OBJECT (session),
710                 e_authentication_session_execute),
711                 E_AUTHENTICATION_SESSION_DISMISSED);
712
713         simple = G_SIMPLE_ASYNC_RESULT (result);
714         async_context = g_simple_async_result_get_op_res_gpointer (simple);
715
716         if (g_simple_async_result_propagate_error (simple, error))
717                 return E_AUTHENTICATION_SESSION_ERROR;
718
719         return async_context->auth_result;
720 }
721
722 static void
723 e_authentication_session_class_init (EAuthenticationSessionClass *class)
724 {
725         GObjectClass *object_class;
726
727         g_type_class_add_private (
728                 class, sizeof (EAuthenticationSessionPrivate));
729
730         object_class = G_OBJECT_CLASS (class);
731         object_class->set_property = authentication_session_set_property;
732         object_class->get_property = authentication_session_get_property;
733         object_class->dispose = authentication_session_dispose;
734         object_class->finalize = authentication_session_finalize;
735         object_class->constructed = authentication_session_constructed;
736
737         class->execute_sync = authentication_session_execute_sync;
738         class->execute = authentication_session_execute;
739         class->execute_finish = authentication_session_execute_finish;
740
741         g_object_class_install_property (
742                 object_class,
743                 PROP_AUTHENTICATOR,
744                 g_param_spec_object (
745                         "authenticator",
746                         "Authenticator",
747                         "Handles authentication attempts",
748                         E_TYPE_SOURCE_AUTHENTICATOR,
749                         G_PARAM_READWRITE |
750                         G_PARAM_CONSTRUCT_ONLY |
751                         G_PARAM_STATIC_STRINGS));
752
753         g_object_class_install_property (
754                 object_class,
755                 PROP_PROMPT_DESCRIPTION,
756                 g_param_spec_string (
757                         "prompt-description",
758                         "Prompt Description",
759                         "The detailed description of the prompt",
760                         NULL,
761                         G_PARAM_READWRITE |
762                         G_PARAM_STATIC_STRINGS));
763
764         g_object_class_install_property (
765                 object_class,
766                 PROP_PROMPT_MESSAGE,
767                 g_param_spec_string (
768                         "prompt-message",
769                         "Prompt Message",
770                         "The prompt message for the user",
771                         NULL,
772                         G_PARAM_READWRITE |
773                         G_PARAM_STATIC_STRINGS));
774
775         g_object_class_install_property (
776                 object_class,
777                 PROP_PROMPT_TITLE,
778                 g_param_spec_string (
779                         "prompt-title",
780                         "Prompt Title",
781                         "The title of the prompt",
782                         NULL,
783                         G_PARAM_READWRITE |
784                         G_PARAM_STATIC_STRINGS));
785
786         g_object_class_install_property (
787                 object_class,
788                 PROP_SERVER,
789                 g_param_spec_object (
790                         "server",
791                         "Server",
792                         "The server to which the session belongs",
793                         E_TYPE_SOURCE_REGISTRY_SERVER,
794                         G_PARAM_READWRITE |
795                         G_PARAM_CONSTRUCT_ONLY |
796                         G_PARAM_STATIC_STRINGS));
797
798         g_object_class_install_property (
799                 object_class,
800                 PROP_SOURCE_UID,
801                 g_param_spec_string (
802                         "source-uid",
803                         "Source UID",
804                         "Unique ID of the data source being authenticated",
805                         NULL,
806                         G_PARAM_READWRITE |
807                         G_PARAM_CONSTRUCT_ONLY |
808                         G_PARAM_STATIC_STRINGS));
809 }
810
811 static void
812 e_authentication_session_init (EAuthenticationSession *session)
813 {
814         session->priv = E_AUTHENTICATION_SESSION_GET_PRIVATE (session);
815         g_mutex_init (&session->priv->property_lock);
816 }
817
818 G_DEFINE_QUARK (
819         e-authentication-session-error-quark,
820         e_authentication_session_error);
821
822 /**
823  * e_authentication_session_new:
824  * @server: an #ESourceRegistryServer
825  * @authenticator: an #ESourceAuthenticator
826  * @source_uid: a data source identifier
827  *
828  * Creates a new #EAuthenticationSession instance for @server using
829  * @authenticator to handle authentication attempts.
830  *
831  * Note that @source_uid does not necessarily have to be known to the
832  * @server, as in the case when configuring a new data source, but it
833  * still has to be unique.
834  *
835  * Returns: a newly-created #EAuthenticationSession
836  *
837  * Since: 3.6
838  **/
839 EAuthenticationSession *
840 e_authentication_session_new (ESourceRegistryServer *server,
841                               ESourceAuthenticator *authenticator,
842                               const gchar *source_uid)
843 {
844         g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
845         g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (authenticator), NULL);
846         g_return_val_if_fail (source_uid != NULL, NULL);
847
848         return g_object_new (
849                 E_TYPE_AUTHENTICATION_SESSION,
850                 "server", server,
851                 "authenticator", authenticator,
852                 "source-uid", source_uid, NULL);
853 }
854
855 /**
856  * e_authentication_session_get_server:
857  * @session: an #EAuthenticationSession
858  *
859  * Returns the #ESourceRegistryServer to which @session belongs.
860  *
861  * Returns: the #ESourceRegistryServer for @session
862  *
863  * Since: 3.6
864  **/
865 ESourceRegistryServer *
866 e_authentication_session_get_server (EAuthenticationSession *session)
867 {
868         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
869
870         return session->priv->server;
871 }
872
873 /**
874  * e_authentication_session_get_authenticator:
875  * @session: an #EAuthenticationSession
876  *
877  * Returns the #ESourceAuthenticator handling authentication attempts for
878  * @session.  This is usually an #EAuthenticationMediator but can also be
879  * a custom collection backend derived from #ECollectionBackend.
880  *
881  * Returns: the #ESourceAuthenticator for @session
882  *
883  * Since: 3.6
884  **/
885 ESourceAuthenticator *
886 e_authentication_session_get_authenticator (EAuthenticationSession *session)
887 {
888         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
889
890         return session->priv->authenticator;
891 }
892
893 /**
894  * e_authentication_session_get_source_uid:
895  * @session: an #EAuthenticationSession
896  *
897  * Returns the #ESource:uid of the authenticating data source.  The data
898  * source may or may not be known to the #EAuthenticationSession:server.
899  *
900  * Returns: the UID of the authenticating data source
901  *
902  * Since: 3.6
903  **/
904 const gchar *
905 e_authentication_session_get_source_uid (EAuthenticationSession *session)
906 {
907         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
908
909         return session->priv->source_uid;
910 }
911
912 /**
913  * e_authentication_session_get_prompt_title:
914  * @session: an #EAuthenticationSession
915  *
916  * Returns the text used for the password prompt title should prompting
917  * be necessary.  See #GcrPrompt for more details about password prompts.
918  *
919  * Returns: the password prompt title
920  *
921  * Since: 3.6
922  **/
923 const gchar *
924 e_authentication_session_get_prompt_title (EAuthenticationSession *session)
925 {
926         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
927
928         return session->priv->prompt_title;
929 }
930
931 /**
932  * e_authentication_session_dup_prompt_title:
933  * @session: an #EAuthenticationSession
934  *
935  * Thread-safe variation of e_authentication_session_get_prompt_title().
936  * Use this function when accessing @session from multiple threads.
937  *
938  * The returned string should be freed with g_free() when no longer needed.
939  *
940  * Returns: a newly-allocated copy of #EAuthenticationSession:prompt-title
941  *
942  * Since: 3.6
943  **/
944 gchar *
945 e_authentication_session_dup_prompt_title (EAuthenticationSession *session)
946 {
947         const gchar *protected;
948         gchar *duplicate;
949
950         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
951
952         g_mutex_lock (&session->priv->property_lock);
953
954         protected = e_authentication_session_get_prompt_title (session);
955         duplicate = g_strdup (protected);
956
957         g_mutex_unlock (&session->priv->property_lock);
958
959         return duplicate;
960 }
961
962 /**
963  * e_authentication_session_set_prompt_title:
964  * @session: an #EAuthenticationSession
965  * @prompt_title: the password prompt title, or %NULL
966  *
967  * Sets the text used for the password prompt title should prompting be
968  * necessary.  See #GcrPrompt for more details about password prompts.
969  *
970  * Since: 3.6
971  **/
972 void
973 e_authentication_session_set_prompt_title (EAuthenticationSession *session,
974                                            const gchar *prompt_title)
975 {
976         g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
977
978         g_mutex_lock (&session->priv->property_lock);
979
980         if (g_strcmp0 (session->priv->prompt_title, prompt_title) == 0) {
981                 g_mutex_unlock (&session->priv->property_lock);
982                 return;
983         }
984
985         g_free (session->priv->prompt_title);
986         session->priv->prompt_title = g_strdup (prompt_title);
987
988         g_mutex_unlock (&session->priv->property_lock);
989
990         g_object_notify (G_OBJECT (session), "prompt-title");
991 }
992
993 /**
994  * e_authentication_session_get_prompt_message:
995  * @session: an #EAuthenticationSession
996  *
997  * Returns the text used for the password prompt message should prompting
998  * be necessary.  See #GcrPrompt for more details about password prompts.
999  *
1000  * Returns: the password prompt message
1001  *
1002  * Since: 3.6
1003  **/
1004 const gchar *
1005 e_authentication_session_get_prompt_message (EAuthenticationSession *session)
1006 {
1007         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
1008
1009         return session->priv->prompt_message;
1010 }
1011
1012 /**
1013  * e_authentication_session_dup_prompt_message:
1014  * @session: an #EAuthenticationSession
1015  *
1016  * Thread-safe variation of e_authentication_session_get_prompt_message().
1017  * Use this function when accessing @session from multiple threads.
1018  *
1019  * The returned string should be freed with g_free() when no longer needed.
1020  *
1021  * Returns: a newly-allocated copy of #EAuthenticationSession:prompt-message
1022  *
1023  * Since: 3.6
1024  **/
1025 gchar *
1026 e_authentication_session_dup_prompt_message (EAuthenticationSession *session)
1027 {
1028         const gchar *protected;
1029         gchar *duplicate;
1030
1031         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
1032
1033         g_mutex_lock (&session->priv->property_lock);
1034
1035         protected = e_authentication_session_get_prompt_message (session);
1036         duplicate = g_strdup (protected);
1037
1038         g_mutex_unlock (&session->priv->property_lock);
1039
1040         return duplicate;
1041 }
1042
1043 /**
1044  * e_authentication_session_set_prompt_message:
1045  * @session: an #EAuthenticationSession
1046  * @prompt_message: the password prompt message, or %NULL
1047  *
1048  * Sets the text used for the password prompt message should prompting be
1049  * necessary.  See #GcrPrompt for more details about password prompts.
1050  *
1051  * Since: 3.6
1052  **/
1053 void
1054 e_authentication_session_set_prompt_message (EAuthenticationSession *session,
1055                                              const gchar *prompt_message)
1056 {
1057         g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
1058
1059         g_mutex_lock (&session->priv->property_lock);
1060
1061         if (g_strcmp0 (session->priv->prompt_message, prompt_message) == 0) {
1062                 g_mutex_unlock (&session->priv->property_lock);
1063                 return;
1064         }
1065
1066         g_free (session->priv->prompt_message);
1067         session->priv->prompt_message = g_strdup (prompt_message);
1068
1069         g_mutex_unlock (&session->priv->property_lock);
1070
1071         g_object_notify (G_OBJECT (session), "prompt-message");
1072 }
1073
1074 /**
1075  * e_authentication_session_get_prompt_description:
1076  * @session: an #EAuthenticationSession
1077  *
1078  * Returns the text used for the password prompt description should prompting
1079  * be necessary.  See #GcrPrompt for more details about password prompts.
1080  *
1081  * Returns: the password prompt description
1082  *
1083  * Since: 3.6
1084  **/
1085 const gchar *
1086 e_authentication_session_get_prompt_description (EAuthenticationSession *session)
1087 {
1088         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
1089
1090         return session->priv->prompt_description;
1091 }
1092
1093 /**
1094  * e_authentication_session_dup_prompt_description:
1095  * @session: an #EAuthenticationSession
1096  *
1097  * Thread-safe variation of e_authentication_session_get_prompt_description().
1098  * Use this function when accessing @session from multiple threads.
1099  *
1100  * The returned string should be freed with g_free() when no longer needed.
1101  *
1102  * Returns: a newly-allocated copy of
1103  *          #EAuthenticationSession:prompt-description
1104  *
1105  * Since: 3.6
1106  **/
1107 gchar *
1108 e_authentication_session_dup_prompt_description (EAuthenticationSession *session)
1109 {
1110         const gchar *protected;
1111         gchar *duplicate;
1112
1113         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), NULL);
1114
1115         g_mutex_lock (&session->priv->property_lock);
1116
1117         protected = e_authentication_session_get_prompt_description (session);
1118         duplicate = g_strdup (protected);
1119
1120         g_mutex_unlock (&session->priv->property_lock);
1121
1122         return duplicate;
1123 }
1124
1125 /**
1126  * e_authentication_session_set_prompt_description:
1127  * @session: an #EAuthenticationSession
1128  * @prompt_description: the password prompt description
1129  *
1130  * Sets the text used for the password prompt description should prompting
1131  * be necessary.  See #GcrPrompt for more details about password prompts.
1132  *
1133  * Since: 3.6
1134  **/
1135 void
1136 e_authentication_session_set_prompt_description (EAuthenticationSession *session,
1137                                                  const gchar *prompt_description)
1138 {
1139         g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
1140
1141         g_mutex_lock (&session->priv->property_lock);
1142
1143         if (g_strcmp0 (session->priv->prompt_description, prompt_description) == 0) {
1144                 g_mutex_unlock (&session->priv->property_lock);
1145                 return;
1146         }
1147
1148         g_free (session->priv->prompt_description);
1149         session->priv->prompt_description = g_strdup (prompt_description);
1150
1151         g_mutex_unlock (&session->priv->property_lock);
1152
1153         g_object_notify (G_OBJECT (session), "prompt-description");
1154 }
1155
1156 /**
1157  * e_authentication_session_execute_sync:
1158  * @session: an #EAuthenticationSession
1159  * @cancellable: optional #GCancellable object, or %NULL
1160  * @error: return location for a #GError, or %NULL
1161  *
1162  * Executes an authentication session.
1163  *
1164  * First the secret service is queried for a stored password.  If found,
1165  * an authentication attempt is made without disturbing the user.  If no
1166  * stored password is found, or if the stored password is rejected, the
1167  * user is shown a system-modal dialog requesting a password.  Further
1168  * authentication attempts are repeated with user-provided passwords
1169  * until authentication is verified or the user dismisses the prompt.
1170  * The returned #EAuthenticationSessionResult indicates the outcome.
1171  *
1172  * If an error occurs while interacting with the secret service, or while
1173  * prompting the user for a password, or while attempting authentication,
1174  * the function sets @error and returns #E_AUTHENTICATION_SESSION_ERROR.
1175  *
1176  * Returns: the result of the authentication session
1177  *
1178  * Since: 3.6
1179  **/
1180 EAuthenticationSessionResult
1181 e_authentication_session_execute_sync (EAuthenticationSession *session,
1182                                        GCancellable *cancellable,
1183                                        GError **error)
1184 {
1185         EAuthenticationSessionClass *class;
1186
1187         g_return_val_if_fail (
1188                 E_IS_AUTHENTICATION_SESSION (session),
1189                 E_AUTHENTICATION_SESSION_DISMISSED);
1190
1191         class = E_AUTHENTICATION_SESSION_GET_CLASS (session);
1192         g_return_val_if_fail (
1193                 class->execute_sync != NULL,
1194                 E_AUTHENTICATION_SESSION_DISMISSED);
1195
1196         return class->execute_sync (session, cancellable, error);
1197 }
1198
1199 /**
1200  * e_authentication_session_execute:
1201  * @session: an #EAuthenticationSession
1202  * @io_priority: the I/O priority of the request
1203  * @cancellable: optional #GCancellable object, or %NULL
1204  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1205  * @user_data: data to pass to the callback function
1206  *
1207  * See e_authentication_session_execute_sync() for details.
1208  *
1209  * When the operation is finished, @callback will be called.  You can then
1210  * call e_authentication_session_execute_finish() to get the result of the
1211  * operation.
1212  *
1213  * Since: 3.6
1214  **/
1215 void
1216 e_authentication_session_execute (EAuthenticationSession *session,
1217                                   gint io_priority,
1218                                   GCancellable *cancellable,
1219                                   GAsyncReadyCallback callback,
1220                                   gpointer user_data)
1221 {
1222         EAuthenticationSessionClass *class;
1223
1224         g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
1225
1226         class = E_AUTHENTICATION_SESSION_GET_CLASS (session);
1227         g_return_if_fail (class->execute != NULL);
1228
1229         return class->execute (
1230                 session, io_priority, cancellable, callback, user_data);
1231 }
1232
1233 /**
1234  * e_authentication_session_execute_finish:
1235  * @session: an #EAuthenticationSession
1236  * @result: a #GAsyncResult
1237  * @error: return location for a #GError, or %NULL
1238  *
1239  * Finishes the operation started with e_authentication_session_execute().
1240  *
1241  * If an error occurs while interacting with the secret service, or while
1242  * prompting the user for a password, or while attempting authentication,
1243  * the function sets @error and returns #E_AUTHENTICATION_SESSION_ERROR.
1244  *
1245  * Returns: the result of the authentication session
1246  *
1247  * Since: 3.6
1248  **/
1249 EAuthenticationSessionResult
1250 e_authentication_session_execute_finish (EAuthenticationSession *session,
1251                                          GAsyncResult *result,
1252                                          GError **error)
1253 {
1254         EAuthenticationSessionClass *class;
1255
1256         g_return_val_if_fail (
1257                 E_IS_AUTHENTICATION_SESSION (session),
1258                 E_AUTHENTICATION_SESSION_DISMISSED);
1259         g_return_val_if_fail (
1260                 G_IS_ASYNC_RESULT (result),
1261                 E_AUTHENTICATION_SESSION_DISMISSED);
1262
1263         class = E_AUTHENTICATION_SESSION_GET_CLASS (session);
1264         g_return_val_if_fail (
1265                 class->execute_finish != NULL,
1266                 E_AUTHENTICATION_SESSION_DISMISSED);
1267
1268         return class->execute_finish (session, result, error);
1269 }
1270
1271 /* Helper for e_authentication_session_store_password() */
1272 static void
1273 authentication_session_store_password_thread (GSimpleAsyncResult *simple,
1274                                               GObject *object,
1275                                               GCancellable *cancellable)
1276 {
1277         AsyncContext *async_context;
1278         GError *error = NULL;
1279
1280         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1281
1282         e_authentication_session_store_password_sync (
1283                 E_AUTHENTICATION_SESSION (object),
1284                 async_context->password,
1285                 async_context->permanently,
1286                 cancellable, &error);
1287
1288         if (error != NULL)
1289                 g_simple_async_result_take_error (simple, error);
1290 }
1291
1292 /**
1293  * e_authentication_session_store_password_sync:
1294  * @session: an #EAuthenticationSession
1295  * @password: the password to store
1296  * @permanently: store permanently or just for the session
1297  * @cancellable: optional #GCancellable object, or %NULL
1298  * @error: return location for a #GError, or %NULL
1299  *
1300  * Store a password for the data source that @session is representing.
1301  * If @permanently is %TRUE, the password is stored in the default keyring.
1302  * Otherwise the password is stored in the memory-only session keyring.  If
1303  * an error occurs, the function sets @error and returns %FALSE.
1304  *
1305  * Returns: %TRUE on success, %FALSE on error
1306  *
1307  * Since: 3.6
1308  **/
1309 gboolean
1310 e_authentication_session_store_password_sync (EAuthenticationSession *session,
1311                                               const gchar *password,
1312                                               gboolean permanently,
1313                                               GCancellable *cancellable,
1314                                               GError **error)
1315 {
1316         gboolean result;
1317         const gchar *collection;
1318         const gchar *uid;
1319         gchar *display_name;
1320
1321         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), FALSE);
1322         g_return_val_if_fail (password != NULL, FALSE);
1323
1324         if (permanently)
1325                 collection = SECRET_COLLECTION_DEFAULT;
1326         else
1327                 collection = SECRET_COLLECTION_SESSION;
1328
1329         uid = e_authentication_session_get_source_uid (session);
1330         display_name = g_strdup_printf (KEYRING_ITEM_DISPLAY_FORMAT, uid);
1331
1332         result = secret_password_store_sync (
1333                 &schema, collection, display_name,
1334                 password, cancellable, error,
1335                 KEYRING_ITEM_ATTRIBUTE_NAME, uid,
1336                 NULL);
1337
1338         g_free (display_name);
1339
1340         return result;
1341 }
1342
1343 /**
1344  * e_authentication_session_store_password:
1345  * @session: an #EAuthenticationSession
1346  * @password: the password to store
1347  * @permanently: store permanently or just for the session
1348  * @io_priority: the I/O priority of the request
1349  * @cancellable: optional #GCancellable object, or %NULL
1350  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1351  * @user_data: data to pass to the callback function
1352  *
1353  * Asynchronously stores a password for the data source that @session
1354  * is representing.  If @permanently is %TRUE, the password is stored in the
1355  * default keyring.  Otherwise the password is stored in the memory-only
1356  * session keyring.
1357  *
1358  * When the operation is finished, @callback will be called.  You can then
1359  * call e_authentication_session_store_password_finish() to get the result
1360  * of the operation.
1361  *
1362  * Since: 3.6
1363  **/
1364 void
1365 e_authentication_session_store_password (EAuthenticationSession *session,
1366                                          const gchar *password,
1367                                          gboolean permanently,
1368                                          gint io_priority,
1369                                          GCancellable *cancellable,
1370                                          GAsyncReadyCallback callback,
1371                                          gpointer user_data)
1372 {
1373         GSimpleAsyncResult *simple;
1374         AsyncContext *async_context;
1375
1376         g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
1377         g_return_if_fail (password != NULL);
1378
1379         async_context = g_slice_new0 (AsyncContext);
1380         async_context->password = g_strdup (password);
1381         async_context->permanently = permanently;
1382
1383         simple = g_simple_async_result_new (
1384                 G_OBJECT (session), callback, user_data,
1385                 e_authentication_session_store_password);
1386
1387         g_simple_async_result_set_check_cancellable (simple, cancellable);
1388
1389         g_simple_async_result_set_op_res_gpointer (
1390                 simple, async_context, (GDestroyNotify) async_context_free);
1391
1392         g_simple_async_result_run_in_thread (
1393                 simple, authentication_session_store_password_thread,
1394                 io_priority, cancellable);
1395
1396         g_object_unref (simple);
1397 }
1398
1399 /**
1400  * e_authentication_session_store_password_finish:
1401  * @session: an #EAuthenticationSession
1402  * @result: a #GAsyncResult
1403  * @error: return location for a #GError, or %NULL
1404  *
1405  * Finished the operation started with
1406  * e_authentication_session_store_password().
1407  *
1408  * Returns: %TRUE on success, %FALSE on error
1409  *
1410  * Since: 3.6
1411  **/
1412 gboolean
1413 e_authentication_session_store_password_finish (EAuthenticationSession *session,
1414                                                 GAsyncResult *result,
1415                                                 GError **error)
1416 {
1417         GSimpleAsyncResult *simple;
1418
1419         g_return_val_if_fail (
1420                 g_simple_async_result_is_valid (
1421                 result, G_OBJECT (session),
1422                 e_authentication_session_store_password), FALSE);
1423
1424         simple = G_SIMPLE_ASYNC_RESULT (result);
1425
1426         /* Assume success unless a GError is set. */
1427         return !g_simple_async_result_propagate_error (simple, error);
1428 }
1429
1430 /* Helper for e_authentication_session_store_password() */
1431 static void
1432 authentication_session_lookup_password_thread (GSimpleAsyncResult *simple,
1433                                                GObject *object,
1434                                                GCancellable *cancellable)
1435 {
1436         AsyncContext *async_context;
1437         GError *error = NULL;
1438
1439         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1440
1441         e_authentication_session_lookup_password_sync (
1442                 E_AUTHENTICATION_SESSION (object), cancellable,
1443                 &async_context->password, &error);
1444
1445         if (error != NULL)
1446                 g_simple_async_result_take_error (simple, error);
1447 }
1448
1449 /**
1450  * e_authentication_session_lookup_password_sync:
1451  * @session: an #EAuthenticationSession
1452  * @cancellable: optional #GCancellable object, or %NULL
1453  * @password: return location for the password, or %NULL
1454  * @error: return location for a #GError, or %NULL
1455  *
1456  * Looks up a password for the data source that @session is
1457  * representing.  Both the default and session keyrings are queried.
1458  *
1459  * Note the boolean return value indicates whether the lookup operation
1460  * itself completed successfully, not whether a password was found.  If
1461  * no password was found, the function will set @password to %NULL but
1462  * still return %TRUE.  If an error occurs, the function sets @error
1463  * and returns %FALSE.
1464  *
1465  * Returns: %TRUE on success, %FALSE on error
1466  *
1467  * Since: 3.6
1468  **/
1469 gboolean
1470 e_authentication_session_lookup_password_sync (EAuthenticationSession *session,
1471                                                GCancellable *cancellable,
1472                                                gchar **password,
1473                                                GError **error)
1474 {
1475         const gchar *uid;
1476         gchar *temp = NULL;
1477         gboolean success = TRUE;
1478         GError *local_error = NULL;
1479
1480         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), FALSE);
1481
1482         uid = e_authentication_session_get_source_uid (session);
1483
1484         temp = secret_password_lookup_sync (
1485                 &schema, cancellable, &local_error,
1486                 KEYRING_ITEM_ATTRIBUTE_NAME, uid, NULL);
1487
1488         if (local_error != NULL) {
1489                 g_warn_if_fail (temp == NULL);
1490                 g_propagate_error (error, local_error);
1491                 success = FALSE;
1492         } else if (password != NULL) {
1493                 *password = temp;  /* takes ownership */
1494         } else {
1495                 secret_password_free (temp);
1496         }
1497
1498         return success;
1499 }
1500
1501 /**
1502  * e_authentication_session_lookup_password:
1503  * @session: an #EAuthenticationSession
1504  * @io_priority: the I/O priority of the request
1505  * @cancellable: optional #GCancellable object, or %NULL
1506  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1507  * @user_data: data to pass to the callback function
1508  *
1509  * Asynchronously looks up a password for the data source that @session
1510  * is representing.  Both the default and session keyrings are queried.
1511  *
1512  * When the operation is finished, @callback will be called.  You can then
1513  * call e_authentication_session_lookup_password_finish() to get the
1514  * result of the operation.
1515  *
1516  * Since: 3.6
1517  **/
1518 void
1519 e_authentication_session_lookup_password (EAuthenticationSession *session,
1520                                           gint io_priority,
1521                                           GCancellable *cancellable,
1522                                           GAsyncReadyCallback callback,
1523                                           gpointer user_data)
1524 {
1525         GSimpleAsyncResult *simple;
1526         AsyncContext *async_context;
1527
1528         g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
1529
1530         async_context = g_slice_new0 (AsyncContext);
1531
1532         simple = g_simple_async_result_new (
1533                 G_OBJECT (session), callback, user_data,
1534                 e_authentication_session_lookup_password);
1535
1536         g_simple_async_result_set_check_cancellable (simple, cancellable);
1537
1538         g_simple_async_result_set_op_res_gpointer (
1539                 simple, async_context, (GDestroyNotify) async_context_free);
1540
1541         g_simple_async_result_run_in_thread (
1542                 simple, authentication_session_lookup_password_thread,
1543                 io_priority, cancellable);
1544
1545         g_object_unref (simple);
1546 }
1547
1548 /**
1549  * e_authentication_session_lookup_password_finish:
1550  * @session: an #EAuthenticationSession
1551  * @result: a #GAsyncResult
1552  * @password: return location for the password, or %NULL
1553  * @error: return location for a #GError, or %NULL
1554  *
1555  * Finishes the operation started with
1556  * e_authentication_session_lookup_password().
1557  *
1558  * Note the boolean return value indicates whether the lookup operation
1559  * itself completed successfully, not whether a password was found.  If
1560  * no password was found, the function will set @password to %NULL but
1561  * still return %TRUE.  If an error occurs, the function sets @error
1562  * and returns %FALSE.
1563  *
1564  * Returns: %TRUE on success, %FALSE on error
1565  *
1566  * Since: 3.6
1567  **/
1568 gboolean
1569 e_authentication_session_lookup_password_finish (EAuthenticationSession *session,
1570                                                  GAsyncResult *result,
1571                                                  gchar **password,
1572                                                  GError **error)
1573 {
1574         GSimpleAsyncResult *simple;
1575         AsyncContext *async_context;
1576
1577         g_return_val_if_fail (
1578                 g_simple_async_result_is_valid (
1579                 result, G_OBJECT (session),
1580                 e_authentication_session_lookup_password), FALSE);
1581
1582         simple = G_SIMPLE_ASYNC_RESULT (result);
1583         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1584
1585         if (g_simple_async_result_propagate_error (simple, error))
1586                 return FALSE;
1587
1588         if (password != NULL) {
1589                 *password = async_context->password;
1590                 async_context->password = NULL;
1591         }
1592
1593         return TRUE;
1594 }
1595
1596 /* Helper for e_authentication_session_delete_password() */
1597 static void
1598 authentication_session_delete_password_thread (GSimpleAsyncResult *simple,
1599                                                GObject *object,
1600                                                GCancellable *cancellable)
1601 {
1602         GError *error = NULL;
1603
1604         e_authentication_session_delete_password_sync (
1605                 E_AUTHENTICATION_SESSION (object), cancellable, &error);
1606
1607         if (error != NULL)
1608                 g_simple_async_result_take_error (simple, error);
1609 }
1610
1611 /**
1612  * e_authentication_session_delete_password_sync:
1613  * @session: an #EAuthenticationSession
1614  * @cancellable: optional #GCancellable object, or %NULL
1615  * @error: return location for a #GError, or %NULL
1616  *
1617  * Deletes the password for the data source that @session is
1618  * representing from either the default keyring or session keyring.
1619  *
1620  * Note the boolean return value indicates whether the delete operation
1621  * itself completed successfully, not whether a password was found and
1622  * deleted.  If no password was found, the function will still return
1623  * %TRUE.  If an error occurs, the function sets @error and returns %FALSE.
1624  *
1625  * Returns: %TRUE on success, %FALSE on error
1626  *
1627  * Since: 3.6
1628  **/
1629 gboolean
1630 e_authentication_session_delete_password_sync (EAuthenticationSession *session,
1631                                                GCancellable *cancellable,
1632                                                GError **error)
1633 {
1634         const gchar *uid;
1635         gboolean success = TRUE;
1636         GError *local_error = NULL;
1637
1638         g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), FALSE);
1639
1640         uid = e_authentication_session_get_source_uid (session);
1641
1642         /* The return value indicates whether any passwords were removed,
1643          * not whether the operation completed successfully.  So we have
1644          * check the GError directly. */
1645         secret_password_clear_sync (
1646                 &schema, cancellable, &local_error,
1647                 KEYRING_ITEM_ATTRIBUTE_NAME, uid, NULL);
1648
1649         if (local_error != NULL) {
1650                 g_propagate_error (error, local_error);
1651                 success = FALSE;
1652         }
1653
1654         return success;
1655 }
1656
1657 /**
1658  * e_authentication_session_delete_password:
1659  * @session: an #EAuthenticationSession
1660  * @io_priority: the I/O priority of the request
1661  * @cancellable: optional #GCancellable object, or %NULL
1662  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1663  * @user_data: data to pass to the callback function
1664  *
1665  * Asyncronously deletes the password for the data source that @session
1666  * is representing from either the default keyring or session keyring.
1667  *
1668  * When the operation is finished, @callback will be called.  You can then
1669  * call e_authentication_session_delete_password_finish() to get the result
1670  * of the operation.
1671  *
1672  * Since: 3.6
1673  **/
1674 void
1675 e_authentication_session_delete_password (EAuthenticationSession *session,
1676                                           gint io_priority,
1677                                           GCancellable *cancellable,
1678                                           GAsyncReadyCallback callback,
1679                                           gpointer user_data)
1680 {
1681         GSimpleAsyncResult *simple;
1682
1683         g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
1684
1685         simple = g_simple_async_result_new (
1686                 G_OBJECT (session), callback, user_data,
1687                 e_authentication_session_delete_password);
1688
1689         g_simple_async_result_set_check_cancellable (simple, cancellable);
1690
1691         g_simple_async_result_run_in_thread (
1692                 simple, authentication_session_delete_password_thread,
1693                 io_priority, cancellable);
1694
1695         g_object_unref (simple);
1696 }
1697
1698 /**
1699  * e_authentication_session_delete_password_finish:
1700  * @session: an #EAuthenticationSession
1701  * @result: a #GAsyncResult
1702  * @error: return location for a #GError, or %NULL
1703  *
1704  * Finishes the operation started with
1705  * e_authentication_session_delete_password().
1706  *
1707  * Note the boolean return value indicates whether the delete operation
1708  * itself completed successfully, not whether a password was found and
1709  * deleted.  If no password was found, the function will still return
1710  * %TRUE.  If an error occurs, the function sets @error and returns %FALSE.
1711  *
1712  * Returns: %TRUE on success, %FALSE on error
1713  *
1714  * Since: 3.6
1715  **/
1716 gboolean
1717 e_authentication_session_delete_password_finish (EAuthenticationSession *session,
1718                                                  GAsyncResult *result,
1719                                                  GError **error)
1720 {
1721         GSimpleAsyncResult *simple;
1722
1723         g_return_val_if_fail (
1724                 g_simple_async_result_is_valid (
1725                 result, G_OBJECT (session),
1726                 e_authentication_session_delete_password), FALSE);
1727
1728         simple = G_SIMPLE_ASYNC_RESULT (result);
1729
1730         /* Assume success unless a GError is set. */
1731         return !g_simple_async_result_propagate_error (simple, error);
1732 }
1733