802582f57548775856d268a06c9f783b6c5790bb
[platform/upstream/evolution-data-server.git] / addressbook / libebook / e-book-client.c
1 /*
2  * e-book-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  * Copyright (C) 2012 Intel Corporation
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <locale.h>
28 #include <glib/gi18n-lib.h>
29 #include <gio/gio.h>
30
31 /* Private D-Bus classes. */
32 #include <e-dbus-address-book.h>
33 #include <e-dbus-address-book-factory.h>
34 #include <e-dbus-direct-book.h>
35
36 #include <libedataserver/libedataserver.h>
37 #include <libedataserver/e-client-private.h>
38
39 #include <libebackend/libebackend.h>
40 #include <libedata-book/libedata-book.h>
41
42 #include "e-book-client.h"
43
44 #define E_BOOK_CLIENT_GET_PRIVATE(obj) \
45         (G_TYPE_INSTANCE_GET_PRIVATE \
46         ((obj), E_TYPE_BOOK_CLIENT, EBookClientPrivate))
47
48 /* Set this to a sufficiently large value
49  * to cover most long-running operations. */
50 #define DBUS_PROXY_TIMEOUT_MS (3 * 60 * 1000)  /* 3 minutes */
51
52 typedef struct _AsyncContext AsyncContext;
53 typedef struct _SignalClosure SignalClosure;
54 typedef struct _ConnectClosure ConnectClosure;
55 typedef struct _RunInThreadClosure RunInThreadClosure;
56
57 struct _EBookClientPrivate {
58         EDBusAddressBook *dbus_proxy;
59         EBookBackend *direct_backend;
60         guint name_watcher_id;
61
62         gulong dbus_proxy_error_handler_id;
63         gulong dbus_proxy_notify_handler_id;
64 };
65
66 struct _AsyncContext {
67         EContact *contact;
68         EBookClientView *client_view;
69         GSList *object_list;
70         GSList *string_list;
71         gchar *sexp;
72         gchar *uid;
73 };
74
75 struct _SignalClosure {
76         GWeakRef client;
77         gchar *property_name;
78         gchar *error_message;
79 };
80
81 struct _ConnectClosure {
82         ESource *source;
83         GCancellable *cancellable;
84 };
85
86 struct _RunInThreadClosure {
87         GSimpleAsyncThreadFunc func;
88         GSimpleAsyncResult *simple;
89         GCancellable *cancellable;
90 };
91
92 /* Forward Declarations */
93 static void     e_book_client_initable_init
94                                         (GInitableIface *interface);
95 static void     e_book_client_async_initable_init
96                                         (GAsyncInitableIface *interface);
97
98 G_DEFINE_TYPE_WITH_CODE (
99         EBookClient,
100         e_book_client,
101         E_TYPE_CLIENT,
102         G_IMPLEMENT_INTERFACE (
103                 G_TYPE_INITABLE,
104                 e_book_client_initable_init)
105         G_IMPLEMENT_INTERFACE (
106                 G_TYPE_ASYNC_INITABLE,
107                 e_book_client_async_initable_init))
108
109 static void
110 async_context_free (AsyncContext *async_context)
111 {
112         if (async_context->contact != NULL)
113                 g_object_unref (async_context->contact);
114
115         if (async_context->client_view != NULL)
116                 g_object_unref (async_context->client_view);
117
118         g_slist_free_full (
119                 async_context->object_list,
120                 (GDestroyNotify) g_object_unref);
121
122         g_slist_free_full (
123                 async_context->string_list,
124                 (GDestroyNotify) g_free);
125
126         g_free (async_context->sexp);
127         g_free (async_context->uid);
128
129         g_slice_free (AsyncContext, async_context);
130 }
131
132 static void
133 signal_closure_free (SignalClosure *signal_closure)
134 {
135         g_weak_ref_set (&signal_closure->client, NULL);
136
137         g_free (signal_closure->property_name);
138         g_free (signal_closure->error_message);
139
140         g_slice_free (SignalClosure, signal_closure);
141 }
142
143 static void
144 connect_closure_free (ConnectClosure *connect_closure)
145 {
146         if (connect_closure->source != NULL)
147                 g_object_unref (connect_closure->source);
148
149         if (connect_closure->cancellable != NULL)
150                 g_object_unref (connect_closure->cancellable);
151
152         g_slice_free (ConnectClosure, connect_closure);
153 }
154
155 static void
156 run_in_thread_closure_free (RunInThreadClosure *run_in_thread_closure)
157 {
158         if (run_in_thread_closure->simple != NULL)
159                 g_object_unref (run_in_thread_closure->simple);
160
161         if (run_in_thread_closure->cancellable != NULL)
162                 g_object_unref (run_in_thread_closure->cancellable);
163
164         g_slice_free (RunInThreadClosure, run_in_thread_closure);
165 }
166
167 /*
168  * Well-known book backend properties:
169  * @BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS: Retrieves comma-separated list
170  *   of required fields by the backend. Use e_client_util_parse_comma_strings()
171  *   to parse returned string value into a #GSList. These fields are required
172  *   to be filled in for all contacts.
173  * @BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS: Retrieves comma-separated list
174  *   of supported fields by the backend. Use e_client_util_parse_comma_strings()
175  *   to parse returned string value into a #GSList. These fields can be
176  *   stored for contacts.
177  *
178  * See also: @CLIENT_BACKEND_PROPERTY_OPENED, @CLIENT_BACKEND_PROPERTY_OPENING,
179  *   @CLIENT_BACKEND_PROPERTY_ONLINE, @CLIENT_BACKEND_PROPERTY_READONLY
180  *   @CLIENT_BACKEND_PROPERTY_CACHE_DIR, @CLIENT_BACKEND_PROPERTY_CAPABILITIES
181  */
182
183 static EBookBackend *
184 book_client_load_direct_backend (ESourceRegistry *registry,
185                                  ESource *source,
186                                  const gchar *backend_path,
187                                  const gchar *backend_name,
188                                  const gchar *config,
189                                  GError **error)
190 {
191         static GHashTable *modules_table = NULL;
192         G_LOCK_DEFINE_STATIC (modules_table);
193
194         EModule *module;
195         GType factory_type;
196         EBookBackend *backend;
197         EBookBackendFactoryClass *factory_class;
198
199         g_return_val_if_fail (backend_path != NULL, NULL);
200         g_return_val_if_fail (backend_name != NULL, NULL);
201
202         G_LOCK (modules_table);
203
204         if (modules_table == NULL)
205                 modules_table = g_hash_table_new (
206                         (GHashFunc) g_str_hash,
207                         (GEqualFunc) g_str_equal);
208
209         module = g_hash_table_lookup (modules_table, backend_path);
210
211         if (module == NULL) {
212                 module = e_module_new (backend_path);
213                 g_hash_table_insert (
214                         modules_table, g_strdup (backend_path), module);
215         }
216
217         G_UNLOCK (modules_table);
218
219         if (!g_type_module_use (G_TYPE_MODULE (module))) {
220                 g_set_error (
221                         error, E_CLIENT_ERROR,
222                         E_CLIENT_ERROR_OTHER_ERROR,
223                         "Failed to use EModule at path '%s'",
224                         backend_path);
225                 return NULL;
226         }
227
228         factory_type = g_type_from_name (backend_name);
229         if (factory_type == G_TYPE_INVALID) {
230                 g_set_error (
231                         error, E_CLIENT_ERROR,
232                         E_CLIENT_ERROR_OTHER_ERROR,
233                         "Failed to get backend factory '%s' "
234                         "from EModule at path '%s'",
235                         backend_name, backend_path);
236                 g_type_module_unuse (G_TYPE_MODULE (module));
237                 return NULL;
238         }
239
240         factory_class = g_type_class_ref (factory_type);
241
242         backend = g_object_new (
243                 factory_class->backend_type,
244                 "registry", registry,
245                 "source", source, NULL);
246
247         /* The backend must be configured for direct access
248          * before calling g_initable_init(), since backends
249          * can access their content in initable_init(). */
250         e_book_backend_configure_direct (backend, config);
251
252         if (!g_initable_init (G_INITABLE (backend), NULL, error)) {
253                 g_type_module_unuse (G_TYPE_MODULE (module));
254                 g_object_unref (backend);
255                 backend = NULL;
256         }
257
258         g_type_class_unref (factory_class);
259
260         /* XXX Until backend methods can be updated, EBookBackend
261          *     still needs an EDataBook to catch "respond" calls. */
262         if (backend != NULL) {
263                 EDataBook *data_book;
264
265                 data_book = g_initable_new (
266                         E_TYPE_DATA_BOOK, NULL, error,
267                         "backend", backend, NULL);
268
269                 if (data_book == NULL) {
270                         g_type_module_unuse (G_TYPE_MODULE (module));
271                         g_object_unref (backend);
272                         backend = NULL;
273                 }
274
275                 g_clear_object (&data_book);
276         }
277
278         return backend;
279 }
280
281 static gpointer
282 book_client_dbus_thread (gpointer user_data)
283 {
284         GMainContext *main_context = user_data;
285         GMainLoop *main_loop;
286
287         g_main_context_push_thread_default (main_context);
288
289         main_loop = g_main_loop_new (main_context, FALSE);
290         g_main_loop_run (main_loop);
291         g_main_loop_unref (main_loop);
292
293         g_main_context_pop_thread_default (main_context);
294
295         g_main_context_unref (main_context);
296
297         return NULL;
298 }
299
300 static gpointer
301 book_client_dbus_thread_init (gpointer unused)
302 {
303         GMainContext *main_context;
304
305         main_context = g_main_context_new ();
306
307         /* This thread terminates when the process itself terminates, so
308          * no need to worry about unreferencing the returned GThread. */
309         g_thread_new (
310                 "book-client-dbus-thread",
311                 book_client_dbus_thread,
312                 g_main_context_ref (main_context));
313
314         return main_context;
315 }
316
317 static GMainContext *
318 book_client_ref_dbus_main_context (void)
319 {
320         static GOnce book_client_dbus_thread_once = G_ONCE_INIT;
321
322         g_once (
323                 &book_client_dbus_thread_once,
324                 book_client_dbus_thread_init, NULL);
325
326         return g_main_context_ref (book_client_dbus_thread_once.retval);
327 }
328
329 static gboolean
330 book_client_run_in_dbus_thread_idle_cb (gpointer user_data)
331 {
332         RunInThreadClosure *closure = user_data;
333         GObject *source_object;
334         GAsyncResult *result;
335
336         result = G_ASYNC_RESULT (closure->simple);
337         source_object = g_async_result_get_source_object (result);
338
339         closure->func (
340                 closure->simple,
341                 source_object,
342                 closure->cancellable);
343
344         if (source_object != NULL)
345                 g_object_unref (source_object);
346
347         g_simple_async_result_complete_in_idle (closure->simple);
348
349         return FALSE;
350 }
351
352 static void
353 book_client_run_in_dbus_thread (GSimpleAsyncResult *simple,
354                                 GSimpleAsyncThreadFunc func,
355                                 gint io_priority,
356                                 GCancellable *cancellable)
357 {
358         RunInThreadClosure *closure;
359         GMainContext *main_context;
360         GSource *idle_source;
361
362         main_context = book_client_ref_dbus_main_context ();
363
364         closure = g_slice_new0 (RunInThreadClosure);
365         closure->func = func;
366         closure->simple = g_object_ref (simple);
367
368         if (G_IS_CANCELLABLE (cancellable))
369                 closure->cancellable = g_object_ref (cancellable);
370
371         idle_source = g_idle_source_new ();
372         g_source_set_priority (idle_source, io_priority);
373         g_source_set_callback (
374                 idle_source, book_client_run_in_dbus_thread_idle_cb,
375                 closure, (GDestroyNotify) run_in_thread_closure_free);
376         g_source_attach (idle_source, main_context);
377         g_source_unref (idle_source);
378
379         g_main_context_unref (main_context);
380 }
381
382 static gboolean
383 book_client_emit_backend_died_idle_cb (gpointer user_data)
384 {
385         SignalClosure *signal_closure = user_data;
386         EClient *client;
387
388         client = g_weak_ref_get (&signal_closure->client);
389
390         if (client != NULL) {
391                 g_signal_emit_by_name (client, "backend-died");
392                 g_object_unref (client);
393         }
394
395         return FALSE;
396 }
397
398 static gboolean
399 book_client_emit_backend_error_idle_cb (gpointer user_data)
400 {
401         SignalClosure *signal_closure = user_data;
402         EClient *client;
403
404         client = g_weak_ref_get (&signal_closure->client);
405
406         if (client != NULL) {
407                 g_signal_emit_by_name (
408                         client, "backend-error",
409                         signal_closure->error_message);
410                 g_object_unref (client);
411         }
412
413         return FALSE;
414 }
415
416 static gboolean
417 book_client_emit_backend_property_changed_idle_cb (gpointer user_data)
418 {
419         SignalClosure *signal_closure = user_data;
420         EClient *client;
421
422         client = g_weak_ref_get (&signal_closure->client);
423
424         if (client != NULL) {
425                 gchar *prop_value = NULL;
426
427                 /* XXX Despite appearances, this function does not block. */
428                 e_client_get_backend_property_sync (
429                         client,
430                         signal_closure->property_name,
431                         &prop_value, NULL, NULL);
432
433                 if (prop_value != NULL) {
434                         g_signal_emit_by_name (
435                                 client,
436                                 "backend-property-changed",
437                                 signal_closure->property_name,
438                                 prop_value);
439                         g_free (prop_value);
440                 }
441
442                 g_object_unref (client);
443         }
444
445         return FALSE;
446 }
447
448 static void
449 book_client_dbus_proxy_error_cb (EDBusAddressBook *dbus_proxy,
450                                  const gchar *error_message,
451                                  GWeakRef *client_weak_ref)
452 {
453         EClient *client;
454
455         client = g_weak_ref_get (client_weak_ref);
456
457         if (client != NULL) {
458                 GSource *idle_source;
459                 GMainContext *main_context;
460                 SignalClosure *signal_closure;
461
462                 signal_closure = g_slice_new0 (SignalClosure);
463                 g_weak_ref_set (&signal_closure->client, client);
464                 signal_closure->error_message = g_strdup (error_message);
465
466                 main_context = e_client_ref_main_context (client);
467
468                 idle_source = g_idle_source_new ();
469                 g_source_set_callback (
470                         idle_source,
471                         book_client_emit_backend_error_idle_cb,
472                         signal_closure,
473                         (GDestroyNotify) signal_closure_free);
474                 g_source_attach (idle_source, main_context);
475                 g_source_unref (idle_source);
476
477                 g_main_context_unref (main_context);
478
479                 g_object_unref (client);
480         }
481 }
482
483 static void
484 book_client_dbus_proxy_notify_cb (EDBusAddressBook *dbus_proxy,
485                                   GParamSpec *pspec,
486                                   GWeakRef *client_weak_ref)
487 {
488         EClient *client;
489         const gchar *backend_prop_name = NULL;
490
491         client = g_weak_ref_get (client_weak_ref);
492         if (client == NULL)
493                 return;
494
495         if (g_str_equal (pspec->name, "cache-dir")) {
496                 backend_prop_name = CLIENT_BACKEND_PROPERTY_CACHE_DIR;
497         }
498
499         if (g_str_equal (pspec->name, "capabilities")) {
500                 gchar **strv;
501                 gchar *csv = NULL;
502
503                 backend_prop_name = CLIENT_BACKEND_PROPERTY_CAPABILITIES;
504
505                 strv = e_dbus_address_book_dup_capabilities (dbus_proxy);
506                 if (strv != NULL) {
507                         csv = g_strjoinv (",", strv);
508                         g_strfreev (strv);
509                 }
510                 e_client_set_capabilities (client, csv);
511                 g_free (csv);
512         }
513
514         if (g_str_equal (pspec->name, "online")) {
515                 gboolean online;
516
517                 backend_prop_name = CLIENT_BACKEND_PROPERTY_ONLINE;
518
519                 online = e_dbus_address_book_get_online (dbus_proxy);
520                 e_client_set_online (client, online);
521         }
522
523         if (g_str_equal (pspec->name, "required-fields")) {
524                 backend_prop_name = BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS;
525         }
526
527         if (g_str_equal (pspec->name, "revision")) {
528                 backend_prop_name = CLIENT_BACKEND_PROPERTY_REVISION;
529         }
530
531         if (g_str_equal (pspec->name, "supported-fields")) {
532                 backend_prop_name = BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS;
533         }
534
535         if (g_str_equal (pspec->name, "writable")) {
536                 gboolean writable;
537
538                 backend_prop_name = CLIENT_BACKEND_PROPERTY_READONLY;
539
540                 writable = e_dbus_address_book_get_writable (dbus_proxy);
541                 e_client_set_readonly (client, !writable);
542         }
543
544         if (backend_prop_name != NULL) {
545                 GSource *idle_source;
546                 GMainContext *main_context;
547                 SignalClosure *signal_closure;
548
549                 signal_closure = g_slice_new0 (SignalClosure);
550                 g_weak_ref_set (&signal_closure->client, client);
551                 signal_closure->property_name = g_strdup (backend_prop_name);
552
553                 main_context = e_client_ref_main_context (client);
554
555                 idle_source = g_idle_source_new ();
556                 g_source_set_callback (
557                         idle_source,
558                         book_client_emit_backend_property_changed_idle_cb,
559                         signal_closure,
560                         (GDestroyNotify) signal_closure_free);
561                 g_source_attach (idle_source, main_context);
562                 g_source_unref (idle_source);
563
564                 g_main_context_unref (main_context);
565         }
566
567         g_object_unref (client);
568 }
569
570 static void
571 book_client_name_vanished_cb (GDBusConnection *connection,
572                               const gchar *name,
573                               GWeakRef *client_weak_ref)
574 {
575         EClient *client;
576
577         client = g_weak_ref_get (client_weak_ref);
578
579         if (client != NULL) {
580                 GSource *idle_source;
581                 GMainContext *main_context;
582                 SignalClosure *signal_closure;
583
584                 signal_closure = g_slice_new0 (SignalClosure);
585                 g_weak_ref_set (&signal_closure->client, client);
586
587                 main_context = e_client_ref_main_context (client);
588
589                 idle_source = g_idle_source_new ();
590                 g_source_set_callback (
591                         idle_source,
592                         book_client_emit_backend_died_idle_cb,
593                         signal_closure,
594                         (GDestroyNotify) signal_closure_free);
595                 g_source_attach (idle_source, main_context);
596                 g_source_unref (idle_source);
597
598                 g_main_context_unref (main_context);
599
600                 g_object_unref (client);
601         }
602 }
603
604 static void
605 book_client_dispose (GObject *object)
606 {
607         EBookClientPrivate *priv;
608
609         priv = E_BOOK_CLIENT_GET_PRIVATE (object);
610
611         if (priv->dbus_proxy_error_handler_id > 0) {
612                 g_signal_handler_disconnect (
613                         priv->dbus_proxy,
614                         priv->dbus_proxy_error_handler_id);
615                 priv->dbus_proxy_error_handler_id = 0;
616         }
617
618         if (priv->dbus_proxy_notify_handler_id > 0) {
619                 g_signal_handler_disconnect (
620                         priv->dbus_proxy,
621                         priv->dbus_proxy_notify_handler_id);
622                 priv->dbus_proxy_notify_handler_id = 0;
623         }
624
625         if (priv->dbus_proxy != NULL) {
626                 /* Call close() asynchronously so we don't block dispose().
627                  * Also omit a callback function, so the GDBusMessage uses
628                  * G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED. */
629                 e_dbus_address_book_call_close (
630                         priv->dbus_proxy, NULL, NULL, NULL);
631                 g_object_unref (priv->dbus_proxy);
632                 priv->dbus_proxy = NULL;
633         }
634
635         if (priv->direct_backend != NULL) {
636                 g_object_unref (priv->direct_backend);
637                 priv->direct_backend = NULL;
638         }
639
640         /* Chain up to parent's dispose() method. */
641         G_OBJECT_CLASS (e_book_client_parent_class)->dispose (object);
642 }
643
644 static void
645 book_client_finalize (GObject *object)
646 {
647         EBookClientPrivate *priv;
648
649         priv = E_BOOK_CLIENT_GET_PRIVATE (object);
650
651         if (priv->name_watcher_id > 0)
652                 g_bus_unwatch_name (priv->name_watcher_id);
653
654         /* Chain up to parent's finalize() method. */
655         G_OBJECT_CLASS (e_book_client_parent_class)->finalize (object);
656 }
657
658 static GDBusProxy *
659 book_client_get_dbus_proxy (EClient *client)
660 {
661         EBookClientPrivate *priv;
662
663         priv = E_BOOK_CLIENT_GET_PRIVATE (client);
664
665         return G_DBUS_PROXY (priv->dbus_proxy);
666 }
667
668 static gboolean
669 book_client_get_backend_property_sync (EClient *client,
670                                        const gchar *prop_name,
671                                        gchar **prop_value,
672                                        GCancellable *cancellable,
673                                        GError **error)
674 {
675         EBookClient *book_client;
676         EDBusAddressBook *dbus_proxy;
677         gchar **strv;
678
679         book_client = E_BOOK_CLIENT (client);
680         dbus_proxy = book_client->priv->dbus_proxy;
681
682         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
683                 *prop_value = g_strdup ("TRUE");
684                 return TRUE;
685         }
686
687         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
688                 *prop_value = g_strdup ("FALSE");
689                 return TRUE;
690         }
691
692         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
693                 if (e_dbus_address_book_get_online (dbus_proxy))
694                         *prop_value = g_strdup ("TRUE");
695                 else
696                         *prop_value = g_strdup ("FALSE");
697                 return TRUE;
698         }
699
700         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
701                 if (e_dbus_address_book_get_writable (dbus_proxy))
702                         *prop_value = g_strdup ("FALSE");
703                 else
704                         *prop_value = g_strdup ("TRUE");
705                 return TRUE;
706         }
707
708         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
709                 *prop_value = e_dbus_address_book_dup_cache_dir (dbus_proxy);
710                 return TRUE;
711         }
712
713         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_REVISION)) {
714                 *prop_value = e_dbus_address_book_dup_revision (dbus_proxy);
715                 return TRUE;
716         }
717
718         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
719                 strv = e_dbus_address_book_dup_capabilities (dbus_proxy);
720                 if (strv != NULL)
721                         *prop_value = g_strjoinv (",", strv);
722                 else
723                         *prop_value = g_strdup ("");
724                 g_strfreev (strv);
725                 return TRUE;
726         }
727
728         if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
729                 strv = e_dbus_address_book_dup_required_fields (dbus_proxy);
730                 if (strv != NULL)
731                         *prop_value = g_strjoinv (",", strv);
732                 else
733                         *prop_value = g_strdup ("");
734                 g_strfreev (strv);
735                 return TRUE;
736         }
737
738         if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
739                 strv = e_dbus_address_book_dup_supported_fields (dbus_proxy);
740                 if (strv != NULL)
741                         *prop_value = g_strjoinv (",", strv);
742                 else
743                         *prop_value = g_strdup ("");
744                 g_strfreev (strv);
745                 return TRUE;
746         }
747
748         g_set_error (
749                 error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED,
750                 _("Unknown book property '%s'"), prop_name);
751
752         return TRUE;
753 }
754
755 static gboolean
756 book_client_set_backend_property_sync (EClient *client,
757                                        const gchar *prop_name,
758                                        const gchar *prop_value,
759                                        GCancellable *cancellable,
760                                        GError **error)
761 {
762         g_set_error (
763                 error, E_CLIENT_ERROR,
764                 E_CLIENT_ERROR_NOT_SUPPORTED,
765                 _("Cannot change value of book property '%s'"),
766                 prop_name);
767
768         return FALSE;
769 }
770
771 static gboolean
772 book_client_open_sync (EClient *client,
773                        gboolean only_if_exists,
774                        GCancellable *cancellable,
775                        GError **error)
776 {
777         EBookClient *book_client;
778         GError *local_error = NULL;
779
780         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
781
782         book_client = E_BOOK_CLIENT (client);
783
784         e_dbus_address_book_call_open_sync (
785                 book_client->priv->dbus_proxy, cancellable, &local_error);
786
787         if (local_error != NULL) {
788                 g_dbus_error_strip_remote_error (local_error);
789                 g_propagate_error (error, local_error);
790                 return FALSE;
791         }
792
793         return TRUE;
794 }
795
796 static gboolean
797 book_client_refresh_sync (EClient *client,
798                           GCancellable *cancellable,
799                           GError **error)
800 {
801         EBookClient *book_client;
802         GError *local_error = NULL;
803
804         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
805
806         book_client = E_BOOK_CLIENT (client);
807
808         e_dbus_address_book_call_refresh_sync (
809                 book_client->priv->dbus_proxy, cancellable, &local_error);
810
811         if (local_error != NULL) {
812                 g_dbus_error_strip_remote_error (local_error);
813                 g_propagate_error (error, local_error);
814                 return FALSE;
815         }
816
817         return TRUE;
818 }
819
820 static void
821 book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
822                                  GObject *source_object,
823                                  GCancellable *cancellable)
824 {
825         EBookClientPrivate *priv;
826         EDBusAddressBookFactory *factory_proxy;
827         GDBusConnection *connection;
828         GDBusProxy *proxy;
829         EClient *client;
830         ESource *source;
831         const gchar *uid;
832         gchar *object_path = NULL;
833         gulong handler_id;
834         GError *local_error = NULL;
835
836         priv = E_BOOK_CLIENT_GET_PRIVATE (source_object);
837
838         client = E_CLIENT (source_object);
839         source = e_client_get_source (client);
840         uid = e_source_get_uid (source);
841
842         connection = g_bus_get_sync (
843                 G_BUS_TYPE_SESSION, cancellable, &local_error);
844
845         /* Sanity check. */
846         g_return_if_fail (
847                 ((connection != NULL) && (local_error == NULL)) ||
848                 ((connection == NULL) && (local_error != NULL)));
849
850         if (local_error != NULL) {
851                 g_dbus_error_strip_remote_error (local_error);
852                 g_simple_async_result_take_error (simple, local_error);
853                 return;
854         }
855
856         factory_proxy = e_dbus_address_book_factory_proxy_new_sync (
857                 connection,
858                 G_DBUS_PROXY_FLAGS_NONE,
859                 ADDRESS_BOOK_DBUS_SERVICE_NAME,
860                 "/org/gnome/evolution/dataserver/AddressBookFactory",
861                 cancellable, &local_error);
862
863         /* Sanity check. */
864         g_return_if_fail (
865                 ((factory_proxy != NULL) && (local_error == NULL)) ||
866                 ((factory_proxy == NULL) && (local_error != NULL)));
867
868         if (local_error != NULL) {
869                 g_dbus_error_strip_remote_error (local_error);
870                 g_simple_async_result_take_error (simple, local_error);
871                 g_object_unref (connection);
872                 return;
873         }
874
875         e_dbus_address_book_factory_call_open_address_book_sync (
876                 factory_proxy, uid, &object_path, cancellable, &local_error);
877
878         g_object_unref (factory_proxy);
879
880         /* Sanity check. */
881         g_return_if_fail (
882                 ((object_path != NULL) && (local_error == NULL)) ||
883                 ((object_path == NULL) && (local_error != NULL)));
884
885         if (local_error != NULL) {
886                 g_dbus_error_strip_remote_error (local_error);
887                 g_simple_async_result_take_error (simple, local_error);
888                 g_object_unref (connection);
889                 return;
890         }
891
892         priv->dbus_proxy = e_dbus_address_book_proxy_new_sync (
893                 connection,
894                 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
895                 ADDRESS_BOOK_DBUS_SERVICE_NAME,
896                 object_path, cancellable, &local_error);
897
898         g_free (object_path);
899
900         /* Sanity check. */
901         g_return_if_fail (
902                 ((priv->dbus_proxy != NULL) && (local_error == NULL)) ||
903                 ((priv->dbus_proxy == NULL) && (local_error != NULL)));
904
905         if (local_error != NULL) {
906                 g_dbus_error_strip_remote_error (local_error);
907                 g_simple_async_result_take_error (simple, local_error);
908                 g_object_unref (connection);
909                 return;
910         }
911
912         /* Configure our new GDBusProxy. */
913
914         proxy = G_DBUS_PROXY (priv->dbus_proxy);
915
916         g_dbus_proxy_set_default_timeout (proxy, DBUS_PROXY_TIMEOUT_MS);
917
918         priv->name_watcher_id = g_bus_watch_name_on_connection (
919                 connection,
920                 g_dbus_proxy_get_name (proxy),
921                 G_BUS_NAME_WATCHER_FLAGS_NONE,
922                 (GBusNameAppearedCallback) NULL,
923                 (GBusNameVanishedCallback) book_client_name_vanished_cb,
924                 e_weak_ref_new (client),
925                 (GDestroyNotify) e_weak_ref_free);
926
927         handler_id = g_signal_connect_data (
928                 proxy, "error",
929                 G_CALLBACK (book_client_dbus_proxy_error_cb),
930                 e_weak_ref_new (client),
931                 (GClosureNotify) e_weak_ref_free,
932                 0);
933         priv->dbus_proxy_error_handler_id = handler_id;
934
935         handler_id = g_signal_connect_data (
936                 proxy, "notify",
937                 G_CALLBACK (book_client_dbus_proxy_notify_cb),
938                 e_weak_ref_new (client),
939                 (GClosureNotify) e_weak_ref_free,
940                 0);
941         priv->dbus_proxy_notify_handler_id = handler_id;
942
943         /* Initialize our public-facing GObject properties. */
944         g_object_notify (G_OBJECT (proxy), "online");
945         g_object_notify (G_OBJECT (proxy), "writable");
946         g_object_notify (G_OBJECT (proxy), "capabilities");
947
948         g_object_unref (connection);
949 }
950
951 static gboolean
952 book_client_initable_init (GInitable *initable,
953                            GCancellable *cancellable,
954                            GError **error)
955 {
956         EAsyncClosure *closure;
957         GAsyncResult *result;
958         gboolean success;
959
960         closure = e_async_closure_new ();
961
962         g_async_initable_init_async (
963                 G_ASYNC_INITABLE (initable),
964                 G_PRIORITY_DEFAULT, cancellable,
965                 e_async_closure_callback, closure);
966
967         result = e_async_closure_wait (closure);
968
969         success = g_async_initable_init_finish (
970                 G_ASYNC_INITABLE (initable), result, error);
971
972         e_async_closure_free (closure);
973
974         return success;
975 }
976
977 static void
978 book_client_initable_init_async (GAsyncInitable *initable,
979                                  gint io_priority,
980                                  GCancellable *cancellable,
981                                  GAsyncReadyCallback callback,
982                                  gpointer user_data)
983 {
984         GSimpleAsyncResult *simple;
985
986         simple = g_simple_async_result_new (
987                 G_OBJECT (initable), callback, user_data,
988                 book_client_initable_init_async);
989
990         g_simple_async_result_set_check_cancellable (simple, cancellable);
991
992         book_client_run_in_dbus_thread (
993                 simple, book_client_init_in_dbus_thread,
994                 io_priority, cancellable);
995
996         g_object_unref (simple);
997 }
998
999 static gboolean
1000 book_client_initable_init_finish (GAsyncInitable *initable,
1001                                   GAsyncResult *result,
1002                                   GError **error)
1003 {
1004         GSimpleAsyncResult *simple;
1005
1006         g_return_val_if_fail (
1007                 g_simple_async_result_is_valid (
1008                 result, G_OBJECT (initable),
1009                 book_client_initable_init_async), FALSE);
1010
1011         simple = G_SIMPLE_ASYNC_RESULT (result);
1012
1013         /* Assume success unless a GError is set. */
1014         return !g_simple_async_result_propagate_error (simple, error);
1015 }
1016
1017 static void
1018 e_book_client_class_init (EBookClientClass *class)
1019 {
1020         GObjectClass *object_class;
1021         EClientClass *client_class;
1022
1023         g_type_class_add_private (class, sizeof (EBookClientPrivate));
1024
1025         object_class = G_OBJECT_CLASS (class);
1026         object_class->dispose = book_client_dispose;
1027         object_class->finalize = book_client_finalize;
1028
1029         client_class = E_CLIENT_CLASS (class);
1030         client_class->get_dbus_proxy = book_client_get_dbus_proxy;
1031         client_class->get_backend_property_sync = book_client_get_backend_property_sync;
1032         client_class->set_backend_property_sync = book_client_set_backend_property_sync;
1033         client_class->open_sync = book_client_open_sync;
1034         client_class->refresh_sync = book_client_refresh_sync;
1035 }
1036
1037 static void
1038 e_book_client_initable_init (GInitableIface *interface)
1039 {
1040         interface->init = book_client_initable_init;
1041 }
1042
1043 static void
1044 e_book_client_async_initable_init (GAsyncInitableIface *interface)
1045 {
1046         interface->init_async = book_client_initable_init_async;
1047         interface->init_finish = book_client_initable_init_finish;
1048 }
1049
1050 static void
1051 e_book_client_init (EBookClient *client)
1052 {
1053         client->priv = E_BOOK_CLIENT_GET_PRIVATE (client);
1054 }
1055
1056 /**
1057  * e_book_client_connect_sync:
1058  * @source: an #ESource
1059  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1060  * @error: return location for a #GError, or %NULL
1061  *
1062  * Creates a new #EBookClient for @source.  If an error occurs, the function
1063  * will set @error and return %FALSE.
1064  *
1065  * Unlike with e_book_client_new(), there is no need to call
1066  * e_client_open_sync() after obtaining the #EBookClient.
1067  *
1068  * For error handling convenience, any error message returned by this
1069  * function will have a descriptive prefix that includes the display
1070  * name of @source.
1071  *
1072  * Returns: (transfer full) (type EBookClient): a new #EBookClient, or %NULL
1073  *
1074  * Since: 3.8
1075  **/
1076 EClient *
1077 e_book_client_connect_sync (ESource *source,
1078                             GCancellable *cancellable,
1079                             GError **error)
1080 {
1081         EBookClient *client;
1082         GError *local_error = NULL;
1083
1084         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1085
1086         client = g_object_new (
1087                 E_TYPE_BOOK_CLIENT,
1088                 "source", source, NULL);
1089
1090         g_initable_init (G_INITABLE (client), cancellable, &local_error);
1091
1092         if (local_error == NULL)
1093                 e_dbus_address_book_call_open_sync (
1094                         client->priv->dbus_proxy, cancellable, &local_error);
1095
1096         if (local_error != NULL) {
1097                 g_dbus_error_strip_remote_error (local_error);
1098                 g_propagate_error (error, local_error);
1099                 g_prefix_error (
1100                         error, _("Unable to connect to '%s': "),
1101                         e_source_get_display_name (source));
1102                 g_object_unref (client);
1103                 return NULL;
1104         }
1105
1106         return E_CLIENT (client);
1107 }
1108
1109 /* Helper for e_book_client_connect() */
1110 static void
1111 book_client_connect_open_cb (GObject *source_object,
1112                              GAsyncResult *result,
1113                              gpointer user_data)
1114 {
1115         GSimpleAsyncResult *simple;
1116         GError *local_error = NULL;
1117
1118         simple = G_SIMPLE_ASYNC_RESULT (user_data);
1119
1120         e_dbus_address_book_call_open_finish (
1121                 E_DBUS_ADDRESS_BOOK (source_object), result, &local_error);
1122
1123         if (local_error != NULL) {
1124                 g_dbus_error_strip_remote_error (local_error);
1125                 g_simple_async_result_take_error (simple, local_error);
1126         }
1127
1128         g_simple_async_result_complete (simple);
1129
1130         g_object_unref (simple);
1131 }
1132
1133 /* Helper for e_book_client_connect() */
1134 static void
1135 book_client_connect_init_cb (GObject *source_object,
1136                              GAsyncResult *result,
1137                              gpointer user_data)
1138 {
1139         GSimpleAsyncResult *simple;
1140         EBookClientPrivate *priv;
1141         ConnectClosure *closure;
1142         GError *local_error = NULL;
1143
1144         simple = G_SIMPLE_ASYNC_RESULT (user_data);
1145
1146         g_async_initable_init_finish (
1147                 G_ASYNC_INITABLE (source_object), result, &local_error);
1148
1149         if (local_error != NULL) {
1150                 g_simple_async_result_take_error (simple, local_error);
1151                 g_simple_async_result_complete (simple);
1152                 goto exit;
1153         }
1154
1155         /* Note, we're repurposing some function parameters. */
1156
1157         result = G_ASYNC_RESULT (simple);
1158         source_object = g_async_result_get_source_object (result);
1159         closure = g_simple_async_result_get_op_res_gpointer (simple);
1160
1161         priv = E_BOOK_CLIENT_GET_PRIVATE (source_object);
1162
1163         e_dbus_address_book_call_open (
1164                 priv->dbus_proxy,
1165                 closure->cancellable,
1166                 book_client_connect_open_cb,
1167                 g_object_ref (simple));
1168
1169         g_object_unref (source_object);
1170
1171 exit:
1172         g_object_unref (simple);
1173 }
1174
1175 /**
1176  * e_book_client_connect:
1177  * @source: an #ESource
1178  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1179  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
1180  *            is satisfied
1181  * @user_data: (closure): data to pass to the callback function
1182  *
1183  * Asynchronously creates a new #EBookClient for @source.
1184  *
1185  * Unlike with e_book_client_new(), there is no need to call e_client_open()
1186  * after obtaining the #EBookClient.
1187  *
1188  * When the operation is finished, @callback will be called.  You can then
1189  * call e_book_client_connect_finish() to get the result of the operation.
1190  *
1191  * Since: 3.8
1192  **/
1193 void
1194 e_book_client_connect (ESource *source,
1195                        GCancellable *cancellable,
1196                        GAsyncReadyCallback callback,
1197                        gpointer user_data)
1198 {
1199         GSimpleAsyncResult *simple;
1200         ConnectClosure *closure;
1201         EBookClient *client;
1202
1203         g_return_if_fail (E_IS_SOURCE (source));
1204
1205         /* Two things with this: 1) instantiate the client object
1206          * immediately to make sure the thread-default GMainContext
1207          * gets plucked, and 2) do not call the D-Bus open() method
1208          * from our designated D-Bus thread -- it may take a long
1209          * time and block other clients from receiving signals. */
1210
1211         closure = g_slice_new0 (ConnectClosure);
1212         closure->source = g_object_ref (source);
1213
1214         if (G_IS_CANCELLABLE (cancellable))
1215                 closure->cancellable = g_object_ref (cancellable);
1216
1217         client = g_object_new (
1218                 E_TYPE_BOOK_CLIENT,
1219                 "source", source, NULL);
1220
1221         simple = g_simple_async_result_new (
1222                 G_OBJECT (client), callback,
1223                 user_data, e_book_client_connect);
1224
1225         g_simple_async_result_set_check_cancellable (simple, cancellable);
1226
1227         g_simple_async_result_set_op_res_gpointer (
1228                 simple, closure, (GDestroyNotify) connect_closure_free);
1229
1230         g_async_initable_init_async (
1231                 G_ASYNC_INITABLE (client),
1232                 G_PRIORITY_DEFAULT, cancellable,
1233                 book_client_connect_init_cb,
1234                 g_object_ref (simple));
1235
1236         g_object_unref (simple);
1237         g_object_unref (client);
1238 }
1239
1240 /**
1241  * e_book_client_connect_finish:
1242  * @result: a #GAsyncResult
1243  * @error: return location for a #GError, or %NULL
1244  *
1245  * Finishes the operation started with e_book_client_connect().  If an
1246  * error occurs in connecting to the D-Bus service, the function sets
1247  * @error and returns %NULL.
1248  *
1249  * For error handling convenience, any error message returned by this
1250  * function will have a descriptive prefix that includes the display
1251  * name of the #ESource passed to e_book_client_connect().
1252  *
1253  * Returns: (transfer full) (type EBookClient): a new #EBookClient, or %NULL
1254  *
1255  * Since: 3.8
1256  **/
1257 EClient *
1258 e_book_client_connect_finish (GAsyncResult *result,
1259                               GError **error)
1260 {
1261         GSimpleAsyncResult *simple;
1262         ConnectClosure *closure;
1263         gpointer source_tag;
1264
1265         g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
1266
1267         simple = G_SIMPLE_ASYNC_RESULT (result);
1268         closure = g_simple_async_result_get_op_res_gpointer (simple);
1269
1270         source_tag = g_simple_async_result_get_source_tag (simple);
1271         g_return_val_if_fail (source_tag == e_book_client_connect, NULL);
1272
1273         if (g_simple_async_result_propagate_error (simple, error)) {
1274                 g_prefix_error (
1275                         error, _("Unable to connect to '%s': "),
1276                         e_source_get_display_name (closure->source));
1277                 return NULL;
1278         }
1279
1280         return E_CLIENT (g_async_result_get_source_object (result));
1281 }
1282
1283 /**
1284  * e_book_client_new:
1285  * @source: An #ESource pointer
1286  * @error: A #GError pointer
1287  *
1288  * Creates a new #EBookClient corresponding to the given source.  There are
1289  * only two operations that are valid on this book at this point:
1290  * e_client_open(), and e_client_remove().
1291  *
1292  * Returns: a new but unopened #EBookClient.
1293  *
1294  * Since: 3.2
1295  *
1296  * Deprecated: 3.8: It covertly makes synchronous D-Bus calls, with no
1297  *                  way to cancel.  Use e_book_client_connect() instead,
1298  *                  which combines e_book_client_new() and e_client_open()
1299  *                  into one step.
1300  **/
1301 EBookClient *
1302 e_book_client_new (ESource *source,
1303                    GError **error)
1304 {
1305         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1306
1307         return g_initable_new (
1308                 E_TYPE_BOOK_CLIENT, NULL, error,
1309                 "source", source, NULL);
1310 }
1311
1312 /* Direct Read Access connect helper */
1313 static void
1314 connect_direct (EBookClient *client,
1315                 GCancellable *cancellable,
1316                 ESourceRegistry *registry)
1317 {
1318         EBookClientPrivate *priv;
1319         EDBusDirectBook *direct_config;
1320         const gchar *backend_name, *backend_path, *config;
1321
1322         priv = E_BOOK_CLIENT_GET_PRIVATE (client);
1323
1324         if (registry)
1325                 g_object_ref (registry);
1326         else {
1327                 registry = e_source_registry_new_sync (cancellable, NULL);
1328
1329                 if (!registry)
1330                         return;
1331         }
1332
1333         direct_config = e_dbus_direct_book_proxy_new_sync (
1334                 g_dbus_proxy_get_connection (G_DBUS_PROXY (priv->dbus_proxy)),
1335                 G_DBUS_PROXY_FLAGS_NONE,
1336                 ADDRESS_BOOK_DBUS_SERVICE_NAME,
1337                 g_dbus_proxy_get_object_path (G_DBUS_PROXY (priv->dbus_proxy)),
1338                 NULL, NULL);
1339
1340         backend_path = e_dbus_direct_book_get_backend_path (direct_config);
1341         backend_name = e_dbus_direct_book_get_backend_name (direct_config);
1342         config = e_dbus_direct_book_get_backend_config (direct_config);
1343
1344         if (backend_path != NULL && *backend_path != '\0' &&
1345             backend_name != NULL && *backend_name != '\0') {
1346                 priv->direct_backend = book_client_load_direct_backend (
1347                         registry, e_client_get_source (E_CLIENT (client)),
1348                         backend_path,
1349                         backend_name,
1350                         config, NULL);
1351
1352         }
1353
1354         g_object_unref (direct_config);
1355         g_object_unref (registry);
1356
1357         /* We have to perform the opening of the direct backend separately
1358          * from the EClient->open() implementation, because the direct
1359          * backend does not exist yet. */
1360         if (priv->direct_backend != NULL &&
1361             !e_book_backend_open_sync (priv->direct_backend,
1362                                        cancellable,
1363                                        NULL))
1364                 g_clear_object (&priv->direct_backend);
1365 }
1366
1367 /**
1368  * e_book_client_connect_direct_sync:
1369  * @registry: an #ESourceRegistry
1370  * @source: an #ESource
1371  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1372  * @error: return location for a #GError, or %NULL
1373  *
1374  * Like e_book_client_connect_sync(), except creates the book client for
1375  * direct read access to the underlying addressbook.
1376  *
1377  * Returns: (transfer full) (type EBookClient): a new but unopened #EBookClient.
1378  *
1379  * Since: 3.8
1380  **/
1381 EClient *
1382 e_book_client_connect_direct_sync (ESourceRegistry *registry,
1383                                    ESource *source,
1384                                    GCancellable *cancellable,
1385                                    GError **error)
1386 {
1387         EClient *client;
1388
1389         client = e_book_client_connect_sync (source, cancellable, error);
1390
1391         if (!client)
1392                 return NULL;
1393
1394         /* Connect the direct EDataBook connection */
1395         connect_direct (E_BOOK_CLIENT (client), cancellable, registry);
1396
1397         return client;
1398
1399 }
1400
1401 /* Helper for e_book_client_connect_direct() */
1402 static void
1403 book_client_connect_direct_init_cb (GObject *source_object,
1404                                     GAsyncResult *result,
1405                                     gpointer user_data)
1406 {
1407         GSimpleAsyncResult *simple;
1408         EBookClientPrivate *priv;
1409         ConnectClosure *closure;
1410         GError *error = NULL;
1411
1412         simple = G_SIMPLE_ASYNC_RESULT (user_data);
1413
1414         g_async_initable_init_finish (
1415                 G_ASYNC_INITABLE (source_object), result, &error);
1416
1417         if (error != NULL) {
1418                 g_simple_async_result_take_error (simple, error);
1419                 g_simple_async_result_complete (simple);
1420                 goto exit;
1421         }
1422
1423         /* Note, we're repurposing some function parameters. */
1424         result = G_ASYNC_RESULT (simple);
1425         source_object = g_async_result_get_source_object (result);
1426         closure = g_simple_async_result_get_op_res_gpointer (simple);
1427
1428         priv = E_BOOK_CLIENT_GET_PRIVATE (source_object);
1429
1430         e_dbus_address_book_call_open (
1431                 priv->dbus_proxy,
1432                 closure->cancellable,
1433                 book_client_connect_open_cb,
1434                 g_object_ref (simple));
1435
1436         /* Make the DRA connection */
1437         connect_direct (E_BOOK_CLIENT (source_object), closure->cancellable, NULL);
1438
1439         g_object_unref (source_object);
1440
1441 exit:
1442         g_object_unref (simple);
1443 }
1444
1445 /**
1446  * e_book_client_connect_direct:
1447  * @source: an #ESource
1448  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
1449  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
1450  *            is satisfied
1451  * @user_data: (closure): data to pass to the callback function
1452  *
1453  * Like e_book_client_connect(), except creates the book client for
1454  * direct read access to the underlying addressbook.
1455  *
1456  * When the operation is finished, @callback will be called.  You can then
1457  * call e_book_client_connect_direct_finish() to get the result of the operation.
1458  *
1459  * Since: 3.10
1460  **/
1461 void
1462 e_book_client_connect_direct (ESource *source,
1463                               GCancellable *cancellable,
1464                               GAsyncReadyCallback callback,
1465                               gpointer user_data)
1466 {
1467         GSimpleAsyncResult *simple;
1468         ConnectClosure *closure;
1469         EBookClient *client;
1470
1471         g_return_if_fail (E_IS_SOURCE (source));
1472
1473         /* Two things with this: 1) instantiate the client object
1474          * immediately to make sure the thread-default GMainContext
1475          * gets plucked, and 2) do not call the D-Bus open() method
1476          * from our designated D-Bus thread -- it may take a long
1477          * time and block other clients from receiving signals. */
1478         closure = g_slice_new0 (ConnectClosure);
1479         closure->source = g_object_ref (source);
1480
1481         if (G_IS_CANCELLABLE (cancellable))
1482                 closure->cancellable = g_object_ref (cancellable);
1483
1484         client = g_object_new (
1485                 E_TYPE_BOOK_CLIENT,
1486                 "source", source, NULL);
1487
1488         simple = g_simple_async_result_new (
1489                 G_OBJECT (client), callback,
1490                 user_data, e_book_client_connect_direct);
1491
1492         g_simple_async_result_set_check_cancellable (simple, cancellable);
1493
1494         g_simple_async_result_set_op_res_gpointer (
1495                 simple, closure, (GDestroyNotify) connect_closure_free);
1496
1497         g_async_initable_init_async (
1498                 G_ASYNC_INITABLE (client),
1499                 G_PRIORITY_DEFAULT, cancellable,
1500                 book_client_connect_direct_init_cb,
1501                 g_object_ref (simple));
1502
1503         g_object_unref (simple);
1504         g_object_unref (client);
1505 }
1506
1507 /**
1508  * e_book_client_connect_direct_finish:
1509  * @result: a #GAsyncResult
1510  * @error: return location for a #GError, or %NULL
1511  *
1512  * Finishes the operation started with e_book_client_connect_direct().
1513  * If an error occurs in connecting to the D-Bus service, the function sets
1514  * @error and returns %NULL.
1515  *
1516  * For error handling convenience, any error message returned by this
1517  * function will have a descriptive prefix that includes the display
1518  * name of the #ESource passed to e_book_client_connect_direct().
1519  *
1520  * Returns: (transfer full) (type EBookClient): a new #EBookClient, or %NULL
1521  *
1522  * Since: 3.10
1523  **/
1524 EClient *
1525 e_book_client_connect_direct_finish (GAsyncResult *result,
1526                                      GError **error)
1527 {
1528         GSimpleAsyncResult *simple;
1529         ConnectClosure *closure;
1530         gpointer source_tag;
1531
1532         g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
1533
1534         simple = G_SIMPLE_ASYNC_RESULT (result);
1535         closure = g_simple_async_result_get_op_res_gpointer (simple);
1536
1537         source_tag = g_simple_async_result_get_source_tag (simple);
1538         g_return_val_if_fail (source_tag == e_book_client_connect_direct, NULL);
1539
1540         if (g_simple_async_result_propagate_error (simple, error)) {
1541                 g_prefix_error (
1542                         error, _("Unable to connect to '%s': "),
1543                         e_source_get_display_name (closure->source));
1544                 return NULL;
1545         }
1546
1547         return E_CLIENT (g_async_result_get_source_object (result));
1548 }
1549
1550 #define SELF_UID_PATH_ID "org.gnome.evolution-data-server.addressbook"
1551 #define SELF_UID_KEY "self-contact-uid"
1552
1553 static EContact *
1554 make_me_card (void)
1555 {
1556         GString *vcard;
1557         const gchar *s;
1558         EContact *contact;
1559
1560         vcard = g_string_new ("BEGIN:VCARD\nVERSION:3.0\n");
1561
1562         s = g_get_user_name ();
1563         if (s)
1564                 g_string_append_printf (vcard, "NICKNAME:%s\n", s);
1565
1566         s = g_get_real_name ();
1567         if (s && strcmp (s, "Unknown") != 0) {
1568                 ENameWestern *western;
1569
1570                 g_string_append_printf (vcard, "FN:%s\n", s);
1571
1572                 western = e_name_western_parse (s);
1573                 g_string_append_printf (
1574                         vcard, "N:%s;%s;%s;%s;%s\n",
1575                         western->last ? western->last : "",
1576                         western->first ? western->first : "",
1577                         western->middle ? western->middle : "",
1578                         western->prefix ? western->prefix : "",
1579                         western->suffix ? western->suffix : "");
1580                 e_name_western_free (western);
1581         }
1582         g_string_append (vcard, "END:VCARD");
1583
1584         contact = e_contact_new_from_vcard (vcard->str);
1585
1586         g_string_free (vcard, TRUE);
1587
1588         return contact;
1589 }
1590
1591 /**
1592  * e_book_client_get_self:
1593  * @registry: an #ESourceRegistry
1594  * @out_contact: (out): an #EContact pointer to set
1595  * @out_client: (out): an #EBookClient pointer to set
1596  * @error: a #GError to set on failure
1597  *
1598  * Get the #EContact referring to the user of the address book
1599  * and set it in @out_contact and @out_client.
1600  *
1601  * Returns: %TRUE if successful, otherwise %FALSE.
1602  *
1603  * Since: 3.2
1604  **/
1605 gboolean
1606 e_book_client_get_self (ESourceRegistry *registry,
1607                         EContact **out_contact,
1608                         EBookClient **out_client,
1609                         GError **error)
1610 {
1611         EBookClient *book_client;
1612         ESource *source;
1613         EContact *contact = NULL;
1614         GSettings *settings;
1615         gchar *uid;
1616         gboolean success;
1617
1618         g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
1619         g_return_val_if_fail (out_contact != NULL, FALSE);
1620         g_return_val_if_fail (out_client != NULL, FALSE);
1621
1622         source = e_source_registry_ref_builtin_address_book (registry);
1623         book_client = e_book_client_new (source, error);
1624         g_object_unref (source);
1625
1626         if (book_client == NULL)
1627                 return FALSE;
1628
1629         success = e_client_open_sync (
1630                 E_CLIENT (book_client), FALSE, NULL, error);
1631         if (!success) {
1632                 g_object_unref (book_client);
1633                 return FALSE;
1634         }
1635
1636         *out_client = book_client;
1637
1638         settings = g_settings_new (SELF_UID_PATH_ID);
1639         uid = g_settings_get_string (settings, SELF_UID_KEY);
1640         g_object_unref (settings);
1641
1642         if (uid) {
1643                 /* Don't care about errors because
1644                  * we'll create a new card on failure. */
1645                 e_book_client_get_contact_sync (
1646                         book_client, uid, &contact, NULL, NULL);
1647                 g_free (uid);
1648
1649                 if (contact != NULL) {
1650                         *out_client = book_client;
1651                         *out_contact = contact;
1652                         return TRUE;
1653                 }
1654         }
1655
1656         uid = NULL;
1657         contact = make_me_card ();
1658         success = e_book_client_add_contact_sync (
1659                 book_client, contact, &uid, NULL, error);
1660         if (!success) {
1661                 g_object_unref (book_client);
1662                 g_object_unref (contact);
1663                 return FALSE;
1664         }
1665
1666         if (uid != NULL) {
1667                 e_contact_set (contact, E_CONTACT_UID, uid);
1668                 g_free (uid);
1669         }
1670
1671         e_book_client_set_self (book_client, contact, NULL);
1672
1673         *out_client = book_client;
1674         *out_contact = contact;
1675
1676         return TRUE;
1677 }
1678
1679 /**
1680  * e_book_client_set_self:
1681  * @client: an #EBookClient
1682  * @contact: an #EContact
1683  * @error: a #GError to set on failure
1684  *
1685  * Specify that @contact residing in @client is the #EContact that
1686  * refers to the user of the address book.
1687  *
1688  * Returns: %TRUE if successful, %FALSE otherwise.
1689  *
1690  * Since: 3.2
1691  **/
1692 gboolean
1693 e_book_client_set_self (EBookClient *client,
1694                         EContact *contact,
1695                         GError **error)
1696 {
1697         GSettings *settings;
1698
1699         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
1700         g_return_val_if_fail (contact != NULL, FALSE);
1701         g_return_val_if_fail (
1702                 e_contact_get_const (contact, E_CONTACT_UID) != NULL, FALSE);
1703
1704         settings = g_settings_new (SELF_UID_PATH_ID);
1705         g_settings_set_string (
1706                 settings, SELF_UID_KEY,
1707                 e_contact_get_const (contact, E_CONTACT_UID));
1708         g_object_unref (settings);
1709
1710         return TRUE;
1711 }
1712
1713 /**
1714  * e_book_client_is_self:
1715  * @contact: an #EContact
1716  *
1717  * Check if @contact is the user of the address book.
1718  *
1719  * Returns: %TRUE if @contact is the user, %FALSE otherwise.
1720  *
1721  * Since: 3.2
1722  **/
1723 gboolean
1724 e_book_client_is_self (EContact *contact)
1725 {
1726         static GSettings *settings;
1727         static GMutex mutex;
1728         const gchar *contact_uid;
1729         gchar *uid;
1730         gboolean is_self;
1731
1732         g_return_val_if_fail (contact && E_IS_CONTACT (contact), FALSE);
1733
1734         /*
1735          * It would be nice to attach this instance to the EBookClient
1736          * instance so that it can be free again later, but
1737          * unfortunately the API doesn't allow that.
1738          */
1739         g_mutex_lock (&mutex);
1740         if (!settings)
1741                 settings = g_settings_new (SELF_UID_PATH_ID);
1742         uid = g_settings_get_string (settings, SELF_UID_KEY);
1743         g_mutex_unlock (&mutex);
1744
1745         contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
1746         is_self = (uid != NULL) && (g_strcmp0 (uid, contact_uid) == 0);
1747
1748         g_free (uid);
1749
1750         return is_self;
1751 }
1752
1753 /* Helper for e_book_client_add_contact() */
1754 static void
1755 book_client_add_contact_thread (GSimpleAsyncResult *simple,
1756                                 GObject *source_object,
1757                                 GCancellable *cancellable)
1758 {
1759         AsyncContext *async_context;
1760         GError *local_error = NULL;
1761
1762         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1763
1764         e_book_client_add_contact_sync (
1765                 E_BOOK_CLIENT (source_object),
1766                 async_context->contact,
1767                 &async_context->uid,
1768                 cancellable, &local_error);
1769
1770         if (local_error != NULL)
1771                 g_simple_async_result_take_error (simple, local_error);
1772 }
1773
1774 /**
1775  * e_book_client_add_contact:
1776  * @client: an #EBookClient
1777  * @contact: an #EContact
1778  * @cancellable: a #GCancellable; can be %NULL
1779  * @callback: callback to call when a result is ready
1780  * @user_data: user data for the @callback
1781  *
1782  * Adds @contact to @client.
1783  * The call is finished by e_book_client_add_contact_finish()
1784  * from the @callback.
1785  *
1786  * Since: 3.2
1787  **/
1788 void
1789 e_book_client_add_contact (EBookClient *client,
1790                            EContact *contact,
1791                            GCancellable *cancellable,
1792                            GAsyncReadyCallback callback,
1793                            gpointer user_data)
1794 {
1795         GSimpleAsyncResult *simple;
1796         AsyncContext *async_context;
1797
1798         g_return_if_fail (E_IS_BOOK_CLIENT (client));
1799         g_return_if_fail (E_IS_CONTACT (contact));
1800
1801         async_context = g_slice_new0 (AsyncContext);
1802         async_context->contact = g_object_ref (contact);
1803
1804         simple = g_simple_async_result_new (
1805                 G_OBJECT (client), callback, user_data,
1806                 e_book_client_add_contact);
1807
1808         g_simple_async_result_set_check_cancellable (simple, cancellable);
1809
1810         g_simple_async_result_set_op_res_gpointer (
1811                 simple, async_context, (GDestroyNotify) async_context_free);
1812
1813         g_simple_async_result_run_in_thread (
1814                 simple, book_client_add_contact_thread,
1815                 G_PRIORITY_DEFAULT, cancellable);
1816
1817         g_object_unref (simple);
1818 }
1819
1820 /**
1821  * e_book_client_add_contact_finish:
1822  * @client: an #EBookClient
1823  * @result: a #GAsyncResult
1824  * @out_added_uid: (out): UID of a newly added contact; can be %NULL
1825  * @error: (out): a #GError to set an error, if any
1826  *
1827  * Finishes previous call of e_book_client_add_contact() and
1828  * sets @out_added_uid to a UID of a newly added contact.
1829  * This string should be freed with g_free().
1830  *
1831  * Note: This is not modifying original #EContact.
1832  *
1833  * Returns: %TRUE if successful, %FALSE otherwise.
1834  *
1835  * Since: 3.2
1836  **/
1837 gboolean
1838 e_book_client_add_contact_finish (EBookClient *client,
1839                                   GAsyncResult *result,
1840                                   gchar **out_added_uid,
1841                                   GError **error)
1842 {
1843         GSimpleAsyncResult *simple;
1844         AsyncContext *async_context;
1845
1846         g_return_val_if_fail (
1847                 g_simple_async_result_is_valid (
1848                 result, G_OBJECT (client),
1849                 e_book_client_add_contact), FALSE);
1850
1851         simple = G_SIMPLE_ASYNC_RESULT (result);
1852         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1853
1854         if (g_simple_async_result_propagate_error (simple, error))
1855                 return FALSE;
1856
1857         g_return_val_if_fail (async_context->uid != NULL, FALSE);
1858
1859         if (out_added_uid != NULL) {
1860                 *out_added_uid = async_context->uid;
1861                 async_context->uid = NULL;
1862         }
1863
1864         return TRUE;
1865 }
1866
1867 /**
1868  * e_book_client_add_contact_sync:
1869  * @client: an #EBookClient
1870  * @contact: an #EContact
1871  * @out_added_uid: (out): UID of a newly added contact; can be %NULL
1872  * @cancellable: a #GCancellable; can be %NULL
1873  * @error: (out): a #GError to set an error, if any
1874  *
1875  * Adds @contact to @client and
1876  * sets @out_added_uid to a UID of a newly added contact.
1877  * This string should be freed with g_free().
1878  *
1879  * Note: This is not modifying original @contact, thus if it's needed,
1880  * then use e_contact_set (contact, E_CONTACT_UID, new_uid).
1881  *
1882  * Returns: %TRUE if successful, %FALSE otherwise.
1883  *
1884  * Since: 3.2
1885  **/
1886 gboolean
1887 e_book_client_add_contact_sync (EBookClient *client,
1888                                 EContact *contact,
1889                                 gchar **out_added_uid,
1890                                 GCancellable *cancellable,
1891                                 GError **error)
1892 {
1893         GSList link = { contact, NULL };
1894         GSList *uids = NULL;
1895         gboolean success;
1896
1897         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
1898         g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
1899
1900         success = e_book_client_add_contacts_sync (
1901                 client, &link, &uids, cancellable, error);
1902
1903         /* Sanity check. */
1904         g_return_val_if_fail (
1905                 (success && (uids != NULL)) ||
1906                 (!success && (uids == NULL)), FALSE);
1907
1908         if (uids != NULL) {
1909                 if (out_added_uid != NULL)
1910                         *out_added_uid = g_strdup (uids->data);
1911
1912                 g_slist_free_full (uids, (GDestroyNotify) g_free);
1913         }
1914
1915         return success;
1916 }
1917
1918 /* Helper for e_book_client_add_contacts() */
1919 static void
1920 book_client_add_contacts_thread (GSimpleAsyncResult *simple,
1921                                  GObject *source_object,
1922                                  GCancellable *cancellable)
1923 {
1924         AsyncContext *async_context;
1925         GError *local_error = NULL;
1926
1927         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1928
1929         e_book_client_add_contacts_sync (
1930                 E_BOOK_CLIENT (source_object),
1931                 async_context->object_list,
1932                 &async_context->string_list,
1933                 cancellable, &local_error);
1934
1935         if (local_error != NULL)
1936                 g_simple_async_result_take_error (simple, local_error);
1937 }
1938
1939 /**
1940  * e_book_client_add_contacts:
1941  * @client: an #EBookClient
1942  * @contacts: (element-type EContact): a #GSList of #EContact objects to add
1943  * @cancellable: (allow-none): a #GCancellable; can be %NULL
1944  * @callback: callback to call when a result is ready
1945  * @user_data: user data for the @callback
1946  *
1947  * Adds @contacts to @client.
1948  * The call is finished by e_book_client_add_contacts_finish()
1949  * from the @callback.
1950  *
1951  * Since: 3.4
1952  **/
1953 void
1954 e_book_client_add_contacts (EBookClient *client,
1955                             GSList *contacts,
1956                             GCancellable *cancellable,
1957                             GAsyncReadyCallback callback,
1958                             gpointer user_data)
1959 {
1960         GSimpleAsyncResult *simple;
1961         AsyncContext *async_context;
1962
1963         g_return_if_fail (E_IS_BOOK_CLIENT (client));
1964         g_return_if_fail (contacts != NULL);
1965
1966         async_context = g_slice_new0 (AsyncContext);
1967         async_context->object_list = g_slist_copy_deep (
1968                 contacts, (GCopyFunc) g_object_ref, NULL);
1969
1970         simple = g_simple_async_result_new (
1971                 G_OBJECT (client), callback, user_data,
1972                 e_book_client_add_contacts);
1973
1974         g_simple_async_result_set_check_cancellable (simple, cancellable);
1975
1976         g_simple_async_result_set_op_res_gpointer (
1977                 simple, async_context, (GDestroyNotify) async_context_free);
1978
1979         g_simple_async_result_run_in_thread (
1980                 simple, book_client_add_contacts_thread,
1981                 G_PRIORITY_DEFAULT, cancellable);
1982
1983         g_object_unref (simple);
1984 }
1985
1986 /**
1987  * e_book_client_add_contacts_finish:
1988  * @client: an #EBookClient
1989  * @result: a #GAsyncResult
1990  * @out_added_uids: (out) (element-type utf8) (allow-none): UIDs of
1991  *                  newly added contacts; can be %NULL
1992  * @error: (out): a #GError to set an error, if any
1993  *
1994  * Finishes previous call of e_book_client_add_contacts() and
1995  * sets @out_added_uids to the UIDs of newly added contacts if successful.
1996  * This #GSList should be freed with e_client_util_free_string_slist().
1997  *
1998  * If any of the contacts cannot be inserted, all of the insertions will be
1999  * reverted and this method will return %FALSE.
2000  *
2001  * Note: This is not modifying original #EContact objects.
2002  *
2003  * Returns: %TRUE if successful, %FALSE otherwise.
2004  *
2005  * Since: 3.4
2006  **/
2007 gboolean
2008 e_book_client_add_contacts_finish (EBookClient *client,
2009                                    GAsyncResult *result,
2010                                    GSList **out_added_uids,
2011                                    GError **error)
2012 {
2013         GSimpleAsyncResult *simple;
2014         AsyncContext *async_context;
2015
2016         g_return_val_if_fail (
2017                 g_simple_async_result_is_valid (
2018                 result, G_OBJECT (client),
2019                 e_book_client_add_contacts), FALSE);
2020
2021         simple = G_SIMPLE_ASYNC_RESULT (result);
2022         async_context = g_simple_async_result_get_op_res_gpointer (simple);
2023
2024         if (g_simple_async_result_propagate_error (simple, error))
2025                 return FALSE;
2026
2027         if (out_added_uids != NULL) {
2028                 *out_added_uids = async_context->string_list;
2029                 async_context->string_list = NULL;
2030         }
2031
2032         return TRUE;
2033 }
2034
2035 /**
2036  * e_book_client_add_contacts_sync:
2037  * @client: an #EBookClient
2038  * @contacts: (element-type EContact): a #GSList of #EContact objects to add
2039  * @out_added_uids: (out) (element-type utf8) (allow-none): UIDs of newly
2040  *                  added contacts; can be %NULL
2041  * @cancellable: a #GCancellable; can be %NULL
2042  * @error: (out): a #GError to set an error, if any
2043  *
2044  * Adds @contacts to @client and
2045  * sets @out_added_uids to the UIDs of newly added contacts if successful.
2046  * This #GSList should be freed with e_client_util_free_string_slist().
2047  *
2048  * If any of the contacts cannot be inserted, all of the insertions will be
2049  * reverted and this method will return %FALSE.
2050  *
2051  * Note: This is not modifying original @contacts, thus if it's needed,
2052  * then use e_contact_set (contact, E_CONTACT_UID, new_uid).
2053  *
2054  * Returns: %TRUE if successful, %FALSE otherwise.
2055  *
2056  * Since: 3.4
2057  **/
2058 gboolean
2059 e_book_client_add_contacts_sync (EBookClient *client,
2060                                  GSList *contacts,
2061                                  GSList **out_added_uids,
2062                                  GCancellable *cancellable,
2063                                  GError **error)
2064 {
2065         GSList *link;
2066         gchar **strv;
2067         gchar **uids = NULL;
2068         gint ii = 0;
2069         GError *local_error = NULL;
2070
2071         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2072         g_return_val_if_fail (contacts != NULL, FALSE);
2073
2074         /* Build a string array, ensuring each element is valid UTF-8. */
2075         strv = g_new0 (gchar *, g_slist_length (contacts) + 1);
2076         for (link = contacts; link != NULL; link = g_slist_next (link)) {
2077                 EVCard *vcard;
2078                 gchar *string;
2079
2080                 vcard = E_VCARD (link->data);
2081                 string = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
2082                 strv[ii++] = e_util_utf8_make_valid (string);
2083                 g_free (string);
2084         }
2085
2086         e_dbus_address_book_call_create_contacts_sync (
2087                 client->priv->dbus_proxy,
2088                 (const gchar * const *) strv,
2089                 &uids, cancellable, &local_error);
2090
2091         g_strfreev (strv);
2092
2093         /* Sanity check. */
2094         g_return_val_if_fail (
2095                 ((uids != NULL) && (local_error == NULL)) ||
2096                 ((uids == NULL) && (local_error != NULL)), FALSE);
2097
2098         if (local_error != NULL) {
2099                 g_dbus_error_strip_remote_error (local_error);
2100                 g_propagate_error (error, local_error);
2101                 return FALSE;
2102         }
2103
2104         /* XXX We should have passed the string array directly
2105          *     back to the caller instead of building a linked
2106          *     list.  This is unnecessary work. */
2107         if (out_added_uids != NULL) {
2108                 GSList *tmp = NULL;
2109                 gint ii;
2110
2111                 /* Take ownership of the string array elements. */
2112                 for (ii = 0; uids[ii] != NULL; ii++) {
2113                         tmp = g_slist_prepend (tmp, uids[ii]);
2114                         uids[ii] = NULL;
2115                 }
2116
2117                 *out_added_uids = g_slist_reverse (tmp);
2118         }
2119
2120         g_strfreev (uids);
2121
2122         return TRUE;
2123 }
2124
2125 /* Helper for e_book_client_modify_contact() */
2126 static void
2127 book_client_modify_contact_thread (GSimpleAsyncResult *simple,
2128                                    GObject *source_object,
2129                                    GCancellable *cancellable)
2130 {
2131         AsyncContext *async_context;
2132         GError *local_error = NULL;
2133
2134         async_context = g_simple_async_result_get_op_res_gpointer (simple);
2135
2136         e_book_client_modify_contact_sync (
2137                 E_BOOK_CLIENT (source_object),
2138                 async_context->contact,
2139                 cancellable, &local_error);
2140
2141         if (local_error != NULL)
2142                 g_simple_async_result_take_error (simple, local_error);
2143 }
2144
2145 /**
2146  * e_book_client_modify_contact:
2147  * @client: an #EBookClient
2148  * @contact: an #EContact
2149  * @cancellable: a #GCancellable; can be %NULL
2150  * @callback: callback to call when a result is ready
2151  * @user_data: user data for the @callback
2152  *
2153  * Applies the changes made to @contact to the stored version in @client.
2154  * The call is finished by e_book_client_modify_contact_finish()
2155  * from the @callback.
2156  *
2157  * Since: 3.2
2158  **/
2159 void
2160 e_book_client_modify_contact (EBookClient *client,
2161                               EContact *contact,
2162                               GCancellable *cancellable,
2163                               GAsyncReadyCallback callback,
2164                               gpointer user_data)
2165 {
2166         GSimpleAsyncResult *simple;
2167         AsyncContext *async_context;
2168
2169         g_return_if_fail (E_IS_BOOK_CLIENT (client));
2170         g_return_if_fail (E_IS_CONTACT (contact));
2171
2172         async_context = g_slice_new0 (AsyncContext);
2173         async_context->contact = g_object_ref (contact);
2174
2175         simple = g_simple_async_result_new (
2176                 G_OBJECT (client), callback, user_data,
2177                 e_book_client_modify_contact);
2178
2179         g_simple_async_result_set_check_cancellable (simple, cancellable);
2180
2181         g_simple_async_result_set_op_res_gpointer (
2182                 simple, async_context, (GDestroyNotify) async_context_free);
2183
2184         g_simple_async_result_run_in_thread (
2185                 simple, book_client_modify_contact_thread,
2186                 G_PRIORITY_DEFAULT, cancellable);
2187
2188         g_object_unref (simple);
2189 }
2190
2191 /**
2192  * e_book_client_modify_contact_finish:
2193  * @client: an #EBookClient
2194  * @result: a #GAsyncResult
2195  * @error: (out): a #GError to set an error, if any
2196  *
2197  * Finishes previous call of e_book_client_modify_contact().
2198  *
2199  * Returns: %TRUE if successful, %FALSE otherwise.
2200  *
2201  * Since: 3.2
2202  **/
2203 gboolean
2204 e_book_client_modify_contact_finish (EBookClient *client,
2205                                      GAsyncResult *result,
2206                                      GError **error)
2207 {
2208         GSimpleAsyncResult *simple;
2209
2210         g_return_val_if_fail (
2211                 g_simple_async_result_is_valid (
2212                 result, G_OBJECT (client),
2213                 e_book_client_modify_contact), FALSE);
2214
2215         simple = G_SIMPLE_ASYNC_RESULT (result);
2216
2217         /* Assume success unless a GError is set. */
2218         return !g_simple_async_result_propagate_error (simple, error);
2219 }
2220
2221 /**
2222  * e_book_client_modify_contact_sync:
2223  * @client: an #EBookClient
2224  * @contact: an #EContact
2225  * @cancellable: a #GCancellable; can be %NULL
2226  * @error: (out): a #GError to set an error, if any
2227  *
2228  * Applies the changes made to @contact to the stored version in @client.
2229  *
2230  * Returns: %TRUE if successful, %FALSE otherwise.
2231  *
2232  * Since: 3.2
2233  **/
2234 gboolean
2235 e_book_client_modify_contact_sync (EBookClient *client,
2236                                    EContact *contact,
2237                                    GCancellable *cancellable,
2238                                    GError **error)
2239 {
2240         GSList link = { contact, NULL };
2241
2242         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2243         g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
2244
2245         return e_book_client_modify_contacts_sync (
2246                 client, &link, cancellable, error);
2247 }
2248
2249 /* Helper for e_book_client_modify_contacts() */
2250 static void
2251 book_client_modify_contacts_thread (GSimpleAsyncResult *simple,
2252                                     GObject *source_object,
2253                                     GCancellable *cancellable)
2254 {
2255         AsyncContext *async_context;
2256         GError *local_error = NULL;
2257
2258         async_context = g_simple_async_result_get_op_res_gpointer (simple);
2259
2260         e_book_client_modify_contacts_sync (
2261                 E_BOOK_CLIENT (source_object),
2262                 async_context->object_list,
2263                 cancellable, &local_error);
2264
2265         if (local_error != NULL)
2266                 g_simple_async_result_take_error (simple, local_error);
2267 }
2268
2269 /**
2270  * e_book_client_modify_contacts:
2271  * @client: an #EBookClient
2272  * @contacts: (element-type EContact): a #GSList of #EContact objects
2273  * @cancellable: (allow-none): a #GCancellable; can be %NULL
2274  * @callback: callback to call when a result is ready
2275  * @user_data: user data for the @callback
2276  *
2277  * Applies the changes made to @contacts to the stored versions in @client.
2278  * The call is finished by e_book_client_modify_contacts_finish()
2279  * from the @callback.
2280  *
2281  * Since: 3.4
2282  **/
2283 void
2284 e_book_client_modify_contacts (EBookClient *client,
2285                                GSList *contacts,
2286                                GCancellable *cancellable,
2287                                GAsyncReadyCallback callback,
2288                                gpointer user_data)
2289 {
2290         GSimpleAsyncResult *simple;
2291         AsyncContext *async_context;
2292
2293         g_return_if_fail (E_IS_BOOK_CLIENT (client));
2294         g_return_if_fail (contacts != NULL);
2295
2296         async_context = g_slice_new0 (AsyncContext);
2297         async_context->object_list = g_slist_copy_deep (
2298                 contacts, (GCopyFunc) g_object_ref, NULL);
2299
2300         simple = g_simple_async_result_new (
2301                 G_OBJECT (client), callback, user_data,
2302                 e_book_client_modify_contacts);
2303
2304         g_simple_async_result_set_check_cancellable (simple, cancellable);
2305
2306         g_simple_async_result_set_op_res_gpointer (
2307                 simple, async_context, (GDestroyNotify) async_context_free);
2308
2309         g_simple_async_result_run_in_thread (
2310                 simple, book_client_modify_contacts_thread,
2311                 G_PRIORITY_DEFAULT, cancellable);
2312
2313         g_object_unref (simple);
2314 }
2315
2316 /**
2317  * e_book_client_modify_contacts_finish:
2318  * @client: an #EBookClient
2319  * @result: a #GAsyncResult
2320  * @error: (out): a #GError to set an error, if any
2321  *
2322  * Finishes previous call of e_book_client_modify_contacts().
2323  *
2324  * Returns: %TRUE if successful, %FALSE otherwise.
2325  *
2326  * Since: 3.4
2327  **/
2328 gboolean
2329 e_book_client_modify_contacts_finish (EBookClient *client,
2330                                       GAsyncResult *result,
2331                                       GError **error)
2332 {
2333         GSimpleAsyncResult *simple;
2334
2335         g_return_val_if_fail (
2336                 g_simple_async_result_is_valid (
2337                 result, G_OBJECT (client),
2338                 e_book_client_modify_contacts), FALSE);
2339
2340         simple = G_SIMPLE_ASYNC_RESULT (result);
2341
2342         /* Assume success unless a GError is set. */
2343         return !g_simple_async_result_propagate_error (simple, error);
2344 }
2345
2346 /**
2347  * e_book_client_modify_contacts_sync:
2348  * @client: an #EBookClient
2349  * @contacts: (element-type EContact): a #GSList of #EContact objects
2350  * @cancellable: (allow-none): a #GCancellable; can be %NULL
2351  * @error: (out): a #GError to set an error, if any
2352  *
2353  * Applies the changes made to @contacts to the stored versions in @client.
2354  *
2355  * Returns: %TRUE if successful, %FALSE otherwise.
2356  *
2357  * Since: 3.4
2358  **/
2359 gboolean
2360 e_book_client_modify_contacts_sync (EBookClient *client,
2361                                     GSList *contacts,
2362                                     GCancellable *cancellable,
2363                                     GError **error)
2364 {
2365         GSList *link;
2366         gchar **strv;
2367         gint ii = 0;
2368         GError *local_error = NULL;
2369
2370         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2371         g_return_val_if_fail (contacts != NULL, FALSE);
2372
2373         /* Build a string array, ensuring each element is valid UTF-8. */
2374         strv = g_new0 (gchar *, g_slist_length (contacts) + 1);
2375         for (link = contacts; link != NULL; link = g_slist_next (link)) {
2376                 EVCard *vcard;
2377                 gchar *string;
2378
2379                 vcard = E_VCARD (link->data);
2380                 string = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
2381                 strv[ii++] = e_util_utf8_make_valid (string);
2382                 g_free (string);
2383         }
2384
2385         e_dbus_address_book_call_modify_contacts_sync (
2386                 client->priv->dbus_proxy,
2387                 (const gchar * const *) strv,
2388                 cancellable, &local_error);
2389
2390         g_strfreev (strv);
2391
2392         if (local_error != NULL) {
2393                 g_dbus_error_strip_remote_error (local_error);
2394                 g_propagate_error (error, local_error);
2395                 return FALSE;
2396         }
2397
2398         return TRUE;
2399 }
2400
2401 /* Helper for e_book_client_remove_contact() */
2402 static void
2403 book_client_remove_contact_thread (GSimpleAsyncResult *simple,
2404                                    GObject *source_object,
2405                                    GCancellable *cancellable)
2406 {
2407         AsyncContext *async_context;
2408         GError *local_error = NULL;
2409
2410         async_context = g_simple_async_result_get_op_res_gpointer (simple);
2411
2412         e_book_client_remove_contact_sync (
2413                 E_BOOK_CLIENT (source_object),
2414                 async_context->contact,
2415                 cancellable, &local_error);
2416
2417         if (local_error != NULL)
2418                 g_simple_async_result_take_error (simple, local_error);
2419 }
2420
2421 /**
2422  * e_book_client_remove_contact:
2423  * @client: an #EBookClient
2424  * @contact: an #EContact
2425  * @cancellable: a #GCancellable; can be %NULL
2426  * @callback: callback to call when a result is ready
2427  * @user_data: user data for the @callback
2428  *
2429  * Removes @contact from the @client.
2430  * The call is finished by e_book_client_remove_contact_finish()
2431  * from the @callback.
2432  *
2433  * Since: 3.2
2434  **/
2435 void
2436 e_book_client_remove_contact (EBookClient *client,
2437                               /* const */ EContact *contact,
2438                               GCancellable *cancellable,
2439                               GAsyncReadyCallback callback,
2440                               gpointer user_data)
2441 {
2442         GSimpleAsyncResult *simple;
2443         AsyncContext *async_context;
2444
2445         g_return_if_fail (E_IS_BOOK_CLIENT (client));
2446         g_return_if_fail (E_IS_CONTACT (contact));
2447
2448         async_context = g_slice_new0 (AsyncContext);
2449         async_context->contact = g_object_ref (contact);
2450
2451         simple = g_simple_async_result_new (
2452                 G_OBJECT (client), callback, user_data,
2453                 e_book_client_remove_contact);
2454
2455         g_simple_async_result_set_check_cancellable (simple, cancellable);
2456
2457         g_simple_async_result_set_op_res_gpointer (
2458                 simple, async_context, (GDestroyNotify) async_context_free);
2459
2460         g_simple_async_result_run_in_thread (
2461                 simple, book_client_remove_contact_thread,
2462                 G_PRIORITY_DEFAULT, cancellable);
2463
2464         g_object_unref (simple);
2465 }
2466
2467 /**
2468  * e_book_client_remove_contact_finish:
2469  * @client: an #EBookClient
2470  * @result: a #GAsyncResult
2471  * @error: (out): a #GError to set an error, if any
2472  *
2473  * Finishes previous call of e_book_client_remove_contact().
2474  *
2475  * Returns: %TRUE if successful, %FALSE otherwise.
2476  *
2477  * Since: 3.2
2478  **/
2479 gboolean
2480 e_book_client_remove_contact_finish (EBookClient *client,
2481                                      GAsyncResult *result,
2482                                      GError **error)
2483 {
2484         GSimpleAsyncResult *simple;
2485
2486         g_return_val_if_fail (
2487                 g_simple_async_result_is_valid (
2488                 result, G_OBJECT (client),
2489                 e_book_client_remove_contact), FALSE);
2490
2491         simple = G_SIMPLE_ASYNC_RESULT (result);
2492
2493         /* Assume success unless a GError is set. */
2494         return !g_simple_async_result_propagate_error (simple, error);
2495 }
2496
2497 /**
2498  * e_book_client_remove_contact_sync:
2499  * @client: an #EBookClient
2500  * @contact: an #EContact
2501  * @cancellable: a #GCancellable; can be %NULL
2502  * @error: (out): a #GError to set an error, if any
2503  *
2504  * Removes @contact from the @client.
2505  *
2506  * Returns: %TRUE if successful, %FALSE otherwise.
2507  *
2508  * Since: 3.2
2509  **/
2510 gboolean
2511 e_book_client_remove_contact_sync (EBookClient *client,
2512                                    EContact *contact,
2513                                    GCancellable *cancellable,
2514                                    GError **error)
2515 {
2516         const gchar *uid;
2517
2518         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2519         g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
2520
2521         uid = e_contact_get_const (contact, E_CONTACT_UID);
2522         g_return_val_if_fail (uid != NULL, FALSE);
2523
2524         return e_book_client_remove_contact_by_uid_sync (
2525                 client, uid, cancellable, error);
2526 }
2527
2528 /* Helper for e_book_client_remove_contact_by_uid() */
2529 static void
2530 book_client_remove_contact_by_uid_thread (GSimpleAsyncResult *simple,
2531                                           GObject *source_object,
2532                                           GCancellable *cancellable)
2533 {
2534         AsyncContext *async_context;
2535         GError *local_error = NULL;
2536
2537         async_context = g_simple_async_result_get_op_res_gpointer (simple);
2538
2539         e_book_client_remove_contact_by_uid_sync (
2540                 E_BOOK_CLIENT (source_object),
2541                 async_context->uid,
2542                 cancellable, &local_error);
2543
2544         if (local_error != NULL)
2545                 g_simple_async_result_take_error (simple, local_error);
2546 }
2547
2548 /**
2549  * e_book_client_remove_contact_by_uid:
2550  * @client: an #EBookClient
2551  * @uid: a UID of a contact to remove
2552  * @cancellable: a #GCancellable; can be %NULL
2553  * @callback: callback to call when a result is ready
2554  * @user_data: user data for the @callback
2555  *
2556  * Removes contact with @uid from the @client.
2557  * The call is finished by e_book_client_remove_contact_by_uid_finish()
2558  * from the @callback.
2559  *
2560  * Since: 3.2
2561  **/
2562 void
2563 e_book_client_remove_contact_by_uid (EBookClient *client,
2564                                      const gchar *uid,
2565                                      GCancellable *cancellable,
2566                                      GAsyncReadyCallback callback,
2567                                      gpointer user_data)
2568 {
2569         GSimpleAsyncResult *simple;
2570         AsyncContext *async_context;
2571
2572         g_return_if_fail (E_IS_BOOK_CLIENT (client));
2573         g_return_if_fail (uid != NULL);
2574
2575         async_context = g_slice_new0 (AsyncContext);
2576         async_context->uid = g_strdup (uid);
2577
2578         simple = g_simple_async_result_new (
2579                 G_OBJECT (client), callback, user_data,
2580                 e_book_client_remove_contact_by_uid);
2581
2582         g_simple_async_result_set_check_cancellable (simple, cancellable);
2583
2584         g_simple_async_result_set_op_res_gpointer (
2585                 simple, async_context, (GDestroyNotify) async_context_free);
2586
2587         g_simple_async_result_run_in_thread (
2588                 simple, book_client_remove_contact_by_uid_thread,
2589                 G_PRIORITY_DEFAULT, cancellable);
2590
2591         g_object_unref (simple);
2592 }
2593
2594 /**
2595  * e_book_client_remove_contact_by_uid_finish:
2596  * @client: an #EBookClient
2597  * @result: a #GAsyncResult
2598  * @error: (out): a #GError to set an error, if any
2599  *
2600  * Finishes previous call of e_book_client_remove_contact_by_uid().
2601  *
2602  * Returns: %TRUE if successful, %FALSE otherwise.
2603  *
2604  * Since: 3.2
2605  **/
2606 gboolean
2607 e_book_client_remove_contact_by_uid_finish (EBookClient *client,
2608                                             GAsyncResult *result,
2609                                             GError **error)
2610 {
2611         GSimpleAsyncResult *simple;
2612
2613         g_return_val_if_fail (
2614                 g_simple_async_result_is_valid (
2615                 result, G_OBJECT (client),
2616                 e_book_client_remove_contact_by_uid), FALSE);
2617
2618         simple = G_SIMPLE_ASYNC_RESULT (result);
2619
2620         /* Assume success unless a GError is set. */
2621         return !g_simple_async_result_propagate_error (simple, error);
2622 }
2623
2624 /**
2625  * e_book_client_remove_contact_by_uid_sync:
2626  * @client: an #EBookClient
2627  * @uid: a UID of a contact to remove
2628  * @cancellable: a #GCancellable; can be %NULL
2629  * @error: (out): a #GError to set an error, if any
2630  *
2631  * Removes contact with @uid from the @client.
2632  *
2633  * Returns: %TRUE if successful, %FALSE otherwise.
2634  *
2635  * Since: 3.2
2636  **/
2637 gboolean
2638 e_book_client_remove_contact_by_uid_sync (EBookClient *client,
2639                                           const gchar *uid,
2640                                           GCancellable *cancellable,
2641                                           GError **error)
2642 {
2643         GSList link = { (gpointer) uid, NULL };
2644
2645         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2646         g_return_val_if_fail (uid != NULL, FALSE);
2647
2648         return e_book_client_remove_contacts_sync (
2649                 client, &link, cancellable, error);
2650 }
2651
2652 /* Helper for e_book_client_remove_contacts() */
2653 static void
2654 book_client_remove_contacts_thread (GSimpleAsyncResult *simple,
2655                                     GObject *source_object,
2656                                     GCancellable *cancellable)
2657 {
2658         AsyncContext *async_context;
2659         GError *local_error = NULL;
2660
2661         async_context = g_simple_async_result_get_op_res_gpointer (simple);
2662
2663         e_book_client_remove_contacts_sync (
2664                 E_BOOK_CLIENT (source_object),
2665                 async_context->string_list,
2666                 cancellable, &local_error);
2667
2668         if (local_error != NULL)
2669                 g_simple_async_result_take_error (simple, local_error);
2670 }
2671
2672 /**
2673  * e_book_client_remove_contacts:
2674  * @client: an #EBookClient
2675  * @uids: (element-type utf8): a #GSList of UIDs to remove
2676  * @cancellable: a #GCancellable; can be %NULL
2677  * @callback: callback to call when a result is ready
2678  * @user_data: user data for the @callback
2679  *
2680  * Removes the contacts with uids from the list @uids from @client.  This is
2681  * always more efficient than calling e_book_client_remove_contact() if you
2682  * have more than one uid to remove, as some backends can implement it
2683  * as a batch request.
2684  * The call is finished by e_book_client_remove_contacts_finish()
2685  * from the @callback.
2686  *
2687  * Since: 3.2
2688  **/
2689 void
2690 e_book_client_remove_contacts (EBookClient *client,
2691                                const GSList *uids,
2692                                GCancellable *cancellable,
2693                                GAsyncReadyCallback callback,
2694                                gpointer user_data)
2695 {
2696         GSimpleAsyncResult *simple;
2697         AsyncContext *async_context;
2698
2699         g_return_if_fail (E_IS_BOOK_CLIENT (client));
2700         g_return_if_fail (uids != NULL);
2701
2702         async_context = g_slice_new0 (AsyncContext);
2703         async_context->string_list = g_slist_copy_deep (
2704                 (GSList *) uids, (GCopyFunc) g_strdup, NULL);
2705
2706         simple = g_simple_async_result_new (
2707                 G_OBJECT (client), callback, user_data,
2708                 e_book_client_remove_contacts);
2709
2710         g_simple_async_result_set_check_cancellable (simple, cancellable);
2711
2712         g_simple_async_result_set_op_res_gpointer (
2713                 simple, async_context, (GDestroyNotify) async_context_free);
2714
2715         g_simple_async_result_run_in_thread (
2716                 simple, book_client_remove_contacts_thread,
2717                 G_PRIORITY_DEFAULT, cancellable);
2718
2719         g_object_unref (simple);
2720 }
2721
2722 /**
2723  * e_book_client_remove_contacts_finish:
2724  * @client: an #EBookClient
2725  * @result: a #GAsyncResult
2726  * @error: (out): a #GError to set an error, if any
2727  *
2728  * Finishes previous call of e_book_client_remove_contacts().
2729  *
2730  * Returns: %TRUE if successful, %FALSE otherwise.
2731  *
2732  * Since: 3.2
2733  **/
2734 gboolean
2735 e_book_client_remove_contacts_finish (EBookClient *client,
2736                                       GAsyncResult *result,
2737                                       GError **error)
2738 {
2739         GSimpleAsyncResult *simple;
2740
2741         g_return_val_if_fail (
2742                 g_simple_async_result_is_valid (
2743                 result, G_OBJECT (client),
2744                 e_book_client_remove_contacts), FALSE);
2745
2746         simple = G_SIMPLE_ASYNC_RESULT (result);
2747
2748         /* Assume success unless a GError is set. */
2749         return !g_simple_async_result_propagate_error (simple, error);
2750 }
2751
2752 /**
2753  * e_book_client_remove_contacts_sync:
2754  * @client: an #EBookClient
2755  * @uids: (element-type utf8): a #GSList of UIDs to remove
2756  * @cancellable: a #GCancellable; can be %NULL
2757  * @error: (out): a #GError to set an error, if any
2758  *
2759  * Removes the contacts with uids from the list @uids from @client.  This is
2760  * always more efficient than calling e_book_client_remove_contact() if you
2761  * have more than one uid to remove, as some backends can implement it
2762  * as a batch request.
2763  *
2764  * Returns: %TRUE if successful, %FALSE otherwise.
2765  *
2766  * Since: 3.2
2767  **/
2768 gboolean
2769 e_book_client_remove_contacts_sync (EBookClient *client,
2770                                     const GSList *uids,
2771                                     GCancellable *cancellable,
2772                                     GError **error)
2773 {
2774         gchar **strv;
2775         gint ii = 0;
2776         GError *local_error = NULL;
2777
2778         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2779         g_return_val_if_fail (uids != NULL, FALSE);
2780
2781         strv = g_new0 (gchar *, g_slist_length ((GSList *) uids) + 1);
2782         while (uids != NULL) {
2783                 strv[ii++] = e_util_utf8_make_valid (uids->data);
2784                 uids = g_slist_next (uids);
2785         }
2786
2787         e_dbus_address_book_call_remove_contacts_sync (
2788                 client->priv->dbus_proxy,
2789                 (const gchar * const *) strv,
2790                 cancellable, &local_error);
2791
2792         g_strfreev (strv);
2793
2794         if (local_error != NULL) {
2795                 g_dbus_error_strip_remote_error (local_error);
2796                 g_propagate_error (error, local_error);
2797                 return FALSE;
2798         }
2799
2800         return TRUE;
2801 }
2802
2803 /* Helper for e_book_client_get_contact() */
2804 static void
2805 book_client_get_contact_thread (GSimpleAsyncResult *simple,
2806                                 GObject *source_object,
2807                                 GCancellable *cancellable)
2808 {
2809         AsyncContext *async_context;
2810         GError *local_error = NULL;
2811
2812         async_context = g_simple_async_result_get_op_res_gpointer (simple);
2813
2814         e_book_client_get_contact_sync (
2815                 E_BOOK_CLIENT (source_object),
2816                 async_context->uid,
2817                 &async_context->contact,
2818                 cancellable, &local_error);
2819
2820         if (local_error != NULL)
2821                 g_simple_async_result_take_error (simple, local_error);
2822 }
2823
2824 /**
2825  * e_book_client_get_contact:
2826  * @client: an #EBookClient
2827  * @uid: a unique string ID specifying the contact
2828  * @cancellable: a #GCancellable; can be %NULL
2829  * @callback: callback to call when a result is ready
2830  * @user_data: user data for the @callback
2831  *
2832  * Receive #EContact from the @client for the gived @uid.
2833  * The call is finished by e_book_client_get_contact_finish()
2834  * from the @callback.
2835  *
2836  * Since: 3.2
2837  **/
2838 void
2839 e_book_client_get_contact (EBookClient *client,
2840                            const gchar *uid,
2841                            GCancellable *cancellable,
2842                            GAsyncReadyCallback callback,
2843                            gpointer user_data)
2844 {
2845         GSimpleAsyncResult *simple;
2846         AsyncContext *async_context;
2847
2848         g_return_if_fail (E_IS_BOOK_CLIENT (client));
2849         g_return_if_fail (uid != NULL);
2850
2851         async_context = g_slice_new0 (AsyncContext);
2852         async_context->uid  = g_strdup (uid);
2853
2854         simple = g_simple_async_result_new (
2855                 G_OBJECT (client), callback, user_data,
2856                 e_book_client_get_contact);
2857
2858         g_simple_async_result_set_check_cancellable (simple, cancellable);
2859
2860         g_simple_async_result_set_op_res_gpointer (
2861                 simple, async_context, (GDestroyNotify) async_context_free);
2862
2863         g_simple_async_result_run_in_thread (
2864                 simple, book_client_get_contact_thread,
2865                 G_PRIORITY_DEFAULT, cancellable);
2866
2867         g_object_unref (simple);
2868 }
2869
2870 /**
2871  * e_book_client_get_contact_finish:
2872  * @client: an #EBookClient
2873  * @result: a #GAsyncResult
2874  * @out_contact: (out): an #EContact for previously given uid
2875  * @error: (out): a #GError to set an error, if any
2876  *
2877  * Finishes previous call of e_book_client_get_contact().
2878  * If successful, then the @out_contact is set to newly allocated
2879  * #EContact, which should be freed with g_object_unref().
2880  *
2881  * Returns: %TRUE if successful, %FALSE otherwise.
2882  *
2883  * Since: 3.2
2884  **/
2885 gboolean
2886 e_book_client_get_contact_finish (EBookClient *client,
2887                                   GAsyncResult *result,
2888                                   EContact **out_contact,
2889                                   GError **error)
2890 {
2891         GSimpleAsyncResult *simple;
2892         AsyncContext *async_context;
2893
2894         g_return_val_if_fail (
2895                 g_simple_async_result_is_valid (
2896                 result, G_OBJECT (client),
2897                 e_book_client_get_contact), FALSE);
2898
2899         simple = G_SIMPLE_ASYNC_RESULT (result);
2900         async_context = g_simple_async_result_get_op_res_gpointer (simple);
2901
2902         if (g_simple_async_result_propagate_error (simple, error))
2903                 return FALSE;
2904
2905         g_return_val_if_fail (async_context->contact != NULL, FALSE);
2906
2907         if (out_contact != NULL)
2908                 *out_contact = g_object_ref (async_context->contact);
2909
2910         return TRUE;
2911 }
2912
2913 /**
2914  * e_book_client_get_contact_sync:
2915  * @client: an #EBookClient
2916  * @uid: a unique string ID specifying the contact
2917  * @out_contact: (out): an #EContact for given @uid
2918  * @cancellable: a #GCancellable; can be %NULL
2919  * @error: (out): a #GError to set an error, if any
2920  *
2921  * Receive #EContact from the @client for the gived @uid.
2922  * If successful, then the @out_contact is set to newly allocated
2923  * #EContact, which should be freed with g_object_unref().
2924  *
2925  * Returns: %TRUE if successful, %FALSE otherwise.
2926  *
2927  * Since: 3.2
2928  **/
2929 gboolean
2930 e_book_client_get_contact_sync (EBookClient *client,
2931                                 const gchar *uid,
2932                                 EContact **out_contact,
2933                                 GCancellable *cancellable,
2934                                 GError **error)
2935 {
2936         gchar *utf8_uid;
2937         gchar *vcard = NULL;
2938         GError *local_error = NULL;
2939
2940         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2941         g_return_val_if_fail (uid != NULL, FALSE);
2942         g_return_val_if_fail (out_contact != NULL, FALSE);
2943
2944         if (client->priv->direct_backend != NULL) {
2945                 EContact *contact;
2946                 gboolean success = FALSE;
2947
2948                 /* Direct backend is not using D-Bus (obviously),
2949                  * so no need to strip D-Bus info from the error. */
2950                 contact = e_book_backend_get_contact_sync (
2951                         client->priv->direct_backend,
2952                         uid, cancellable, error);
2953
2954                 if (contact != NULL) {
2955                         *out_contact = g_object_ref (contact);
2956                         g_object_unref (contact);
2957                         success = TRUE;
2958                 }
2959
2960                 return success;
2961         }
2962
2963         utf8_uid = e_util_utf8_make_valid (uid);
2964
2965         e_dbus_address_book_call_get_contact_sync (
2966                 client->priv->dbus_proxy, utf8_uid,
2967                 &vcard, cancellable, &local_error);
2968
2969         /* Sanity check. */
2970         g_return_val_if_fail (
2971                 ((vcard != NULL) && (local_error == NULL)) ||
2972                 ((vcard == NULL) && (local_error != NULL)), FALSE);
2973
2974         if (vcard != NULL) {
2975                 *out_contact =
2976                         e_contact_new_from_vcard_with_uid (vcard, utf8_uid);
2977                 g_free (vcard);
2978         }
2979
2980         g_free (utf8_uid);
2981
2982         if (local_error != NULL) {
2983                 g_dbus_error_strip_remote_error (local_error);
2984                 g_propagate_error (error, local_error);
2985                 return FALSE;
2986         }
2987
2988         return TRUE;
2989 }
2990
2991 /* Helper for e_book_client_get_contacts() */
2992 static void
2993 book_client_get_contacts_thread (GSimpleAsyncResult *simple,
2994                                  GObject *source_object,
2995                                  GCancellable *cancellable)
2996 {
2997         AsyncContext *async_context;
2998         GError *local_error = NULL;
2999
3000         async_context = g_simple_async_result_get_op_res_gpointer (simple);
3001
3002         e_book_client_get_contacts_sync (
3003                 E_BOOK_CLIENT (source_object),
3004                 async_context->sexp,
3005                 &async_context->object_list,
3006                 cancellable, &local_error);
3007
3008         if (local_error != NULL)
3009                 g_simple_async_result_take_error (simple, local_error);
3010 }
3011
3012 /**
3013  * e_book_client_get_contacts:
3014  * @client: an #EBookClient
3015  * @sexp: an S-expression representing the query
3016  * @cancellable: a #GCancellable; can be %NULL
3017  * @callback: callback to call when a result is ready
3018  * @user_data: user data for the @callback
3019  *
3020  * Query @client with @sexp, receiving a list of contacts which
3021  * matched. The call is finished by e_book_client_get_contacts_finish()
3022  * from the @callback.
3023  *
3024  * Note: @sexp can be obtained through #EBookQuery, by converting it
3025  * to a string with e_book_query_to_string().
3026  *
3027  * Since: 3.2
3028  **/
3029 void
3030 e_book_client_get_contacts (EBookClient *client,
3031                             const gchar *sexp,
3032                             GCancellable *cancellable,
3033                             GAsyncReadyCallback callback,
3034                             gpointer user_data)
3035 {
3036         GSimpleAsyncResult *simple;
3037         AsyncContext *async_context;
3038
3039         g_return_if_fail (E_IS_BOOK_CLIENT (client));
3040         g_return_if_fail (sexp != NULL);
3041
3042         async_context = g_slice_new0 (AsyncContext);
3043         async_context->sexp = g_strdup (sexp);
3044
3045         simple = g_simple_async_result_new (
3046                 G_OBJECT (client), callback, user_data,
3047                 e_book_client_get_contacts);
3048
3049         g_simple_async_result_set_check_cancellable (simple, cancellable);
3050
3051         g_simple_async_result_set_op_res_gpointer (
3052                 simple, async_context, (GDestroyNotify) async_context_free);
3053
3054         g_simple_async_result_run_in_thread (
3055                 simple, book_client_get_contacts_thread,
3056                 G_PRIORITY_DEFAULT, cancellable);
3057
3058         g_object_unref (simple);
3059 }
3060
3061 /**
3062  * e_book_client_get_contacts_finish:
3063  * @client: an #EBookClient
3064  * @result: a #GAsyncResult
3065  * @out_contacts: (element-type EContact) (out) (transfer full): a #GSList
3066  *                of matched #EContact-s
3067  * @error: (out): a #GError to set an error, if any
3068  *
3069  * Finishes previous call of e_book_client_get_contacts().
3070  * If successful, then the @out_contacts is set to newly allocated list of
3071  * #EContact-s, which should be freed with e_client_util_free_object_slist().
3072  *
3073  * Returns: %TRUE if successful, %FALSE otherwise.
3074  *
3075  * Since: 3.2
3076  **/
3077 gboolean
3078 e_book_client_get_contacts_finish (EBookClient *client,
3079                                    GAsyncResult *result,
3080                                    GSList **out_contacts,
3081                                    GError **error)
3082 {
3083         GSimpleAsyncResult *simple;
3084         AsyncContext *async_context;
3085
3086         g_return_val_if_fail (
3087                 g_simple_async_result_is_valid (
3088                 result, G_OBJECT (client),
3089                 e_book_client_get_contacts), FALSE);
3090
3091         simple = G_SIMPLE_ASYNC_RESULT (result);
3092         async_context = g_simple_async_result_get_op_res_gpointer (simple);
3093
3094         if (g_simple_async_result_propagate_error (simple, error))
3095                 return FALSE;
3096
3097         if (out_contacts != NULL) {
3098                 *out_contacts = async_context->object_list;
3099                 async_context->object_list = NULL;
3100         }
3101
3102         return TRUE;
3103 }
3104
3105 /**
3106  * e_book_client_get_contacts_sync:
3107  * @client: an #EBookClient
3108  * @sexp: an S-expression representing the query
3109  * @out_contacts: (element-type EContact) (out): a #GSList of matched
3110  *                #EContact-s
3111  * @cancellable: a #GCancellable; can be %NULL
3112  * @error: (out): a #GError to set an error, if any
3113  *
3114  * Query @client with @sexp, receiving a list of contacts which matched.
3115  * If successful, then the @out_contacts is set to newly allocated #GSList of
3116  * #EContact-s, which should be freed with e_client_util_free_object_slist().
3117  *
3118  * Note: @sexp can be obtained through #EBookQuery, by converting it
3119  * to a string with e_book_query_to_string().
3120  *
3121  * Returns: %TRUE if successful, %FALSE otherwise.
3122  *
3123  * Since: 3.2
3124  **/
3125 gboolean
3126 e_book_client_get_contacts_sync (EBookClient *client,
3127                                  const gchar *sexp,
3128                                  GSList **out_contacts,
3129                                  GCancellable *cancellable,
3130                                  GError **error)
3131 {
3132         gchar *utf8_sexp;
3133         gchar **vcards = NULL;
3134         GError *local_error = NULL;
3135
3136         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
3137         g_return_val_if_fail (sexp != NULL, FALSE);
3138         g_return_val_if_fail (out_contacts != NULL, FALSE);
3139
3140         if (client->priv->direct_backend != NULL) {
3141                 GQueue queue = G_QUEUE_INIT;
3142                 GSList *list = NULL;
3143                 gboolean success;
3144
3145                 /* Direct backend is not using D-Bus (obviously),
3146                  * so no need to strip D-Bus info from the error. */
3147                 success = e_book_backend_get_contact_list_sync (
3148                         client->priv->direct_backend,
3149                         sexp, &queue, cancellable, error);
3150
3151                 if (success) {
3152                         while (!g_queue_is_empty (&queue)) {
3153                                 EContact *contact;
3154
3155                                 contact = g_queue_pop_head (&queue);
3156                                 list = g_slist_prepend (list, contact);
3157                         }
3158
3159                         *out_contacts = g_slist_reverse (list);
3160                 }
3161
3162                 return success;
3163         }
3164
3165         utf8_sexp = e_util_utf8_make_valid (sexp);
3166
3167         e_dbus_address_book_call_get_contact_list_sync (
3168                 client->priv->dbus_proxy, utf8_sexp,
3169                 &vcards, cancellable, &local_error);
3170
3171         g_free (utf8_sexp);
3172
3173         /* Sanity check. */
3174         g_return_val_if_fail (
3175                 ((vcards != NULL) && (local_error == NULL)) ||
3176                 ((vcards == NULL) && (local_error != NULL)), FALSE);
3177
3178         if (vcards != NULL) {
3179                 EContact *contact;
3180                 GSList *tmp = NULL;
3181                 gint ii;
3182
3183                 for (ii = 0; vcards[ii] != NULL; ii++) {
3184                         contact = e_contact_new_from_vcard (vcards[ii]);
3185                         tmp = g_slist_prepend (tmp, contact);
3186                 }
3187
3188                 *out_contacts = g_slist_reverse (tmp);
3189
3190                 g_strfreev (vcards);
3191         }
3192
3193         if (local_error != NULL) {
3194                 g_dbus_error_strip_remote_error (local_error);
3195                 g_propagate_error (error, local_error);
3196                 return FALSE;
3197         }
3198
3199         return TRUE;
3200 }
3201
3202 /* Helper for e_book_client_get_contacts_uids() */
3203 static void
3204 book_client_get_contacts_uids_thread (GSimpleAsyncResult *simple,
3205                                       GObject *source_object,
3206                                       GCancellable *cancellable)
3207 {
3208         AsyncContext *async_context;
3209         GError *local_error = NULL;
3210
3211         async_context = g_simple_async_result_get_op_res_gpointer (simple);
3212
3213         e_book_client_get_contacts_uids_sync (
3214                 E_BOOK_CLIENT (source_object),
3215                 async_context->sexp,
3216                 &async_context->string_list,
3217                 cancellable, &local_error);
3218
3219         if (local_error != NULL)
3220                 g_simple_async_result_take_error (simple, local_error);
3221 }
3222
3223 /**
3224  * e_book_client_get_contacts_uids:
3225  * @client: an #EBookClient
3226  * @sexp: an S-expression representing the query
3227  * @cancellable: a #GCancellable; can be %NULL
3228  * @callback: callback to call when a result is ready
3229  * @user_data: user data for the @callback
3230  *
3231  * Query @client with @sexp, receiving a list of contacts UIDs which
3232  * matched. The call is finished by e_book_client_get_contacts_uids_finish()
3233  * from the @callback.
3234  *
3235  * Note: @sexp can be obtained through #EBookQuery, by converting it
3236  * to a string with e_book_query_to_string().
3237  *
3238  * Since: 3.2
3239  **/
3240 void
3241 e_book_client_get_contacts_uids (EBookClient *client,
3242                                  const gchar *sexp,
3243                                  GCancellable *cancellable,
3244                                  GAsyncReadyCallback callback,
3245                                  gpointer user_data)
3246 {
3247         GSimpleAsyncResult *simple;
3248         AsyncContext *async_context;
3249
3250         g_return_if_fail (E_IS_BOOK_CLIENT (client));
3251         g_return_if_fail (sexp != NULL);
3252
3253         async_context = g_slice_new0 (AsyncContext);
3254         async_context->sexp = g_strdup (sexp);
3255
3256         simple = g_simple_async_result_new (
3257                 G_OBJECT (client), callback, user_data,
3258                 e_book_client_get_contacts_uids);
3259
3260         g_simple_async_result_set_check_cancellable (simple, cancellable);
3261
3262         g_simple_async_result_set_op_res_gpointer (
3263                 simple, async_context, (GDestroyNotify) async_context_free);
3264
3265         g_simple_async_result_run_in_thread (
3266                 simple, book_client_get_contacts_uids_thread,
3267                 G_PRIORITY_DEFAULT, cancellable);
3268
3269         g_object_unref (simple);
3270 }
3271
3272 /**
3273  * e_book_client_get_contacts_uids_finish:
3274  * @client: an #EBookClient
3275  * @result: a #GAsyncResult
3276  * @out_contact_uids: (element-type utf8) (out): a #GSList of matched
3277  *                    contact UIDs stored as strings
3278  * @error: (out): a #GError to set an error, if any
3279  *
3280  * Finishes previous call of e_book_client_get_contacts_uids().
3281  * If successful, then the @out_contact_uids is set to newly allocated list
3282  * of UID strings, which should be freed with e_client_util_free_string_slist().
3283  *
3284  * Returns: %TRUE if successful, %FALSE otherwise.
3285  *
3286  * Since: 3.2
3287  **/
3288 gboolean
3289 e_book_client_get_contacts_uids_finish (EBookClient *client,
3290                                         GAsyncResult *result,
3291                                         GSList **out_contact_uids,
3292                                         GError **error)
3293 {
3294         GSimpleAsyncResult *simple;
3295         AsyncContext *async_context;
3296
3297         g_return_val_if_fail (
3298                 g_simple_async_result_is_valid (
3299                 result, G_OBJECT (client),
3300                 e_book_client_get_contacts_uids), FALSE);
3301
3302         simple = G_SIMPLE_ASYNC_RESULT (result);
3303         async_context = g_simple_async_result_get_op_res_gpointer (simple);
3304
3305         if (g_simple_async_result_propagate_error (simple, error))
3306                 return FALSE;
3307
3308         if (out_contact_uids != NULL) {
3309                 *out_contact_uids = async_context->string_list;
3310                 async_context->string_list = NULL;
3311         }
3312
3313         return TRUE;
3314 }
3315
3316 /**
3317  * e_book_client_get_contacts_uids_sync:
3318  * @client: an #EBookClient
3319  * @sexp: an S-expression representing the query
3320  * @out_contact_uids: (element-type utf8) (out): a #GSList of matched
3321  *                    contacts UIDs stored as strings
3322  * @cancellable: a #GCancellable; can be %NULL
3323  * @error: (out): a #GError to set an error, if any
3324  *
3325  * Query @client with @sexp, receiving a list of contacts UIDs which matched.
3326  * If successful, then the @out_contact_uids is set to newly allocated list
3327  * of UID strings, which should be freed with e_client_util_free_string_slist().
3328  *
3329  * Note: @sexp can be obtained through #EBookQuery, by converting it
3330  * to a string with e_book_query_to_string().
3331  *
3332  * Returns: %TRUE if successful, %FALSE otherwise.
3333  *
3334  * Since: 3.2
3335  **/
3336 gboolean
3337 e_book_client_get_contacts_uids_sync (EBookClient *client,
3338                                       const gchar *sexp,
3339                                       GSList **out_contact_uids,
3340                                       GCancellable *cancellable,
3341                                       GError **error)
3342 {
3343         gchar *utf8_sexp;
3344         gchar **uids = NULL;
3345         GError *local_error = NULL;
3346
3347         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
3348         g_return_val_if_fail (sexp != NULL, FALSE);
3349         g_return_val_if_fail (out_contact_uids != NULL, FALSE);
3350
3351         if (client->priv->direct_backend != NULL) {
3352                 GQueue queue = G_QUEUE_INIT;
3353                 GSList *list = NULL;
3354                 gboolean success;
3355
3356                 /* Direct backend is not using D-Bus (obviously),
3357                  * so no need to strip D-Bus info from the error. */
3358                 success = e_book_backend_get_contact_list_uids_sync (
3359                         client->priv->direct_backend,
3360                         sexp, &queue, cancellable, error);
3361
3362                 if (success) {
3363                         while (!g_queue_is_empty (&queue)) {
3364                                 gchar *uid;
3365
3366                                 uid = g_queue_pop_head (&queue);
3367                                 list = g_slist_prepend (list, uid);
3368                         }
3369
3370                         *out_contact_uids = g_slist_reverse (list);
3371                 }
3372
3373                 return success;
3374         }
3375
3376         utf8_sexp = e_util_utf8_make_valid (sexp);
3377
3378         e_dbus_address_book_call_get_contact_list_uids_sync (
3379                 client->priv->dbus_proxy, utf8_sexp,
3380                 &uids, cancellable, &local_error);
3381
3382         g_free (utf8_sexp);
3383
3384         /* Sanity check. */
3385         g_return_val_if_fail (
3386                 ((uids != NULL) && (local_error == NULL)) ||
3387                 ((uids == NULL) && (local_error != NULL)), FALSE);
3388
3389         /* XXX We should have passed the string array directly
3390          *     back to the caller instead of building a linked
3391          *     list.  This is unnecessary work. */
3392         if (uids != NULL) {
3393                 GSList *tmp = NULL;
3394                 gint ii;
3395
3396                 /* Take ownership of the string array elements. */
3397                 for (ii = 0; uids[ii] != NULL; ii++) {
3398                         tmp = g_slist_prepend (tmp, uids[ii]);
3399                         uids[ii] = NULL;
3400                 }
3401
3402                 *out_contact_uids = g_slist_reverse (tmp);
3403
3404                 g_free (uids);
3405         }
3406
3407         if (local_error != NULL) {
3408                 g_dbus_error_strip_remote_error (local_error);
3409                 g_propagate_error (error, local_error);
3410                 return FALSE;
3411         }
3412
3413         return TRUE;
3414 }
3415
3416 /* Helper for e_book_client_get_view() */
3417 static void
3418 book_client_get_view_in_dbus_thread (GSimpleAsyncResult *simple,
3419                                      GObject *source_object,
3420                                      GCancellable *cancellable)
3421 {
3422         EBookClient *client = E_BOOK_CLIENT (source_object);
3423         AsyncContext *async_context;
3424         gchar *utf8_sexp;
3425         gchar *object_path = NULL;
3426         GError *local_error = NULL;
3427
3428         async_context = g_simple_async_result_get_op_res_gpointer (simple);
3429
3430         utf8_sexp = e_util_utf8_make_valid (async_context->sexp);
3431
3432         e_dbus_address_book_call_get_view_sync (
3433                 client->priv->dbus_proxy, utf8_sexp,
3434                 &object_path, cancellable, &local_error);
3435
3436         g_free (utf8_sexp);
3437
3438         /* Sanity check. */
3439         g_return_if_fail (
3440                 ((object_path != NULL) && (local_error == NULL)) ||
3441                 ((object_path == NULL) && (local_error != NULL)));
3442
3443         if (object_path != NULL) {
3444                 GDBusConnection *connection;
3445                 EBookClientView *client_view;
3446
3447                 connection = g_dbus_proxy_get_connection (
3448                         G_DBUS_PROXY (client->priv->dbus_proxy));
3449
3450                 client_view = g_initable_new (
3451                         E_TYPE_BOOK_CLIENT_VIEW,
3452                         cancellable, &local_error,
3453                         "client", client,
3454                         "connection", connection,
3455                         "object-path", object_path,
3456                         "direct-backend", client->priv->direct_backend,
3457                         NULL);
3458
3459                 /* Sanity check. */
3460                 g_return_if_fail (
3461                         ((client_view != NULL) && (local_error == NULL)) ||
3462                         ((client_view == NULL) && (local_error != NULL)));
3463
3464                 async_context->client_view = client_view;
3465
3466                 g_free (object_path);
3467         }
3468
3469         if (local_error != NULL) {
3470                 g_dbus_error_strip_remote_error (local_error);
3471                 g_simple_async_result_take_error (simple, local_error);
3472         }
3473 }
3474
3475 /**
3476  * e_book_client_get_view:
3477  * @client: an #EBookClient
3478  * @sexp: an S-expression representing the query
3479  * @cancellable: a #GCancellable; can be %NULL
3480  * @callback: callback to call when a result is ready
3481  * @user_data: user data for the @callback
3482  *
3483  * Query @client with @sexp, creating an #EBookClientView.
3484  * The call is finished by e_book_client_get_view_finish()
3485  * from the @callback.
3486  *
3487  * Note: @sexp can be obtained through #EBookQuery, by converting it
3488  * to a string with e_book_query_to_string().
3489  *
3490  * Since: 3.2
3491  **/
3492 void
3493 e_book_client_get_view (EBookClient *client,
3494                         const gchar *sexp,
3495                         GCancellable *cancellable,
3496                         GAsyncReadyCallback callback,
3497                         gpointer user_data)
3498 {
3499         GSimpleAsyncResult *simple;
3500         AsyncContext *async_context;
3501
3502         g_return_if_fail (E_IS_BOOK_CLIENT (client));
3503         g_return_if_fail (sexp != NULL);
3504
3505         async_context = g_slice_new0 (AsyncContext);
3506         async_context->sexp = g_strdup (sexp);
3507
3508         simple = g_simple_async_result_new (
3509                 G_OBJECT (client), callback, user_data,
3510                 e_book_client_get_view);
3511
3512         g_simple_async_result_set_check_cancellable (simple, cancellable);
3513
3514         g_simple_async_result_set_op_res_gpointer (
3515                 simple, async_context, (GDestroyNotify) async_context_free);
3516
3517         book_client_run_in_dbus_thread (
3518                 simple, book_client_get_view_in_dbus_thread,
3519                 G_PRIORITY_DEFAULT, cancellable);
3520
3521         g_object_unref (simple);
3522 }
3523
3524 /**
3525  * e_book_client_get_view_finish:
3526  * @client: an #EBookClient
3527  * @result: a #GAsyncResult
3528  * @out_view: (out): an #EBookClientView
3529  * @error: (out): a #GError to set an error, if any
3530  *
3531  * Finishes previous call of e_book_client_get_view().
3532  * If successful, then the @out_view is set to newly allocated
3533  * #EBookClientView, which should be freed with g_object_unref().
3534  *
3535  * Returns: %TRUE if successful, %FALSE otherwise.
3536  *
3537  * Since: 3.2
3538  **/
3539 gboolean
3540 e_book_client_get_view_finish (EBookClient *client,
3541                                GAsyncResult *result,
3542                                EBookClientView **out_view,
3543                                GError **error)
3544 {
3545         GSimpleAsyncResult *simple;
3546         AsyncContext *async_context;
3547
3548         g_return_val_if_fail (
3549                 g_simple_async_result_is_valid (
3550                 result, G_OBJECT (client),
3551                 e_book_client_get_view), FALSE);
3552
3553         simple = G_SIMPLE_ASYNC_RESULT (result);
3554         async_context = g_simple_async_result_get_op_res_gpointer (simple);
3555
3556         if (g_simple_async_result_propagate_error (simple, error))
3557                 return FALSE;
3558
3559         g_return_val_if_fail (async_context->client_view != NULL, FALSE);
3560
3561         if (out_view != NULL)
3562                 *out_view = g_object_ref (async_context->client_view);
3563
3564         return TRUE;
3565 }
3566
3567 /**
3568  * e_book_client_get_view_sync:
3569  * @client: an #EBookClient
3570  * @sexp: an S-expression representing the query
3571  * @out_view: (out) an #EBookClientView
3572  * @cancellable: a #GCancellable; can be %NULL
3573  * @error: (out): a #GError to set an error, if any
3574  *
3575  * Query @client with @sexp, creating an #EBookClientView.
3576  * If successful, then the @out_view is set to newly allocated
3577  * #EBookClientView, which should be freed with g_object_unref().
3578  *
3579  * Note: @sexp can be obtained through #EBookQuery, by converting it
3580  * to a string with e_book_query_to_string().
3581  *
3582  * Returns: %TRUE if successful, %FALSE otherwise.
3583  *
3584  * Since: 3.2
3585  **/
3586 gboolean
3587 e_book_client_get_view_sync (EBookClient *client,
3588                              const gchar *sexp,
3589                              EBookClientView **out_view,
3590                              GCancellable *cancellable,
3591                              GError **error)
3592 {
3593         EAsyncClosure *closure;
3594         GAsyncResult *result;
3595         gboolean success;
3596
3597         g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
3598         g_return_val_if_fail (sexp != NULL, FALSE);
3599         g_return_val_if_fail (out_view != NULL, FALSE);
3600
3601         closure = e_async_closure_new ();
3602
3603         e_book_client_get_view (
3604                 client, sexp, cancellable,
3605                 e_async_closure_callback, closure);
3606
3607         result = e_async_closure_wait (closure);
3608
3609         success = e_book_client_get_view_finish (
3610                 client, result, out_view, error);
3611
3612         e_async_closure_free (closure);
3613
3614         return success;
3615 }
3616