1 /*
2 * e-backend.c
3 *
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.
7 *
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.
12 *
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/>.
15 *
16 */
17
18 /**
19 * SECTION: e-backend
20 * @include: libebackend/libebackend.h
21 * @short_description: An abstract base class for backends
22 *
23 * An #EBackend is paired with an #ESource to facilitate performing
24 * actions on the local or remote resource described by the #ESource.
25 *
26 * In other words, whereas a certain backend type knows how to talk to a
27 * certain type of server or data store, the #ESource fills in configuration
28 * details such as host name, user name, resource path, etc.
29 *
30 * All #EBackend instances are created by an #EBackendFactory.
31 **/
32
33 #include "evolution-data-server-config.h"
34
35 #include <glib/gi18n-lib.h>
36
37 #include <gio/gio.h>
38
39 #include <libedataserver/libedataserver.h>
40
41 #include "e-backend.h"
42 #include "e-user-prompter.h"
43
44 #define G_IS_IO_ERROR(error, code) \
45 (g_error_matches ((error), G_IO_ERROR, (code)))
46
47 #define G_IS_RESOLVER_ERROR(error, code) \
48 (g_error_matches ((error), G_RESOLVER_ERROR, (code)))
49
50 struct _EBackendPrivate {
51 GMutex property_lock;
52 ESource *source;
53 EUserPrompter *prompter;
54 GMainContext *main_context;
55 GSocketConnectable *connectable;
56 gboolean online;
57 gboolean tried_with_empty_credentials;
58
59 GNetworkMonitor *network_monitor;
60 gulong network_changed_handler_id;
61
62 GSource *update_online_state;
63 GMutex update_online_state_lock;
64
65 GMutex network_monitor_cancellable_lock;
66 GCancellable *network_monitor_cancellable;
67
68 GMutex authenticate_lock; /* To not run multiple authenticate requests simultaneously */
69 GMutex authenticate_cancellable_lock;
70 GCancellable *authenticate_cancellable;
71 };
72
73 enum {
74 PROP_0,
75 PROP_CONNECTABLE,
76 PROP_MAIN_CONTEXT,
77 PROP_ONLINE,
78 PROP_SOURCE,
79 PROP_USER_PROMPTER
80 };
81
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(EBackend,e_backend,G_TYPE_OBJECT)82 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (EBackend, e_backend, G_TYPE_OBJECT)
83
84 static void
85 backend_source_unset_last_credentials_required_arguments_cb (GObject *source_object,
86 GAsyncResult *result,
87 gpointer user_data)
88 {
89 GError *local_error = NULL;
90
91 g_return_if_fail (E_IS_SOURCE (source_object));
92
93 e_source_unset_last_credentials_required_arguments_finish (E_SOURCE (source_object), result, &local_error);
94
95 if (local_error)
96 g_debug ("%s: Call failed: %s", G_STRFUNC, local_error->message);
97
98 g_clear_error (&local_error);
99 }
100
101 static void
backend_set_source_disconnected(ESource * source)102 backend_set_source_disconnected (ESource *source)
103 {
104 g_return_if_fail (E_IS_SOURCE (source));
105
106 /* This is to force notification about status change on the client side */
107 if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_DISCONNECTED)
108 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
109
110 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
111
112 e_source_unset_last_credentials_required_arguments (source, NULL,
113 backend_source_unset_last_credentials_required_arguments_cb, NULL);
114 }
115
116 typedef struct _CanReachData {
117 EBackend *backend;
118 GCancellable *cancellable;
119 } CanReachData;
120
121 static void
can_reach_data_free(gpointer ptr)122 can_reach_data_free (gpointer ptr)
123 {
124 CanReachData *crd = ptr;
125
126 if (crd) {
127 g_clear_object (&crd->backend);
128 g_slice_free (CanReachData, crd);
129 }
130 }
131
132 static void
backend_network_monitor_can_reach_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)133 backend_network_monitor_can_reach_cb (GObject *source_object,
134 GAsyncResult *result,
135 gpointer user_data)
136 {
137 CanReachData *crd = user_data;
138 ESource *source;
139 gboolean host_is_reachable;
140 GError *error = NULL;
141
142 g_return_if_fail (crd != NULL);
143
144 host_is_reachable = g_network_monitor_can_reach_finish (
145 G_NETWORK_MONITOR (source_object), result, &error);
146
147 /* Sanity check. */
148 g_return_if_fail (
149 (host_is_reachable && (error == NULL)) ||
150 (!host_is_reachable && (error != NULL)));
151
152 g_mutex_lock (&crd->backend->priv->network_monitor_cancellable_lock);
153 if (crd->backend->priv->network_monitor_cancellable == crd->cancellable)
154 g_clear_object (&crd->backend->priv->network_monitor_cancellable);
155 g_mutex_unlock (&crd->backend->priv->network_monitor_cancellable_lock);
156
157 source = e_backend_get_source (crd->backend);
158
159 if (G_IS_IO_ERROR (error, G_IO_ERROR_CANCELLED) ||
160 host_is_reachable == e_backend_get_online (crd->backend)) {
161 if (!G_IS_IO_ERROR (error, G_IO_ERROR_CANCELLED) && !host_is_reachable) {
162 if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_SSL_FAILED)
163 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
164
165 e_source_unset_last_credentials_required_arguments (source, NULL,
166 backend_source_unset_last_credentials_required_arguments_cb, NULL);
167 }
168
169 g_clear_error (&error);
170 can_reach_data_free (crd);
171 return;
172 }
173
174 g_clear_error (&error);
175
176 if (!host_is_reachable || e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_SSL_FAILED) {
177 backend_set_source_disconnected (source);
178 } else {
179 e_source_unset_last_credentials_required_arguments (source, NULL,
180 backend_source_unset_last_credentials_required_arguments_cb, NULL);
181 }
182
183 e_backend_set_online (crd->backend, host_is_reachable);
184
185 can_reach_data_free (crd);
186 }
187
188 static GSocketConnectable *
backend_ref_connectable_internal(EBackend * backend)189 backend_ref_connectable_internal (EBackend *backend)
190 {
191 GSocketConnectable *connectable;
192
193 g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
194
195 connectable = e_backend_ref_connectable (backend);
196
197 if (!connectable) {
198 gchar *host = NULL;
199 guint16 port = 0;
200
201 if (e_backend_get_destination_address (backend, &host, &port) && host)
202 connectable = g_network_address_new (host, port);
203
204 g_free (host);
205 }
206
207 return connectable;
208 }
209
210 static gboolean
backend_update_online_state_timeout_cb(gpointer user_data)211 backend_update_online_state_timeout_cb (gpointer user_data)
212 {
213 EBackend *backend;
214 GSocketConnectable *connectable;
215 GCancellable *cancellable;
216 GSource *current_source;
217
218 current_source = g_main_current_source ();
219 if (current_source && g_source_is_destroyed (current_source))
220 return FALSE;
221
222 backend = g_weak_ref_get (user_data);
223 if (!backend)
224 return FALSE;
225
226 connectable = backend_ref_connectable_internal (backend);
227
228 g_mutex_lock (&backend->priv->update_online_state_lock);
229 g_source_unref (backend->priv->update_online_state);
230 backend->priv->update_online_state = NULL;
231 g_mutex_unlock (&backend->priv->update_online_state_lock);
232
233 g_mutex_lock (&backend->priv->network_monitor_cancellable_lock);
234
235 cancellable = backend->priv->network_monitor_cancellable;
236 backend->priv->network_monitor_cancellable = NULL;
237
238 if (cancellable != NULL) {
239 g_cancellable_cancel (cancellable);
240 g_object_unref (cancellable);
241 cancellable = NULL;
242 }
243
244 if (connectable == NULL) {
245 backend->priv->network_monitor_cancellable = cancellable;
246 g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);
247
248 if (!e_backend_get_online (backend)) {
249 e_source_unset_last_credentials_required_arguments (e_backend_get_source (backend), NULL,
250 backend_source_unset_last_credentials_required_arguments_cb, NULL);
251 }
252
253 e_backend_set_online (backend, TRUE);
254 } else {
255 CanReachData *crd;
256
257 cancellable = g_cancellable_new ();
258
259 crd = g_slice_new0 (CanReachData);
260 crd->backend = g_object_ref (backend);
261 crd->cancellable = cancellable;
262
263 g_network_monitor_can_reach_async (
264 backend->priv->network_monitor,
265 connectable, cancellable,
266 backend_network_monitor_can_reach_cb,
267 crd);
268
269 backend->priv->network_monitor_cancellable = cancellable;
270 g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);
271 }
272
273 g_clear_object (&connectable);
274 g_clear_object (&backend);
275
276 return FALSE;
277 }
278
279 static void
backend_update_online_state(EBackend * backend)280 backend_update_online_state (EBackend *backend)
281 {
282 GMainContext *main_context;
283 GSource *timeout_source;
284
285 g_mutex_lock (&backend->priv->update_online_state_lock);
286
287 /* Reference the backend before destroying any already scheduled GSource,
288 in case the backend's last reference is held by that GSource. */
289 g_object_ref (backend);
290
291 if (backend->priv->update_online_state) {
292 g_source_destroy (backend->priv->update_online_state);
293 g_source_unref (backend->priv->update_online_state);
294 backend->priv->update_online_state = NULL;
295 }
296
297 main_context = e_backend_ref_main_context (backend);
298
299 timeout_source = g_timeout_source_new_seconds (5);
300 g_source_set_priority (timeout_source, G_PRIORITY_LOW);
301 g_source_set_callback (
302 timeout_source,
303 backend_update_online_state_timeout_cb,
304 e_weak_ref_new (backend), (GDestroyNotify) e_weak_ref_free);
305 g_source_attach (timeout_source, main_context);
306 backend->priv->update_online_state =
307 g_source_ref (timeout_source);
308 g_source_unref (timeout_source);
309
310 g_main_context_unref (main_context);
311
312 g_mutex_unlock (&backend->priv->update_online_state_lock);
313
314 g_object_unref (backend);
315 }
316
317 static void
backend_network_changed_cb(GNetworkMonitor * network_monitor,gboolean network_available,EBackend * backend)318 backend_network_changed_cb (GNetworkMonitor *network_monitor,
319 gboolean network_available,
320 EBackend *backend)
321 {
322 if (network_available) {
323 backend_update_online_state (backend);
324 } else {
325 GSocketConnectable *connectable;
326
327 e_source_unset_last_credentials_required_arguments (e_backend_get_source (backend), NULL,
328 backend_source_unset_last_credentials_required_arguments_cb, NULL);
329
330 connectable = backend_ref_connectable_internal (backend);
331 e_backend_set_online (backend, !connectable);
332 g_clear_object (&connectable);
333 }
334 }
335
336 static ESourceAuthenticationResult
e_backend_authenticate_sync(EBackend * backend,const ENamedParameters * credentials,gchar ** out_certificate_pem,GTlsCertificateFlags * out_certificate_errors,GCancellable * cancellable,GError ** error)337 e_backend_authenticate_sync (EBackend *backend,
338 const ENamedParameters *credentials,
339 gchar **out_certificate_pem,
340 GTlsCertificateFlags *out_certificate_errors,
341 GCancellable *cancellable,
342 GError **error)
343 {
344 EBackendClass *class;
345 ESourceAuthenticationResult res;
346
347 g_return_val_if_fail (E_IS_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR);
348 g_return_val_if_fail (credentials != NULL, E_SOURCE_AUTHENTICATION_ERROR);
349
350 class = E_BACKEND_GET_CLASS (backend);
351 g_return_val_if_fail (class != NULL, E_SOURCE_AUTHENTICATION_ERROR);
352 g_return_val_if_fail (class->authenticate_sync != NULL, E_SOURCE_AUTHENTICATION_ERROR);
353
354 g_mutex_lock (&backend->priv->authenticate_lock);
355
356 if (g_cancellable_set_error_if_cancelled (cancellable, error))
357 res = E_SOURCE_AUTHENTICATION_ERROR;
358 else
359 res = class->authenticate_sync (backend, credentials, out_certificate_pem, out_certificate_errors, cancellable, error);
360
361 g_mutex_unlock (&backend->priv->authenticate_lock);
362
363 return res;
364 }
365
366 typedef struct _AuthenticateThreadData {
367 EBackend *backend;
368 GCancellable *cancellable;
369 ENamedParameters *credentials;
370 } AuthenticateThreadData;
371
372 static AuthenticateThreadData *
authenticate_thread_data_new(EBackend * backend,GCancellable * cancellable,const ENamedParameters * credentials)373 authenticate_thread_data_new (EBackend *backend,
374 GCancellable *cancellable,
375 const ENamedParameters *credentials)
376 {
377 AuthenticateThreadData *data;
378
379 data = g_slice_new0 (AuthenticateThreadData);
380 data->backend = g_object_ref (backend);
381 data->cancellable = g_object_ref (cancellable);
382 data->credentials = credentials ? e_named_parameters_new_clone (credentials) : e_named_parameters_new ();
383
384 return data;
385 }
386
387 static void
authenticate_thread_data_free(AuthenticateThreadData * data)388 authenticate_thread_data_free (AuthenticateThreadData *data)
389 {
390 if (data) {
391 if (data->backend) {
392 g_mutex_lock (&data->backend->priv->authenticate_cancellable_lock);
393 if (data->backend->priv->authenticate_cancellable &&
394 data->backend->priv->authenticate_cancellable == data->cancellable) {
395 g_clear_object (&data->backend->priv->authenticate_cancellable);
396 }
397 g_mutex_unlock (&data->backend->priv->authenticate_cancellable_lock);
398 }
399
400 g_clear_object (&data->backend);
401 g_clear_object (&data->cancellable);
402 e_named_parameters_free (data->credentials);
403 g_slice_free (AuthenticateThreadData, data);
404 }
405 }
406
407 static gpointer
backend_source_authenticate_thread(gpointer user_data)408 backend_source_authenticate_thread (gpointer user_data)
409 {
410 ESourceAuthenticationResult auth_result;
411 AuthenticateThreadData *thread_data = user_data;
412 gchar *certificate_pem = NULL;
413 GTlsCertificateFlags certificate_errors = 0;
414 gboolean empty_crendetials;
415 GError *local_error = NULL;
416 ESource *source;
417
418 g_return_val_if_fail (thread_data != NULL, NULL);
419
420 source = e_backend_get_source (thread_data->backend);
421
422 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
423
424 /* Update the SSL trust transparently. */
425 if (e_named_parameters_get (thread_data->credentials, E_SOURCE_CREDENTIAL_SSL_TRUST) &&
426 e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) {
427 ESourceWebdav *webdav_extension;
428
429 webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
430 e_source_webdav_set_ssl_trust (webdav_extension,
431 e_named_parameters_get (thread_data->credentials, E_SOURCE_CREDENTIAL_SSL_TRUST));
432 }
433
434 auth_result = e_backend_authenticate_sync (thread_data->backend, thread_data->credentials,
435 &certificate_pem, &certificate_errors, thread_data->cancellable, &local_error);
436
437 empty_crendetials = auth_result == E_SOURCE_AUTHENTICATION_REQUIRED &&
438 (!thread_data->credentials || !e_named_parameters_count (thread_data->credentials)) &&
439 !g_cancellable_is_cancelled (thread_data->cancellable);
440
441 if (empty_crendetials && thread_data->backend->priv->tried_with_empty_credentials) {
442 /* When tried repeatedly with empty credentials and both resulted in 'REQUIRED',
443 then change it to 'REJECTED' to avoid loop. */
444 auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
445 }
446
447 thread_data->backend->priv->tried_with_empty_credentials = empty_crendetials;
448
449 if (!g_cancellable_is_cancelled (thread_data->cancellable)) {
450 ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_ERROR;
451
452 switch (auth_result) {
453 case E_SOURCE_AUTHENTICATION_UNKNOWN:
454 case E_SOURCE_AUTHENTICATION_ERROR:
455 reason = E_SOURCE_CREDENTIALS_REASON_ERROR;
456 break;
457 case E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED:
458 reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
459 break;
460 case E_SOURCE_AUTHENTICATION_ACCEPTED:
461 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
462 break;
463 case E_SOURCE_AUTHENTICATION_REQUIRED:
464 reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
465 break;
466 case E_SOURCE_AUTHENTICATION_REJECTED:
467 reason = E_SOURCE_CREDENTIALS_REASON_REJECTED;
468 break;
469 }
470
471 if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
472 const gchar *username = e_named_parameters_get (thread_data->credentials, E_SOURCE_CREDENTIAL_USERNAME);
473 gboolean call_write = FALSE;
474
475 if (username && *username && e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
476 ESourceAuthentication *extension_authentication = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
477
478 if (g_strcmp0 (username, e_source_authentication_get_user (extension_authentication)) != 0) {
479 e_source_authentication_set_user (extension_authentication, username);
480 call_write = TRUE;
481 }
482 }
483
484 if (username && *username && e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
485 ESourceCollection *extension_collection = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
486
487 if (g_strcmp0 (username, e_source_collection_get_identity (extension_collection)) != 0) {
488 e_source_collection_set_identity (extension_collection, username);
489 call_write = TRUE;
490 }
491 }
492
493 if (call_write) {
494 GError *local_error2 = NULL;
495
496 if (!e_source_write_sync (source, thread_data->cancellable, &local_error2)) {
497 g_warning ("%s: Failed to store changed user name on '%s' (%s): %s", G_STRFUNC,
498 e_source_get_display_name (source),
499 e_source_get_uid (source),
500 local_error2 ? local_error2->message : "Unknown error");
501 }
502
503 g_clear_error (&local_error2);
504 }
505 } else {
506 GError *local_error2 = NULL;
507
508 e_source_set_connection_status (source,
509 auth_result == E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED ?
510 E_SOURCE_CONNECTION_STATUS_SSL_FAILED :
511 E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
512
513 if (!e_source_invoke_credentials_required_sync (source, reason, certificate_pem, certificate_errors,
514 local_error, thread_data->cancellable, &local_error2)) {
515 g_warning ("%s: Failed to invoke credentials required for '%s' (%s): %s", G_STRFUNC,
516 e_source_get_display_name (source),
517 e_source_get_uid (source),
518 local_error2 ? local_error2->message : "Unknown error");
519 }
520
521 g_clear_error (&local_error2);
522 }
523 } else {
524 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
525 }
526
527 g_free (certificate_pem);
528 g_clear_error (&local_error);
529
530 authenticate_thread_data_free (thread_data);
531
532 return NULL;
533 }
534
535 static void
backend_source_authenticate_cb(ESource * source,const ENamedParameters * credentials,EBackend * backend)536 backend_source_authenticate_cb (ESource *source,
537 const ENamedParameters *credentials,
538 EBackend *backend)
539 {
540 g_return_if_fail (E_IS_BACKEND (backend));
541 g_return_if_fail (credentials != NULL);
542
543 e_backend_schedule_authenticate (backend, credentials);
544 }
545
546 static void
backend_set_source(EBackend * backend,ESource * source)547 backend_set_source (EBackend *backend,
548 ESource *source)
549 {
550 g_return_if_fail (E_IS_SOURCE (source));
551 g_return_if_fail (backend->priv->source == NULL);
552
553 backend->priv->source = g_object_ref (source);
554
555 g_signal_connect (backend->priv->source, "authenticate", G_CALLBACK (backend_source_authenticate_cb), backend);
556 }
557
558 static void
backend_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)559 backend_set_property (GObject *object,
560 guint property_id,
561 const GValue *value,
562 GParamSpec *pspec)
563 {
564 switch (property_id) {
565 case PROP_CONNECTABLE:
566 e_backend_set_connectable (
567 E_BACKEND (object),
568 g_value_get_object (value));
569 return;
570
571 case PROP_ONLINE:
572 e_backend_set_online (
573 E_BACKEND (object),
574 g_value_get_boolean (value));
575 return;
576
577 case PROP_SOURCE:
578 backend_set_source (
579 E_BACKEND (object),
580 g_value_get_object (value));
581 return;
582 }
583
584 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
585 }
586
587 static void
backend_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)588 backend_get_property (GObject *object,
589 guint property_id,
590 GValue *value,
591 GParamSpec *pspec)
592 {
593 switch (property_id) {
594 case PROP_CONNECTABLE:
595 g_value_take_object (
596 value, e_backend_ref_connectable (
597 E_BACKEND (object)));
598 return;
599
600 case PROP_MAIN_CONTEXT:
601 g_value_take_boxed (
602 value, e_backend_ref_main_context (
603 E_BACKEND (object)));
604 return;
605
606 case PROP_ONLINE:
607 g_value_set_boolean (
608 value, e_backend_get_online (
609 E_BACKEND (object)));
610 return;
611
612 case PROP_SOURCE:
613 g_value_set_object (
614 value, e_backend_get_source (
615 E_BACKEND (object)));
616 return;
617
618 case PROP_USER_PROMPTER:
619 g_value_set_object (
620 value, e_backend_get_user_prompter (
621 E_BACKEND (object)));
622 return;
623 }
624
625 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
626 }
627
628 static void
backend_dispose(GObject * object)629 backend_dispose (GObject *object)
630 {
631 EBackendPrivate *priv;
632
633 priv = E_BACKEND (object)->priv;
634
635 if (priv->network_changed_handler_id > 0) {
636 g_signal_handler_disconnect (
637 priv->network_monitor,
638 priv->network_changed_handler_id);
639 priv->network_changed_handler_id = 0;
640 }
641
642 g_clear_pointer (&priv->main_context, g_main_context_unref);
643
644 if (priv->update_online_state != NULL) {
645 g_source_destroy (priv->update_online_state);
646 g_source_unref (priv->update_online_state);
647 priv->update_online_state = NULL;
648 }
649
650 if (priv->source) {
651 g_signal_handlers_disconnect_by_func (priv->source, backend_source_authenticate_cb, object);
652 backend_set_source_disconnected (priv->source);
653 }
654
655 g_mutex_lock (&priv->authenticate_cancellable_lock);
656 if (priv->authenticate_cancellable) {
657 g_cancellable_cancel (priv->authenticate_cancellable);
658 g_clear_object (&priv->authenticate_cancellable);
659 }
660 g_mutex_unlock (&priv->authenticate_cancellable_lock);
661
662 g_clear_object (&priv->source);
663 g_clear_object (&priv->prompter);
664 g_clear_object (&priv->connectable);
665 g_clear_object (&priv->network_monitor);
666 g_clear_object (&priv->network_monitor_cancellable);
667
668 /* Chain up to parent's dispose() method. */
669 G_OBJECT_CLASS (e_backend_parent_class)->dispose (object);
670 }
671
672 static void
backend_finalize(GObject * object)673 backend_finalize (GObject *object)
674 {
675 EBackendPrivate *priv;
676
677 priv = E_BACKEND (object)->priv;
678
679 g_mutex_clear (&priv->property_lock);
680 g_mutex_clear (&priv->update_online_state_lock);
681 g_mutex_clear (&priv->network_monitor_cancellable_lock);
682 g_mutex_clear (&priv->authenticate_lock);
683 g_mutex_clear (&priv->authenticate_cancellable_lock);
684
685 /* Chain up to parent's finalize() method. */
686 G_OBJECT_CLASS (e_backend_parent_class)->finalize (object);
687 }
688
689 static void
backend_constructed(GObject * object)690 backend_constructed (GObject *object)
691 {
692 EBackend *backend;
693 ESource *source;
694 const gchar *extension_name;
695
696 backend = E_BACKEND (object);
697
698 /* Chain up to parent's constructed() method. */
699 G_OBJECT_CLASS (e_backend_parent_class)->constructed (object);
700
701 /* Get an initial GSocketConnectable from the data
702 * source's [Authentication] extension, if present. */
703 source = e_backend_get_source (backend);
704 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
705 if (e_source_has_extension (source, extension_name)) {
706 ESourceAuthentication *extension;
707
708 extension = e_source_get_extension (source, extension_name);
709
710 backend->priv->connectable =
711 e_source_authentication_ref_connectable (extension);
712
713 backend_update_online_state (backend);
714 }
715 }
716
717 static ESourceAuthenticationResult
backend_authenticate_sync(EBackend * backend,const ENamedParameters * credentials,gchar ** out_certificate_pem,GTlsCertificateFlags * out_certificate_errors,GCancellable * cancellable,GError ** error)718 backend_authenticate_sync (EBackend *backend,
719 const ENamedParameters *credentials,
720 gchar **out_certificate_pem,
721 GTlsCertificateFlags *out_certificate_errors,
722 GCancellable *cancellable,
723 GError **error)
724 {
725 /* The default implementation just reports success, it's for backends
726 which do not use (nor define) authentication routines, because
727 they use different methods to get to the credentials. */
728
729 return E_SOURCE_AUTHENTICATION_ACCEPTED;
730 }
731
732 static gboolean
backend_get_destination_address(EBackend * backend,gchar ** host,guint16 * port)733 backend_get_destination_address (EBackend *backend,
734 gchar **host,
735 guint16 *port)
736 {
737 GSocketConnectable *connectable;
738 GNetworkAddress *address;
739
740 g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
741 g_return_val_if_fail (host != NULL, FALSE);
742 g_return_val_if_fail (port != NULL, FALSE);
743
744 connectable = e_backend_ref_connectable (backend);
745 if (!connectable)
746 return FALSE;
747
748 if (!G_IS_NETWORK_ADDRESS (connectable)) {
749 g_object_unref (connectable);
750 return FALSE;
751 }
752
753 address = G_NETWORK_ADDRESS (connectable);
754
755 *host = g_strdup (g_network_address_get_hostname (address));
756 *port = g_network_address_get_port (address);
757
758 g_object_unref (connectable);
759
760 return *host != NULL;
761 }
762
763 static void
backend_prepare_shutdown(EBackend * backend)764 backend_prepare_shutdown (EBackend *backend)
765 {
766 }
767
768 static void
e_backend_class_init(EBackendClass * class)769 e_backend_class_init (EBackendClass *class)
770 {
771 GObjectClass *object_class;
772
773 object_class = G_OBJECT_CLASS (class);
774 object_class->set_property = backend_set_property;
775 object_class->get_property = backend_get_property;
776 object_class->dispose = backend_dispose;
777 object_class->finalize = backend_finalize;
778 object_class->constructed = backend_constructed;
779
780 class->authenticate_sync = backend_authenticate_sync;
781 class->get_destination_address = backend_get_destination_address;
782 class->prepare_shutdown = backend_prepare_shutdown;
783
784 g_object_class_install_property (
785 object_class,
786 PROP_CONNECTABLE,
787 g_param_spec_object (
788 "connectable",
789 "Connectable",
790 "Socket endpoint of a network service",
791 G_TYPE_SOCKET_CONNECTABLE,
792 G_PARAM_READWRITE |
793 G_PARAM_EXPLICIT_NOTIFY |
794 G_PARAM_STATIC_STRINGS));
795
796 g_object_class_install_property (
797 object_class,
798 PROP_MAIN_CONTEXT,
799 g_param_spec_boxed (
800 "main-context",
801 "Main Context",
802 "The main loop context on "
803 "which to attach event sources",
804 G_TYPE_MAIN_CONTEXT,
805 G_PARAM_READABLE |
806 G_PARAM_STATIC_STRINGS));
807
808 g_object_class_install_property (
809 object_class,
810 PROP_ONLINE,
811 g_param_spec_boolean (
812 "online",
813 "Online",
814 "Whether the backend is online",
815 TRUE,
816 G_PARAM_READWRITE |
817 G_PARAM_EXPLICIT_NOTIFY |
818 G_PARAM_STATIC_STRINGS));
819
820 g_object_class_install_property (
821 object_class,
822 PROP_SOURCE,
823 g_param_spec_object (
824 "source",
825 "Source",
826 "The data source being acted upon",
827 E_TYPE_SOURCE,
828 G_PARAM_READWRITE |
829 G_PARAM_CONSTRUCT_ONLY |
830 G_PARAM_STATIC_STRINGS));
831
832 g_object_class_install_property (
833 object_class,
834 PROP_USER_PROMPTER,
835 g_param_spec_object (
836 "user-prompter",
837 "User Prompter",
838 "User prompter instance",
839 E_TYPE_USER_PROMPTER,
840 G_PARAM_READABLE |
841 G_PARAM_STATIC_STRINGS));
842 }
843
844 static void
e_backend_init(EBackend * backend)845 e_backend_init (EBackend *backend)
846 {
847 GNetworkMonitor *network_monitor;
848 gulong handler_id;
849
850 backend->priv = e_backend_get_instance_private (backend);
851 backend->priv->prompter = e_user_prompter_new ();
852 backend->priv->main_context = g_main_context_ref_thread_default ();
853 backend->priv->tried_with_empty_credentials = FALSE;
854
855 g_mutex_init (&backend->priv->property_lock);
856 g_mutex_init (&backend->priv->update_online_state_lock);
857 g_mutex_init (&backend->priv->network_monitor_cancellable_lock);
858 g_mutex_init (&backend->priv->authenticate_lock);
859 g_mutex_init (&backend->priv->authenticate_cancellable_lock);
860
861 backend->priv->authenticate_cancellable = NULL;
862
863 /* Configure network monitoring. */
864
865 network_monitor = e_network_monitor_get_default ();
866 backend->priv->network_monitor = g_object_ref (network_monitor);
867 backend->priv->online = g_network_monitor_get_network_available (network_monitor);
868
869 handler_id = g_signal_connect (
870 backend->priv->network_monitor, "network-changed",
871 G_CALLBACK (backend_network_changed_cb), backend);
872 backend->priv->network_changed_handler_id = handler_id;
873 }
874
875 /**
876 * e_backend_get_online:
877 * @backend: an #EBackend
878 *
879 * Returns the online state of @backend: %TRUE if @backend is online,
880 * %FALSE if offline.
881 *
882 * If the #EBackend:connectable property is non-%NULL, the @backend will
883 * automatically determine whether the network service should be reachable,
884 * and hence whether the @backend is #EBackend:online. But subclasses may
885 * override the online state if, for example, a connection attempt fails.
886 *
887 * Returns: the online state
888 *
889 * Since: 3.4
890 **/
891 gboolean
e_backend_get_online(EBackend * backend)892 e_backend_get_online (EBackend *backend)
893 {
894 g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
895
896 return backend->priv->online;
897 }
898
899 /**
900 * e_backend_set_online:
901 * @backend: an #EBackend
902 * @online: the online state
903 *
904 * Sets the online state of @backend: %TRUE if @backend is online,
905 * @FALSE if offline.
906 *
907 * If the #EBackend:connectable property is non-%NULL, the @backend will
908 * automatically determine whether the network service should be reachable,
909 * and hence whether the @backend is #EBackend:online. But subclasses may
910 * override the online state if, for example, a connection attempt fails.
911 *
912 * Since: 3.4
913 **/
914 void
e_backend_set_online(EBackend * backend,gboolean online)915 e_backend_set_online (EBackend *backend,
916 gboolean online)
917 {
918 g_return_if_fail (E_IS_BACKEND (backend));
919
920 /* Avoid unnecessary "notify" signals. */
921 if (backend->priv->online == online)
922 return;
923
924 backend->priv->online = online;
925
926 /* Cancel any automatic "online" state update in progress. */
927 g_mutex_lock (&backend->priv->network_monitor_cancellable_lock);
928 g_cancellable_cancel (backend->priv->network_monitor_cancellable);
929 g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);
930
931 g_object_notify (G_OBJECT (backend), "online");
932
933 if (!backend->priv->online && backend->priv->source)
934 backend_set_source_disconnected (backend->priv->source);
935 }
936
937 /**
938 * e_backend_ensure_online_state_updated:
939 * @backend: an #EBackend
940 * @cancellable: optional #GCancellable object, or %NULL
941 *
942 * Makes sure that the "online" property is updated, that is, if there
943 * is any destination reachability test pending, it'll be done immediately
944 * and the only state will be updated as well.
945 *
946 * Since: 3.18
947 **/
948 void
e_backend_ensure_online_state_updated(EBackend * backend,GCancellable * cancellable)949 e_backend_ensure_online_state_updated (EBackend *backend,
950 GCancellable *cancellable)
951 {
952 gboolean needs_update = FALSE;
953
954 g_return_if_fail (E_IS_BACKEND (backend));
955
956 g_object_ref (backend);
957
958 g_mutex_lock (&backend->priv->update_online_state_lock);
959
960 if (backend->priv->update_online_state) {
961 g_source_destroy (backend->priv->update_online_state);
962 g_source_unref (backend->priv->update_online_state);
963 backend->priv->update_online_state = NULL;
964
965 needs_update = TRUE;
966 }
967
968 g_mutex_unlock (&backend->priv->update_online_state_lock);
969
970 if (!needs_update) {
971 g_mutex_lock (&backend->priv->network_monitor_cancellable_lock);
972 needs_update = backend->priv->network_monitor_cancellable != NULL;
973 g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);
974 }
975
976 if (needs_update)
977 e_backend_set_online (backend, e_backend_is_destination_reachable (backend, cancellable, NULL));
978
979 g_object_unref (backend);
980 }
981
982 /**
983 * e_backend_get_source:
984 * @backend: an #EBackend
985 *
986 * Returns the #ESource to which @backend is paired.
987 *
988 * Returns: (transfer none): the #ESource to which @backend is paired
989 *
990 * Since: 3.4
991 **/
992 ESource *
e_backend_get_source(EBackend * backend)993 e_backend_get_source (EBackend *backend)
994 {
995 g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
996
997 return backend->priv->source;
998 }
999
1000 /**
1001 * e_backend_ref_connectable:
1002 * @backend: an #EBackend
1003 *
1004 * Returns the socket endpoint for the network service to which @backend
1005 * is a client, or %NULL if @backend does not use network sockets.
1006 *
1007 * The initial value of the #EBackend:connectable property is derived from
1008 * the #ESourceAuthentication extension of the @backend's #EBackend:source
1009 * property, if the extension is present.
1010 *
1011 * The returned #GSocketConnectable is referenced for thread-safety and
1012 * must be unreferenced with g_object_unref() when finished with it.
1013 *
1014 * Returns: (transfer full) (nullable): a #GSocketConnectable, or %NULL
1015 *
1016 * Since: 3.8
1017 **/
1018 GSocketConnectable *
e_backend_ref_connectable(EBackend * backend)1019 e_backend_ref_connectable (EBackend *backend)
1020 {
1021 GSocketConnectable *connectable = NULL;
1022
1023 g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
1024
1025 g_mutex_lock (&backend->priv->property_lock);
1026
1027 if (backend->priv->connectable != NULL)
1028 connectable = g_object_ref (backend->priv->connectable);
1029
1030 g_mutex_unlock (&backend->priv->property_lock);
1031
1032 return connectable;
1033 }
1034
1035 /**
1036 * e_backend_set_connectable:
1037 * @backend: an #EBackend
1038 * @connectable: a #GSocketConnectable, or %NULL
1039 *
1040 * Sets the socket endpoint for the network service to which @backend is
1041 * a client. This can be %NULL if @backend does not use network sockets.
1042 *
1043 * The initial value of the #EBackend:connectable property is derived from
1044 * the #ESourceAuthentication extension of the @backend's #EBackend:source
1045 * property, if the extension is present.
1046 *
1047 * Since: 3.8
1048 **/
1049 void
e_backend_set_connectable(EBackend * backend,GSocketConnectable * connectable)1050 e_backend_set_connectable (EBackend *backend,
1051 GSocketConnectable *connectable)
1052 {
1053 g_return_if_fail (E_IS_BACKEND (backend));
1054
1055 if (connectable != NULL) {
1056 g_return_if_fail (G_IS_SOCKET_CONNECTABLE (connectable));
1057 g_object_ref (connectable);
1058 }
1059
1060 g_mutex_lock (&backend->priv->property_lock);
1061
1062 if (backend->priv->connectable != NULL)
1063 g_object_unref (backend->priv->connectable);
1064
1065 backend->priv->connectable = connectable;
1066
1067 g_mutex_unlock (&backend->priv->property_lock);
1068
1069 backend_update_online_state (backend);
1070
1071 g_object_notify (G_OBJECT (backend), "connectable");
1072 }
1073
1074 /**
1075 * e_backend_ref_main_context:
1076 * @backend: an #EBackend
1077 *
1078 * Returns the #GMainContext on which event sources for @backend are to
1079 * be attached.
1080 *
1081 * The returned #GMainContext is referenced for thread-safety and must be
1082 * unreferenced with g_main_context_unref() when finished with it.
1083 *
1084 * Returns: (transfer full): a #GMainContext
1085 *
1086 * Since: 3.8
1087 **/
1088 GMainContext *
e_backend_ref_main_context(EBackend * backend)1089 e_backend_ref_main_context (EBackend *backend)
1090 {
1091 g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
1092
1093 return g_main_context_ref (backend->priv->main_context);
1094 }
1095
1096 /**
1097 * e_backend_credentials_required_sync:
1098 * @backend: an #EBackend
1099 * @reason: an #ESourceCredentialsReason, why the credentials are required
1100 * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
1101 * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
1102 * @op_error: (nullable): a #GError with a description of the previous credentials error, or %NULL
1103 * @cancellable: optional #GCancellable object, or %NULL
1104 * @error: return location for a #GError, or %NULL
1105 *
1106 * Synchronously lets the clients know that the backned requires credentials to be
1107 * properly opened. It's a proxy function for e_source_invoke_credentials_required_sync(),
1108 * where can be found more information about actual parameters meaning.
1109 *
1110 * The provided credentials are received through #EBackendClass.authenticate_sync()
1111 * method asynchronously.
1112 *
1113 * If an error occurs, the function sets @error and returns %FALSE.
1114 *
1115 * Returns: %TRUE on success, %FALSE on error
1116 *
1117 * Since: 3.16
1118 **/
1119 gboolean
e_backend_credentials_required_sync(EBackend * backend,ESourceCredentialsReason reason,const gchar * certificate_pem,GTlsCertificateFlags certificate_errors,const GError * op_error,GCancellable * cancellable,GError ** error)1120 e_backend_credentials_required_sync (EBackend *backend,
1121 ESourceCredentialsReason reason,
1122 const gchar *certificate_pem,
1123 GTlsCertificateFlags certificate_errors,
1124 const GError *op_error,
1125 GCancellable *cancellable,
1126 GError **error)
1127 {
1128 ESource *source;
1129
1130 g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
1131
1132 source = e_backend_get_source (backend);
1133 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1134
1135 return e_source_invoke_credentials_required_sync (source,
1136 reason, certificate_pem, certificate_errors, op_error, cancellable, error);
1137 }
1138
1139 typedef struct _CredentialsRequiredData {
1140 ESourceCredentialsReason reason;
1141 gchar *certificate_pem;
1142 GTlsCertificateFlags certificate_errors;
1143 GError *op_error;
1144 } CredentialsRequiredData;
1145
1146 static void
credentials_required_data_free(gpointer ptr)1147 credentials_required_data_free (gpointer ptr)
1148 {
1149 CredentialsRequiredData *data = ptr;
1150
1151 if (data) {
1152 g_free (data->certificate_pem);
1153 g_clear_error (&data->op_error);
1154 g_slice_free (CredentialsRequiredData, data);
1155 }
1156 }
1157
1158 static void
backend_credentials_required_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1159 backend_credentials_required_thread (GTask *task,
1160 gpointer source_object,
1161 gpointer task_data,
1162 GCancellable *cancellable)
1163 {
1164 CredentialsRequiredData *data = task_data;
1165 gboolean success;
1166 GError *local_error = NULL;
1167
1168 success = e_backend_credentials_required_sync (
1169 E_BACKEND (source_object), data->reason, data->certificate_pem,
1170 data->certificate_errors, data->op_error,
1171 cancellable, &local_error);
1172
1173 if (local_error != NULL) {
1174 g_task_return_error (task, local_error);
1175 } else {
1176 g_task_return_boolean (task, success);
1177 }
1178 }
1179
1180 /**
1181 * e_backend_credentials_required:
1182 * @backend: an #EBackend
1183 * @reason: an #ESourceCredentialsReason, why the credentials are required
1184 * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
1185 * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
1186 * @op_error: (nullable): a #GError with a description of the previous credentials error, or %NULL
1187 * @cancellable: optional #GCancellable object, or %NULL
1188 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1189 * @user_data: data to pass to the callback function
1190 *
1191 * Asynchronously calls the e_backend_credentials_required_sync() on the @backend,
1192 * to inform clients that credentials are required.
1193 *
1194 * When the operation is finished, @callback will be called. You can then
1195 * call e_backend_credentials_required_finish() to get the result of the operation.
1196 *
1197 * Since: 3.16
1198 **/
1199 void
e_backend_credentials_required(EBackend * backend,ESourceCredentialsReason reason,const gchar * certificate_pem,GTlsCertificateFlags certificate_errors,const GError * op_error,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1200 e_backend_credentials_required (EBackend *backend,
1201 ESourceCredentialsReason reason,
1202 const gchar *certificate_pem,
1203 GTlsCertificateFlags certificate_errors,
1204 const GError *op_error,
1205 GCancellable *cancellable,
1206 GAsyncReadyCallback callback,
1207 gpointer user_data)
1208 {
1209 CredentialsRequiredData *data;
1210 GTask *task;
1211
1212 g_return_if_fail (E_IS_BACKEND (backend));
1213
1214 data = g_slice_new0 (CredentialsRequiredData);
1215 data->reason = reason;
1216 data->certificate_pem = g_strdup (certificate_pem);
1217 data->certificate_errors = certificate_errors;
1218 data->op_error = op_error ? g_error_copy (op_error) : NULL;
1219
1220 task = g_task_new (backend, cancellable, callback, user_data);
1221 g_task_set_source_tag (task, e_backend_credentials_required);
1222 g_task_set_task_data (task, data, credentials_required_data_free);
1223
1224 g_task_run_in_thread (task, backend_credentials_required_thread);
1225
1226 g_object_unref (task);
1227 }
1228
1229 /**
1230 * e_backend_credentials_required_finish:
1231 * @backend: an #EBackend
1232 * @result: a #GAsyncResult
1233 * @error: return location for a #GError, or %NULL
1234 *
1235 * Finishes the operation started with e_backend_credentials_required().
1236 *
1237 * If an error occurs, the function sets @error and returns %FALSE.
1238 *
1239 * Returns: %TRUE on success, %FALSE on error
1240 *
1241 * Since: 3.16
1242 **/
1243 gboolean
e_backend_credentials_required_finish(EBackend * backend,GAsyncResult * result,GError ** error)1244 e_backend_credentials_required_finish (EBackend *backend,
1245 GAsyncResult *result,
1246 GError **error)
1247 {
1248 g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
1249 g_return_val_if_fail (g_task_is_valid (result, backend), FALSE);
1250
1251 g_return_val_if_fail (
1252 g_async_result_is_tagged (
1253 result, e_backend_credentials_required), FALSE);
1254
1255 return g_task_propagate_boolean (G_TASK (result), error);
1256 }
1257
1258 static void
backend_scheduled_credentials_required_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1259 backend_scheduled_credentials_required_done_cb (GObject *source_object,
1260 GAsyncResult *result,
1261 gpointer user_data)
1262 {
1263 GError *error = NULL;
1264 gchar *who_calls = user_data;
1265
1266 g_return_if_fail (E_IS_BACKEND (source_object));
1267
1268 if (!e_backend_credentials_required_finish (E_BACKEND (source_object), result, &error) &&
1269 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1270 ESource *source = e_backend_get_source (E_BACKEND (source_object));
1271
1272 g_warning ("%s: Failed to invoke credentials required on '%s' (%s): %s", who_calls ? who_calls : G_STRFUNC,
1273 e_source_get_display_name (source),
1274 e_source_get_uid (source),
1275 error ? error->message : "Unknown error");
1276 }
1277
1278 g_clear_error (&error);
1279 g_free (who_calls);
1280 }
1281
1282 /**
1283 * e_backend_schedule_credentials_required:
1284 * @backend: an #EBackend
1285 * @reason: an #ESourceCredentialsReason, why the credentials are required
1286 * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
1287 * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
1288 * @op_error: (nullable): a #GError with a description of the previous credentials error, or %NULL
1289 * @cancellable: optional #GCancellable object, or %NULL
1290 * @who_calls: (nullable): an identification who calls this
1291 *
1292 * Asynchronously invokes e_backend_credentials_required(), but installs its
1293 * own callback which only prints a runtime warning on the console when
1294 * the call fails. The @who_calls is a prefix of the console message.
1295 * This is useful when the caller just wants to start the operation
1296 * without having actual place where to show the operation result.
1297 *
1298 * Since: 3.16
1299 **/
1300 void
e_backend_schedule_credentials_required(EBackend * backend,ESourceCredentialsReason reason,const gchar * certificate_pem,GTlsCertificateFlags certificate_errors,const GError * op_error,GCancellable * cancellable,const gchar * who_calls)1301 e_backend_schedule_credentials_required (EBackend *backend,
1302 ESourceCredentialsReason reason,
1303 const gchar *certificate_pem,
1304 GTlsCertificateFlags certificate_errors,
1305 const GError *op_error,
1306 GCancellable *cancellable,
1307 const gchar *who_calls)
1308 {
1309 g_return_if_fail (E_IS_BACKEND (backend));
1310
1311 e_backend_credentials_required (backend, reason, certificate_pem, certificate_errors,
1312 op_error, cancellable, backend_scheduled_credentials_required_done_cb, g_strdup (who_calls));
1313 }
1314
1315 /**
1316 * e_backend_schedule_authenticate:
1317 * @backend: an #EBackend
1318 * @credentials: (nullable): a credentials to use to authenticate, or %NULL
1319 *
1320 * Schedules a new authenticate session, cancelling any previously run.
1321 * This is usually done automatically, when an 'authenticate' signal is
1322 * received for the associated #ESource. With %NULL @credentials an attempt
1323 * without it is run.
1324 *
1325 * Since: 3.16
1326 **/
1327 void
e_backend_schedule_authenticate(EBackend * backend,const ENamedParameters * credentials)1328 e_backend_schedule_authenticate (EBackend *backend,
1329 const ENamedParameters *credentials)
1330 {
1331 GCancellable *cancellable;
1332 AuthenticateThreadData *thread_data;
1333
1334 g_return_if_fail (E_IS_BACKEND (backend));
1335
1336 g_mutex_lock (&backend->priv->authenticate_cancellable_lock);
1337 if (backend->priv->authenticate_cancellable) {
1338 g_cancellable_cancel (backend->priv->authenticate_cancellable);
1339 g_clear_object (&backend->priv->authenticate_cancellable);
1340 }
1341
1342 backend->priv->authenticate_cancellable = g_cancellable_new ();
1343 cancellable = g_object_ref (backend->priv->authenticate_cancellable);
1344
1345 g_mutex_unlock (&backend->priv->authenticate_cancellable_lock);
1346
1347 thread_data = authenticate_thread_data_new (backend, cancellable, credentials);
1348
1349 g_thread_unref (g_thread_new (NULL, backend_source_authenticate_thread, thread_data));
1350
1351 g_clear_object (&cancellable);
1352 }
1353
1354 /**
1355 * e_backend_ensure_source_status_connected:
1356 * @backend: an #EBackend
1357 *
1358 * Makes sure that the associated ESource::connection-status is connected. This is
1359 * useful in cases when the backend can connect to the destination without invoking
1360 * #EBackendClass.authenticate_sync(), possibly through e_backend_schedule_authenticate().
1361 *
1362 * Since: 3.18
1363 **/
1364 void
e_backend_ensure_source_status_connected(EBackend * backend)1365 e_backend_ensure_source_status_connected (EBackend *backend)
1366 {
1367 ESource *source;
1368
1369 g_return_if_fail (E_IS_BACKEND (backend));
1370
1371 source = e_backend_get_source (backend);
1372
1373 g_return_if_fail (E_IS_SOURCE (source));
1374
1375 if (e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_CONNECTED)
1376 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
1377 }
1378
1379 /**
1380 * e_backend_get_user_prompter:
1381 * @backend: an #EBackend
1382 *
1383 * Gets an instance of #EUserPrompter, associated with this @backend.
1384 *
1385 * The returned instance is owned by the @backend.
1386 *
1387 * Returns: (transfer none): an #EUserPrompter instance
1388 *
1389 * Since: 3.8
1390 **/
1391 EUserPrompter *
e_backend_get_user_prompter(EBackend * backend)1392 e_backend_get_user_prompter (EBackend *backend)
1393 {
1394 g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
1395
1396 return backend->priv->prompter;
1397 }
1398
1399 /**
1400 * e_backend_trust_prompt_sync:
1401 * @backend: an #EBackend
1402 * @parameters: an #ENamedParameters with values for the trust prompt
1403 * @cancellable: optional #GCancellable object, or %NULL
1404 * @error: return location for a #GError, or %NULL
1405 *
1406 * Asks a user a trust prompt with given @parameters, and returns what
1407 * user responded. This blocks until the response is delivered.
1408 *
1409 * Returns: an #ETrustPromptResponse what user responded
1410 *
1411 * Note: The function can return also %E_TRUST_PROMPT_RESPONSE_UNKNOWN,
1412 * it's on error or if user closes the trust prompt dialog with other
1413 * than the offered buttons. Usual behaviour in such case is to treat
1414 * it as a temporary reject.
1415 *
1416 * Since: 3.8
1417 **/
1418 ETrustPromptResponse
e_backend_trust_prompt_sync(EBackend * backend,const ENamedParameters * parameters,GCancellable * cancellable,GError ** error)1419 e_backend_trust_prompt_sync (EBackend *backend,
1420 const ENamedParameters *parameters,
1421 GCancellable *cancellable,
1422 GError **error)
1423 {
1424 EUserPrompter *prompter;
1425 gint response;
1426
1427 g_return_val_if_fail (
1428 E_IS_BACKEND (backend), E_TRUST_PROMPT_RESPONSE_UNKNOWN);
1429 g_return_val_if_fail (
1430 parameters != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
1431
1432 prompter = e_backend_get_user_prompter (backend);
1433 g_return_val_if_fail (
1434 prompter != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
1435
1436 response = e_user_prompter_extension_prompt_sync (
1437 prompter, "ETrustPrompt::trust-prompt",
1438 parameters, NULL, cancellable, error);
1439
1440 if (response == 0)
1441 return E_TRUST_PROMPT_RESPONSE_REJECT;
1442 if (response == 1)
1443 return E_TRUST_PROMPT_RESPONSE_ACCEPT;
1444 if (response == 2)
1445 return E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY;
1446 if (response == -1)
1447 return E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY;
1448
1449 return E_TRUST_PROMPT_RESPONSE_UNKNOWN;
1450 }
1451
1452 /**
1453 * e_backend_trust_prompt:
1454 * @backend: an #EBackend
1455 * @parameters: an #ENamedParameters with values for the trust prompt
1456 * @cancellable: optional #GCancellable object, or %NULL
1457 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1458 * @user_data: data to pass to the callback function
1459 *
1460 * Initiates a user trust prompt with given @parameters.
1461 *
1462 * When the operation is finished, @callback will be called. You can then
1463 * call e_backend_trust_prompt_finish() to get the result of the operation.
1464 *
1465 * Since: 3.8
1466 **/
1467 void
e_backend_trust_prompt(EBackend * backend,const ENamedParameters * parameters,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1468 e_backend_trust_prompt (EBackend *backend,
1469 const ENamedParameters *parameters,
1470 GCancellable *cancellable,
1471 GAsyncReadyCallback callback,
1472 gpointer user_data)
1473 {
1474 EUserPrompter *prompter;
1475
1476 g_return_if_fail (E_IS_BACKEND (backend));
1477 g_return_if_fail (parameters != NULL);
1478
1479 prompter = e_backend_get_user_prompter (backend);
1480 g_return_if_fail (prompter != NULL);
1481
1482 e_user_prompter_extension_prompt (
1483 prompter, "ETrustPrompt::trust-prompt",
1484 parameters, cancellable, callback, user_data);
1485 }
1486
1487 /**
1488 * e_backend_trust_prompt_finish:
1489 * @backend: an #EBackend
1490 * @result: a #GAsyncResult
1491 * @error: return location for a #GError, or %NULL
1492 *
1493 * Finishes the operation started with e_backend_trust_prompt().
1494 * If an error occurred, the function will set @error and return
1495 * %E_TRUST_PROMPT_RESPONSE_UNKNOWN.
1496 *
1497 * Returns: an #ETrustPromptResponse what user responded
1498 *
1499 * Note: The function can return also %E_TRUST_PROMPT_RESPONSE_UNKNOWN,
1500 * it's on error or if user closes the trust prompt dialog with other
1501 * than the offered buttons. Usual behaviour in such case is to treat
1502 * it as a temporary reject.
1503 *
1504 * Since: 3.8
1505 **/
1506 ETrustPromptResponse
e_backend_trust_prompt_finish(EBackend * backend,GAsyncResult * result,GError ** error)1507 e_backend_trust_prompt_finish (EBackend *backend,
1508 GAsyncResult *result,
1509 GError **error)
1510 {
1511 EUserPrompter *prompter;
1512 gint response;
1513
1514 g_return_val_if_fail (
1515 E_IS_BACKEND (backend), E_TRUST_PROMPT_RESPONSE_UNKNOWN);
1516
1517 prompter = e_backend_get_user_prompter (backend);
1518 g_return_val_if_fail (
1519 prompter != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
1520
1521 response = e_user_prompter_extension_prompt_finish (
1522 prompter, result, NULL, error);
1523
1524 if (response == 0)
1525 return E_TRUST_PROMPT_RESPONSE_REJECT;
1526 if (response == 1)
1527 return E_TRUST_PROMPT_RESPONSE_ACCEPT;
1528 if (response == 2)
1529 return E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY;
1530 if (response == -1)
1531 return E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY;
1532
1533 return E_TRUST_PROMPT_RESPONSE_UNKNOWN;
1534 }
1535
1536 /**
1537 * e_backend_get_destination_address:
1538 * @backend: an #EBackend instance
1539 * @host: (out): destination server host name
1540 * @port: (out): destination server port
1541 *
1542 * Provides destination server host name and port to which
1543 * the backend connects. This is used to determine required
1544 * connection point for e_backend_is_destination_reachable().
1545 * The @host is a newly allocated string, which will be freed
1546 * with g_free(). When @backend sets both @host and @port, then
1547 * it should return %TRUE, indicating it's a remote backend.
1548 * Default implementation returns %FALSE, which is treated
1549 * like the backend is local, no checking for server reachability
1550 * is possible.
1551 *
1552 * Returns: %TRUE, when it's a remote backend and provides both
1553 * @host and @port; %FALSE otherwise.
1554 *
1555 * Since: 3.8
1556 **/
1557 gboolean
e_backend_get_destination_address(EBackend * backend,gchar ** host,guint16 * port)1558 e_backend_get_destination_address (EBackend *backend,
1559 gchar **host,
1560 guint16 *port)
1561 {
1562 EBackendClass *klass;
1563
1564 g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
1565 g_return_val_if_fail (host != NULL, FALSE);
1566 g_return_val_if_fail (port != NULL, FALSE);
1567
1568 klass = E_BACKEND_GET_CLASS (backend);
1569 g_return_val_if_fail (klass != NULL, FALSE);
1570 g_return_val_if_fail (klass->get_destination_address != NULL, FALSE);
1571
1572 return klass->get_destination_address (backend, host, port);
1573 }
1574
1575 /**
1576 * e_backend_is_destination_reachable:
1577 * @backend: an #EBackend instance
1578 * @cancellable: a #GCancellable instance, or %NULL
1579 * @error: a #GError for errors, or %NULL
1580 *
1581 * Checks whether the @backend<!-- -->'s destination server, as returned
1582 * by e_backend_get_destination_address(), is reachable.
1583 * If the e_backend_get_destination_address() returns %FALSE, this function
1584 * returns %TRUE, meaning the destination is always reachable.
1585 * This uses #GNetworkMonitor<!-- -->'s g_network_monitor_can_reach()
1586 * for reachability tests.
1587 *
1588 * Returns: %TRUE, when destination server address is reachable or
1589 * the backend doesn't provide destination address; %FALSE if
1590 * the backend destination server cannot be reached currently.
1591 *
1592 * Since: 3.8
1593 **/
1594 gboolean
e_backend_is_destination_reachable(EBackend * backend,GCancellable * cancellable,GError ** error)1595 e_backend_is_destination_reachable (EBackend *backend,
1596 GCancellable *cancellable,
1597 GError **error)
1598 {
1599 gboolean reachable = TRUE;
1600 gchar *host = NULL;
1601 guint16 port = 0;
1602
1603 g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
1604
1605 if (e_backend_get_destination_address (backend, &host, &port)) {
1606 g_warn_if_fail (host != NULL);
1607
1608 if (host) {
1609 GNetworkMonitor *network_monitor;
1610 GSocketConnectable *connectable;
1611
1612 network_monitor = backend->priv->network_monitor;
1613
1614 connectable = g_network_address_new (host, port);
1615 if (connectable) {
1616 reachable = g_network_monitor_can_reach (
1617 network_monitor, connectable,
1618 cancellable, error);
1619 g_object_unref (connectable);
1620 } else {
1621 reachable = FALSE;
1622 }
1623 }
1624 }
1625
1626 g_free (host);
1627
1628 return reachable;
1629 }
1630
1631 /**
1632 * e_backend_prepare_shutdown:
1633 * @backend: an #EBackend instance
1634 *
1635 * Let's the @backend know that it'll be shut down shortly, no client connects
1636 * to it anymore. The @backend can free any resources which reference it, for
1637 * example the opened views.
1638 *
1639 * Since: 3.16
1640 */
1641 void
e_backend_prepare_shutdown(EBackend * backend)1642 e_backend_prepare_shutdown (EBackend *backend)
1643 {
1644 EBackendClass *class;
1645
1646 g_return_if_fail (E_IS_BACKEND (backend));
1647
1648 class = E_BACKEND_GET_CLASS (backend);
1649 g_return_if_fail (class != NULL);
1650 g_return_if_fail (class->prepare_shutdown != NULL);
1651
1652 g_object_ref (backend);
1653
1654 class->prepare_shutdown (backend);
1655
1656 g_object_unref (backend);
1657 }
1658