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
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"
23 #include <glib/gi18n-lib.h>
25 #define GCR_API_SUBJECT_TO_CHANGE
27 #undef GCR_API_SUBJECT_TO_CHANGE
29 #include <camel/camel.h>
30 #include <libebackend/libebackend.h>
31 #include <libedataserver/libedataserver.h>
33 #include "e-trust-prompt.h"
36 trust_prompt_add_info_line (GtkGrid
*grid
,
37 const gchar
*label_text
,
38 const gchar
*value_text
,
48 g_return_if_fail (grid
!= NULL
);
49 g_return_if_fail (label_text
!= NULL
);
50 g_return_if_fail (at_row
!= NULL
);
52 if (!value_text
|| !*value_text
)
55 bold
= pango_attr_list_new ();
56 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
57 pango_attr_list_insert (bold
, attr
);
59 widget
= gtk_label_new (label_text
);
60 gtk_misc_set_padding (GTK_MISC (widget
), 0, 0);
61 gtk_misc_set_alignment (GTK_MISC (widget
), 0.0, 0.0);
63 gtk_grid_attach (grid
, widget
, 1, *at_row
, 1, 1);
65 widget
= gtk_label_new (value_text
);
66 gtk_label_set_line_wrap (GTK_LABEL (widget
), wrap
);
70 "halign", GTK_ALIGN_FILL
,
71 "justify", GTK_JUSTIFY_LEFT
,
72 "attributes", use_bold
? bold
: NULL
,
74 "ellipsize", ellipsize
? PANGO_ELLIPSIZE_END
: PANGO_ELLIPSIZE_NONE
,
76 "max-width-chars", 80,
81 gtk_grid_attach (grid
, widget
, 2, *at_row
, 1, 1);
83 *at_row
= (*at_row
) + 1;
85 pango_attr_list_unref (bold
);
88 static ETrustPromptResponse
89 trust_prompt_show (GtkWindow
*parent
,
90 const gchar
*source_extension
,
91 const gchar
*source_display_name
,
93 const gchar
*error_text
,
96 void (* dialog_ready_cb
) (GtkDialog
*dialog
, gpointer user_data
),
99 ETrustPromptResponse response
;
100 GcrCertificateWidget
*certificate_widget
;
101 GcrCertificate
*certificate
;
102 GckAttributes
*attributes
;
103 GtkWidget
*dialog
, *widget
;
110 dialog
= gtk_dialog_new_with_buttons (
111 _("Certificate trust..."), parent
, GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
112 _("_Cancel"), GTK_RESPONSE_CANCEL
,
113 _("_Reject"), GTK_RESPONSE_REJECT
,
114 _("Accept _Temporarily"), GTK_RESPONSE_YES
,
115 _("_Accept Permanently"), GTK_RESPONSE_ACCEPT
,
118 widget
= gtk_dialog_get_content_area (GTK_DIALOG (dialog
));
120 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 5);
122 grid
= g_object_new (
124 "orientation", GTK_ORIENTATION_HORIZONTAL
,
125 "row-homogeneous", FALSE
,
127 "column-homogeneous", FALSE
,
128 "column-spacing", 12,
130 "halign", GTK_ALIGN_FILL
,
132 "valign", GTK_ALIGN_FILL
,
135 gtk_container_set_border_width (GTK_CONTAINER (grid
), 5);
136 gtk_container_add (GTK_CONTAINER (widget
), GTK_WIDGET (grid
));
138 widget
= gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_DIALOG
);
142 "valign", GTK_ALIGN_START
,
144 gtk_grid_attach (grid
, widget
, 0, row
, 1, 3);
146 tmp
= g_markup_escape_text (host
, -1);
147 bhost
= g_strconcat ("<b>", tmp
, "</b>", NULL
);
150 if (source_extension
&& source_display_name
) {
151 gchar
*bsource_display_name
= g_strconcat ("<b>", source_display_name
, "</b>", NULL
);
153 if (g_str_equal (source_extension
, E_SOURCE_EXTENSION_ADDRESS_BOOK
)) {
154 tmp
= g_strdup_printf (
155 "An address book '%s' cannot connect, because an SSL/TLS certificate for '%s' is not trusted. Do you wish to accept it?",
156 bsource_display_name
, bhost
);
157 } else if (g_str_equal (source_extension
, E_SOURCE_EXTENSION_CALENDAR
)) {
158 tmp
= g_strdup_printf (
159 "A calendar '%s' cannot connect, because an SSL/TLS certificate for '%s' is not trusted. Do you wish to accept it?",
160 bsource_display_name
, bhost
);
161 } else if (g_str_equal (source_extension
, E_SOURCE_EXTENSION_MEMO_LIST
)) {
162 tmp
= g_strdup_printf (
163 "A memo list '%s' cannot connect, because an SSL/TLS certificate for '%s' is not trusted. Do you wish to accept it?",
164 bsource_display_name
, bhost
);
165 } else if (g_str_equal (source_extension
, E_SOURCE_EXTENSION_TASK_LIST
)) {
166 tmp
= g_strdup_printf (
167 "A task list '%s' cannot connect, because an SSL/TLS certificate for '%s' is not trusted. Do you wish to accept it?",
168 bsource_display_name
, bhost
);
169 } else if (g_str_equal (source_extension
, E_SOURCE_EXTENSION_MAIL_ACCOUNT
)) {
170 tmp
= g_strdup_printf (
171 "A mail account '%s' cannot connect, because an SSL/TLS certificate for '%s' is not trusted. Do you wish to accept it?",
172 bsource_display_name
, bhost
);
173 } else if (g_str_equal (source_extension
, E_SOURCE_EXTENSION_MAIL_TRANSPORT
)) {
174 tmp
= g_strdup_printf (
175 "A mail transport '%s' cannot connect, because an SSL/TLS certificate for '%s' is not trusted. Do you wish to accept it?",
176 bsource_display_name
, bhost
);
178 tmp
= g_strdup_printf (
179 "An account '%s' cannot connect, because an SSL/TLS certificate for '%s' is not trusted. Do you wish to accept it?",
180 bsource_display_name
, bhost
);
183 g_free (bsource_display_name
);
186 tmp
= g_strdup_printf (_("SSL/TLS certificate for ā%sā is not trusted. Do you wish to accept it?"), bhost
);
189 widget
= gtk_label_new (NULL
);
190 gtk_label_set_line_wrap (GTK_LABEL (widget
), TRUE
);
191 gtk_label_set_markup (GTK_LABEL (widget
), tmp
);
195 "halign", GTK_ALIGN_FILL
,
196 "valign", GTK_ALIGN_CENTER
,
198 "max-width-chars", 80,
205 gtk_grid_attach (grid
, widget
, 1, row
, 2, 1);
208 trust_prompt_add_info_line (grid
, _("Reason:"), reason
, FALSE
, FALSE
, TRUE
, &row
);
211 trust_prompt_add_info_line (grid
, _("Detailed error:"), error_text
, FALSE
, TRUE
, FALSE
, &row
);
213 data
= gcr_parsed_get_data (parsed
, &length
);
214 attributes
= gcr_parsed_get_attributes (parsed
);
216 certificate
= gcr_simple_certificate_new (data
, length
);
218 certificate_widget
= gcr_certificate_widget_new (certificate
);
219 gcr_certificate_widget_set_attributes (certificate_widget
, attributes
);
221 widget
= GTK_WIDGET (certificate_widget
);
222 gtk_grid_attach (grid
, widget
, 1, row
, 2, 1);
223 gtk_widget_show (widget
);
225 g_clear_object (&certificate
);
227 gtk_widget_show_all (GTK_WIDGET (grid
));
230 dialog_ready_cb (GTK_DIALOG (dialog
), user_data
);
232 switch (gtk_dialog_run (GTK_DIALOG (dialog
))) {
233 case GTK_RESPONSE_REJECT
:
234 response
= E_TRUST_PROMPT_RESPONSE_REJECT
;
236 case GTK_RESPONSE_ACCEPT
:
237 response
= E_TRUST_PROMPT_RESPONSE_ACCEPT
;
239 case GTK_RESPONSE_YES
:
240 response
= E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY
;
243 response
= E_TRUST_PROMPT_RESPONSE_UNKNOWN
;
247 gtk_widget_destroy (dialog
);
253 * e_trust_prompt_describe_certificate_errors:
254 * @flags: a #GTlsCertificateFlags to describe
256 * Converts @flags into a localized text description of the set bits, one
257 * bit description per line. If no bit is set, then an empty string is
260 * Returns: A newly allocated string with text description
261 * of @flags. Free the returned pointer with g_free() when no longer needed.
266 e_trust_prompt_describe_certificate_errors (GTlsCertificateFlags flags
)
268 struct _convert_table
{
269 GTlsCertificateFlags flag
;
270 const gchar
*description
;
271 } convert_table
[] = {
272 { G_TLS_CERTIFICATE_UNKNOWN_CA
,
273 N_("The signing certificate authority is not known.") },
274 { G_TLS_CERTIFICATE_BAD_IDENTITY
,
275 N_("The certificate does not match the expected identity of the site that it was retrieved from.") },
276 { G_TLS_CERTIFICATE_NOT_ACTIVATED
,
277 N_("The certificateās activation time is still in the future.") },
278 { G_TLS_CERTIFICATE_EXPIRED
,
279 N_("The certificate has expired.") },
280 { G_TLS_CERTIFICATE_REVOKED
,
281 N_("The certificate has been revoked according to the connectionās certificate revocation list.") },
282 { G_TLS_CERTIFICATE_INSECURE
,
283 N_("The certificateās algorithm is considered insecure.") }
286 GString
*reason
= g_string_new ("");
289 for (ii
= 0; ii
< G_N_ELEMENTS (convert_table
); ii
++) {
290 if ((flags
& convert_table
[ii
].flag
) != 0) {
292 g_string_append (reason
, "\n");
294 g_string_append (reason
, _(convert_table
[ii
].description
));
298 return g_string_free (reason
, FALSE
);
302 trust_prompt_parser_parsed_cb (GcrParser
*parser
,
303 GcrParsed
**out_parsed
)
307 parsed
= gcr_parser_get_parsed (parser
);
308 g_return_if_fail (parsed
!= NULL
);
310 *out_parsed
= gcr_parsed_ref (parsed
);
313 static ETrustPromptResponse
314 e_trust_prompt_run_with_dialog_ready_callback (GtkWindow
*parent
,
315 const gchar
*source_extension
,
316 const gchar
*source_display_name
,
318 const gchar
*certificate_pem
,
319 GTlsCertificateFlags certificate_errors
,
320 const gchar
*error_text
,
321 void (* dialog_ready_cb
) (GtkDialog
*dialog
, gpointer user_data
),
324 ETrustPromptResponse response
= E_TRUST_PROMPT_RESPONSE_UNKNOWN
;
326 GcrParsed
*parsed
= NULL
;
327 GError
*local_error
= NULL
;
330 g_return_val_if_fail (GTK_IS_WINDOW (parent
), E_TRUST_PROMPT_RESPONSE_UNKNOWN
);
331 g_return_val_if_fail (host
!= NULL
, E_TRUST_PROMPT_RESPONSE_UNKNOWN
);
332 g_return_val_if_fail (certificate_pem
!= NULL
, E_TRUST_PROMPT_RESPONSE_UNKNOWN
);
334 /* Continue even if PKCS#11 module registration fails.
335 * Certificate details won't display correctly but the
336 * user can still respond to the prompt. */
337 gcr_pkcs11_initialize (NULL
, &local_error
);
338 if (local_error
!= NULL
) {
339 g_warning ("%s: gcr_pkcs11_initialize() call failed: %s", G_STRFUNC
, local_error
->message
);
340 g_clear_error (&local_error
);
343 parser
= gcr_parser_new ();
347 G_CALLBACK (trust_prompt_parser_parsed_cb
), &parsed
);
349 gcr_parser_parse_data (parser
, (const guchar
*) certificate_pem
, strlen (certificate_pem
), &local_error
);
351 g_object_unref (parser
);
355 ((parsed
!= NULL
) && (local_error
== NULL
)) ||
356 ((parsed
== NULL
) && (local_error
!= NULL
)));
358 if (parsed
!= NULL
) {
361 reason
= e_trust_prompt_describe_certificate_errors (certificate_errors
);
363 response
= trust_prompt_show (parent
, source_extension
, source_display_name
, host
, error_text
, parsed
, reason
, dialog_ready_cb
, user_data
);
365 gcr_parsed_unref (parsed
);
369 g_clear_error (&local_error
);
375 * e_trust_prompt_run_modal:
376 * @parent: A #GtkWindow to use as a parent for the trust prompt dialog
377 * @source_extension: (allow-none): an #ESource extension, to identify a kind of the source; or %NULL
378 * @source_display_name: (allow-none): an #ESource display name, to identify what prompts; or %NULL
379 * @host: a host name to which the certificate belongs
380 * @certificate_pem: a PEM-encoded certificate for which to show the trust prompt
381 * @certificate_errors: errors of the @certificate_pem
382 * @error_text: (allow-none): an optional error text to show in the dialog; can be %NULL
384 * Runs modal (doesn't return until the dialog is closed) a trust prompt dialog,
385 * it is a prompt whether a user wants to accept or reject the @certificate_pem
386 * for the @host due to the @certificate_errors errors.
388 * The pair @source_extension and @source_display_name influences the trust prompt message.
389 * If both are set, then the message also contains which source failed to connect according
390 * to these two arguments.
392 * The dialog can contain a custom error text, passed in as @error_text.
393 * The error might be a detailed error string returned by the server. If set,
394 * it is prefixed with "Detailed error:" string.
396 * Returns: A code of the user's choice. The #E_TRUST_PROMPT_RESPONSE_UNKNOWN
397 * is returned, when the user cancelled the trust prompt dialog.
402 e_trust_prompt_run_modal (GtkWindow
*parent
,
403 const gchar
*source_extension
,
404 const gchar
*source_display_name
,
406 const gchar
*certificate_pem
,
407 GTlsCertificateFlags certificate_errors
,
408 const gchar
*error_text
)
411 g_return_val_if_fail (GTK_IS_WINDOW (parent
), E_TRUST_PROMPT_RESPONSE_UNKNOWN
);
412 g_return_val_if_fail (host
!= NULL
, E_TRUST_PROMPT_RESPONSE_UNKNOWN
);
413 g_return_val_if_fail (certificate_pem
!= NULL
, E_TRUST_PROMPT_RESPONSE_UNKNOWN
);
415 return e_trust_prompt_run_with_dialog_ready_callback (parent
, source_extension
, source_display_name
, host
,
416 certificate_pem
, certificate_errors
, error_text
, NULL
, NULL
);
420 source_connection_status_changed_cb (ESource
*source
,
424 g_return_if_fail (GTK_IS_DIALOG (dialog
));
426 /* Do not close the prompt when the source is still waiting for the credentials. */
427 if (e_source_get_connection_status (source
) != E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS
&&
428 e_source_get_connection_status (source
) != E_SOURCE_CONNECTION_STATUS_SSL_FAILED
)
429 gtk_dialog_response (dialog
, GTK_RESPONSE_CANCEL
);
433 trust_prompt_listen_for_source_changes_cb (GtkDialog
*dialog
,
436 ESource
*source
= user_data
;
438 g_return_if_fail (GTK_IS_DIALOG (dialog
));
439 g_return_if_fail (E_IS_SOURCE (source
));
441 g_signal_connect (source
, "notify::connection-status",
442 G_CALLBACK (source_connection_status_changed_cb
), dialog
);
445 typedef struct _SaveSourceData
{
446 ETrustPromptResponse response
;
452 save_source_data_free (gpointer ptr
)
454 SaveSourceData
*data
= ptr
;
457 g_clear_error (&data
->error
);
463 save_source_thread (GTask
*task
,
464 gpointer source_object
,
466 GCancellable
*cancellable
)
468 ESource
*source
= source_object
;
469 SaveSourceData
*data
= task_data
;
470 GError
*local_error
= NULL
;
472 g_return_if_fail (E_IS_SOURCE (source
));
473 g_return_if_fail (data
!= NULL
);
476 local_error
= g_error_copy (data
->error
);
477 else if (data
->call_save
)
478 e_source_write_sync (source
, cancellable
, &local_error
);
480 if (local_error
!= NULL
) {
481 g_task_return_error (task
, local_error
);
483 g_task_return_boolean (task
, TRUE
);
488 trust_prompt_get_host_from_url (const gchar
*url
)
496 suri
= soup_uri_new (url
);
500 host
= g_strdup (soup_uri_get_host (suri
));
502 if (!host
|| !*host
) {
507 soup_uri_free (suri
);
513 * e_trust_prompt_run_for_source:
514 * @parent: A #GtkWindow to use as a parent for the trust prompt dialog
515 * @source: an #ESource, with %E_SOURCE_EXTENSION_AUTHENTICATION
516 * @certificate_pem: a PEM-encoded certificate for which to show the trust prompt
517 * @certificate_errors: errors of the @certificate_pem
518 * @error_text: (allow-none): an optional error text to show in the dialog; can be %NULL
519 * @allow_source_save: whether can also save any @source changes
520 * @cancellable: (allow-none): a #GCancellable, or %NULL
521 * @callback: a callback to call, when the prompt (an @source save) is done
522 * @user_data: user data passed into @callback
524 * Similar to e_trust_prompt_run_modal(), except it also manages all the necessary things
525 * around the @source<!-- -->'s SSL/TLS trust properties when it also contains %E_SOURCE_EXTENSION_WEBDAV,
526 * thus the SSL/TLS trust on the WebDAV @source is properly updated based on the user's choice.
527 * The call is finished with e_trust_prompt_run_for_source_finish(),
528 * which also returns the user's choice. The finish happens in the @callback.
529 * This is necessary, because the @source can be also saved.
531 * The function fails, if the @source doesn't contain the %E_SOURCE_EXTENSION_AUTHENTICATION.
533 * Note: The dialog is not shown when the stored certificate trust in the WebDAV @source
534 * matches the @certificate_pem and the stored result is #E_TRUST_PROMPT_RESPONSE_REJECT.
539 e_trust_prompt_run_for_source (GtkWindow
*parent
,
541 const gchar
*certificate_pem
,
542 GTlsCertificateFlags certificate_errors
,
543 const gchar
*error_text
,
544 gboolean allow_source_save
,
545 GCancellable
*cancellable
,
546 GAsyncReadyCallback callback
,
549 ESourceAuthentication
*extension_authentication
= NULL
;
550 ESourceWebdav
*extension_webdav
= NULL
;
551 SaveSourceData
*save_data
;
552 GTlsCertificate
*certificate
;
557 g_return_if_fail (GTK_IS_WINDOW (parent
));
558 g_return_if_fail (E_IS_SOURCE (source
));
559 g_return_if_fail (certificate_pem
!= NULL
);
561 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_GOA
) ||
562 e_source_has_extension (source
, E_SOURCE_EXTENSION_UOA
)) {
563 /* Make sure that GOA/UOA collection sources contain these extensions too */
564 g_warn_if_fail (e_source_get_extension (source
, E_SOURCE_EXTENSION_AUTHENTICATION
));
565 g_warn_if_fail (e_source_get_extension (source
, E_SOURCE_EXTENSION_WEBDAV_BACKEND
));
568 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_AUTHENTICATION
))
569 extension_authentication
= e_source_get_extension (source
, E_SOURCE_EXTENSION_AUTHENTICATION
);
570 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_WEBDAV_BACKEND
))
571 extension_webdav
= e_source_get_extension (source
, E_SOURCE_EXTENSION_WEBDAV_BACKEND
);
573 save_data
= g_new0 (SaveSourceData
, 1);
574 save_data
->response
= E_TRUST_PROMPT_RESPONSE_UNKNOWN
;
575 save_data
->call_save
= FALSE
;
577 /* Lookup used host name */
578 if (extension_authentication
)
579 host
= e_source_authentication_dup_host (extension_authentication
);
583 if (!host
|| !*host
) {
587 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_GOA
)) {
588 ESourceGoa
*goa_extension
;
591 goa_extension
= e_source_get_extension (source
, E_SOURCE_EXTENSION_GOA
);
593 url
= e_source_goa_dup_calendar_url (goa_extension
);
594 host
= trust_prompt_get_host_from_url (url
);
598 url
= e_source_goa_dup_contacts_url (goa_extension
);
599 host
= trust_prompt_get_host_from_url (url
);
605 certificate
= g_tls_certificate_new_from_pem (certificate_pem
, -1, &save_data
->error
);
607 if (extension_webdav
&& host
)
608 save_data
->response
= e_source_webdav_verify_ssl_trust (extension_webdav
, host
, certificate
, 0);
610 save_data
->response
= E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY
;
612 if (save_data
->response
!= E_TRUST_PROMPT_RESPONSE_REJECT
) {
613 const gchar
*source_extension
= NULL
;
615 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_ADDRESS_BOOK
))
616 source_extension
= E_SOURCE_EXTENSION_ADDRESS_BOOK
;
618 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_CALENDAR
)) {
619 if (!source_extension
)
620 source_extension
= E_SOURCE_EXTENSION_CALENDAR
;
622 source_extension
= E_SOURCE_EXTENSION_COLLECTION
;
625 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_MEMO_LIST
)) {
626 if (!source_extension
)
627 source_extension
= E_SOURCE_EXTENSION_MEMO_LIST
;
629 source_extension
= E_SOURCE_EXTENSION_COLLECTION
;
632 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_TASK_LIST
)) {
633 if (!source_extension
)
634 source_extension
= E_SOURCE_EXTENSION_TASK_LIST
;
636 source_extension
= E_SOURCE_EXTENSION_COLLECTION
;
639 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_MAIL_ACCOUNT
)) {
640 if (!source_extension
)
641 source_extension
= E_SOURCE_EXTENSION_MAIL_ACCOUNT
;
643 source_extension
= E_SOURCE_EXTENSION_COLLECTION
;
646 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_MAIL_TRANSPORT
)) {
647 if (!source_extension
)
648 source_extension
= E_SOURCE_EXTENSION_MAIL_TRANSPORT
;
650 source_extension
= E_SOURCE_EXTENSION_COLLECTION
;
653 save_data
->response
= e_trust_prompt_run_with_dialog_ready_callback (parent
,
654 source_extension
, e_source_get_display_name (source
), host
,
655 certificate_pem
, certificate_errors
, error_text
,
656 trust_prompt_listen_for_source_changes_cb
, source
);
660 g_signal_handlers_disconnect_matched (source
, G_SIGNAL_MATCH_FUNC
, 0, 0, NULL
,
661 source_connection_status_changed_cb
, NULL
);
663 if (save_data
->response
!= E_TRUST_PROMPT_RESPONSE_UNKNOWN
) {
664 if (certificate
&& extension_webdav
) {
665 e_source_webdav_update_ssl_trust (extension_webdav
, host
, certificate
, save_data
->response
);
666 save_data
->call_save
= allow_source_save
;
670 g_clear_object (&certificate
);
673 task
= g_task_new (source
, cancellable
, callback
, user_data
);
674 g_task_set_source_tag (task
, e_trust_prompt_run_for_source
);
675 g_task_set_task_data (task
, save_data
, save_source_data_free
);
677 g_task_run_in_thread (task
, save_source_thread
);
679 g_object_unref (task
);
683 * e_trust_prompt_run_for_source_finish:
684 * @source: an #ESource which was used with e_trust_prompt_run_for_source()
685 * @result: a #GAsyncResult
686 * @response: an output argument, user's response to the trust prompt
687 * @error: return location for a #GError, or %NULL
689 * Finishes the operation started with e_trust_prompt_run_for_source().
690 * The @response will contain a code of the user's choice.
691 * The #E_TRUST_PROMPT_RESPONSE_UNKNOWN is used, when the user cancelled the trust
692 * prompt dialog and no changes are made with the @source.
694 * If an error occurs, the function sets @error and returns %FALSE.
696 * Returns: %TRUE on success, %FALSE on error
701 e_trust_prompt_run_for_source_finish (ESource
*source
,
702 GAsyncResult
*result
,
703 ETrustPromptResponse
*response
,
708 g_return_val_if_fail (E_IS_SOURCE (source
), FALSE
);
709 g_return_val_if_fail (g_task_is_valid (result
, source
), FALSE
);
710 g_return_val_if_fail (response
!= NULL
, FALSE
);
712 g_return_val_if_fail (
713 g_async_result_is_tagged (
714 result
, e_trust_prompt_run_for_source
), FALSE
);
716 success
= g_task_propagate_boolean (G_TASK (result
), error
);
719 SaveSourceData
*save_data
;
721 save_data
= g_task_get_task_data (G_TASK (result
));
722 g_return_val_if_fail (save_data
!= NULL
, FALSE
);
724 *response
= save_data
->response
;