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