updated changelog
[platform/upstream/evolution-data-server.git] / libebackend / e-backend.c
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 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 <config.h>
34 #include <glib/gi18n-lib.h>
35
36 #include <gio/gio.h>
37
38 #include <libedataserver/libedataserver.h>
39
40 #include "e-backend.h"
41 #include "e-user-prompter.h"
42
43 #define E_BACKEND_GET_PRIVATE(obj) \
44         (G_TYPE_INSTANCE_GET_PRIVATE \
45         ((obj), E_TYPE_BACKEND, EBackendPrivate))
46
47 #define G_IS_IO_ERROR(error, code) \
48         (g_error_matches ((error), G_IO_ERROR, (code)))
49
50 #define G_IS_RESOLVER_ERROR(error, code) \
51         (g_error_matches ((error), G_RESOLVER_ERROR, (code)))
52
53 typedef struct _AsyncContext AsyncContext;
54
55 struct _EBackendPrivate {
56         GMutex property_lock;
57         ESource *source;
58         EUserPrompter *prompter;
59         GMainContext *main_context;
60         GSocketConnectable *connectable;
61         gboolean online;
62
63         GNetworkMonitor *network_monitor;
64         gulong network_changed_handler_id;
65
66         GSource *update_online_state;
67         GMutex update_online_state_lock;
68
69         GMutex network_monitor_cancellable_lock;
70         GCancellable *network_monitor_cancellable;
71 };
72
73 struct _AsyncContext {
74         ESourceAuthenticator *auth;
75 };
76
77 enum {
78         PROP_0,
79         PROP_CONNECTABLE,
80         PROP_MAIN_CONTEXT,
81         PROP_ONLINE,
82         PROP_SOURCE,
83         PROP_USER_PROMPTER
84 };
85
86 G_DEFINE_ABSTRACT_TYPE (EBackend, e_backend, G_TYPE_OBJECT)
87
88 static void
89 async_context_free (AsyncContext *async_context)
90 {
91         if (async_context->auth != NULL)
92                 g_object_unref (async_context->auth);
93
94         g_slice_free (AsyncContext, async_context);
95 }
96
97 static void
98 backend_network_monitor_can_reach_cb (GObject *source_object,
99                                       GAsyncResult *result,
100                                       gpointer user_data)
101 {
102         EBackend *backend = E_BACKEND (user_data);
103         gboolean host_is_reachable;
104         GError *error = NULL;
105
106         host_is_reachable = g_network_monitor_can_reach_finish (
107                 G_NETWORK_MONITOR (source_object), result, &error);
108
109         /* Sanity check. */
110         g_return_if_fail (
111                 (host_is_reachable && (error == NULL)) ||
112                 (!host_is_reachable && (error != NULL)));
113
114         if (G_IS_IO_ERROR (error, G_IO_ERROR_CANCELLED) ||
115             host_is_reachable == e_backend_get_online (backend)) {
116                 g_clear_error (&error);
117                 g_object_unref (backend);
118                 return;
119         }
120
121         g_clear_error (&error);
122
123         e_backend_set_online (backend, host_is_reachable);
124
125         g_object_unref (backend);
126 }
127
128 static gboolean
129 backend_update_online_state_idle_cb (gpointer user_data)
130 {
131         EBackend *backend;
132         GSocketConnectable *connectable;
133         GCancellable *cancellable;
134
135         backend = E_BACKEND (user_data);
136         connectable = e_backend_ref_connectable (backend);
137
138         g_mutex_lock (&backend->priv->update_online_state_lock);
139         g_source_unref (backend->priv->update_online_state);
140         backend->priv->update_online_state = NULL;
141         g_mutex_unlock (&backend->priv->update_online_state_lock);
142
143         g_mutex_lock (&backend->priv->network_monitor_cancellable_lock);
144
145         cancellable = backend->priv->network_monitor_cancellable;
146         backend->priv->network_monitor_cancellable = NULL;
147
148         if (cancellable != NULL) {
149                 g_cancellable_cancel (cancellable);
150                 g_object_unref (cancellable);
151                 cancellable = NULL;
152         }
153
154         if (!connectable) {
155                 gchar *host = NULL;
156                 guint16 port = 0;
157
158                 if (e_backend_get_destination_address (backend, &host, &port) && host)
159                         connectable = g_network_address_new (host, port);
160
161                 g_free (host);
162         }
163
164         if (connectable == NULL) {
165                 e_backend_set_online (backend, TRUE);
166         } else {
167                 cancellable = g_cancellable_new ();
168
169                 g_network_monitor_can_reach_async (
170                         backend->priv->network_monitor,
171                         connectable, cancellable,
172                         backend_network_monitor_can_reach_cb,
173                         g_object_ref (backend));
174         }
175
176         backend->priv->network_monitor_cancellable = cancellable;
177
178         g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);
179
180         if (connectable != NULL)
181                 g_object_unref (connectable);
182
183         return FALSE;
184 }
185
186 static void
187 backend_update_online_state (EBackend *backend)
188 {
189         g_mutex_lock (&backend->priv->update_online_state_lock);
190
191         if (backend->priv->update_online_state == NULL) {
192                 GMainContext *main_context;
193                 GSource *idle_source;
194
195                 main_context = e_backend_ref_main_context (backend);
196
197                 idle_source = g_idle_source_new ();
198                 g_source_set_priority (idle_source, G_PRIORITY_LOW);
199                 g_source_set_callback (
200                         idle_source,
201                         backend_update_online_state_idle_cb,
202                         g_object_ref (backend),
203                         (GDestroyNotify) g_object_unref);
204                 g_source_attach (idle_source, main_context);
205                 backend->priv->update_online_state =
206                         g_source_ref (idle_source);
207                 g_source_unref (idle_source);
208
209                 g_main_context_unref (main_context);
210         }
211
212         g_mutex_unlock (&backend->priv->update_online_state_lock);
213 }
214
215 static void
216 backend_network_changed_cb (GNetworkMonitor *network_monitor,
217                             gboolean network_available,
218                             EBackend *backend)
219 {
220         backend_update_online_state (backend);
221 }
222
223 static void
224 backend_set_source (EBackend *backend,
225                     ESource *source)
226 {
227         g_return_if_fail (E_IS_SOURCE (source));
228         g_return_if_fail (backend->priv->source == NULL);
229
230         backend->priv->source = g_object_ref (source);
231 }
232
233 static void
234 backend_set_property (GObject *object,
235                       guint property_id,
236                       const GValue *value,
237                       GParamSpec *pspec)
238 {
239         switch (property_id) {
240                 case PROP_CONNECTABLE:
241                         e_backend_set_connectable (
242                                 E_BACKEND (object),
243                                 g_value_get_object (value));
244                         return;
245
246                 case PROP_ONLINE:
247                         e_backend_set_online (
248                                 E_BACKEND (object),
249                                 g_value_get_boolean (value));
250                         return;
251
252                 case PROP_SOURCE:
253                         backend_set_source (
254                                 E_BACKEND (object),
255                                 g_value_get_object (value));
256                         return;
257         }
258
259         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
260 }
261
262 static void
263 backend_get_property (GObject *object,
264                       guint property_id,
265                       GValue *value,
266                       GParamSpec *pspec)
267 {
268         switch (property_id) {
269                 case PROP_CONNECTABLE:
270                         g_value_take_object (
271                                 value, e_backend_ref_connectable (
272                                 E_BACKEND (object)));
273                         return;
274
275                 case PROP_MAIN_CONTEXT:
276                         g_value_take_boxed (
277                                 value, e_backend_ref_main_context (
278                                 E_BACKEND (object)));
279                         return;
280
281                 case PROP_ONLINE:
282                         g_value_set_boolean (
283                                 value, e_backend_get_online (
284                                 E_BACKEND (object)));
285                         return;
286
287                 case PROP_SOURCE:
288                         g_value_set_object (
289                                 value, e_backend_get_source (
290                                 E_BACKEND (object)));
291                         return;
292
293                 case PROP_USER_PROMPTER:
294                         g_value_set_object (
295                                 value, e_backend_get_user_prompter (
296                                 E_BACKEND (object)));
297                         return;
298         }
299
300         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
301 }
302
303 static void
304 backend_dispose (GObject *object)
305 {
306         EBackendPrivate *priv;
307
308         priv = E_BACKEND_GET_PRIVATE (object);
309
310         if (priv->network_changed_handler_id > 0) {
311                 g_signal_handler_disconnect (
312                         priv->network_monitor,
313                         priv->network_changed_handler_id);
314                 priv->network_changed_handler_id = 0;
315         }
316
317         if (priv->main_context != NULL) {
318                 g_main_context_unref (priv->main_context);
319                 priv->main_context = NULL;
320         }
321
322         if (priv->update_online_state != NULL) {
323                 g_source_destroy (priv->update_online_state);
324                 g_source_unref (priv->update_online_state);
325                 priv->update_online_state = NULL;
326         }
327
328         g_clear_object (&priv->source);
329         g_clear_object (&priv->prompter);
330         g_clear_object (&priv->connectable);
331         g_clear_object (&priv->network_monitor);
332         g_clear_object (&priv->network_monitor_cancellable);
333
334         /* Chain up to parent's dispose() method. */
335         G_OBJECT_CLASS (e_backend_parent_class)->dispose (object);
336 }
337
338 static void
339 backend_finalize (GObject *object)
340 {
341         EBackendPrivate *priv;
342
343         priv = E_BACKEND_GET_PRIVATE (object);
344
345         g_mutex_clear (&priv->property_lock);
346         g_mutex_clear (&priv->update_online_state_lock);
347         g_mutex_clear (&priv->network_monitor_cancellable_lock);
348
349         /* Chain up to parent's finalize() method. */
350         G_OBJECT_CLASS (e_backend_parent_class)->finalize (object);
351 }
352
353 static void
354 backend_constructed (GObject *object)
355 {
356         EBackend *backend;
357         ESource *source;
358         const gchar *extension_name;
359
360         backend = E_BACKEND (object);
361
362         /* Chain up to parent's constructed() method. */
363         G_OBJECT_CLASS (e_backend_parent_class)->constructed (object);
364
365         /* Get an initial GSocketConnectable from the data
366          * source's [Authentication] extension, if present. */
367         source = e_backend_get_source (backend);
368         extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
369         if (e_source_has_extension (source, extension_name)) {
370                 ESourceAuthentication *extension;
371
372                 extension = e_source_get_extension (source, extension_name);
373
374                 backend->priv->connectable =
375                         e_source_authentication_ref_connectable (extension);
376
377                 backend_update_online_state (backend);
378         }
379 }
380
381 static void
382 backend_authenticate_thread (GSimpleAsyncResult *simple,
383                              GObject *object,
384                              GCancellable *cancellable)
385 {
386         AsyncContext *async_context;
387         GError *error = NULL;
388
389         async_context = g_simple_async_result_get_op_res_gpointer (simple);
390
391         e_backend_authenticate_sync (
392                 E_BACKEND (object),
393                 async_context->auth,
394                 cancellable, &error);
395
396         if (error != NULL)
397                 g_simple_async_result_take_error (simple, error);
398 }
399
400 static gboolean
401 backend_authenticate_sync (EBackend *backend,
402                            ESourceAuthenticator *auth,
403                            GCancellable *cancellable,
404                            GError **error)
405 {
406         g_set_error (
407                 error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
408                 _("%s does not support authentication"),
409                 G_OBJECT_TYPE_NAME (backend));
410
411         return FALSE;
412 }
413
414 static void
415 backend_authenticate (EBackend *backend,
416                       ESourceAuthenticator *auth,
417                       GCancellable *cancellable,
418                       GAsyncReadyCallback callback,
419                       gpointer user_data)
420 {
421         GSimpleAsyncResult *simple;
422         AsyncContext *async_context;
423
424         async_context = g_slice_new0 (AsyncContext);
425         async_context->auth = g_object_ref (auth);
426
427         simple = g_simple_async_result_new (
428                 G_OBJECT (backend), callback,
429                 user_data, backend_authenticate);
430
431         g_simple_async_result_set_check_cancellable (simple, cancellable);
432
433         g_simple_async_result_set_op_res_gpointer (
434                 simple, async_context, (GDestroyNotify) async_context_free);
435
436         g_simple_async_result_run_in_thread (
437                 simple, backend_authenticate_thread,
438                 G_PRIORITY_DEFAULT, cancellable);
439
440         g_object_unref (simple);
441 }
442
443 static gboolean
444 backend_authenticate_finish (EBackend *backend,
445                              GAsyncResult *result,
446                              GError **error)
447 {
448         GSimpleAsyncResult *simple;
449
450         g_return_val_if_fail (
451                 g_simple_async_result_is_valid (
452                 result, G_OBJECT (backend),
453                 backend_authenticate), FALSE);
454
455         simple = G_SIMPLE_ASYNC_RESULT (result);
456
457         /* Assume success unless a GError is set. */
458         return !g_simple_async_result_propagate_error (simple, error);
459 }
460
461 static gboolean
462 backend_get_destination_address (EBackend *backend,
463                                  gchar **host,
464                                  guint16 *port)
465 {
466         /* default implementation returns FALSE, indicating
467          * no remote destination being used for this backend */
468         return FALSE;
469 }
470
471 static void
472 e_backend_class_init (EBackendClass *class)
473 {
474         GObjectClass *object_class;
475
476         g_type_class_add_private (class, sizeof (EBackendPrivate));
477
478         object_class = G_OBJECT_CLASS (class);
479         object_class->set_property = backend_set_property;
480         object_class->get_property = backend_get_property;
481         object_class->dispose = backend_dispose;
482         object_class->finalize = backend_finalize;
483         object_class->constructed = backend_constructed;
484
485         class->authenticate_sync = backend_authenticate_sync;
486         class->authenticate = backend_authenticate;
487         class->authenticate_finish = backend_authenticate_finish;
488         class->get_destination_address = backend_get_destination_address;
489
490         g_object_class_install_property (
491                 object_class,
492                 PROP_CONNECTABLE,
493                 g_param_spec_object (
494                         "connectable",
495                         "Connectable",
496                         "Socket endpoint of a network service",
497                         G_TYPE_SOCKET_CONNECTABLE,
498                         G_PARAM_READWRITE |
499                         G_PARAM_STATIC_STRINGS));
500
501         g_object_class_install_property (
502                 object_class,
503                 PROP_MAIN_CONTEXT,
504                 g_param_spec_boxed (
505                         "main-context",
506                         "Main Context",
507                         "The main loop context on "
508                         "which to attach event sources",
509                         G_TYPE_MAIN_CONTEXT,
510                         G_PARAM_READABLE |
511                         G_PARAM_STATIC_STRINGS));
512
513         g_object_class_install_property (
514                 object_class,
515                 PROP_ONLINE,
516                 g_param_spec_boolean (
517                         "online",
518                         "Online",
519                         "Whether the backend is online",
520                         TRUE,
521                         G_PARAM_READWRITE |
522                         G_PARAM_CONSTRUCT |
523                         G_PARAM_STATIC_STRINGS));
524
525         g_object_class_install_property (
526                 object_class,
527                 PROP_SOURCE,
528                 g_param_spec_object (
529                         "source",
530                         "Source",
531                         "The data source being acted upon",
532                         E_TYPE_SOURCE,
533                         G_PARAM_READWRITE |
534                         G_PARAM_CONSTRUCT_ONLY |
535                         G_PARAM_STATIC_STRINGS));
536
537         g_object_class_install_property (
538                 object_class,
539                 PROP_USER_PROMPTER,
540                 g_param_spec_object (
541                         "user-prompter",
542                         "User Prompter",
543                         "User prompter instance",
544                         E_TYPE_USER_PROMPTER,
545                         G_PARAM_READABLE |
546                         G_PARAM_STATIC_STRINGS));
547 }
548
549 static void
550 e_backend_init (EBackend *backend)
551 {
552         GNetworkMonitor *network_monitor;
553         gulong handler_id;
554
555         backend->priv = E_BACKEND_GET_PRIVATE (backend);
556         backend->priv->prompter = e_user_prompter_new ();
557         backend->priv->main_context = g_main_context_ref_thread_default ();
558
559         g_mutex_init (&backend->priv->property_lock);
560         g_mutex_init (&backend->priv->update_online_state_lock);
561         g_mutex_init (&backend->priv->network_monitor_cancellable_lock);
562
563         /* Configure network monitoring. */
564
565         network_monitor = g_network_monitor_get_default ();
566         backend->priv->network_monitor = g_object_ref (network_monitor);
567
568         handler_id = g_signal_connect (
569                 backend->priv->network_monitor, "network-changed",
570                 G_CALLBACK (backend_network_changed_cb), backend);
571         backend->priv->network_changed_handler_id = handler_id;
572 }
573
574 /**
575  * e_backend_get_online:
576  * @backend: an #EBackend
577  *
578  * Returns the online state of @backend: %TRUE if @backend is online,
579  * %FALSE if offline.
580  *
581  * If the #EBackend:connectable property is non-%NULL, the @backend will
582  * automatically determine whether the network service should be reachable,
583  * and hence whether the @backend is #EBackend:online.  But subclasses may
584  * override the online state if, for example, a connection attempt fails.
585  *
586  * Returns: the online state
587  *
588  * Since: 3.4
589  **/
590 gboolean
591 e_backend_get_online (EBackend *backend)
592 {
593         g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
594
595         return backend->priv->online;
596 }
597
598 /**
599  * e_backend_set_online:
600  * @backend: an #EBackend
601  * @online: the online state
602  *
603  * Sets the online state of @backend: %TRUE if @backend is online,
604  * @FALSE if offline.
605  *
606  * If the #EBackend:connectable property is non-%NULL, the @backend will
607  * automatically determine whether the network service should be reachable,
608  * and hence whether the @backend is #EBackend:online.  But subclasses may
609  * override the online state if, for example, a connection attempt fails.
610  *
611  * Since: 3.4
612  **/
613 void
614 e_backend_set_online (EBackend *backend,
615                       gboolean online)
616 {
617         g_return_if_fail (E_IS_BACKEND (backend));
618
619         /* Avoid unnecessary "notify" signals. */
620         if (backend->priv->online == online)
621                 return;
622
623         backend->priv->online = online;
624
625         /* Cancel any automatic "online" state update in progress. */
626         g_mutex_lock (&backend->priv->network_monitor_cancellable_lock);
627         g_cancellable_cancel (backend->priv->network_monitor_cancellable);
628         g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);
629
630         g_object_notify (G_OBJECT (backend), "online");
631 }
632
633 /**
634  * e_backend_get_source:
635  * @backend: an #EBackend
636  *
637  * Returns the #ESource to which @backend is paired.
638  *
639  * Returns: the #ESource to which @backend is paired
640  *
641  * Since: 3.4
642  **/
643 ESource *
644 e_backend_get_source (EBackend *backend)
645 {
646         g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
647
648         return backend->priv->source;
649 }
650
651 /**
652  * e_backend_ref_connectable:
653  * @backend: an #EBackend
654  *
655  * Returns the socket endpoint for the network service to which @backend
656  * is a client, or %NULL if @backend does not use network sockets.
657  *
658  * The initial value of the #EBackend:connectable property is derived from
659  * the #ESourceAuthentication extension of the @backend's #EBackend:source
660  * property, if the extension is present.
661  *
662  * The returned #GSocketConnectable is referenced for thread-safety and
663  * must be unreferenced with g_object_unref() when finished with it.
664  *
665  * Returns: a #GSocketConnectable, or %NULL
666  *
667  * Since: 3.8
668  **/
669 GSocketConnectable *
670 e_backend_ref_connectable (EBackend *backend)
671 {
672         GSocketConnectable *connectable = NULL;
673
674         g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
675
676         g_mutex_lock (&backend->priv->property_lock);
677
678         if (backend->priv->connectable != NULL)
679                 connectable = g_object_ref (backend->priv->connectable);
680
681         g_mutex_unlock (&backend->priv->property_lock);
682
683         return connectable;
684 }
685
686 /**
687  * e_backend_set_connectable:
688  * @backend: an #EBackend
689  * @connectable: a #GSocketConnectable, or %NULL
690  *
691  * Sets the socket endpoint for the network service to which @backend is
692  * a client.  This can be %NULL if @backend does not use network sockets.
693  *
694  * The initial value of the #EBackend:connectable property is derived from
695  * the #ESourceAuthentication extension of the @backend's #EBackend:source
696  * property, if the extension is present.
697  *
698  * Since: 3.8
699  **/
700 void
701 e_backend_set_connectable (EBackend *backend,
702                            GSocketConnectable *connectable)
703 {
704         g_return_if_fail (E_IS_BACKEND (backend));
705
706         if (connectable != NULL) {
707                 g_return_if_fail (G_IS_SOCKET_CONNECTABLE (connectable));
708                 g_object_ref (connectable);
709         }
710
711         g_mutex_lock (&backend->priv->property_lock);
712
713         if (backend->priv->connectable != NULL)
714                 g_object_unref (backend->priv->connectable);
715
716         backend->priv->connectable = connectable;
717
718         g_mutex_unlock (&backend->priv->property_lock);
719
720         backend_update_online_state (backend);
721
722         g_object_notify (G_OBJECT (backend), "connectable");
723 }
724
725 /**
726  * e_backend_ref_main_context:
727  * @backend: an #EBackend
728  *
729  * Returns the #GMainContext on which event sources for @backend are to
730  * be attached.
731  *
732  * The returned #GMainContext is referenced for thread-safety and must be
733  * unreferenced with g_main_context_unref() when finished with it.
734  *
735  * Returns: (transfer full): a #GMainContext
736  *
737  * Since: 3.8
738  **/
739 GMainContext *
740 e_backend_ref_main_context (EBackend *backend)
741 {
742         g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
743
744         return g_main_context_ref (backend->priv->main_context);
745 }
746
747 /**
748  * e_backend_authenticate_sync:
749  * @backend: an #EBackend
750  * @auth: an #ESourceAuthenticator
751  * @cancellable: optional #GCancellable object, or %NULL
752  * @error: return location for a #GError, or %NULL
753  *
754  * Convenience function providing a consistent authentication interface
755  * for backends running in either the registry service itself or a client
756  * process communicating with the registry service over D-Bus.
757  *
758  * Authenticates @backend's #EBackend:source, using @auth to handle
759  * authentication attempts.  The @backend and @auth arguments may be one
760  * and the same if @backend implements the #ESourceAuthenticator interface.
761  * The operation loops until authentication is successful or the user aborts
762  * further authentication attempts.  If an error occurs, the function will
763  * set @error and return %FALSE.
764  *
765  * Returns: %TRUE on success, %FALSE on failure
766  *
767  * Since: 3.6
768  **/
769 gboolean
770 e_backend_authenticate_sync (EBackend *backend,
771                              ESourceAuthenticator *auth,
772                              GCancellable *cancellable,
773                              GError **error)
774 {
775         EBackendClass *class;
776
777         g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
778         g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth), FALSE);
779
780         class = E_BACKEND_GET_CLASS (backend);
781         g_return_val_if_fail (class->authenticate_sync != NULL, FALSE);
782
783         return class->authenticate_sync (backend, auth, cancellable, error);
784 }
785
786 /**
787  * e_backend_authenticate:
788  * @backend: an #EBackend
789  * @auth: an #ESourceAuthenticator
790  * @cancellable: optional #GCancellable object, or %NULL
791  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
792  * @user_data: data to pass to the callback function
793  *
794  * Convenience function providing a consistent authentication interface
795  * for backends running in either the registry service itself or a client
796  * process communicating with the registry service over D-Bus.
797  *
798  * Asynchronously authenticates @backend's #EBackend:source, using @auth
799  * to handle authentication attempts.  The @backend and @auth arguments may
800  * be one and the same if @backend implements the #ESourceAuthenticator
801  * interface.  The operation loops until authentication is succesful or the
802  * user aborts further authentication attempts.
803  *
804  * When the operation is finished, @callback will be called.  You can then
805  * call e_backend_authenticate_finish() to get the result of the operation.
806  *
807  * Since: 3.6
808  **/
809 void
810 e_backend_authenticate (EBackend *backend,
811                         ESourceAuthenticator *auth,
812                         GCancellable *cancellable,
813                         GAsyncReadyCallback callback,
814                         gpointer user_data)
815 {
816         EBackendClass *class;
817
818         g_return_if_fail (E_IS_BACKEND (backend));
819         g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
820
821         class = E_BACKEND_GET_CLASS (backend);
822         g_return_if_fail (class->authenticate != NULL);
823
824         class->authenticate (backend, auth, cancellable, callback, user_data);
825 }
826
827 /**
828  * e_backend_authenticate_finish:
829  * @backend: an #EBackend
830  * @result: a #GAsyncResult
831  * @error: return location for a #GError, or %NULL
832  *
833  * Finishes the operation started with e_backend_authenticate().  If
834  * an error occurred, the function will set @error and return %FALSE.
835  *
836  * Returns: %TRUE on success, %FALSE on failure
837  *
838  * Since: 3.6
839  **/
840 gboolean
841 e_backend_authenticate_finish (EBackend *backend,
842                                GAsyncResult *result,
843                                GError **error)
844 {
845         EBackendClass *class;
846
847         g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
848         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
849
850         class = E_BACKEND_GET_CLASS (backend);
851         g_return_val_if_fail (class->authenticate_finish != NULL, FALSE);
852
853         return class->authenticate_finish (backend, result, error);
854 }
855
856 /**
857  * e_backend_get_user_prompter:
858  * @backend: an #EBackend
859  *
860  * Gets an instance of #EUserPrompter, associated with this @backend.
861  *
862  * The returned instance is owned by the @backend.
863  *
864  * Returns: (transfer none): an #EUserPrompter instance
865  *
866  * Since: 3.8
867  **/
868 EUserPrompter *
869 e_backend_get_user_prompter (EBackend *backend)
870 {
871         g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
872
873         return backend->priv->prompter;
874 }
875
876 /**
877  * e_backend_trust_prompt_sync:
878  * @backend: an #EBackend
879  * @parameters: an #ENamedParameters with values for the trust prompt
880  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
881  * @error: return location for a #GError, or %NULL
882  *
883  * Asks a user a trust prompt with given @parameters, and returns what
884  * user responded. This blocks until the response is delivered.
885  *
886  * Returns: an #ETrustPromptResponse what user responded
887  *
888  * Note: The function can return also %E_TRUST_PROMPT_RESPONSE_UNKNOWN,
889  *    it's on error or if user closes the trust prompt dialog with other
890  *    than the offered buttons. Usual behaviour in such case is to treat
891  *    it as a temporary reject.
892  *
893  * Since: 3.8
894  **/
895 ETrustPromptResponse
896 e_backend_trust_prompt_sync (EBackend *backend,
897                              const ENamedParameters *parameters,
898                              GCancellable *cancellable,
899                              GError **error)
900 {
901         EUserPrompter *prompter;
902         gint response;
903
904         g_return_val_if_fail (
905                 E_IS_BACKEND (backend), E_TRUST_PROMPT_RESPONSE_UNKNOWN);
906         g_return_val_if_fail (
907                 parameters != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
908
909         prompter = e_backend_get_user_prompter (backend);
910         g_return_val_if_fail (
911                 prompter != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
912
913         response = e_user_prompter_extension_prompt_sync (
914                 prompter, "ETrustPrompt::trust-prompt",
915                 parameters, NULL, cancellable, error);
916
917         if (response == 0)
918                 return E_TRUST_PROMPT_RESPONSE_REJECT;
919         if (response == 1)
920                 return E_TRUST_PROMPT_RESPONSE_ACCEPT;
921         if (response == 2)
922                 return E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY;
923         if (response == -1)
924                 return E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY;
925
926         return E_TRUST_PROMPT_RESPONSE_UNKNOWN;
927 }
928
929 /**
930  * e_backend_trust_prompt:
931  * @backend: an #EBackend
932  * @parameters: an #ENamedParameters with values for the trust prompt
933  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
934  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
935  * @user_data: data to pass to the callback function
936  *
937  * Initiates a user trust prompt with given @parameters.
938  *
939  * When the operation is finished, @callback will be called. You can then
940  * call e_backend_trust_prompt_finish() to get the result of the operation.
941  *
942  * Since: 3.8
943  **/
944 void
945 e_backend_trust_prompt (EBackend *backend,
946                         const ENamedParameters *parameters,
947                         GCancellable *cancellable,
948                         GAsyncReadyCallback callback,
949                         gpointer user_data)
950 {
951         EUserPrompter *prompter;
952
953         g_return_if_fail (E_IS_BACKEND (backend));
954         g_return_if_fail (parameters != NULL);
955
956         prompter = e_backend_get_user_prompter (backend);
957         g_return_if_fail (prompter != NULL);
958
959         e_user_prompter_extension_prompt (
960                 prompter, "ETrustPrompt::trust-prompt",
961                 parameters, cancellable, callback, user_data);
962 }
963
964 /**
965  * e_backend_trust_prompt_finish:
966  * @backend: an #EBackend
967  * @result: a #GAsyncResult
968  * @error: return location for a #GError, or %NULL
969  *
970  * Finishes the operation started with e_backend_trust_prompt().
971  * If an error occurred, the function will set @error and return
972  * %E_TRUST_PROMPT_RESPONSE_UNKNOWN.
973  *
974  * Returns: an #ETrustPromptResponse what user responded
975  *
976  * Note: The function can return also %E_TRUST_PROMPT_RESPONSE_UNKNOWN,
977  *    it's on error or if user closes the trust prompt dialog with other
978  *    than the offered buttons. Usual behaviour in such case is to treat
979  *    it as a temporary reject.
980  *
981  * Since: 3.8
982  **/
983 ETrustPromptResponse
984 e_backend_trust_prompt_finish (EBackend *backend,
985                                GAsyncResult *result,
986                                GError **error)
987 {
988         EUserPrompter *prompter;
989         gint response;
990
991         g_return_val_if_fail (
992                 E_IS_BACKEND (backend), E_TRUST_PROMPT_RESPONSE_UNKNOWN);
993
994         prompter = e_backend_get_user_prompter (backend);
995         g_return_val_if_fail (
996                 prompter != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
997
998         response = e_user_prompter_extension_prompt_finish (
999                 prompter, result, NULL, error);
1000
1001         if (response == 0)
1002                 return E_TRUST_PROMPT_RESPONSE_REJECT;
1003         if (response == 1)
1004                 return E_TRUST_PROMPT_RESPONSE_ACCEPT;
1005         if (response == 2)
1006                 return E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY;
1007         if (response == -1)
1008                 return E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY;
1009
1010         return E_TRUST_PROMPT_RESPONSE_UNKNOWN;
1011 }
1012
1013 /**
1014  * e_backend_get_destination_address:
1015  * @backend: an #EBackend instance
1016  * @host: (out): destination server host name
1017  * @port: (out): destination server port
1018  *
1019  * Provides destination server host name and port to which
1020  * the backend connects. This is used to determine required
1021  * connection point for e_backend_destination_is_reachable().
1022  * The @host is a newly allocated string, which will be freed
1023  * with g_free(). When @backend sets both @host and @port, then
1024  * it should return %TRUE, indicating it's a remote backend.
1025  * Default implementation returns %FALSE, which is treated
1026  * like the backend is local, no checking for server reachability
1027  * is possible.
1028  *
1029  * Returns: %TRUE, when it's a remote backend and provides both
1030  *   @host and @port; %FALSE otherwise.
1031  *
1032  * Since: 3.8
1033  **/
1034 gboolean
1035 e_backend_get_destination_address (EBackend *backend,
1036                                    gchar **host,
1037                                    guint16 *port)
1038 {
1039         EBackendClass *klass;
1040
1041         g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
1042         g_return_val_if_fail (host != NULL, FALSE);
1043         g_return_val_if_fail (port != NULL, FALSE);
1044
1045         klass = E_BACKEND_GET_CLASS (backend);
1046         g_return_val_if_fail (klass->get_destination_address != NULL, FALSE);
1047
1048         return klass->get_destination_address (backend, host, port);
1049 }
1050
1051 /**
1052  * e_backend_is_destination_reachable:
1053  * @backend: an #EBackend instance
1054  * @cancellable: a #GCancellable instance, or %NULL
1055  * @error: a #GError for errors, or %NULL
1056  *
1057  * Checks whether the @backend<!-- -->'s destination server, as returned
1058  * by e_backend_get_destination_address(), is reachable.
1059  * If the e_backend_get_destination_address() returns %FALSE, this function
1060  * returns %TRUE, meaning the destination is always reachable.
1061  * This uses #GNetworkMonitor<!-- -->'s g_network_monitor_can_reach()
1062  * for reachability tests.
1063  *
1064  * Returns: %TRUE, when destination server address is reachable or
1065  *    the backend doesn't provide destination address; %FALSE if
1066  *    the backend destination server cannot be reached currently.
1067  *
1068  * Since: 3.8
1069  **/
1070 gboolean
1071 e_backend_is_destination_reachable (EBackend *backend,
1072                                     GCancellable *cancellable,
1073                                     GError **error)
1074 {
1075         gboolean reachable = TRUE;
1076         gchar *host = NULL;
1077         guint16 port = 0;
1078
1079         g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
1080
1081         if (e_backend_get_destination_address (backend, &host, &port)) {
1082                 g_warn_if_fail (host != NULL);
1083
1084                 if (host) {
1085                         GNetworkMonitor *network_monitor;
1086                         GSocketConnectable *connectable;
1087
1088                         network_monitor = backend->priv->network_monitor;
1089
1090                         connectable = g_network_address_new (host, port);
1091                         if (connectable) {
1092                                 reachable = g_network_monitor_can_reach (
1093                                         network_monitor, connectable,
1094                                         cancellable, error);
1095                                 g_object_unref (connectable);
1096                         } else {
1097                                 reachable = FALSE;
1098                         }
1099                 }
1100         }
1101
1102         g_free (host);
1103
1104         return reachable;
1105 }