I#27 - [IMAPx] Ignore DavMail's CR/LF in BODYSTRUCTURE response
[evolution-data-server.git] / src / libedataserverui / e-credentials-prompter.c
blobf8c94aaf6686f810021deb733f57445575530441
1 /*
2 * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
18 #include "evolution-data-server-config.h"
20 #include <glib.h>
21 #include <glib/gi18n-lib.h>
23 #include <camel/camel.h>
24 #include <libedataserver/libedataserver.h>
26 #include "e-credentials-prompter.h"
28 /* built-in credentials prompter implementations */
29 #include "e-credentials-prompter-impl-password.h"
30 #include "e-credentials-prompter-impl-oauth2.h"
32 typedef struct _ProcessPromptData {
33 GWeakRef *prompter;
34 ECredentialsPrompterImpl *prompter_impl;
35 ESource *auth_source;
36 ESource *cred_source;
37 ESourceConnectionStatus connection_status; /* of the auth_source */
38 gboolean remember_password; /* of the cred_source, to check for changes */
39 gulong notify_handler_id;
40 gchar *error_text;
41 ENamedParameters *credentials;
42 gboolean allow_source_save;
43 GSimpleAsyncResult *async_result;
44 } ProcessPromptData;
46 struct _ECredentialsPrompterPrivate {
47 ESourceRegistry *registry;
48 ESourceCredentialsProvider *provider;
49 gboolean auto_prompt;
50 GCancellable *cancellable;
52 GMutex disabled_auto_prompt_lock;
53 GHashTable *disabled_auto_prompt; /* gchar *source_uid ~> 1; Source UIDs for which the auto-prompt is disabled */
55 GMutex prompters_lock;
56 GHashTable *prompters; /* gchar *method ~> ECredentialsPrompterImpl *impl */
57 GHashTable *known_prompters; /* gpointer [ECredentialsPrompterImpl] ~> UINT known instances; the prompter_impl is not referenced */
59 GRecMutex queue_lock; /* guards all queue and schedule related properties */
60 GSList *queue; /* ProcessPromptData * */
61 ProcessPromptData *processing_prompt;
62 gulong schedule_idle_id;
65 enum {
66 PROP_0,
67 PROP_AUTO_PROMPT,
68 PROP_REGISTRY,
69 PROP_PROVIDER
72 enum {
73 GET_DIALOG_PARENT,
74 LAST_SIGNAL
77 static guint signals[LAST_SIGNAL];
79 G_DEFINE_TYPE_WITH_CODE (ECredentialsPrompter, e_credentials_prompter, G_TYPE_OBJECT,
80 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
82 static void
83 process_prompt_data_free (gpointer ptr)
85 ProcessPromptData *ppd = ptr;
87 if (ppd) {
88 if (ppd->notify_handler_id > 0)
89 g_signal_handler_disconnect (ppd->auth_source, ppd->notify_handler_id);
91 if (ppd->async_result) {
92 ECredentialsPrompter *prompter;
94 prompter = g_weak_ref_get (ppd->prompter);
95 if (prompter) {
96 e_credentials_prompter_complete_prompt_call (prompter, ppd->async_result, ppd->auth_source, NULL, NULL);
97 g_clear_object (&prompter);
101 e_weak_ref_free (ppd->prompter);
102 g_clear_object (&ppd->prompter_impl);
103 g_clear_object (&ppd->auth_source);
104 g_clear_object (&ppd->cred_source);
105 g_free (ppd->error_text);
106 e_named_parameters_free (ppd->credentials);
107 g_free (ppd);
111 typedef struct _LookupSourceDetailsData {
112 ESource *auth_source; /* an ESource which asked for credentials */
113 ESource *cred_source; /* this might be auth_source or a parent collection source, if applicable, from where the credentials come */
114 ENamedParameters *credentials; /* actual stored credentials */
115 } LookupSourceDetailsData;
117 static void
118 lookup_source_details_data_free (gpointer ptr)
120 LookupSourceDetailsData *data = ptr;
122 if (data) {
123 g_clear_object (&data->auth_source);
124 g_clear_object (&data->cred_source);
125 e_named_parameters_free (data->credentials);
126 g_free (data);
130 static void
131 credentials_prompter_lookup_source_details_thread (GTask *task,
132 gpointer source_object,
133 gpointer task_data,
134 GCancellable *cancellable)
136 ESource *source, *cred_source = NULL;
137 ECredentialsPrompter *prompter;
138 ESourceCredentialsProvider *provider;
139 ENamedParameters *credentials = NULL;
140 GError *local_error = NULL;
142 g_return_if_fail (E_IS_SOURCE (source_object));
144 source = E_SOURCE (source_object);
146 prompter = g_weak_ref_get (task_data);
147 if (!prompter)
148 return;
150 provider = e_credentials_prompter_get_provider (prompter);
151 cred_source = e_source_credentials_provider_ref_credentials_source (provider, source);
153 e_source_credentials_provider_lookup_sync (provider, cred_source ? cred_source : source, cancellable, &credentials, &local_error);
155 /* Interested only in the cancelled error, which means the prompter is freed. */
156 if (local_error != NULL && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
157 g_task_return_error (task, local_error);
158 local_error = NULL;
159 } else {
160 LookupSourceDetailsData *data;
162 data = g_new0 (LookupSourceDetailsData, 1);
163 data->auth_source = g_object_ref (source);
164 data->cred_source = g_object_ref (cred_source ? cred_source : source); /* always set both, for simplicity */
165 data->credentials = credentials; /* NULL for no credentials available */
167 /* To not be freed below. */
168 credentials = NULL;
170 g_task_return_pointer (task, data, lookup_source_details_data_free);
173 e_named_parameters_free (credentials);
174 g_clear_object (&cred_source);
175 g_clear_object (&prompter);
176 g_clear_error (&local_error);
179 static void
180 credentials_prompter_lookup_source_details (ESource *source,
181 ECredentialsPrompter *prompter,
182 GAsyncReadyCallback callback,
183 gpointer user_data)
185 GTask *task;
187 g_return_if_fail (E_IS_SOURCE (source));
188 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
190 task = g_task_new (source, prompter->priv->cancellable, callback, user_data);
191 g_task_set_source_tag (task, credentials_prompter_lookup_source_details_thread);
192 g_task_set_task_data (task, e_weak_ref_new (prompter), (GDestroyNotify) e_weak_ref_free);
194 g_task_run_in_thread (task, credentials_prompter_lookup_source_details_thread);
196 g_object_unref (task);
199 static gboolean
200 credentials_prompter_lookup_source_details_finish (ESource *source,
201 GAsyncResult *result,
202 ECredentialsPrompter **out_prompter, /* will be referenced, if not NULL */
203 LookupSourceDetailsData **out_data,
204 GError **error)
206 LookupSourceDetailsData *data;
208 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
209 g_return_val_if_fail (out_prompter != NULL, FALSE);
210 g_return_val_if_fail (out_data != NULL, FALSE);
211 g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
213 g_return_val_if_fail (
214 g_async_result_is_tagged (
215 result, credentials_prompter_lookup_source_details_thread), FALSE);
217 data = g_task_propagate_pointer (G_TASK (result), error);
218 if (!data)
219 return FALSE;
221 *out_data = data;
222 *out_prompter = g_weak_ref_get (g_task_get_task_data (G_TASK (result)));
224 return TRUE;
227 static void
228 credentials_prompter_invoke_authenticate_cb (GObject *source_object,
229 GAsyncResult *result,
230 gpointer user_data)
232 GError *error = NULL;
234 if (!e_source_invoke_authenticate_finish (E_SOURCE (source_object), result, &error) &&
235 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
236 g_debug ("%s: Failed to invoke authenticate: %s", G_STRFUNC, error ? error->message : "Unknown error");
239 g_clear_error (&error);
242 typedef struct _CredentialsPromptData {
243 ESource *source;
244 gchar *error_text;
245 ECredentialsPrompterPromptFlags flags;
246 GTask *complete_task;
247 GSimpleAsyncResult *async_result;
248 } CredentialsPromptData;
250 static void
251 credentials_prompt_data_free (gpointer ptr)
253 CredentialsPromptData *data = ptr;
255 if (data) {
256 if (data->async_result) {
257 g_simple_async_result_set_error (data->async_result,
258 G_IO_ERROR, G_IO_ERROR_CANCELLED, "%s", _("Credentials prompt was cancelled"));
259 g_simple_async_result_complete_in_idle (data->async_result);
260 g_clear_object (&data->async_result);
263 g_clear_object (&data->source);
264 g_free (data->error_text);
265 g_free (data);
269 typedef struct _CredentialsResultData {
270 ESource *source;
271 ENamedParameters *credentials;
272 } CredentialsResultData;
274 static void
275 credentials_result_data_free (gpointer ptr)
277 CredentialsResultData *data = ptr;
279 if (data) {
280 g_clear_object (&data->source);
281 e_named_parameters_free (data->credentials);
282 g_free (data);
287 static void
288 credentials_prompter_maybe_process_next_prompt (ECredentialsPrompter *prompter)
290 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
292 g_rec_mutex_lock (&prompter->priv->queue_lock);
294 /* Already processing one */
295 if (prompter->priv->processing_prompt) {
296 g_rec_mutex_unlock (&prompter->priv->queue_lock);
297 return;
300 if (prompter->priv->queue) {
301 ProcessPromptData *ppd = prompter->priv->queue->data;
303 g_warn_if_fail (ppd != NULL);
305 prompter->priv->queue = g_slist_remove (prompter->priv->queue, ppd);
306 prompter->priv->processing_prompt = ppd;
308 e_credentials_prompter_impl_prompt (ppd->prompter_impl, ppd, ppd->auth_source,
309 ppd->cred_source, ppd->error_text, ppd->credentials);
312 g_rec_mutex_unlock (&prompter->priv->queue_lock);
315 static gboolean
316 credentials_prompter_process_next_prompt_idle_cb (gpointer user_data)
318 ECredentialsPrompter *prompter = user_data;
320 if (g_source_is_destroyed (g_main_current_source ()))
321 return FALSE;
323 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
325 g_rec_mutex_lock (&prompter->priv->queue_lock);
327 if (g_source_get_id (g_main_current_source ()) == prompter->priv->schedule_idle_id) {
328 prompter->priv->schedule_idle_id = 0;
330 credentials_prompter_maybe_process_next_prompt (prompter);
333 g_rec_mutex_unlock (&prompter->priv->queue_lock);
335 return FALSE;
338 static void
339 credentials_prompter_schedule_process_next_prompt (ECredentialsPrompter *prompter)
341 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
343 g_rec_mutex_lock (&prompter->priv->queue_lock);
345 /* Already processing one */
346 if (prompter->priv->processing_prompt ||
347 prompter->priv->schedule_idle_id) {
348 g_rec_mutex_unlock (&prompter->priv->queue_lock);
349 return;
352 prompter->priv->schedule_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
353 credentials_prompter_process_next_prompt_idle_cb,
354 prompter, NULL);
356 g_rec_mutex_unlock (&prompter->priv->queue_lock);
359 static void
360 credentials_prompter_connection_status_changed_cb (ESource *source,
361 GParamSpec *param,
362 ECredentialsPrompter *prompter)
364 g_return_if_fail (E_IS_SOURCE (source));
365 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
367 /* Do not cancel the prompt when the source is still waiting for the credentials. */
368 if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS)
369 return;
371 g_rec_mutex_lock (&prompter->priv->queue_lock);
373 if (prompter->priv->processing_prompt &&
374 e_source_equal (prompter->priv->processing_prompt->auth_source, source)) {
375 e_credentials_prompter_impl_cancel_prompt (prompter->priv->processing_prompt->prompter_impl, prompter->priv->processing_prompt);
376 } else {
377 GSList *link;
379 for (link = prompter->priv->queue; link; link = g_slist_next (link)) {
380 ProcessPromptData *ppd = link->data;
382 g_warn_if_fail (ppd != NULL);
384 if (ppd && e_source_equal (ppd->auth_source, source)) {
385 if (ppd->connection_status != e_source_get_connection_status (source)) {
386 prompter->priv->queue = g_slist_remove (prompter->priv->queue, ppd);
387 process_prompt_data_free (ppd);
389 break;
394 g_rec_mutex_unlock (&prompter->priv->queue_lock);
397 static gboolean
398 e_credentials_prompter_eval_remember_password (ESource *source)
400 gboolean remember_password = FALSE;
402 if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
403 remember_password = e_source_authentication_get_remember_password (
404 e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION));
406 return remember_password;
409 static void
410 e_credentials_prompter_manage_impl_prompt (ECredentialsPrompter *prompter,
411 ECredentialsPrompterImpl *prompter_impl,
412 ESource *auth_source,
413 ESource *cred_source,
414 const gchar *error_text,
415 const ENamedParameters *credentials,
416 gboolean allow_source_save,
417 GSimpleAsyncResult *async_result)
419 GSList *link;
420 gboolean success = TRUE;
422 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
423 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl));
424 g_return_if_fail (E_IS_SOURCE (auth_source));
425 g_return_if_fail (E_IS_SOURCE (cred_source));
426 g_return_if_fail (credentials != NULL);
428 g_rec_mutex_lock (&prompter->priv->queue_lock);
430 for (link = prompter->priv->queue; link; link = g_slist_next (link)) {
431 ProcessPromptData *ppd = link->data;
433 g_warn_if_fail (ppd != NULL);
435 if (ppd && e_source_equal (ppd->auth_source, auth_source)) {
436 break;
440 if (link != NULL || (prompter->priv->processing_prompt &&
441 e_source_equal (prompter->priv->processing_prompt->auth_source, auth_source))) {
442 /* have queued or already asking for credentials for this source */
443 success = FALSE;
444 } else {
445 ProcessPromptData *ppd;
447 ppd = g_new0 (ProcessPromptData, 1);
448 ppd->prompter = e_weak_ref_new (prompter);
449 ppd->prompter_impl = g_object_ref (prompter_impl);
450 ppd->auth_source = g_object_ref (auth_source);
451 ppd->cred_source = g_object_ref (cred_source);
452 ppd->connection_status = e_source_get_connection_status (ppd->auth_source);
453 ppd->remember_password = e_credentials_prompter_eval_remember_password (ppd->cred_source);
454 ppd->error_text = g_strdup (error_text);
455 ppd->credentials = e_named_parameters_new_clone (credentials);
456 ppd->allow_source_save = allow_source_save;
457 ppd->async_result = async_result ? g_object_ref (async_result) : NULL;
459 /* If the prompter doesn't auto-prompt, then it should not auto-close the prompt as well. */
460 if (e_credentials_prompter_get_auto_prompt (prompter)) {
461 ppd->notify_handler_id = g_signal_connect (ppd->auth_source, "notify::connection-status",
462 G_CALLBACK (credentials_prompter_connection_status_changed_cb), prompter);
463 } else {
464 ppd->notify_handler_id = 0;
467 prompter->priv->queue = g_slist_append (prompter->priv->queue, ppd);
469 credentials_prompter_schedule_process_next_prompt (prompter);
472 g_rec_mutex_unlock (&prompter->priv->queue_lock);
474 if (!success && async_result) {
475 e_credentials_prompter_complete_prompt_call (prompter, async_result, auth_source, NULL, NULL);
479 static void
480 credentials_prompter_store_credentials_cb (GObject *source_object,
481 GAsyncResult *result,
482 gpointer user_data)
484 GError *error = NULL;
486 if (!e_source_credentials_provider_store_finish (E_SOURCE_CREDENTIALS_PROVIDER (source_object), result, &error) &&
487 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
488 g_warning ("%s: Failed to store source credentials: %s", G_STRFUNC, error ? error->message : "Unknown error");
491 g_clear_error (&error);
494 static void
495 credentials_prompter_source_write_cb (GObject *source_object,
496 GAsyncResult *result,
497 gpointer user_data)
499 GError *error = NULL;
501 if (!e_source_write_finish (E_SOURCE (source_object), result, &error) &&
502 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
503 g_warning ("%s: Failed to write source changes: %s", G_STRFUNC, error ? error->message : "Unknown error");
506 g_clear_error (&error);
509 static void
510 e_credentials_prompter_prompt_finish_for_source (ECredentialsPrompter *prompter,
511 ProcessPromptData *ppd,
512 const ENamedParameters *credentials)
514 gboolean changed = FALSE;
516 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
517 g_return_if_fail (ppd != NULL);
519 if (!credentials)
520 return;
522 if (e_source_has_extension (ppd->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
523 ESourceAuthentication *auth_extension = e_source_get_extension (ppd->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION);
525 if (e_source_credentials_provider_can_store (e_credentials_prompter_get_provider (prompter), ppd->cred_source)) {
526 e_source_credentials_provider_store (e_credentials_prompter_get_provider (prompter), ppd->cred_source, credentials,
527 e_source_authentication_get_remember_password (auth_extension),
528 prompter->priv->cancellable,
529 credentials_prompter_store_credentials_cb, NULL);
532 if (e_source_get_writable (ppd->cred_source)) {
533 const gchar *username;
535 username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
536 if (username && *username &&
537 g_strcmp0 (username, e_source_authentication_get_user (auth_extension)) != 0) {
538 e_source_authentication_set_user (auth_extension, username);
539 changed = TRUE;
544 if (ppd->allow_source_save && e_source_get_writable (ppd->cred_source) &&
545 (changed || (ppd->remember_password ? 1 : 0) != (e_credentials_prompter_eval_remember_password (ppd->cred_source) ? 1 : 0))) {
546 e_source_write (ppd->cred_source, prompter->priv->cancellable,
547 credentials_prompter_source_write_cb, NULL);
550 if (ppd->async_result) {
551 ECredentialsPrompter *ppd_prompter;
553 ppd_prompter = g_weak_ref_get (ppd->prompter);
554 if (ppd_prompter) {
555 e_credentials_prompter_complete_prompt_call (ppd_prompter, ppd->async_result, ppd->auth_source, credentials, NULL);
556 g_clear_object (&ppd_prompter);
558 /* To not be completed multiple times */
559 g_clear_object (&ppd->async_result);
561 } else {
562 e_source_invoke_authenticate (ppd->auth_source, credentials, prompter->priv->cancellable,
563 credentials_prompter_invoke_authenticate_cb, NULL);
567 static void
568 credentials_prompter_prompt_finished_cb (ECredentialsPrompterImpl *prompter_impl,
569 gpointer prompt_id,
570 const ENamedParameters *credentials,
571 ECredentialsPrompter *prompter)
573 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl));
574 g_return_if_fail (prompt_id != NULL);
575 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
577 g_rec_mutex_lock (&prompter->priv->queue_lock);
579 if (prompt_id == prompter->priv->processing_prompt) {
580 ProcessPromptData *ppd = prompter->priv->processing_prompt;
581 GSList *link, *to_remove = NULL;
583 prompter->priv->processing_prompt = NULL;
585 e_credentials_prompter_prompt_finish_for_source (prompter, ppd, credentials);
587 /* Finish also any other pending prompts for the same credentials source
588 as was finished this one. This can be relevant to collection sources. */
589 for (link = prompter->priv->queue; link; link = g_slist_next (link)) {
590 ProcessPromptData *sub_ppd = link->data;
592 if (sub_ppd && sub_ppd->cred_source && e_source_equal (sub_ppd->cred_source, ppd->cred_source)) {
593 to_remove = g_slist_prepend (to_remove, sub_ppd);
597 for (link = to_remove; link; link = g_slist_next (link)) {
598 ProcessPromptData *sub_ppd = link->data;
600 if (sub_ppd) {
601 prompter->priv->queue = g_slist_remove (prompter->priv->queue, sub_ppd);
602 e_credentials_prompter_prompt_finish_for_source (prompter, sub_ppd, credentials);
606 g_slist_free_full (to_remove, process_prompt_data_free);
607 process_prompt_data_free (ppd);
609 credentials_prompter_schedule_process_next_prompt (prompter);
610 } else {
611 g_warning ("%s: Unknown prompt_id %p", G_STRFUNC, prompt_id);
614 g_rec_mutex_unlock (&prompter->priv->queue_lock);
617 static gboolean
618 credentials_prompter_prompt_with_source_details (ECredentialsPrompter *prompter,
619 LookupSourceDetailsData *data,
620 const gchar *error_text,
621 ECredentialsPrompterPromptFlags flags,
622 GSimpleAsyncResult *async_result)
624 ECredentialsPrompterImpl *prompter_impl = NULL;
625 gchar *method = NULL;
626 gboolean success = TRUE;
628 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
629 g_return_val_if_fail (data != NULL, FALSE);
631 if (e_source_has_extension (data->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
632 ESourceAuthentication *authentication = e_source_get_extension (data->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION);
634 method = e_source_authentication_dup_method (authentication);
637 g_mutex_lock (&prompter->priv->prompters_lock);
639 prompter_impl = g_hash_table_lookup (prompter->priv->prompters, method ? method : "");
640 if (!prompter_impl && method && *method)
641 prompter_impl = g_hash_table_lookup (prompter->priv->prompters, "");
643 if (prompter_impl)
644 g_object_ref (prompter_impl);
646 g_mutex_unlock (&prompter->priv->prompters_lock);
648 if (prompter_impl) {
649 ENamedParameters *credentials;
651 credentials = e_named_parameters_new ();
652 if (data->credentials)
653 e_named_parameters_assign (credentials, data->credentials);
655 if (async_result && data->credentials && (flags & E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS) != 0) {
656 e_credentials_prompter_complete_prompt_call (prompter, async_result, data->auth_source, credentials, NULL);
657 } else if (!e_source_credentials_provider_can_prompt (prompter->priv->provider, data->auth_source)) {
658 /* This source cannot be asked for credentials, thus end with a 'not supported' error. */
659 GError *error;
661 error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
662 _("Source “%s” doesn’t support prompt for credentials"),
663 e_source_get_display_name (data->cred_source));
665 if (async_result)
666 e_credentials_prompter_complete_prompt_call (prompter, async_result, data->auth_source, NULL, error);
668 g_clear_error (&error);
669 } else {
670 e_credentials_prompter_manage_impl_prompt (prompter, prompter_impl,
671 data->auth_source, data->cred_source, error_text, credentials,
672 !async_result || (flags & E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_SOURCE_SAVE) != 0,
673 async_result);
676 e_named_parameters_free (credentials);
677 } else {
678 /* Shoud not happen, because the password prompter is added as the default prompter. */
679 g_warning ("%s: No prompter impl found for an authentication method '%s'", G_STRFUNC, method ? method : "");
680 success = FALSE;
683 g_clear_object (&prompter_impl);
684 g_free (method);
686 return success;
689 static void
690 credentials_prompter_lookup_source_details_before_prompt_cb (GObject *source_object,
691 GAsyncResult *result,
692 gpointer user_data)
694 CredentialsPromptData *prompt_data = user_data;
695 ECredentialsPrompter *prompter = NULL;
696 LookupSourceDetailsData *data = NULL;
697 GError *error = NULL;
699 g_return_if_fail (prompt_data != NULL);
700 g_return_if_fail (E_IS_SOURCE (source_object));
702 if (!credentials_prompter_lookup_source_details_finish (E_SOURCE (source_object), result, &prompter, &data, &error)) {
703 g_clear_error (&error);
704 credentials_prompt_data_free (prompt_data);
705 return;
708 if (credentials_prompter_prompt_with_source_details (prompter, data, prompt_data->error_text,
709 prompt_data->flags, prompt_data->async_result)) {
710 /* To not finish the async_result multiple times */
711 g_clear_object (&prompt_data->async_result);
714 g_clear_object (&prompter);
716 credentials_prompt_data_free (prompt_data);
717 lookup_source_details_data_free (data);
720 static void
721 credentials_prompter_lookup_source_details_cb (GObject *source_object,
722 GAsyncResult *result,
723 gpointer user_data)
725 LookupSourceDetailsData *data = NULL;
726 ECredentialsPrompter *prompter = NULL;
727 ESource *source;
728 GError *error = NULL;
730 g_return_if_fail (E_IS_SOURCE (source_object));
732 source = E_SOURCE (source_object);
734 if (!credentials_prompter_lookup_source_details_finish (source, result, &prompter, &data, &error)) {
735 g_clear_error (&error);
736 return;
739 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
740 g_return_if_fail (data != NULL);
742 if (data->credentials) {
743 e_source_invoke_authenticate (E_SOURCE (data->auth_source), data->credentials, prompter->priv->cancellable, credentials_prompter_invoke_authenticate_cb, NULL);
744 } else {
745 credentials_prompter_prompt_with_source_details (prompter, data, NULL, 0, NULL);
748 lookup_source_details_data_free (data);
749 g_clear_object (&prompter);
752 static void
753 credentials_prompter_credentials_required_cb (ESourceRegistry *registry,
754 ESource *source,
755 ESourceCredentialsReason reason,
756 const gchar *certificate_pem,
757 GTlsCertificateFlags certificate_errors,
758 const GError *op_error,
759 ECredentialsPrompter *prompter)
761 ESource *cred_source;
763 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
764 g_return_if_fail (E_IS_SOURCE (source));
765 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
767 /* Only these two reasons are meant to be used to prompt the user for credentials. */
768 if (reason != E_SOURCE_CREDENTIALS_REASON_REQUIRED &&
769 reason != E_SOURCE_CREDENTIALS_REASON_REJECTED) {
770 return;
773 cred_source = e_source_credentials_provider_ref_credentials_source (e_credentials_prompter_get_provider (prompter), source);
775 /* Global auto-prompt or the source's auto-prompt is disabled. */
776 if (!e_credentials_prompter_get_auto_prompt (prompter) ||
777 (e_credentials_prompter_get_auto_prompt_disabled_for (prompter, source) &&
778 (!cred_source || e_credentials_prompter_get_auto_prompt_disabled_for (prompter, cred_source)))) {
779 g_clear_object (&cred_source);
780 return;
783 g_clear_object (&cred_source);
785 /* This is a re-prompt, but the source cannot be prompted for credentials. */
786 if (reason == E_SOURCE_CREDENTIALS_REASON_REJECTED &&
787 !e_source_credentials_provider_can_prompt (prompter->priv->provider, source)) {
788 return;
791 if (reason == E_SOURCE_CREDENTIALS_REASON_REQUIRED) {
792 credentials_prompter_lookup_source_details (source, prompter,
793 credentials_prompter_lookup_source_details_cb, NULL);
794 return;
797 e_credentials_prompter_prompt (prompter, source, op_error ? op_error->message : NULL, 0, NULL, NULL);
800 static gboolean
801 credentials_prompter_get_dialog_parent_accumulator (GSignalInvocationHint *ihint,
802 GValue *return_accu,
803 const GValue *handler_return,
804 gpointer data)
806 if (handler_return && g_value_get_object (handler_return) != NULL) {
807 g_value_set_object (return_accu, g_value_get_object (handler_return));
808 return FALSE;
811 return TRUE;
814 static void
815 credentials_prompter_set_registry (ECredentialsPrompter *prompter,
816 ESourceRegistry *registry)
818 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
819 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
820 g_return_if_fail (prompter->priv->registry == NULL);
822 prompter->priv->registry = g_object_ref (registry);
823 prompter->priv->provider = e_source_credentials_provider_new (prompter->priv->registry);
825 g_signal_connect (prompter->priv->registry, "credentials-required",
826 G_CALLBACK (credentials_prompter_credentials_required_cb), prompter);
829 static void
830 credentials_prompter_set_property (GObject *object,
831 guint property_id,
832 const GValue *value,
833 GParamSpec *pspec)
835 switch (property_id) {
836 case PROP_REGISTRY:
837 credentials_prompter_set_registry (
838 E_CREDENTIALS_PROMPTER (object),
839 g_value_get_object (value));
840 return;
842 case PROP_AUTO_PROMPT:
843 e_credentials_prompter_set_auto_prompt (
844 E_CREDENTIALS_PROMPTER (object),
845 g_value_get_boolean (value));
846 return;
849 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
852 static void
853 credentials_prompter_get_property (GObject *object,
854 guint property_id,
855 GValue *value,
856 GParamSpec *pspec)
858 switch (property_id) {
859 case PROP_REGISTRY:
860 g_value_set_object (value,
861 e_credentials_prompter_get_registry (
862 E_CREDENTIALS_PROMPTER (object)));
863 return;
865 case PROP_PROVIDER:
866 g_value_set_object (value,
867 e_credentials_prompter_get_provider (
868 E_CREDENTIALS_PROMPTER (object)));
869 return;
871 case PROP_AUTO_PROMPT:
872 g_value_set_boolean (value,
873 e_credentials_prompter_get_auto_prompt (
874 E_CREDENTIALS_PROMPTER (object)));
875 return;
878 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
881 static void
882 credentials_prompter_constructed (GObject *object)
884 /* Chain up to parent's method. */
885 G_OBJECT_CLASS (e_credentials_prompter_parent_class)->constructed (object);
887 e_extensible_load_extensions (E_EXTENSIBLE (object));
890 static void
891 credentials_prompter_dispose (GObject *object)
893 ECredentialsPrompter *prompter = E_CREDENTIALS_PROMPTER (object);
894 GHashTableIter iter;
895 gpointer key, value;
897 if (prompter->priv->cancellable) {
898 g_cancellable_cancel (prompter->priv->cancellable);
899 g_clear_object (&prompter->priv->cancellable);
902 if (prompter->priv->registry) {
903 g_signal_handlers_disconnect_by_data (prompter->priv->registry, prompter);
904 g_clear_object (&prompter->priv->registry);
907 g_rec_mutex_lock (&prompter->priv->queue_lock);
909 if (prompter->priv->schedule_idle_id) {
910 g_source_remove (prompter->priv->schedule_idle_id);
911 prompter->priv->schedule_idle_id = 0;
914 g_rec_mutex_unlock (&prompter->priv->queue_lock);
916 g_clear_object (&prompter->priv->provider);
918 g_mutex_lock (&prompter->priv->prompters_lock);
920 g_hash_table_iter_init (&iter, prompter->priv->prompters);
921 while (g_hash_table_iter_next (&iter, &key, &value)) {
922 ECredentialsPrompterImpl *prompter_impl = value;
924 g_signal_handlers_disconnect_by_func (prompter_impl, credentials_prompter_prompt_finished_cb, prompter);
927 g_hash_table_remove_all (prompter->priv->prompters);
928 g_hash_table_remove_all (prompter->priv->known_prompters);
929 g_mutex_unlock (&prompter->priv->prompters_lock);
931 /* Chain up to parent's method. */
932 G_OBJECT_CLASS (e_credentials_prompter_parent_class)->dispose (object);
935 static void
936 credentials_prompter_finalize (GObject *object)
938 ECredentialsPrompter *prompter = E_CREDENTIALS_PROMPTER (object);
940 g_hash_table_destroy (prompter->priv->prompters);
941 g_hash_table_destroy (prompter->priv->known_prompters);
942 g_mutex_clear (&prompter->priv->prompters_lock);
944 g_hash_table_destroy (prompter->priv->disabled_auto_prompt);
945 g_mutex_clear (&prompter->priv->disabled_auto_prompt_lock);
947 g_rec_mutex_clear (&prompter->priv->queue_lock);
949 /* Chain up to parent's method. */
950 G_OBJECT_CLASS (e_credentials_prompter_parent_class)->finalize (object);
953 static void
954 e_credentials_prompter_class_init (ECredentialsPrompterClass *class)
956 GObjectClass *object_class;
958 g_type_class_add_private (class, sizeof (ECredentialsPrompterPrivate));
960 object_class = G_OBJECT_CLASS (class);
961 object_class->set_property = credentials_prompter_set_property;
962 object_class->get_property = credentials_prompter_get_property;
963 object_class->constructed = credentials_prompter_constructed;
964 object_class->dispose = credentials_prompter_dispose;
965 object_class->finalize = credentials_prompter_finalize;
968 * ECredentialsPrompter:auto-prompt:
970 * Whether the #ECredentialsPrompter can response to credential
971 * requests automatically.
973 * Since: 3.16
975 g_object_class_install_property (
976 object_class,
977 PROP_AUTO_PROMPT,
978 g_param_spec_boolean (
979 "auto-prompt",
980 "Auto Prompt",
981 "Whether can response to credential requests automatically",
982 TRUE,
983 G_PARAM_READWRITE |
984 G_PARAM_CONSTRUCT |
985 G_PARAM_STATIC_STRINGS));
988 * ECredentialsPrompter:registry:
990 * The #ESourceRegistry object, to whose credential requests the prompter listens.
992 * Since: 3.16
994 g_object_class_install_property (
995 object_class,
996 PROP_REGISTRY,
997 g_param_spec_object (
998 "registry",
999 "Registry",
1000 "An ESourceRegistry",
1001 E_TYPE_SOURCE_REGISTRY,
1002 G_PARAM_READWRITE |
1003 G_PARAM_CONSTRUCT_ONLY |
1004 G_PARAM_STATIC_STRINGS));
1007 * ECredentialsPrompter:provider:
1009 * The #ESourceCredentialsProvider object, which the prompter uses.
1011 * Since: 3.16
1013 g_object_class_install_property (
1014 object_class,
1015 PROP_PROVIDER,
1016 g_param_spec_object (
1017 "provider",
1018 "Provider",
1019 "An ESourceCredentialsProvider",
1020 E_TYPE_SOURCE_CREDENTIALS_PROVIDER,
1021 G_PARAM_READABLE |
1022 G_PARAM_STATIC_STRINGS));
1025 * ECredentialsPrompter::get-dialog-parent:
1026 * @prompter: the #ECredentialsPrompter which emitted the signal
1028 * Emitted when a new dialog will be shown, to get the right parent
1029 * window for it. If the result of the call is %NULL, then it tries
1030 * to get the window from the default GtkApplication.
1032 * Returns: (transfer none): a #GtkWindow, to be used as a dialog parent,
1033 * or %NULL.
1035 * Since: 3.16
1037 signals[GET_DIALOG_PARENT] = g_signal_new (
1038 "get-dialog-parent",
1039 G_OBJECT_CLASS_TYPE (object_class),
1040 G_SIGNAL_RUN_LAST,
1041 G_STRUCT_OFFSET (ECredentialsPrompterClass, get_dialog_parent),
1042 credentials_prompter_get_dialog_parent_accumulator, NULL, NULL,
1043 GTK_TYPE_WINDOW, 0, G_TYPE_NONE);
1045 /* Ensure built-in credential providers implementation types */
1046 g_type_ensure (E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD);
1047 g_type_ensure (E_TYPE_CREDENTIALS_PROMPTER_IMPL_OAUTH2);
1050 static void
1051 e_credentials_prompter_init (ECredentialsPrompter *prompter)
1053 prompter->priv = G_TYPE_INSTANCE_GET_PRIVATE (prompter, E_TYPE_CREDENTIALS_PROMPTER, ECredentialsPrompterPrivate);
1055 prompter->priv->auto_prompt = TRUE;
1056 prompter->priv->provider = NULL;
1057 prompter->priv->cancellable = g_cancellable_new ();
1059 g_mutex_init (&prompter->priv->prompters_lock);
1060 prompter->priv->prompters = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, g_object_unref);
1061 prompter->priv->known_prompters = g_hash_table_new (g_direct_hash, g_direct_equal);
1063 g_mutex_init (&prompter->priv->disabled_auto_prompt_lock);
1064 prompter->priv->disabled_auto_prompt = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1066 g_rec_mutex_init (&prompter->priv->queue_lock);
1070 * e_credentials_prompter_new:
1071 * @registry: an #ESourceRegistry to have the prompter listen to
1073 * Creates a new #ECredentialsPrompter, which listens for credential requests
1074 * from @registry.
1076 * Returns: (transfer full): a new #ECredentialsPrompter
1078 * Since: 3.16
1080 ECredentialsPrompter *
1081 e_credentials_prompter_new (ESourceRegistry *registry)
1083 return g_object_new (E_TYPE_CREDENTIALS_PROMPTER,
1084 "registry", registry,
1085 NULL);
1089 * e_credentials_prompter_get_registry:
1090 * @prompter: an #ECredentialsPrompter
1092 * Returns an #ESourceRegistry, to which the @prompter listens.
1094 * Returns: (transfer none): an #ESourceRegistry, to which the @prompter listens.
1096 * Since: 3.16
1098 ESourceRegistry *
1099 e_credentials_prompter_get_registry (ECredentialsPrompter *prompter)
1101 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), NULL);
1103 return prompter->priv->registry;
1107 * e_credentials_prompter_get_provider:
1108 * @prompter: an #ECredentialsPrompter
1110 * Returns an #ESourceCredentialsProvider, which the @prompter uses.
1112 * Returns: (transfer none): an #ESourceCredentialsProvider, which the @prompter uses.
1114 * Since: 3.16
1116 ESourceCredentialsProvider *
1117 e_credentials_prompter_get_provider (ECredentialsPrompter *prompter)
1119 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), NULL);
1120 g_return_val_if_fail (prompter->priv->provider != NULL, NULL);
1122 return prompter->priv->provider;
1126 * e_credentials_prompter_get_auto_prompt:
1127 * @prompter: an #ECredentialsPrompter
1129 * Returns, whether can respond to credential prompts automatically.
1130 * Default value is %TRUE.
1132 * This property does not influence direct calls of e_credentials_prompter_prompt().
1134 * Returns: Whether can respond to credential prompts automatically.
1136 * Since: 3.16
1138 gboolean
1139 e_credentials_prompter_get_auto_prompt (ECredentialsPrompter *prompter)
1141 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
1143 return prompter->priv->auto_prompt;
1147 * e_credentials_prompter_set_auto_prompt:
1148 * @prompter: an #ECredentialsPrompter
1149 * @auto_prompt: new value of the auto-prompt property
1151 * Sets whether can respond to credential prompts automatically. That means that
1152 * whenever any ESource will ask for credentials, it'll try to provide them.
1154 * Use e_credentials_prompter_set_auto_prompt_disabled_for() to influence
1155 * auto-prompt per an #ESource.
1157 * This property does not influence direct calls of e_credentials_prompter_prompt().
1159 * Since: 3.16
1161 void
1162 e_credentials_prompter_set_auto_prompt (ECredentialsPrompter *prompter,
1163 gboolean auto_prompt)
1165 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
1167 if ((prompter->priv->auto_prompt ? 1 : 0) == (auto_prompt ? 1 : 0))
1168 return;
1170 prompter->priv->auto_prompt = auto_prompt;
1172 g_object_notify (G_OBJECT (prompter), "auto-prompt");
1176 * e_credentials_prompter_set_auto_prompt_disabled_for:
1177 * @prompter: an #ECredentialsPrompter
1178 * @source: an #ESource
1179 * @is_disabled: whether the auto-prompt should be disabled for this @source
1181 * Sets whether the auto-prompt should be disabled for the given @source.
1182 * All sources can be auto-prompted by default. This is a complementary
1183 * value for the ECredentialsPrompter::auto-prompt property.
1185 * This value does not influence direct calls of e_credentials_prompter_prompt().
1187 * Since: 3.16
1189 void
1190 e_credentials_prompter_set_auto_prompt_disabled_for (ECredentialsPrompter *prompter,
1191 ESource *source,
1192 gboolean is_disabled)
1194 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
1195 g_return_if_fail (E_IS_SOURCE (source));
1196 g_return_if_fail (e_source_get_uid (source) != NULL);
1198 g_mutex_lock (&prompter->priv->disabled_auto_prompt_lock);
1200 if (is_disabled)
1201 g_hash_table_insert (prompter->priv->disabled_auto_prompt, g_strdup (e_source_get_uid (source)), GINT_TO_POINTER (1));
1202 else
1203 g_hash_table_remove (prompter->priv->disabled_auto_prompt, e_source_get_uid (source));
1205 g_mutex_unlock (&prompter->priv->disabled_auto_prompt_lock);
1209 * e_credentials_prompter_get_auto_prompt_disabled_for:
1210 * @prompter: an #ECredentialsPrompter
1211 * @source: an #ESource
1213 * Returns whether the auto-prompt is disabled for the given @source.
1214 * All sources can be auto-prompted by default. This is a complementary
1215 * value for the ECredentialsPrompter::auto-prompt property.
1217 * This value does not influence direct calls of e_credentials_prompter_prompt().
1219 * Returns: Whether the auto-prompt is disabled for the given @source
1221 * Since: 3.16
1223 gboolean
1224 e_credentials_prompter_get_auto_prompt_disabled_for (ECredentialsPrompter *prompter,
1225 ESource *source)
1227 gboolean is_disabled;
1229 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), TRUE);
1230 g_return_val_if_fail (E_IS_SOURCE (source), TRUE);
1231 g_return_val_if_fail (e_source_get_uid (source) != NULL, TRUE);
1233 g_mutex_lock (&prompter->priv->disabled_auto_prompt_lock);
1235 is_disabled = g_hash_table_contains (prompter->priv->disabled_auto_prompt, e_source_get_uid (source));
1237 g_mutex_unlock (&prompter->priv->disabled_auto_prompt_lock);
1239 return is_disabled;
1242 static GtkWindow *
1243 credentials_prompter_guess_dialog_parent (ECredentialsPrompter *prompter)
1245 GApplication *app;
1247 app = g_application_get_default ();
1248 if (!app)
1249 return NULL;
1251 if (GTK_IS_APPLICATION (app))
1252 return gtk_application_get_active_window (GTK_APPLICATION (app));
1254 return NULL;
1258 * e_credentials_prompter_get_dialog_parent:
1259 * @prompter: an #ECredentialsPrompter
1261 * Returns a #GtkWindow, which should be used as a dialog parent. This is determined
1262 * by an ECredentialsPrompter::get-dialog-parent signal emission. If there is no callback
1263 * registered or the current callbacks don't have any suitable window, then there's
1264 * chosen the last active window from the default GApplication, if any available.
1266 * Returns: (transfer none): a #GtkWindow, to be used as a dialog parent, or %NULL.
1268 * Since: 3.16
1270 GtkWindow *
1271 e_credentials_prompter_get_dialog_parent (ECredentialsPrompter *prompter)
1273 GtkWindow *parent = NULL;
1275 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), NULL);
1277 g_signal_emit (prompter, signals[GET_DIALOG_PARENT], 0, &parent);
1279 if (!parent)
1280 parent = credentials_prompter_guess_dialog_parent (prompter);
1282 return parent;
1286 * e_credentials_prompter_register_impl:
1287 * @prompter: an #ECredentialsPrompter
1288 * @authentication_method: (allow-none): an authentication method to registr @prompter_impl for; or %NULL
1289 * @prompter_impl: an #ECredentialsPrompterImpl
1291 * Registers a prompter implementation for a given authentication method. If there is
1292 * registered a prompter for the same @authentication_method, then the function does
1293 * nothing, otherwise it adds its own reference on the @prompter_impl, and uses it
1294 * for that authentication method. One @prompter_impl can be registered for multiple
1295 * authentication methods.
1297 * A special value %NULL can be used for the @authentication_method, which means
1298 * a default credentials prompter, that is to be used when there is no prompter
1299 * registered for the exact authentication method.
1301 * Returns: %TRUE on success, %FALSE on failure or when there was another prompter
1302 * implementation registered for the given authentication method.
1304 * Since: 3.16
1306 gboolean
1307 e_credentials_prompter_register_impl (ECredentialsPrompter *prompter,
1308 const gchar *authentication_method,
1309 ECredentialsPrompterImpl *prompter_impl)
1311 guint known_prompters;
1313 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
1314 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl), FALSE);
1316 if (!authentication_method)
1317 authentication_method = "";
1319 g_mutex_lock (&prompter->priv->prompters_lock);
1321 if (g_hash_table_lookup (prompter->priv->prompters, authentication_method) != NULL) {
1322 g_mutex_unlock (&prompter->priv->prompters_lock);
1323 return FALSE;
1326 g_hash_table_insert (prompter->priv->prompters, g_strdup (authentication_method), g_object_ref (prompter_impl));
1328 known_prompters = GPOINTER_TO_UINT (g_hash_table_lookup (prompter->priv->known_prompters, prompter_impl));
1329 if (!known_prompters) {
1330 g_signal_connect (prompter_impl, "prompt-finished", G_CALLBACK (credentials_prompter_prompt_finished_cb), prompter);
1332 g_hash_table_insert (prompter->priv->known_prompters, prompter_impl, GUINT_TO_POINTER (known_prompters + 1));
1334 g_mutex_unlock (&prompter->priv->prompters_lock);
1336 return TRUE;
1340 * e_credentials_prompter_unregister_impl:
1341 * @prompter: an #ECredentialsPrompter
1342 * @authentication_method: (allow-none): an authentication method to registr @prompter_impl for; or %NULL
1343 * @prompter_impl: an #ECredentialsPrompterImpl
1345 * Unregisters previously registered @prompter_impl for the given @autnetication_method with
1346 * e_credentials_prompter_register_impl(). Function does nothing, if no such authentication
1347 * method is registered or if it has set a different prompter implementation.
1349 * Since: 3.16
1351 void
1352 e_credentials_prompter_unregister_impl (ECredentialsPrompter *prompter,
1353 const gchar *authentication_method,
1354 ECredentialsPrompterImpl *prompter_impl)
1356 ECredentialsPrompterImpl *current_prompter_impl;
1358 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
1360 if (!authentication_method)
1361 authentication_method = "";
1363 g_mutex_lock (&prompter->priv->prompters_lock);
1365 current_prompter_impl = g_hash_table_lookup (prompter->priv->prompters, authentication_method);
1366 if (current_prompter_impl == prompter_impl) {
1367 guint known_prompters;
1369 known_prompters = GPOINTER_TO_UINT (g_hash_table_lookup (prompter->priv->known_prompters, prompter_impl));
1370 if (known_prompters == 1) {
1371 g_signal_handlers_disconnect_by_func (prompter_impl, credentials_prompter_prompt_finished_cb, prompter);
1372 g_hash_table_remove (prompter->priv->known_prompters, prompter_impl);
1373 } else {
1374 known_prompters--;
1375 g_hash_table_insert (prompter->priv->known_prompters, prompter_impl, GUINT_TO_POINTER (known_prompters + 1));
1378 g_hash_table_remove (prompter->priv->prompters, authentication_method);
1381 g_mutex_unlock (&prompter->priv->prompters_lock);
1384 static void
1385 credentials_prompter_get_last_credentials_required_arguments_cb (GObject *source_object,
1386 GAsyncResult *result,
1387 gpointer user_data)
1389 ECredentialsPrompter *prompter = user_data;
1390 ESource *source;
1391 ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN;
1392 gchar *certificate_pem = NULL;
1393 GTlsCertificateFlags certificate_errors = 0;
1394 GError *op_error = NULL;
1395 GError *error = NULL;
1397 g_return_if_fail (E_IS_SOURCE (source_object));
1399 source = E_SOURCE (source_object);
1401 if (!e_source_get_last_credentials_required_arguments_finish (source, result,
1402 &reason, &certificate_pem, &certificate_errors, &op_error, &error)) {
1403 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1404 g_warning ("%s: Failed to get last credential values: %s", G_STRFUNC, error ? error->message : "Unknown error");
1407 g_clear_error (&error);
1408 return;
1411 /* Can check only now, when know the operation was not cancelled and the prompter freed. */
1412 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
1414 /* Check once again, as this was called asynchronously and anything could change meanwhile. */
1415 if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS) {
1416 credentials_prompter_credentials_required_cb (prompter->priv->registry,
1417 source, reason, certificate_pem, certificate_errors, op_error, prompter);
1420 g_free (certificate_pem);
1421 g_clear_error (&op_error);
1425 * e_credentials_prompter_process_awaiting_credentials:
1426 * @prompter: an #ECredentialsPrompter
1428 * Process all enabled sources with connection state #E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS,
1429 * like if they just asked for its credentials for the first time.
1431 * Since: 3.16
1433 void
1434 e_credentials_prompter_process_awaiting_credentials (ECredentialsPrompter *prompter)
1436 GList *sources, *link;
1438 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
1440 sources = e_source_registry_list_enabled (prompter->priv->registry, NULL);
1441 for (link = sources; link; link = g_list_next (link)) {
1442 ESource *source = link->data;
1444 if (!source)
1445 continue;
1447 if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS) {
1448 /* Check what failed the last time */
1449 e_credentials_prompter_process_source (prompter, source);
1453 g_list_free_full (sources, g_object_unref);
1457 * e_credentials_prompter_process_source:
1458 * @prompter: an #ECredentialsPrompter
1459 * @source: an #ESource
1461 * Continues a credential prompt for @source. Returns, whether anything wil be done.
1462 * The %FALSE either means that the @source<!-- -->'s connection status is not
1463 * the %E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS.
1465 * Returns: Whether continues with the credentials prompt.
1467 * Since: 3.16
1469 gboolean
1470 e_credentials_prompter_process_source (ECredentialsPrompter *prompter,
1471 ESource *source)
1473 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
1474 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1476 if (e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS)
1477 return FALSE;
1479 e_source_get_last_credentials_required_arguments (source, prompter->priv->cancellable,
1480 credentials_prompter_get_last_credentials_required_arguments_cb, prompter);
1482 return TRUE;
1486 * e_credentials_prompter_prompt:
1487 * @prompter: an #ECredentialsPrompter
1488 * @source: an #ESource, which prompt the credentials for
1489 * @error_text: (allow-none): Additional error text to show to a user, or %NULL
1490 * @flags: a bit-or of #ECredentialsPrompterPromptFlags
1491 * @callback: (allow-none): a callback to call when the credentials are ready, or %NULL
1492 * @user_data: user data passed into @callback
1494 * Asks the @prompter to prompt for credentials, which are returned
1495 * to the caller through @callback, when available.The @flags are ignored,
1496 * when the @callback is %NULL; the credentials are passed to the @source
1497 * with e_source_invoke_authenticate() directly, in this case.
1498 * Call e_credentials_prompter_prompt_finish() in @callback to get to
1499 * the provided credentials.
1501 * Since: 3.16
1503 void
1504 e_credentials_prompter_prompt (ECredentialsPrompter *prompter,
1505 ESource *source,
1506 const gchar *error_text,
1507 ECredentialsPrompterPromptFlags flags,
1508 GAsyncReadyCallback callback,
1509 gpointer user_data)
1511 CredentialsPromptData *prompt_data;
1513 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
1514 g_return_if_fail (E_IS_SOURCE (source));
1516 prompt_data = g_new0 (CredentialsPromptData, 1);
1517 prompt_data->source = g_object_ref (source);
1518 prompt_data->error_text = g_strdup (error_text);
1519 prompt_data->flags = flags;
1520 prompt_data->async_result = callback ? g_simple_async_result_new (G_OBJECT (prompter),
1521 callback, user_data, e_credentials_prompter_prompt) : NULL;
1523 /* Just it can be shown in the UI as a prefilled value and the right source (collection) is used. */
1524 credentials_prompter_lookup_source_details (source, prompter,
1525 credentials_prompter_lookup_source_details_before_prompt_cb, prompt_data);
1529 * e_credentials_prompter_prompt_finish:
1530 * @prompter: an #ECredentialsPrompter
1531 * @result: a #GAsyncResult
1532 * @out_source: (transfer full): (allow-none): optionally set to an #ESource, on which the prompt was started; can be %NULL
1533 * @out_credentials: (transfer full): set to an #ENamedParameters with provied credentials
1534 * @error: return location for a #GError, or %NULL
1536 * Finishes a credentials prompt previously started with e_credentials_prompter_prompt().
1537 * The @out_source will have set a referenced #ESource, for which the prompt
1538 * was started. Unref it, when no longer needed. Similarly the @out_credentials
1539 * will have set a newly allocated #ENamedParameters structure with provided credentials,
1540 * which should be freed with e_named_credentials_free() when no longer needed.
1541 * Both output arguments will be set to %NULL on error and %FALSE will be returned.
1543 * Returns: %TRUE on success, %FALSE otherwise.
1545 * Since: 3.16
1547 gboolean
1548 e_credentials_prompter_prompt_finish (ECredentialsPrompter *prompter,
1549 GAsyncResult *result,
1550 ESource **out_source,
1551 ENamedParameters **out_credentials,
1552 GError **error)
1554 CredentialsResultData *data;
1556 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
1557 g_return_val_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result))
1558 == e_credentials_prompter_prompt, FALSE);
1559 g_return_val_if_fail (out_credentials, FALSE);
1561 if (out_source)
1562 *out_source = NULL;
1563 *out_credentials = NULL;
1565 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
1566 return FALSE;
1568 data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
1569 g_return_val_if_fail (data != NULL, FALSE);
1571 if (data->credentials) {
1572 if (out_source)
1573 *out_source = g_object_ref (data->source);
1574 *out_credentials = e_named_parameters_new_clone (data->credentials);
1575 } else {
1576 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Credentials prompt was cancelled"));
1578 return FALSE;
1581 return TRUE;
1585 * e_credentials_prompter_complete_prompt_call:
1586 * @prompter: an #ECredentialsPrompter
1587 * @async_result: a #GSimpleAsyncResult
1588 * @source: an #ESource, on which the prompt was started
1589 * @credentials: (allow-none): credentials, as provided by a user, on %NULL, when the prompt was cancelled
1590 * @error: (allow-none): a resulting #GError, or %NULL
1592 * Completes an ongoing credentials prompt on idle, by finishing the @async_result.
1593 * This function is meant to be used by an #ECredentialsPrompterImpl implementation.
1594 * To actually finish the credentials prompt previously started with
1595 * e_credentials_prompter_prompt(), the e_credentials_prompter_prompt_finish() should
1596 * be called from the provided callback.
1598 * Using %NULL @credentials will result in a G_IO_ERROR_CANCELLED error, if
1599 * no other @error is provided.
1601 * Since: 3.16
1603 void
1604 e_credentials_prompter_complete_prompt_call (ECredentialsPrompter *prompter,
1605 GSimpleAsyncResult *async_result,
1606 ESource *source,
1607 const ENamedParameters *credentials,
1608 const GError *error)
1610 g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
1611 g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (async_result));
1612 g_return_if_fail (g_simple_async_result_get_source_tag (async_result) == e_credentials_prompter_prompt);
1613 g_return_if_fail (source == NULL || E_IS_SOURCE (source));
1614 if (credentials)
1615 g_return_if_fail (E_IS_SOURCE (source));
1617 if (error) {
1618 g_simple_async_result_set_from_error (async_result, error);
1619 } else if (!credentials) {
1620 g_simple_async_result_set_error (async_result, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Credentials prompt was cancelled"));
1621 } else {
1622 CredentialsResultData *result;
1624 result = g_new0 (CredentialsResultData, 1);
1625 result->source = g_object_ref (source);
1626 result->credentials = e_named_parameters_new_clone (credentials);
1628 g_simple_async_result_set_op_res_gpointer (async_result, result, credentials_result_data_free);
1631 g_simple_async_result_complete_in_idle (async_result);
1634 static gboolean
1635 credentials_prompter_prompt_sync (ECredentialsPrompter *prompter,
1636 ESource *source,
1637 gboolean is_retry,
1638 ECredentialsPrompterPromptFlags *flags,
1639 const gchar *error_text,
1640 ENamedParameters **out_credentials,
1641 GCancellable *cancellable,
1642 GError **error)
1644 gboolean res = FALSE;
1645 ESourceCredentialsProvider *credentials_provider;
1646 ENamedParameters *credentials = NULL;
1648 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
1649 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1650 g_return_val_if_fail (flags != NULL, FALSE);
1651 g_return_val_if_fail (out_credentials != NULL, FALSE);
1653 if (g_cancellable_set_error_if_cancelled (cancellable, error))
1654 return FALSE;
1656 credentials_provider = e_credentials_prompter_get_provider (prompter);
1658 if (!is_retry) {
1659 ESource *cred_source;
1660 GError *local_error = NULL;
1662 cred_source = e_source_credentials_provider_ref_credentials_source (credentials_provider, source);
1664 if (e_source_credentials_provider_lookup_sync (credentials_provider, cred_source ? cred_source : source,
1665 cancellable, &credentials, &local_error)) {
1666 res = TRUE;
1667 } else if (!g_cancellable_is_cancelled (cancellable)) {
1668 /* To prompt for the password directly */
1669 is_retry = TRUE;
1670 g_clear_error (&local_error);
1671 } else {
1672 g_propagate_error (error, local_error);
1675 g_clear_object (&cred_source);
1678 if (is_retry) {
1679 EAsyncClosure *closure;
1680 GAsyncResult *result;
1682 *flags = (*flags) & (~E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS);
1684 closure = e_async_closure_new ();
1686 e_credentials_prompter_prompt (prompter, source, error_text, *flags,
1687 e_async_closure_callback, closure);
1689 result = e_async_closure_wait (closure);
1691 if (e_credentials_prompter_prompt_finish (prompter, result, NULL, &credentials, error)) {
1692 res = TRUE;
1695 e_async_closure_free (closure);
1698 if (res && credentials)
1699 *out_credentials = e_named_parameters_new_clone (credentials);
1701 e_named_parameters_free (credentials);
1703 return res;
1707 * e_credentials_prompter_loop_prompt_sync:
1708 * @prompter: an #ECredentialsPrompter
1709 * @source: an #ESource to be prompted credentials for
1710 * @flags: a bit-or of #ECredentialsPrompterPromptFlags initial flags
1711 * @func: (scope call): an #ECredentialsPrompterLoopPromptFunc user function to call to check provided credentials
1712 * @user_data: user data to pass to @func
1713 * @cancellable: (allow-none): an optional #GCancellable, or %NULL
1714 * @error: (allow-none): a #GError, to store any errors to, or %NULL
1716 * Runs a credentials prompt loop for @source, as long as the @func doesn't
1717 * indicate that the provided credentials can be used to successfully
1718 * authenticate against @source<!-- -->'s server, or that the @func
1719 * returns %FALSE. The loop is also teminated when a used cancels
1720 * the credentials prompt or the @cancellable is cancelled, though
1721 * not sooner than the credentials prompt dialog is closed.
1723 * Note: The function doesn't return until the loop is terminated, either
1724 * successfully or unsuccessfully. The function can be called from any
1725 * thread, though a dedicated thread is preferred.
1727 * Returns: %TRUE, when the credentials were provided sucessfully and they
1728 * can be used to authenticate the @source; %FALSE otherwise.
1730 * Since: 3.16
1732 gboolean
1733 e_credentials_prompter_loop_prompt_sync (ECredentialsPrompter *prompter,
1734 ESource *source,
1735 ECredentialsPrompterPromptFlags flags,
1736 ECredentialsPrompterLoopPromptFunc func,
1737 gpointer user_data,
1738 GCancellable *cancellable,
1739 GError **error)
1741 gboolean is_retry, authenticated;
1742 ENamedParameters *credentials = NULL;
1744 g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
1745 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1746 g_return_val_if_fail (func != NULL, FALSE);
1748 is_retry = FALSE;
1749 authenticated = FALSE;
1751 while (!authenticated && !g_cancellable_is_cancelled (cancellable)) {
1752 GError *local_error = NULL;
1754 e_named_parameters_free (credentials);
1755 credentials = NULL;
1757 if (!credentials_prompter_prompt_sync (prompter, source, is_retry, &flags, NULL,
1758 &credentials, cancellable, error))
1759 break;
1761 if (g_cancellable_set_error_if_cancelled (cancellable, error))
1762 break;
1764 g_clear_error (&local_error);
1766 if (!func (prompter, source, credentials, &authenticated, user_data, cancellable, &local_error)) {
1767 if (local_error)
1768 g_propagate_error (error, local_error);
1769 break;
1772 is_retry = TRUE;
1775 e_named_parameters_free (credentials);
1777 return authenticated;