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