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