updated changelog
[platform/upstream/evolution-data-server.git] / libedataserver / e-client.c
1 /*
2  * e-client.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  * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
18  *
19  */
20
21 /* TODO The next time we have a good excuse to break libedataserver's API,
22  *      I'd like to purge all the deprecated cruft here and convert EClient
23  *      from a GObjectClass to a GTypeInterface, implemented by EBookClient
24  *      and ECalClient.  Then we could just bind the "online", "readonly"
25  *      and "capabilities" properties to equivalent GDBusProxy properties
26  *      and kill e-client-private.h.  Would simplify things.  --mbarnes
27  */
28
29 /**
30  * SECTION: e-client
31  * @include: libedataserver/libedataserver.h
32  * @short_description: Base class for client handles
33  *
34  * This class provides some base functionality for clients
35  * such as #EBookClient and #ECalClient.
36  **/
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #include <glib/gi18n-lib.h>
43 #include <gio/gio.h>
44
45 #include <libedataserver/e-data-server-util.h>
46
47 #include "e-client.h"
48 #include "e-client-private.h"
49
50 #define E_CLIENT_GET_PRIVATE(obj) \
51         (G_TYPE_INSTANCE_GET_PRIVATE \
52         ((obj), E_TYPE_CLIENT, EClientPrivate))
53
54 typedef struct _AsyncContext AsyncContext;
55
56 struct _EClientPrivate {
57         GRecMutex prop_mutex;
58
59         ESource *source;
60         gboolean online;
61         gboolean readonly;
62         GSList *capabilities;
63         GMainContext *main_context;
64 };
65
66 struct _AsyncContext {
67         gchar *capabilities;
68         gchar *prop_name;
69         gchar *prop_value;
70         gboolean only_if_exists;
71 };
72
73 enum {
74         PROP_0,
75         PROP_CAPABILITIES,
76         PROP_MAIN_CONTEXT,
77         PROP_ONLINE,
78         PROP_OPENED,
79         PROP_READONLY,
80         PROP_SOURCE
81 };
82
83 enum {
84         OPENED,
85         BACKEND_ERROR,
86         BACKEND_DIED,
87         BACKEND_PROPERTY_CHANGED,
88         LAST_SIGNAL
89 };
90
91 static guint signals[LAST_SIGNAL];
92
93 G_DEFINE_ABSTRACT_TYPE (EClient, e_client, G_TYPE_OBJECT)
94
95 static void
96 async_context_free (AsyncContext *async_context)
97 {
98         g_free (async_context->capabilities);
99         g_free (async_context->prop_name);
100         g_free (async_context->prop_value);
101
102         g_slice_free (AsyncContext, async_context);
103 }
104
105 /*
106  * Well-known client backend properties, which are common for each #EClient:
107  * @CLIENT_BACKEND_PROPERTY_OPENED: Is set to "TRUE" or "FALSE" depending
108  *   whether the backend is fully opened.
109  * @CLIENT_BACKEND_PROPERTY_OPENING: Is set to "TRUE" or "FALSE" depending
110  *   whether the backend is processing its opening phase.
111  * @CLIENT_BACKEND_PROPERTY_ONLINE: Is set to "TRUE" or "FALSE" depending
112  *   on the backend's loaded state. See also e_client_is_online().
113  * @CLIENT_BACKEND_PROPERTY_READONLY: Is set to "TRUE" or "FALSE" depending
114  *   on the backend's readonly state. See also e_client_is_readonly().
115  * @CLIENT_BACKEND_PROPERTY_CACHE_DIR: Local folder with cached data used
116  *   by the backend.
117  * @CLIENT_BACKEND_PROPERTY_CAPABILITIES: Retrieves comma-separated list
118  *   of capabilities supported by the backend. Preferred method of retreiving
119  *   and working with capabilities is e_client_get_capabilities() and
120  *   e_client_check_capability().
121  */
122
123 G_DEFINE_QUARK (e-client-error-quark, e_client_error)
124
125 /**
126  * e_client_error_to_string:
127  *
128  * FIXME: Document me.
129  *
130  * Since: 3.2
131  **/
132 const gchar *
133 e_client_error_to_string (EClientError code)
134 {
135         switch (code) {
136         case E_CLIENT_ERROR_INVALID_ARG:
137                 return _("Invalid argument");
138         case E_CLIENT_ERROR_BUSY:
139                 return _("Backend is busy");
140         case E_CLIENT_ERROR_SOURCE_NOT_LOADED:
141                 return _("Source not loaded");
142         case E_CLIENT_ERROR_SOURCE_ALREADY_LOADED:
143                 return _("Source already loaded");
144         case E_CLIENT_ERROR_AUTHENTICATION_FAILED:
145                 return _("Authentication failed");
146         case E_CLIENT_ERROR_AUTHENTICATION_REQUIRED:
147                 return _("Authentication required");
148         case E_CLIENT_ERROR_REPOSITORY_OFFLINE:
149                 return _("Repository offline");
150         case E_CLIENT_ERROR_OFFLINE_UNAVAILABLE:
151                 /* Translators: This means that the EClient does not
152                  * support offline mode, or it's not set to by a user,
153                  * thus it is unavailable while user is not connected. */
154                 return _("Offline unavailable");
155         case E_CLIENT_ERROR_PERMISSION_DENIED:
156                 return _("Permission denied");
157         case E_CLIENT_ERROR_CANCELLED:
158                 return _("Cancelled");
159         case E_CLIENT_ERROR_COULD_NOT_CANCEL:
160                 return _("Could not cancel");
161         case E_CLIENT_ERROR_NOT_SUPPORTED:
162                 return _("Not supported");
163         case E_CLIENT_ERROR_UNSUPPORTED_AUTHENTICATION_METHOD:
164                 return _("Unsupported authentication method");
165         case E_CLIENT_ERROR_TLS_NOT_AVAILABLE:
166                 return _("TLS not available");
167         case E_CLIENT_ERROR_SEARCH_SIZE_LIMIT_EXCEEDED:
168                 return _("Search size limit exceeded");
169         case E_CLIENT_ERROR_SEARCH_TIME_LIMIT_EXCEEDED:
170                 return _("Search time limit exceeded");
171         case E_CLIENT_ERROR_INVALID_QUERY:
172                 return _("Invalid query");
173         case E_CLIENT_ERROR_QUERY_REFUSED:
174                 return _("Query refused");
175         case E_CLIENT_ERROR_DBUS_ERROR:
176                 return _("D-Bus error");
177         case E_CLIENT_ERROR_OTHER_ERROR:
178                 return _("Other error");
179         case E_CLIENT_ERROR_NOT_OPENED:
180                 return _("Backend is not opened yet");
181         case E_CLIENT_ERROR_OUT_OF_SYNC:
182                 return _("Object is out of sync");
183         }
184
185         return _("Unknown error");
186 }
187
188 /**
189  * e_client_error_create:
190  * @code: an #EClientError code to create
191  * @custom_msg: custom message to use for the error; can be %NULL
192  *
193  * Returns: a new #GError containing an E_CLIENT_ERROR of the given
194  * @code. If the @custom_msg is NULL, then the error message is
195  * the one returned from e_client_error_to_string() for the @code,
196  * otherwise the given message is used.
197  *
198  * Returned pointer should be freed with g_error_free().
199  *
200  * Since: 3.2
201  *
202  * Deprecated: 3.8: Just use the #GError API directly.
203  **/
204 GError *
205 e_client_error_create (EClientError code,
206                        const gchar *custom_msg)
207 {
208         if (custom_msg == NULL)
209                 custom_msg = e_client_error_to_string (code);
210
211         return g_error_new_literal (E_CLIENT_ERROR, code, custom_msg);
212 }
213
214 static void
215 client_set_source (EClient *client,
216                    ESource *source)
217 {
218         g_return_if_fail (E_IS_SOURCE (source));
219         g_return_if_fail (client->priv->source == NULL);
220
221         client->priv->source = g_object_ref (source);
222 }
223
224 static void
225 client_set_property (GObject *object,
226                      guint property_id,
227                      const GValue *value,
228                      GParamSpec *pspec)
229 {
230         switch (property_id) {
231                 case PROP_ONLINE:
232                         e_client_set_online (
233                                 E_CLIENT (object),
234                                 g_value_get_boolean (value));
235                         return;
236
237                 case PROP_SOURCE:
238                         client_set_source (
239                                 E_CLIENT (object),
240                                 g_value_get_object (value));
241                         return;
242         }
243
244         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
245 }
246
247 static void
248 client_get_property (GObject *object,
249                      guint property_id,
250                      GValue *value,
251                      GParamSpec *pspec)
252 {
253         switch (property_id) {
254                 case PROP_CAPABILITIES:
255                         g_value_set_pointer (
256                                 value,
257                                 (gpointer) e_client_get_capabilities (
258                                 E_CLIENT (object)));
259                         return;
260
261                 case PROP_MAIN_CONTEXT:
262                         g_value_take_boxed (
263                                 value,
264                                 e_client_ref_main_context (
265                                 E_CLIENT (object)));
266                         return;
267
268                 case PROP_ONLINE:
269                         g_value_set_boolean (
270                                 value,
271                                 e_client_is_online (
272                                 E_CLIENT (object)));
273                         return;
274
275                 case PROP_OPENED:
276                         g_value_set_boolean (
277                                 value,
278                                 e_client_is_opened (
279                                 E_CLIENT (object)));
280                         return;
281
282                 case PROP_READONLY:
283                         g_value_set_boolean (
284                                 value,
285                                 e_client_is_readonly (
286                                 E_CLIENT (object)));
287                         return;
288
289                 case PROP_SOURCE:
290                         g_value_set_object (
291                                 value,
292                                 e_client_get_source (
293                                 E_CLIENT (object)));
294                         return;
295         }
296
297         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
298 }
299
300 static void
301 client_dispose (GObject *object)
302 {
303         EClientPrivate *priv;
304
305         priv = E_CLIENT_GET_PRIVATE (object);
306
307         if (priv->main_context != NULL) {
308                 g_main_context_unref (priv->main_context);
309                 priv->main_context = NULL;
310         }
311
312         g_clear_object (&priv->source);
313
314         /* Chain up to parent's dispose() method. */
315         G_OBJECT_CLASS (e_client_parent_class)->dispose (object);
316 }
317
318 static void
319 client_finalize (GObject *object)
320 {
321         EClientPrivate *priv;
322
323         priv = E_CLIENT_GET_PRIVATE (object);
324
325         g_slist_free_full (priv->capabilities, (GDestroyNotify) g_free);
326
327         g_rec_mutex_clear (&priv->prop_mutex);
328
329         /* Chain up to parent's finalize() method. */
330         G_OBJECT_CLASS (e_client_parent_class)->finalize (object);
331 }
332
333 static void
334 client_unwrap_dbus_error (EClient *client,
335                           GError *dbus_error,
336                           GError **out_error)
337 {
338         /* This method is deprecated.  Make it a no-op. */
339
340         if (out_error != NULL)
341                 *out_error = dbus_error;
342 }
343
344 /* Helper for client_retrieve_capabilities() */
345 static void
346 client_retrieve_capabilities_thread (GSimpleAsyncResult *simple,
347                                      GObject *source_object,
348                                      GCancellable *cancellable)
349 {
350         AsyncContext *async_context;
351         GError *error = NULL;
352
353         async_context = g_simple_async_result_get_op_res_gpointer (simple);
354
355         e_client_retrieve_capabilities_sync (
356                 E_CLIENT (source_object),
357                 &async_context->capabilities,
358                 cancellable, &error);
359
360         if (error != NULL)
361                 g_simple_async_result_take_error (simple, error);
362 }
363
364 static void
365 client_retrieve_capabilities (EClient *client,
366                               GCancellable *cancellable,
367                               GAsyncReadyCallback callback,
368                               gpointer user_data)
369 {
370         GSimpleAsyncResult *simple;
371         AsyncContext *async_context;
372
373         async_context = g_slice_new0 (AsyncContext);
374
375         simple = g_simple_async_result_new (
376                 G_OBJECT (client), callback,
377                 user_data, client_retrieve_capabilities);
378
379         g_simple_async_result_set_check_cancellable (simple, cancellable);
380
381         g_simple_async_result_set_op_res_gpointer (
382                 simple, async_context, (GDestroyNotify) async_context_free);
383
384         g_simple_async_result_run_in_thread (
385                 simple, client_retrieve_capabilities_thread,
386                 G_PRIORITY_DEFAULT, cancellable);
387
388         g_object_unref (simple);
389 }
390
391 static gboolean
392 client_retrieve_capabilities_finish (EClient *client,
393                                      GAsyncResult *result,
394                                      gchar **capabilities,
395                                      GError **error)
396 {
397         GSimpleAsyncResult *simple;
398         AsyncContext *async_context;
399
400         g_return_val_if_fail (
401                 g_simple_async_result_is_valid (
402                 result, G_OBJECT (client),
403                 client_retrieve_capabilities), FALSE);
404
405         simple = G_SIMPLE_ASYNC_RESULT (result);
406         async_context = g_simple_async_result_get_op_res_gpointer (simple);
407
408         if (g_simple_async_result_propagate_error (simple, error))
409                 return FALSE;
410
411         g_return_val_if_fail (async_context->capabilities != NULL, FALSE);
412
413         if (capabilities != NULL) {
414                 *capabilities = async_context->capabilities;
415                 async_context->capabilities = NULL;
416         }
417
418         return TRUE;
419 }
420
421 static gboolean
422 client_retrieve_capabilities_sync (EClient *client,
423                                    gchar **capabilities,
424                                    GCancellable *cancellable,
425                                    GError **error)
426 {
427         return e_client_get_backend_property_sync (
428                 client, CLIENT_BACKEND_PROPERTY_CAPABILITIES,
429                 capabilities, cancellable, error);
430 }
431
432 /* Helper for client_get_backend_property() */
433 static void
434 client_get_backend_property_thread (GSimpleAsyncResult *simple,
435                                     GObject *source_object,
436                                     GCancellable *cancellable)
437 {
438         AsyncContext *async_context;
439         GError *error = NULL;
440
441         async_context = g_simple_async_result_get_op_res_gpointer (simple);
442
443         e_client_get_backend_property_sync (
444                 E_CLIENT (source_object),
445                 async_context->prop_name,
446                 &async_context->prop_value,
447                 cancellable, &error);
448
449         if (error != NULL)
450                 g_simple_async_result_take_error (simple, error);
451 }
452
453 static void
454 client_get_backend_property (EClient *client,
455                              const gchar *prop_name,
456                              GCancellable *cancellable,
457                              GAsyncReadyCallback callback,
458                              gpointer user_data)
459 {
460         GSimpleAsyncResult *simple;
461         AsyncContext *async_context;
462
463         async_context = g_slice_new0 (AsyncContext);
464         async_context->prop_name = g_strdup (prop_name);
465
466         simple = g_simple_async_result_new (
467                 G_OBJECT (client), callback,
468                 user_data, client_get_backend_property);
469
470         g_simple_async_result_set_check_cancellable (simple, cancellable);
471
472         g_simple_async_result_set_op_res_gpointer (
473                 simple, async_context, (GDestroyNotify) async_context_free);
474
475         g_simple_async_result_run_in_thread (
476                 simple, client_get_backend_property_thread,
477                 G_PRIORITY_DEFAULT, cancellable);
478
479         g_object_unref (simple);
480 }
481
482 static gboolean
483 client_get_backend_property_finish (EClient *client,
484                                     GAsyncResult *result,
485                                     gchar **prop_value,
486                                     GError **error)
487 {
488         GSimpleAsyncResult *simple;
489         AsyncContext *async_context;
490
491         g_return_val_if_fail (
492                 g_simple_async_result_is_valid (
493                 result, G_OBJECT (client),
494                 client_get_backend_property), FALSE);
495
496         simple = G_SIMPLE_ASYNC_RESULT (result);
497         async_context = g_simple_async_result_get_op_res_gpointer (simple);
498
499         if (g_simple_async_result_propagate_error (simple, error))
500                 return FALSE;
501
502         g_return_val_if_fail (async_context->prop_value != NULL, FALSE);
503
504         if (prop_value != NULL) {
505                 *prop_value = async_context->prop_value;
506                 async_context->prop_value = NULL;
507         }
508
509         return TRUE;
510 }
511
512 /* Helper for client_set_backend_property() */
513 static void
514 client_set_backend_property_thread (GSimpleAsyncResult *simple,
515                                     GObject *source_object,
516                                     GCancellable *cancellable)
517 {
518         AsyncContext *async_context;
519         GError *error = NULL;
520
521         async_context = g_simple_async_result_get_op_res_gpointer (simple);
522
523         e_client_set_backend_property_sync (
524                 E_CLIENT (source_object),
525                 async_context->prop_name,
526                 async_context->prop_value,
527                 cancellable, &error);
528
529         if (error != NULL)
530                 g_simple_async_result_take_error (simple, error);
531 }
532
533 static void
534 client_set_backend_property (EClient *client,
535                              const gchar *prop_name,
536                              const gchar *prop_value,
537                              GCancellable *cancellable,
538                              GAsyncReadyCallback callback,
539                              gpointer user_data)
540 {
541         GSimpleAsyncResult *simple;
542         AsyncContext *async_context;
543
544         async_context = g_slice_new0 (AsyncContext);
545         async_context->prop_name = g_strdup (prop_name);
546         async_context->prop_value = g_strdup (prop_value);
547
548         simple = g_simple_async_result_new (
549                 G_OBJECT (client), callback,
550                 user_data, client_set_backend_property);
551
552         g_simple_async_result_set_check_cancellable (simple, cancellable);
553
554         g_simple_async_result_set_op_res_gpointer (
555                 simple, async_context, (GDestroyNotify) async_context_free);
556
557         g_simple_async_result_run_in_thread (
558                 simple, client_set_backend_property_thread,
559                 G_PRIORITY_DEFAULT, cancellable);
560
561         g_object_unref (simple);
562 }
563
564 static gboolean
565 client_set_backend_property_finish (EClient *client,
566                                     GAsyncResult *result,
567                                     GError **error)
568 {
569         GSimpleAsyncResult *simple;
570
571         g_return_val_if_fail (
572                 g_simple_async_result_is_valid (
573                 result, G_OBJECT (client),
574                 client_set_backend_property), FALSE);
575
576         simple = G_SIMPLE_ASYNC_RESULT (result);
577
578         /* Assume success unless a GError is set. */
579         return !g_simple_async_result_propagate_error (simple, error);
580 }
581
582 /* Helper for client_open() */
583 static void
584 client_open_thread (GSimpleAsyncResult *simple,
585                     GObject *source_object,
586                     GCancellable *cancellable)
587 {
588         AsyncContext *async_context;
589         GError *error = NULL;
590
591         async_context = g_simple_async_result_get_op_res_gpointer (simple);
592
593         e_client_open_sync (
594                 E_CLIENT (source_object),
595                 async_context->only_if_exists,
596                 cancellable, &error);
597
598         if (error != NULL)
599                 g_simple_async_result_take_error (simple, error);
600 }
601
602 static void
603 client_open (EClient *client,
604              gboolean only_if_exists,
605              GCancellable *cancellable,
606              GAsyncReadyCallback callback,
607              gpointer user_data)
608 {
609         GSimpleAsyncResult *simple;
610         AsyncContext *async_context;
611
612         async_context = g_slice_new0 (AsyncContext);
613         async_context->only_if_exists = only_if_exists;
614
615         simple = g_simple_async_result_new (
616                 G_OBJECT (client), callback, user_data, client_open);
617
618         g_simple_async_result_set_check_cancellable (simple, cancellable);
619
620         g_simple_async_result_set_op_res_gpointer (
621                 simple, async_context, (GDestroyNotify) async_context_free);
622
623         g_simple_async_result_run_in_thread (
624                 simple, client_open_thread,
625                 G_PRIORITY_DEFAULT, cancellable);
626
627         g_object_unref (simple);
628 }
629
630 static gboolean
631 client_open_finish (EClient *client,
632                     GAsyncResult *result,
633                     GError **error)
634 {
635         GSimpleAsyncResult *simple;
636
637         g_return_val_if_fail (
638                 g_simple_async_result_is_valid (
639                 result, G_OBJECT (client), client_open), FALSE);
640
641         simple = G_SIMPLE_ASYNC_RESULT (result);
642
643         /* Assume success unless a GError is set. */
644         return !g_simple_async_result_propagate_error (simple, error);
645 }
646
647 /* Helper for client_remove() */
648 static void
649 client_remove_thread (GSimpleAsyncResult *simple,
650                       GObject *source_object,
651                       GCancellable *cancellable)
652 {
653         GError *error = NULL;
654
655         e_client_remove_sync (
656                 E_CLIENT (source_object), cancellable, &error);
657
658         if (error != NULL)
659                 g_simple_async_result_take_error (simple, error);
660 }
661
662 static void
663 client_remove (EClient *client,
664                GCancellable *cancellable,
665                GAsyncReadyCallback callback,
666                gpointer user_data)
667 {
668         GSimpleAsyncResult *simple;
669
670         simple = g_simple_async_result_new (
671                 G_OBJECT (client), callback, user_data, client_remove);
672
673         g_simple_async_result_set_check_cancellable (simple, cancellable);
674
675         g_simple_async_result_run_in_thread (
676                 simple, client_remove_thread,
677                 G_PRIORITY_DEFAULT, cancellable);
678
679         g_object_unref (simple);
680 }
681
682 static gboolean
683 client_remove_finish (EClient *client,
684                       GAsyncResult *result,
685                       GError **error)
686 {
687         GSimpleAsyncResult *simple;
688
689         g_return_val_if_fail (
690                 g_simple_async_result_is_valid (
691                 result, G_OBJECT (client), client_remove), FALSE);
692
693         simple = G_SIMPLE_ASYNC_RESULT (result);
694
695         /* Assume success unless a GError is set. */
696         return !g_simple_async_result_propagate_error (simple, error);
697 }
698
699 static gboolean
700 client_remove_sync (EClient *client,
701                     GCancellable *cancellable,
702                     GError **error)
703 {
704         ESource *source;
705
706         source = e_client_get_source (client);
707
708         return e_source_remove_sync (source, cancellable, error);
709 }
710
711 /* Helper for client_refresh() */
712 static void
713 client_refresh_thread (GSimpleAsyncResult *simple,
714                        GObject *source_object,
715                        GCancellable *cancellable)
716 {
717         GError *error = NULL;
718
719         e_client_refresh_sync (
720                 E_CLIENT (source_object), cancellable, &error);
721
722         if (error != NULL)
723                 g_simple_async_result_take_error (simple, error);
724 }
725
726 static void
727 client_refresh (EClient *client,
728                 GCancellable *cancellable,
729                 GAsyncReadyCallback callback,
730                 gpointer user_data)
731 {
732         GSimpleAsyncResult *simple;
733
734         simple = g_simple_async_result_new (
735                 G_OBJECT (client), callback, user_data, client_refresh);
736
737         g_simple_async_result_set_check_cancellable (simple, cancellable);
738
739         g_simple_async_result_run_in_thread (
740                 simple, client_refresh_thread,
741                 G_PRIORITY_DEFAULT, cancellable);
742
743         g_object_unref (simple);
744 }
745
746 static gboolean
747 client_refresh_finish (EClient *client,
748                        GAsyncResult *result,
749                        GError **error)
750 {
751         GSimpleAsyncResult *simple;
752
753         g_return_val_if_fail (
754                 g_simple_async_result_is_valid (
755                 result, G_OBJECT (client), client_refresh), FALSE);
756
757         simple = G_SIMPLE_ASYNC_RESULT (result);
758
759         /* Assume success unless a GError is set. */
760         return !g_simple_async_result_propagate_error (simple, error);
761 }
762
763 static void
764 e_client_class_init (EClientClass *class)
765 {
766         GObjectClass *object_class;
767
768         g_type_class_add_private (class, sizeof (EClientPrivate));
769
770         object_class = G_OBJECT_CLASS (class);
771         object_class->set_property = client_set_property;
772         object_class->get_property = client_get_property;
773         object_class->dispose = client_dispose;
774         object_class->finalize = client_finalize;
775
776         class->unwrap_dbus_error = client_unwrap_dbus_error;
777         class->retrieve_capabilities = client_retrieve_capabilities;
778         class->retrieve_capabilities_finish = client_retrieve_capabilities_finish;
779         class->retrieve_capabilities_sync = client_retrieve_capabilities_sync;
780         class->get_backend_property = client_get_backend_property;
781         class->get_backend_property_finish = client_get_backend_property_finish;
782         class->set_backend_property = client_set_backend_property;
783         class->set_backend_property_finish = client_set_backend_property_finish;
784         class->open = client_open;
785         class->open_finish = client_open_finish;
786         class->remove = client_remove;
787         class->remove_finish = client_remove_finish;
788         class->remove_sync = client_remove_sync;
789         class->refresh = client_refresh;
790         class->refresh_finish = client_refresh_finish;
791
792         /**
793          * EClient:capabilities:
794          *
795          * The capabilities of this client
796          */
797         g_object_class_install_property (
798                 object_class,
799                 PROP_CAPABILITIES,
800                 g_param_spec_pointer (
801                         "capabilities",
802                         "Capabilities",
803                         "The capabilities of this client",
804                         G_PARAM_READABLE |
805                         G_PARAM_STATIC_STRINGS));
806
807         /**
808          * EClient:main-context:
809          *
810          * The main loop context in which notifications for
811          * this client will be delivered.
812          */
813         g_object_class_install_property (
814                 object_class,
815                 PROP_MAIN_CONTEXT,
816                 g_param_spec_boxed (
817                         "main-context",
818                         "Main Context",
819                         "The main loop context on "
820                         "which to attach event sources",
821                         G_TYPE_MAIN_CONTEXT,
822                         G_PARAM_READABLE |
823                         G_PARAM_STATIC_STRINGS));
824
825         /**
826          * EClient:online:
827          *
828          * Whether this client's backing data is online.
829          */
830         g_object_class_install_property (
831                 object_class,
832                 PROP_ONLINE,
833                 g_param_spec_boolean (
834                         "online",
835                         "Online",
836                         "Whether this client is online",
837                         FALSE,
838                         G_PARAM_READWRITE |
839                         G_PARAM_STATIC_STRINGS));
840
841         /**
842          * EClient:opened:
843          *
844          * Whether this client is open and ready to use.
845          *
846          * Deprecated: 3.8: This property is no longer relevant and
847          * will always be %TRUE after successfully creating any concrete
848          * type of #EClient.
849          */
850         g_object_class_install_property (
851                 object_class,
852                 PROP_OPENED,
853                 g_param_spec_boolean (
854                         "opened",
855                         "Opened",
856                         "Whether this client is open and ready to use",
857                         FALSE,
858                         G_PARAM_READABLE |
859                         G_PARAM_STATIC_STRINGS));
860
861         /**
862          * EClient:readonly:
863          *
864          * Whether this client's backing data is readonly.
865          */
866         g_object_class_install_property (
867                 object_class,
868                 PROP_READONLY,
869                 g_param_spec_boolean (
870                         "readonly",
871                         "Read only",
872                         "Whether this client's backing data is readonly",
873                         FALSE,
874                         G_PARAM_READABLE |
875                         G_PARAM_STATIC_STRINGS));
876
877         /**
878          * EClient:source:
879          *
880          * The #ESource for which this client was created.
881          */
882         g_object_class_install_property (
883                 object_class,
884                 PROP_SOURCE,
885                 g_param_spec_object (
886                         "source",
887                         "Source",
888                         "The ESource for which this client was created",
889                         E_TYPE_SOURCE,
890                         G_PARAM_READWRITE |
891                         G_PARAM_CONSTRUCT_ONLY |
892                         G_PARAM_STATIC_STRINGS));
893
894         /**
895          * EClient::opened:
896          *
897          * Deprecated: 3.8: This signal is no longer emitted.
898          **/
899         signals[OPENED] = g_signal_new (
900                 "opened",
901                 G_OBJECT_CLASS_TYPE (class),
902                 G_SIGNAL_RUN_LAST |
903                 G_SIGNAL_DEPRECATED,
904                 G_STRUCT_OFFSET (EClientClass, opened),
905                 NULL, NULL, NULL,
906                 G_TYPE_NONE, 1,
907                 G_TYPE_ERROR);
908
909         signals[BACKEND_ERROR] = g_signal_new (
910                 "backend-error",
911                 G_OBJECT_CLASS_TYPE (class),
912                 G_SIGNAL_RUN_FIRST,
913                 G_STRUCT_OFFSET (EClientClass, backend_error),
914                 NULL, NULL, NULL,
915                 G_TYPE_NONE, 1,
916                 G_TYPE_STRING);
917
918         signals[BACKEND_DIED] = g_signal_new (
919                 "backend-died",
920                 G_OBJECT_CLASS_TYPE (class),
921                 G_SIGNAL_RUN_LAST,
922                 G_STRUCT_OFFSET (EClientClass, backend_died),
923                 NULL, NULL, NULL,
924                 G_TYPE_NONE, 0);
925
926         signals[BACKEND_PROPERTY_CHANGED] = g_signal_new (
927                 "backend-property-changed",
928                 G_OBJECT_CLASS_TYPE (class),
929                 G_SIGNAL_RUN_LAST,
930                 G_STRUCT_OFFSET (EClientClass, backend_property_changed),
931                 NULL, NULL, NULL,
932                 G_TYPE_NONE, 2,
933                 G_TYPE_STRING,
934                 G_TYPE_STRING);
935 }
936
937 static void
938 e_client_init (EClient *client)
939 {
940         client->priv = E_CLIENT_GET_PRIVATE (client);
941
942         client->priv->readonly = TRUE;
943         client->priv->main_context = g_main_context_ref_thread_default ();
944
945         g_rec_mutex_init (&client->priv->prop_mutex);
946 }
947
948 /**
949  * e_client_get_source:
950  * @client: an #EClient
951  *
952  * Get the #ESource that this client has assigned.
953  *
954  * Returns: (transfer none): The source.
955  *
956  * Since: 3.2
957  **/
958 ESource *
959 e_client_get_source (EClient *client)
960 {
961         g_return_val_if_fail (E_IS_CLIENT (client), NULL);
962
963         return client->priv->source;
964 }
965
966 static void
967 client_ensure_capabilities (EClient *client)
968 {
969         gchar *capabilities = NULL;
970
971         g_return_if_fail (E_IS_CLIENT (client));
972
973         if (client->priv->capabilities != NULL)
974                 return;
975
976         /* Despite appearances this function does not actually block. */
977         e_client_get_backend_property_sync (
978                 client, CLIENT_BACKEND_PROPERTY_CAPABILITIES,
979                 &capabilities, NULL, NULL);
980         e_client_set_capabilities (client, capabilities);
981         g_free (capabilities);
982 }
983
984 /**
985  * e_client_get_capabilities:
986  * @client: an #EClient
987  *
988  * Get list of strings with capabilities advertised by a backend.
989  * This list, together with inner strings, is owned by the @client.
990  * To check for individual capabilities use e_client_check_capability().
991  *
992  * Returns: (element-type utf8) (transfer none): #GSList of const strings
993  *          of capabilities
994  *
995  * Since: 3.2
996  **/
997 const GSList *
998 e_client_get_capabilities (EClient *client)
999 {
1000         g_return_val_if_fail (E_IS_CLIENT (client), NULL);
1001
1002         client_ensure_capabilities (client);
1003
1004         return client->priv->capabilities;
1005 }
1006
1007 /**
1008  * e_client_ref_main_context:
1009  * @client: an #EClient
1010  *
1011  * Returns the #GMainContext on which event sources for @client are to
1012  * be attached.
1013  *
1014  * The returned #GMainContext is referenced for thread-safety and must be
1015  * unreferenced with g_main_context_unref() when finished with it.
1016  *
1017  * Returns: (transfer full): a #GMainContext
1018  *
1019  * Since: 3.8
1020  **/
1021 GMainContext *
1022 e_client_ref_main_context (EClient *client)
1023 {
1024         g_return_val_if_fail (E_IS_CLIENT (client), NULL);
1025
1026         return g_main_context_ref (client->priv->main_context);
1027 }
1028
1029 /**
1030  * e_client_check_capability:
1031  * @client: an #EClient
1032  * @capability: a capability
1033  *
1034  * Check if backend supports particular capability.
1035  * To get all capabilities use e_client_get_capabilities().
1036  *
1037  * Returns: #GSList of const strings of capabilities
1038  *
1039  * Since: 3.2
1040  **/
1041 gboolean
1042 e_client_check_capability (EClient *client,
1043                            const gchar *capability)
1044 {
1045         GSList *iter;
1046
1047         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1048         g_return_val_if_fail (capability, FALSE);
1049
1050         g_rec_mutex_lock (&client->priv->prop_mutex);
1051
1052         client_ensure_capabilities (client);
1053
1054         for (iter = client->priv->capabilities; iter; iter = g_slist_next (iter)) {
1055                 const gchar *cap = iter->data;
1056
1057                 if (cap && g_ascii_strcasecmp (cap, capability) == 0) {
1058                         g_rec_mutex_unlock (&client->priv->prop_mutex);
1059                         return TRUE;
1060                 }
1061         }
1062
1063         g_rec_mutex_unlock (&client->priv->prop_mutex);
1064
1065         return FALSE;
1066 }
1067
1068 /**
1069  * e_client_check_refresh_supported:
1070  * @client: A client.
1071  *
1072  * Checks whether a client supports explicit refreshing
1073  * (see e_client_refresh()).
1074  *
1075  * Returns: TRUE if the client supports refreshing, FALSE otherwise.
1076  *
1077  * Since: 3.2
1078  **/
1079 gboolean
1080 e_client_check_refresh_supported (EClient *client)
1081 {
1082         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1083
1084         return e_client_check_capability (client, "refresh-supported");
1085 }
1086
1087 /* capabilities - comma-separated list of capabilities; can be NULL to unset */
1088 void
1089 e_client_set_capabilities (EClient *client,
1090                            const gchar *capabilities)
1091 {
1092         g_return_if_fail (E_IS_CLIENT (client));
1093
1094         g_rec_mutex_lock (&client->priv->prop_mutex);
1095
1096         g_slist_foreach (client->priv->capabilities, (GFunc) g_free, NULL);
1097         g_slist_free (client->priv->capabilities);
1098         client->priv->capabilities = e_client_util_parse_comma_strings (capabilities);
1099
1100         g_rec_mutex_unlock (&client->priv->prop_mutex);
1101
1102         g_object_notify (G_OBJECT (client), "capabilities");
1103 }
1104
1105 /**
1106  * e_client_is_readonly:
1107  * @client: an #EClient
1108  *
1109  * Check if this @client is read-only.
1110  *
1111  * Returns: %TRUE if this @client is read-only, otherwise %FALSE.
1112  *
1113  * Since: 3.2
1114  **/
1115 gboolean
1116 e_client_is_readonly (EClient *client)
1117 {
1118         g_return_val_if_fail (E_IS_CLIENT (client), TRUE);
1119
1120         return client->priv->readonly;
1121 }
1122
1123 void
1124 e_client_set_readonly (EClient *client,
1125                        gboolean readonly)
1126 {
1127         g_return_if_fail (E_IS_CLIENT (client));
1128
1129         g_rec_mutex_lock (&client->priv->prop_mutex);
1130         if (client->priv->readonly == readonly) {
1131                 g_rec_mutex_unlock (&client->priv->prop_mutex);
1132                 return;
1133         }
1134
1135         client->priv->readonly = readonly;
1136
1137         g_rec_mutex_unlock (&client->priv->prop_mutex);
1138
1139         g_object_notify (G_OBJECT (client), "readonly");
1140 }
1141
1142 /**
1143  * e_client_is_online:
1144  * @client: an #EClient
1145  *
1146  * Check if this @client is connected.
1147  *
1148  * Returns: %TRUE if this @client is connected, otherwise %FALSE.
1149  *
1150  * Since: 3.2
1151  **/
1152 gboolean
1153 e_client_is_online (EClient *client)
1154 {
1155         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1156
1157         return client->priv->online;
1158 }
1159
1160 void
1161 e_client_set_online (EClient *client,
1162                      gboolean is_online)
1163 {
1164         g_return_if_fail (E_IS_CLIENT (client));
1165
1166         /* newly connected/disconnected => make sure capabilities will be correct */
1167         e_client_set_capabilities (client, NULL);
1168
1169         g_rec_mutex_lock (&client->priv->prop_mutex);
1170         if (client->priv->online == is_online) {
1171                 g_rec_mutex_unlock (&client->priv->prop_mutex);
1172                 return;
1173         }
1174
1175         client->priv->online = is_online;
1176
1177         g_rec_mutex_unlock (&client->priv->prop_mutex);
1178
1179         g_object_notify (G_OBJECT (client), "online");
1180 }
1181
1182 /**
1183  * e_client_is_opened:
1184  * @client: an #EClient
1185  *
1186  * Check if this @client is fully opened. This includes
1187  * everything from e_client_open() call up to the authentication,
1188  * if required by a backend. Client cannot do any other operation
1189  * during the opening phase except of authenticate or cancel it.
1190  * Every other operation results in an %E_CLIENT_ERROR_BUSY error.
1191  *
1192  * Returns: always %TRUE
1193  *
1194  * Since: 3.2.
1195  *
1196  * Deprecated: 3.8: Clients don't need to care if they're fully opened
1197  *                  anymore.  This function always returns %TRUE.
1198  **/
1199 gboolean
1200 e_client_is_opened (EClient *client)
1201 {
1202         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1203
1204         return TRUE;
1205 }
1206
1207 /**
1208  * e_client_cancel_all:
1209  * @client: an #EClient
1210  *
1211  * Cancels all pending operations started on @client.
1212  *
1213  * Since: 3.2
1214  *
1215  * Deprecated: 3.8: The function no longer does anything.
1216  **/
1217 void
1218 e_client_cancel_all (EClient *client)
1219 {
1220         /* Do nothing. */
1221 }
1222
1223 /**
1224  * e_client_retrieve_capabilities:
1225  * @client: an #EClient
1226  * @cancellable: a #GCancellable; can be %NULL
1227  * @callback: callback to call when a result is ready
1228  * @user_data: user data for the @callback
1229  *
1230  * Initiates retrieval of capabilities on the @client. This is usually
1231  * required only once, after the @client is opened. The returned value
1232  * is cached and any subsequent call of e_client_get_capabilities() and
1233  * e_client_check_capability() is using the cached value.
1234  * The call is finished by e_client_retrieve_capabilities_finish()
1235  * from the @callback.
1236  *
1237  * Since: 3.2
1238  *
1239  * Deprecated: 3.8: Use e_client_get_capabilities() instead.
1240  **/
1241 void
1242 e_client_retrieve_capabilities (EClient *client,
1243                                 GCancellable *cancellable,
1244                                 GAsyncReadyCallback callback,
1245                                 gpointer user_data)
1246 {
1247         EClientClass *class;
1248
1249         g_return_if_fail (E_IS_CLIENT (client));
1250         g_return_if_fail (callback != NULL);
1251
1252         class = E_CLIENT_GET_CLASS (client);
1253         g_return_if_fail (class != NULL);
1254         g_return_if_fail (class->retrieve_capabilities != NULL);
1255
1256         class->retrieve_capabilities (client, cancellable, callback, user_data);
1257 }
1258
1259 /**
1260  * e_client_retrieve_capabilities_finish:
1261  * @client: an #EClient
1262  * @result: a #GAsyncResult
1263  * @capabilities: (out): Comma-separated list of capabilities of the @client
1264  * @error: (out): a #GError to set an error, if any
1265  *
1266  * Finishes previous call of e_client_retrieve_capabilities().
1267  * Returned value of @capabilities should be freed with g_free(),
1268  * when no longer needed.
1269  *
1270  * Returns: %TRUE if successful, %FALSE otherwise.
1271  *
1272  * Since: 3.2
1273  *
1274  * Deprecated: 3.8: Use e_client_get_capabilities() instead.
1275  **/
1276 gboolean
1277 e_client_retrieve_capabilities_finish (EClient *client,
1278                                        GAsyncResult *result,
1279                                        gchar **capabilities,
1280                                        GError **error)
1281 {
1282         EClientClass *class;
1283         gboolean res;
1284
1285         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1286         g_return_val_if_fail (capabilities != NULL, FALSE);
1287
1288         class = E_CLIENT_GET_CLASS (client);
1289         g_return_val_if_fail (class != NULL, FALSE);
1290         g_return_val_if_fail (class->retrieve_capabilities_finish != NULL, FALSE);
1291
1292         *capabilities = NULL;
1293         res = class->retrieve_capabilities_finish (
1294                 client, result, capabilities, error);
1295
1296         e_client_set_capabilities (client, res ? *capabilities : NULL);
1297
1298         return res;
1299 }
1300
1301 /**
1302  * e_client_retrieve_capabilities_sync:
1303  * @client: an #EClient
1304  * @capabilities: (out): Comma-separated list of capabilities of the @client
1305  * @cancellable: a #GCancellable; can be %NULL
1306  * @error: (out): a #GError to set an error, if any
1307  *
1308  * Initiates retrieval of capabilities on the @client. This is usually
1309  * required only once, after the @client is opened. The returned value
1310  * is cached and any subsequent call of e_client_get_capabilities() and
1311  * e_client_check_capability() is using the cached value. Returned value
1312  * of @capabilities should be freed with g_free(), when no longer needed.
1313  *
1314  * Returns: %TRUE if successful, %FALSE otherwise.
1315  *
1316  * Since: 3.2
1317  *
1318  * Deprecated: 3.8: Use e_client_get_capabilities() instead.
1319  **/
1320 gboolean
1321 e_client_retrieve_capabilities_sync (EClient *client,
1322                                      gchar **capabilities,
1323                                      GCancellable *cancellable,
1324                                      GError **error)
1325 {
1326         EClientClass *class;
1327         gboolean res = FALSE;
1328
1329         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1330         g_return_val_if_fail (capabilities != NULL, FALSE);
1331
1332         class = E_CLIENT_GET_CLASS (client);
1333         g_return_val_if_fail (class != NULL, FALSE);
1334         g_return_val_if_fail (class->retrieve_capabilities_sync != NULL, FALSE);
1335
1336         *capabilities = NULL;
1337         res = class->retrieve_capabilities_sync (
1338                 client, capabilities, cancellable, error);
1339
1340         e_client_set_capabilities (client, res ? *capabilities : NULL);
1341
1342         return res;
1343 }
1344
1345 /**
1346  * e_client_get_backend_property:
1347  * @client: an #EClient
1348  * @prop_name: property name, whose value to retrieve; cannot be %NULL
1349  * @cancellable: a #GCancellable; can be %NULL
1350  * @callback: callback to call when a result is ready
1351  * @user_data: user data for the @callback
1352  *
1353  * Queries @client's backend for a property of name @prop_name.
1354  * The call is finished by e_client_get_backend_property_finish()
1355  * from the @callback.
1356  *
1357  * Since: 3.2
1358  **/
1359 void
1360 e_client_get_backend_property (EClient *client,
1361                                const gchar *prop_name,
1362                                GCancellable *cancellable,
1363                                GAsyncReadyCallback callback,
1364                                gpointer user_data)
1365 {
1366         EClientClass *class;
1367
1368         g_return_if_fail (callback != NULL);
1369         g_return_if_fail (E_IS_CLIENT (client));
1370         g_return_if_fail (prop_name != NULL);
1371
1372         class = E_CLIENT_GET_CLASS (client);
1373         g_return_if_fail (class != NULL);
1374         g_return_if_fail (class->get_backend_property != NULL);
1375
1376         class->get_backend_property (
1377                 client, prop_name, cancellable, callback, user_data);
1378 }
1379
1380 /**
1381  * e_client_get_backend_property_finish:
1382  * @client: an #EClient
1383  * @result: a #GAsyncResult
1384  * @prop_value: (out): Retrieved backend property value; cannot be %NULL
1385  * @error: (out): a #GError to set an error, if any
1386  *
1387  * Finishes previous call of e_client_get_backend_property().
1388  *
1389  * Returns: %TRUE if successful, %FALSE otherwise.
1390  *
1391  * Since: 3.2
1392  **/
1393 gboolean
1394 e_client_get_backend_property_finish (EClient *client,
1395                                       GAsyncResult *result,
1396                                       gchar **prop_value,
1397                                       GError **error)
1398 {
1399         EClientClass *class;
1400
1401         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1402         g_return_val_if_fail (prop_value != NULL, FALSE);
1403
1404         class = E_CLIENT_GET_CLASS (client);
1405         g_return_val_if_fail (class != NULL, FALSE);
1406         g_return_val_if_fail (class->get_backend_property_finish != NULL, FALSE);
1407
1408         return class->get_backend_property_finish (
1409                 client, result, prop_value, error);
1410 }
1411
1412 /**
1413  * e_client_get_backend_property_sync:
1414  * @client: an #EClient
1415  * @prop_name: property name, whose value to retrieve; cannot be %NULL
1416  * @prop_value: (out): Retrieved backend property value; cannot be %NULL
1417  * @cancellable: a #GCancellable; can be %NULL
1418  * @error: (out): a #GError to set an error, if any
1419  *
1420  * Queries @client's backend for a property of name @prop_name.
1421  *
1422  * Returns: %TRUE if successful, %FALSE otherwise.
1423  *
1424  * Since: 3.2
1425  **/
1426 gboolean
1427 e_client_get_backend_property_sync (EClient *client,
1428                                     const gchar *prop_name,
1429                                     gchar **prop_value,
1430                                     GCancellable *cancellable,
1431                                     GError **error)
1432 {
1433         EClientClass *class;
1434
1435         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1436         g_return_val_if_fail (prop_name != NULL, FALSE);
1437         g_return_val_if_fail (prop_value != NULL, FALSE);
1438
1439         class = E_CLIENT_GET_CLASS (client);
1440         g_return_val_if_fail (class != NULL, FALSE);
1441         g_return_val_if_fail (class->get_backend_property_sync != NULL, FALSE);
1442
1443         return class->get_backend_property_sync (
1444                 client, prop_name, prop_value, cancellable, error);
1445 }
1446
1447 /**
1448  * e_client_set_backend_property:
1449  * @client: an #EClient
1450  * @prop_name: property name, whose value to change; cannot be %NULL
1451  * @prop_value: property value, to set; cannot be %NULL
1452  * @cancellable: a #GCancellable; can be %NULL
1453  * @callback: callback to call when a result is ready
1454  * @user_data: user data for the @callback
1455  *
1456  * Sets @client's backend property of name @prop_name
1457  * to value @prop_value. The call is finished
1458  * by e_client_set_backend_property_finish() from the @callback.
1459  *
1460  * Since: 3.2
1461  *
1462  * Deprecated: 3.8: Clients cannot set backend properties.  Any attempt
1463  *                  will fail with an %E_CLIENT_ERROR_NOT_SUPPORTED error.
1464  **/
1465 void
1466 e_client_set_backend_property (EClient *client,
1467                                const gchar *prop_name,
1468                                const gchar *prop_value,
1469                                GCancellable *cancellable,
1470                                GAsyncReadyCallback callback,
1471                                gpointer user_data)
1472 {
1473         EClientClass *class;
1474
1475         g_return_if_fail (callback != NULL);
1476         g_return_if_fail (E_IS_CLIENT (client));
1477         g_return_if_fail (prop_name != NULL);
1478         g_return_if_fail (prop_value != NULL);
1479
1480         class = E_CLIENT_GET_CLASS (client);
1481         g_return_if_fail (class != NULL);
1482         g_return_if_fail (class->set_backend_property != NULL);
1483
1484         class->set_backend_property (
1485                 client, prop_name, prop_value,
1486                 cancellable, callback, user_data);
1487 }
1488
1489 /**
1490  * e_client_set_backend_property_finish:
1491  * @client: an #EClient
1492  * @result: a #GAsyncResult
1493  * @error: (out): a #GError to set an error, if any
1494  *
1495  * Finishes previous call of e_client_set_backend_property().
1496  *
1497  * Returns: %TRUE if successful, %FALSE otherwise.
1498  *
1499  * Since: 3.2
1500  *
1501  * Deprecated: 3.8: Clients cannot set backend properties.  Any attempt
1502  *                  will fail with an %E_CLIENT_ERROR_NOT_SUPPORTED error.
1503  **/
1504 gboolean
1505 e_client_set_backend_property_finish (EClient *client,
1506                                       GAsyncResult *result,
1507                                       GError **error)
1508 {
1509         EClientClass *class;
1510
1511         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1512
1513         class = E_CLIENT_GET_CLASS (client);
1514         g_return_val_if_fail (class != NULL, FALSE);
1515         g_return_val_if_fail (class->set_backend_property_finish != NULL, FALSE);
1516
1517         return class->set_backend_property_finish (client, result, error);
1518 }
1519
1520 /**
1521  * e_client_set_backend_property_sync:
1522  * @client: an #EClient
1523  * @prop_name: property name, whose value to change; cannot be %NULL
1524  * @prop_value: property value, to set; cannot be %NULL
1525  * @cancellable: a #GCancellable; can be %NULL
1526  * @error: (out): a #GError to set an error, if any
1527  *
1528  * Sets @client's backend property of name @prop_name
1529  * to value @prop_value.
1530  *
1531  * Returns: %TRUE if successful, %FALSE otherwise.
1532  *
1533  * Since: 3.2
1534  *
1535  * Deprecated: 3.8: Clients cannot set backend properties.  Any attempt
1536  *                  will fail with an %E_CLIENT_ERROR_NOT_SUPPORTED error.
1537  **/
1538 gboolean
1539 e_client_set_backend_property_sync (EClient *client,
1540                                     const gchar *prop_name,
1541                                     const gchar *prop_value,
1542                                     GCancellable *cancellable,
1543                                     GError **error)
1544 {
1545         EClientClass *class;
1546
1547         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1548         g_return_val_if_fail (prop_name != NULL, FALSE);
1549         g_return_val_if_fail (prop_value != NULL, FALSE);
1550
1551         class = E_CLIENT_GET_CLASS (client);
1552         g_return_val_if_fail (class != NULL, FALSE);
1553         g_return_val_if_fail (class->set_backend_property_sync != NULL, FALSE);
1554
1555         return class->set_backend_property_sync (
1556                 client, prop_name, prop_value, cancellable, error);
1557 }
1558
1559 /**
1560  * e_client_open:
1561  * @client: an #EClient
1562  * @only_if_exists: if %TRUE, fail if this book doesn't already exist,
1563  *                  otherwise create it first
1564  * @cancellable: a #GCancellable; can be %NULL
1565  * @callback: callback to call when a result is ready
1566  * @user_data: user data for the @callback
1567  *
1568  * Opens the @client, making it ready for queries and other operations.
1569  * The call is finished by e_client_open_finish() from the @callback.
1570  *
1571  * Since: 3.2
1572  *
1573  * Deprecated: 3.8: Use e_book_client_connect() and
1574  *                  e_book_client_connect_finish() or
1575  *                  e_cal_client_connect() and
1576  *                  e_cal_client_connect_finish() instead.
1577  **/
1578 void
1579 e_client_open (EClient *client,
1580                gboolean only_if_exists,
1581                GCancellable *cancellable,
1582                GAsyncReadyCallback callback,
1583                gpointer user_data)
1584 {
1585         EClientClass *class;
1586
1587         g_return_if_fail (callback != NULL);
1588         g_return_if_fail (E_IS_CLIENT (client));
1589
1590         class = E_CLIENT_GET_CLASS (client);
1591         g_return_if_fail (class != NULL);
1592         g_return_if_fail (class->open != NULL);
1593
1594         class->open (client, only_if_exists, cancellable, callback, user_data);
1595 }
1596
1597 /**
1598  * e_client_open_finish:
1599  * @client: an #EClient
1600  * @result: a #GAsyncResult
1601  * @error: (out): a #GError to set an error, if any
1602  *
1603  * Finishes previous call of e_client_open().
1604  *
1605  * Returns: %TRUE if successful, %FALSE otherwise.
1606  *
1607  * Since: 3.2
1608  *
1609  * Deprecated: 3.8: Use e_book_client_connect() and
1610  *                  e_book_client_connect_finish() or
1611  *                  e_cal_client_connect() and
1612  *                  e_cal_client_connect_finish() instead.
1613  **/
1614 gboolean
1615 e_client_open_finish (EClient *client,
1616                       GAsyncResult *result,
1617                       GError **error)
1618 {
1619         EClientClass *class;
1620
1621         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1622
1623         class = E_CLIENT_GET_CLASS (client);
1624         g_return_val_if_fail (class != NULL, FALSE);
1625         g_return_val_if_fail (class->open_finish != NULL, FALSE);
1626
1627         return class->open_finish (client, result, error);
1628 }
1629
1630 /**
1631  * e_client_open_sync:
1632  * @client: an #EClient
1633  * @only_if_exists: if %TRUE, fail if this book doesn't already exist,
1634  *                  otherwise create it first
1635  * @cancellable: a #GCancellable; can be %NULL
1636  * @error: (out): a #GError to set an error, if any
1637  *
1638  * Opens the @client, making it ready for queries and other operations.
1639  *
1640  * Returns: %TRUE if successful, %FALSE otherwise.
1641  *
1642  * Since: 3.2
1643  *
1644  * Deprecated: 3.8: Use e_book_client_connect_sync() or
1645  *                  e_cal_client_connect_sync() instead.
1646  **/
1647 gboolean
1648 e_client_open_sync (EClient *client,
1649                     gboolean only_if_exists,
1650                     GCancellable *cancellable,
1651                     GError **error)
1652 {
1653         EClientClass *class;
1654
1655         class = E_CLIENT_GET_CLASS (client);
1656         g_return_val_if_fail (class != NULL, FALSE);
1657         g_return_val_if_fail (class->open_sync != NULL, FALSE);
1658
1659         return class->open_sync (client, only_if_exists, cancellable, error);
1660 }
1661
1662 /**
1663  * e_client_remove:
1664  * @client: an #EClient
1665  * @cancellable: a #GCancellable; can be %NULL
1666  * @callback: callback to call when a result is ready
1667  * @user_data: user data for the @callback
1668  *
1669  * Removes the backing data for this #EClient. For example, with the file
1670  * backend this deletes the database file. You cannot get it back!
1671  * The call is finished by e_client_remove_finish() from the @callback.
1672  *
1673  * Since: 3.2
1674  *
1675  * Deprecated: 3.6: Use e_source_remove() instead.
1676  **/
1677 void
1678 e_client_remove (EClient *client,
1679                  GCancellable *cancellable,
1680                  GAsyncReadyCallback callback,
1681                  gpointer user_data)
1682 {
1683         EClientClass *class;
1684
1685         g_return_if_fail (E_IS_CLIENT (client));
1686         g_return_if_fail (callback != NULL);
1687
1688         class = E_CLIENT_GET_CLASS (client);
1689         g_return_if_fail (class != NULL);
1690         g_return_if_fail (class->remove != NULL);
1691
1692         class->remove (client, cancellable, callback, user_data);
1693 }
1694
1695 /**
1696  * e_client_remove_finish:
1697  * @client: an #EClient
1698  * @result: a #GAsyncResult
1699  * @error: (out): a #GError to set an error, if any
1700  *
1701  * Finishes previous call of e_client_remove().
1702  *
1703  * Returns: %TRUE if successful, %FALSE otherwise.
1704  *
1705  * Since: 3.2
1706  *
1707  * Deprecated: 3.6: Use e_source_remove_finish() instead.
1708  **/
1709 gboolean
1710 e_client_remove_finish (EClient *client,
1711                         GAsyncResult *result,
1712                         GError **error)
1713 {
1714         EClientClass *class;
1715
1716         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1717
1718         class = E_CLIENT_GET_CLASS (client);
1719         g_return_val_if_fail (class != NULL, FALSE);
1720         g_return_val_if_fail (class->remove_finish != NULL, FALSE);
1721
1722         return class->remove_finish (client, result, error);
1723 }
1724
1725 /**
1726  * e_client_remove_sync:
1727  * @client: an #EClient
1728  * @cancellable: a #GCancellable; can be %NULL
1729  * @error: (out): a #GError to set an error, if any
1730  *
1731  * Removes the backing data for this #EClient. For example, with the file
1732  * backend this deletes the database file. You cannot get it back!
1733  *
1734  * Returns: %TRUE if successful, %FALSE otherwise.
1735  *
1736  * Since: 3.2
1737  *
1738  * Deprecated: 3.6: Use e_source_remove_sync() instead.
1739  **/
1740 gboolean
1741 e_client_remove_sync (EClient *client,
1742                       GCancellable *cancellable,
1743                       GError **error)
1744 {
1745         EClientClass *class;
1746
1747         class = E_CLIENT_GET_CLASS (client);
1748         g_return_val_if_fail (class != NULL, FALSE);
1749         g_return_val_if_fail (class->remove_sync != NULL, FALSE);
1750
1751         return class->remove_sync (client, cancellable, error);
1752 }
1753
1754 /**
1755  * e_client_refresh:
1756  * @client: an #EClient
1757  * @cancellable: a #GCancellable; can be %NULL
1758  * @callback: callback to call when a result is ready
1759  * @user_data: user data for the @callback
1760  *
1761  * Initiates refresh on the @client. Finishing the method doesn't mean
1762  * that the refresh is done, backend only notifies whether it started
1763  * refreshing or not. Use e_client_check_refresh_supported() to check
1764  * whether the backend supports this method.
1765  * The call is finished by e_client_refresh_finish() from the @callback.
1766  *
1767  * Since: 3.2
1768  **/
1769 void
1770 e_client_refresh (EClient *client,
1771                   GCancellable *cancellable,
1772                   GAsyncReadyCallback callback,
1773                   gpointer user_data)
1774 {
1775         EClientClass *class;
1776
1777         g_return_if_fail (E_IS_CLIENT (client));
1778         g_return_if_fail (callback != NULL);
1779
1780         class = E_CLIENT_GET_CLASS (client);
1781         g_return_if_fail (class != NULL);
1782         g_return_if_fail (class->refresh != NULL);
1783
1784         class->refresh (client, cancellable, callback, user_data);
1785 }
1786
1787 /**
1788  * e_client_refresh_finish:
1789  * @client: an #EClient
1790  * @result: a #GAsyncResult
1791  * @error: (out): a #GError to set an error, if any
1792  *
1793  * Finishes previous call of e_client_refresh().
1794  *
1795  * Returns: %TRUE if successful, %FALSE otherwise.
1796  *
1797  * Since: 3.2
1798  **/
1799 gboolean
1800 e_client_refresh_finish (EClient *client,
1801                          GAsyncResult *result,
1802                          GError **error)
1803 {
1804         EClientClass *class;
1805
1806         g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1807
1808         class = E_CLIENT_GET_CLASS (client);
1809         g_return_val_if_fail (class != NULL, FALSE);
1810         g_return_val_if_fail (class->refresh_finish != NULL, FALSE);
1811
1812         return class->refresh_finish (client, result, error);
1813 }
1814
1815 /**
1816  * e_client_refresh_sync:
1817  * @client: an #EClient
1818  * @cancellable: a #GCancellable; can be %NULL
1819  * @error: (out): a #GError to set an error, if any
1820  *
1821  * Initiates refresh on the @client. Finishing the method doesn't mean
1822  * that the refresh is done, backend only notifies whether it started
1823  * refreshing or not. Use e_client_check_refresh_supported() to check
1824  * whether the backend supports this method.
1825  *
1826  * Returns: %TRUE if successful, %FALSE otherwise.
1827  *
1828  * Since: 3.2
1829  **/
1830 gboolean
1831 e_client_refresh_sync (EClient *client,
1832                        GCancellable *cancellable,
1833                        GError **error)
1834 {
1835         EClientClass *class;
1836
1837         class = E_CLIENT_GET_CLASS (client);
1838         g_return_val_if_fail (class != NULL, FALSE);
1839         g_return_val_if_fail (class->refresh_sync != NULL, FALSE);
1840
1841         return class->refresh_sync (client, cancellable, error);
1842 }
1843
1844 /**
1845  * e_client_util_slist_to_strv:
1846  * @strings: (element-type utf8): a #GSList of strings (const gchar *)
1847  *
1848  * Convert a list of strings into a %NULL-terminated array of strings.
1849  *
1850  * Returns: (transfer full): Newly allocated %NULL-terminated array of strings.
1851  * The returned pointer should be freed with g_strfreev().
1852  *
1853  * Note: Paired function for this is e_client_util_strv_to_slist().
1854  *
1855  * Since: 3.2
1856  *
1857  * Deprecated: 3.8: Use e_util_slist_to_strv() instead.
1858  **/
1859 gchar **
1860 e_client_util_slist_to_strv (const GSList *strings)
1861 {
1862         return e_util_slist_to_strv (strings);
1863 }
1864
1865 /**
1866  * e_client_util_strv_to_slist:
1867  * @strv: a %NULL-terminated array of strings (const gchar *)
1868  *
1869  * Convert a %NULL-terminated array of strings to a list of strings.
1870  *
1871  * Returns: (transfer full) (element-type utf8): Newly allocated #GSList of
1872  * newly allocated strings. The returned pointer should be freed with
1873  * e_client_util_free_string_slist().
1874  *
1875  * Note: Paired function for this is e_client_util_slist_to_strv().
1876  *
1877  * Since: 3.2
1878  *
1879  * Deprecated: 3.8: Use e_util_strv_to_slist() instead.
1880  **/
1881 GSList *
1882 e_client_util_strv_to_slist (const gchar * const *strv)
1883 {
1884         return e_util_strv_to_slist (strv);
1885 }
1886
1887 /**
1888  * e_client_util_copy_string_slist:
1889  * @copy_to: (element-type utf8) (allow-none): Where to copy; may be %NULL
1890  * @strings: (element-type utf8): #GSList of strings to be copied
1891  *
1892  * Copies the #GSList of strings to the end of @copy_to.
1893  *
1894  * Returns: (transfer full) (element-type utf8): New head of @copy_to.
1895  * The returned pointer can be freed with e_client_util_free_string_slist().
1896  *
1897  * Since: 3.2
1898  *
1899  * Deprecated: 3.8: Use e_util_copy_string_slist() instead.
1900  **/
1901 GSList *
1902 e_client_util_copy_string_slist (GSList *copy_to,
1903                                  const GSList *strings)
1904 {
1905         return e_util_copy_string_slist (copy_to, strings);
1906 }
1907
1908 /**
1909  * e_client_util_copy_object_slist:
1910  * @copy_to: (element-type GObject) (allow-none): Where to copy; may be %NULL
1911  * @objects: (element-type GObject): #GSList of #GObject<!-- -->s to be copied
1912  *
1913  * Copies a #GSList of #GObject<!-- -->s to the end of @copy_to.
1914  *
1915  * Returns: (transfer full) (element-type GObject): New head of @copy_to.
1916  * The returned pointer can be freed with e_client_util_free_object_slist().
1917  *
1918  * Since: 3.2
1919  *
1920  * Deprecated: 3.8: Use e_util_copy_object_slist() instead.
1921  **/
1922 GSList *
1923 e_client_util_copy_object_slist (GSList *copy_to,
1924                                  const GSList *objects)
1925 {
1926         return e_util_copy_object_slist (copy_to, objects);
1927 }
1928
1929 /**
1930  * e_client_util_free_string_slist:
1931  * @strings: (element-type utf8): a #GSList of strings (gchar *)
1932  *
1933  * Frees memory previously allocated by e_client_util_strv_to_slist().
1934  *
1935  * Since: 3.2
1936  *
1937  * Deprecated: 3.8: Use g_slist_free_full() instead.
1938  **/
1939 void
1940 e_client_util_free_string_slist (GSList *strings)
1941 {
1942         e_util_free_string_slist (strings);
1943 }
1944
1945 /**
1946  * e_client_util_free_object_slist:
1947  * @objects: (element-type GObject): a #GSList of #GObject<!-- -->s
1948  *
1949  * Calls g_object_unref() on each member of @objects and then frees @objects
1950  * itself.
1951  *
1952  * Since: 3.2
1953  *
1954  * Deprecated: 3.8: Use g_slist_free_full() instead.
1955  **/
1956 void
1957 e_client_util_free_object_slist (GSList *objects)
1958 {
1959         e_util_free_object_slist (objects);
1960 }
1961
1962 /**
1963  * e_client_util_parse_comma_strings:
1964  * @strings: string of comma-separated values
1965  *
1966  * Parses comma-separated list of values into #GSList.
1967  *
1968  * Returns: (transfer full) (element-type utf8): Newly allocated #GSList of
1969  * newly allocated strings corresponding to values parsed from @strings.
1970  * Free the returned pointer with e_client_util_free_string_slist().
1971  *
1972  * Since: 3.2
1973  **/
1974 GSList *
1975 e_client_util_parse_comma_strings (const gchar *strings)
1976 {
1977         GSList *strs_slist = NULL;
1978         gchar **strs_strv = NULL;
1979         gint ii;
1980
1981         if (!strings || !*strings)
1982                 return NULL;
1983
1984         strs_strv = g_strsplit (strings, ",", -1);
1985         g_return_val_if_fail (strs_strv != NULL, NULL);
1986
1987         for (ii = 0; strs_strv && strs_strv[ii]; ii++) {
1988                 gchar *str = g_strstrip (strs_strv[ii]);
1989
1990                 if (str && *str)
1991                         strs_slist = g_slist_prepend (strs_slist, g_strdup (str));
1992         }
1993
1994         g_strfreev (strs_strv);
1995
1996         return g_slist_reverse (strs_slist);
1997 }
1998
1999 /**
2000  * e_client_unwrap_dbus_error:
2001  * @client: an #EClient
2002  * @dbus_error: a #GError returned bu D-Bus
2003  * @out_error: a #GError variable where to store the result
2004  *
2005  * Unwraps D-Bus error to local error. @dbus_error is automatically freed.
2006  * @dbus_erorr and @out_error can point to the same variable.
2007  *
2008  * Since: 3.2
2009  *
2010  * Deprecated: 3.8: Use g_dbus_error_strip_remote_error() instead.
2011  **/
2012 void
2013 e_client_unwrap_dbus_error (EClient *client,
2014                             GError *dbus_error,
2015                             GError **out_error)
2016 {
2017         EClientClass *class;
2018
2019         g_return_if_fail (E_IS_CLIENT (client));
2020
2021         class = E_CLIENT_GET_CLASS (client);
2022         g_return_if_fail (class != NULL);
2023         g_return_if_fail (class->unwrap_dbus_error != NULL);
2024
2025         if (!dbus_error || !out_error) {
2026                 if (dbus_error)
2027                         g_error_free (dbus_error);
2028         } else {
2029                 class->unwrap_dbus_error (client, dbus_error, out_error);
2030         }
2031 }
2032
2033 /**
2034  * e_client_util_unwrap_dbus_error:
2035  * @dbus_error: DBus #GError to unwrap
2036  * @client_error: (out): Resulting #GError; can be %NULL
2037  * @known_errors: List of known errors against which try to match
2038  * @known_errors_count: How many items are stored in @known_errors
2039  * @known_errors_domain: Error domain for @known_errors
2040  * @fail_when_none_matched: Whether to fail when none of @known_errors matches
2041  *
2042  * The function takes a @dbus_error and tries to find a match in @known_errors
2043  * for it, if it is a G_IO_ERROR, G_IO_ERROR_DBUS_ERROR. If it is anything else
2044  * then the @dbus_error is moved to @client_error.
2045  *
2046  * The @fail_when_none_matched influences behaviour. If it's %TRUE, and none of
2047  * @known_errors matches, or this is not a G_IO_ERROR_DBUS_ERROR, then %FALSE
2048  * is returned and the @client_error is left without change. Otherwise, the
2049  * @fail_when_none_matched is %FALSE, the error is always processed and will
2050  * result in E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR if none of @known_error
2051  * matches.
2052  *
2053  * Returns: Whether was @dbus_error processed into @client_error.
2054  *
2055  * Note: The @dbus_error is automatically freed if returned %TRUE.
2056  *
2057  * Since: 3.2
2058  *
2059  * Deprecated: 3.8: This function is no longer used.
2060  **/
2061 gboolean
2062 e_client_util_unwrap_dbus_error (GError *dbus_error,
2063                                  GError **client_error,
2064                                  const EClientErrorsList *known_errors,
2065                                  guint known_errors_count,
2066                                  GQuark known_errors_domain,
2067                                  gboolean fail_when_none_matched)
2068 {
2069         if (!client_error) {
2070                 if (dbus_error)
2071                         g_error_free (dbus_error);
2072                 return TRUE;
2073         }
2074
2075         if (!dbus_error) {
2076                 *client_error = NULL;
2077                 return TRUE;
2078         }
2079
2080         if (dbus_error->domain == known_errors_domain) {
2081                 *client_error = dbus_error;
2082                 return TRUE;
2083         }
2084
2085         if (known_errors) {
2086                 if (g_error_matches (dbus_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) {
2087                         gchar *name;
2088                         gint ii;
2089
2090                         name = g_dbus_error_get_remote_error (dbus_error);
2091
2092                         for (ii = 0; ii < known_errors_count; ii++) {
2093                                 if (g_ascii_strcasecmp (known_errors[ii].name, name) == 0) {
2094                                         g_free (name);
2095
2096                                         g_dbus_error_strip_remote_error (dbus_error);
2097                                         *client_error = g_error_new_literal (
2098                                                 known_errors_domain,
2099                                                 known_errors[ii].err_code,
2100                                                 dbus_error->message);
2101                                         g_error_free (dbus_error);
2102                                         return TRUE;
2103                                 }
2104                         }
2105
2106                         g_free (name);
2107                 }
2108         }
2109
2110         if (fail_when_none_matched)
2111                 return FALSE;
2112
2113         if (g_error_matches (dbus_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) {
2114                 g_dbus_error_strip_remote_error (dbus_error);
2115                 *client_error = g_error_new_literal (
2116                         E_CLIENT_ERROR,
2117                         E_CLIENT_ERROR_OTHER_ERROR,
2118                         dbus_error->message);
2119                 g_error_free (dbus_error);
2120         } else {
2121                 g_dbus_error_strip_remote_error (dbus_error);
2122                 *client_error = dbus_error;
2123         }
2124
2125         return TRUE;
2126 }
2127