1 /*
2 * e-book-client.c
3 *
4 * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
5 * Copyright (C) 2012 Intel Corporation
6 *
7 * This library is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation.
10 *
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this library. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 /**
22 * SECTION: e-book-client
23 * @include: libebook/libebook.h
24 * @short_description: Accessing and modifying an addressbook
25 *
26 * This class is the main user facing API for accessing and modifying
27 * the addressbook.
28 **/
29
30 #include "evolution-data-server-config.h"
31
32 #include <locale.h>
33 #include <glib/gi18n-lib.h>
34 #include <gio/gio.h>
35
36 /* Private D-Bus classes. */
37 #include <e-dbus-address-book.h>
38 #include <e-dbus-address-book-factory.h>
39 #include <e-dbus-direct-book.h>
40
41 #include <libedataserver/libedataserver.h>
42 #include <libedataserver/e-client-private.h>
43
44 #include <libebackend/libebackend.h>
45 #include <libedata-book/libedata-book.h>
46
47 #include "e-book-client.h"
48
49 /* Set this to a sufficiently large value
50 * to cover most long-running operations. */
51 #define DBUS_PROXY_TIMEOUT_MS (3 * 60 * 1000) /* 3 minutes */
52
53 typedef struct _AsyncContext AsyncContext;
54 typedef struct _SignalClosure SignalClosure;
55 typedef struct _ConnectClosure ConnectClosure;
56 typedef struct _RunInThreadClosure RunInThreadClosure;
57
58 struct _EBookClientPrivate {
59 EDBusAddressBook *dbus_proxy;
60 EBookBackend *direct_backend;
61 guint name_watcher_id;
62
63 gulong dbus_proxy_error_handler_id;
64 gulong dbus_proxy_notify_handler_id;
65
66 gchar *locale;
67 };
68
69 struct _AsyncContext {
70 EContact *contact;
71 EBookClientView *client_view;
72 EBookClientCursor *client_cursor;
73 GSList *object_list;
74 GSList *string_list;
75 EContactField *sort_fields;
76 EBookCursorSortType *sort_types;
77 guint n_sort_fields;
78 gchar *sexp;
79 gchar *uid;
80 GMainContext *context;
81 guint32 opflags;
82 };
83
84 struct _SignalClosure {
85 GWeakRef client;
86 gchar *property_name;
87 gchar *property_value;
88 gchar *error_message;
89 };
90
91 struct _ConnectClosure {
92 ESource *source;
93 GCancellable *cancellable;
94 guint32 wait_for_connected_seconds;
95 };
96
97 struct _RunInThreadClosure {
98 GSimpleAsyncThreadFunc func;
99 GSimpleAsyncResult *simple;
100 GCancellable *cancellable;
101 };
102
103 /* Forward Declarations */
104 static void e_book_client_initable_init
105 (GInitableIface *iface);
106 static void e_book_client_async_initable_init
107 (GAsyncInitableIface *iface);
108 static void book_client_set_locale (EBookClient *client,
109 const gchar *locale);
110
111 enum {
112 PROP_0,
113 PROP_LOCALE
114 };
115
G_DEFINE_TYPE_WITH_CODE(EBookClient,e_book_client,E_TYPE_CLIENT,G_ADD_PRIVATE (EBookClient)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,e_book_client_initable_init)G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,e_book_client_async_initable_init))116 G_DEFINE_TYPE_WITH_CODE (
117 EBookClient,
118 e_book_client,
119 E_TYPE_CLIENT,
120 G_ADD_PRIVATE (EBookClient)
121 G_IMPLEMENT_INTERFACE (
122 G_TYPE_INITABLE,
123 e_book_client_initable_init)
124 G_IMPLEMENT_INTERFACE (
125 G_TYPE_ASYNC_INITABLE,
126 e_book_client_async_initable_init))
127
128 static void
129 async_context_free (AsyncContext *async_context)
130 {
131 if (async_context->contact != NULL)
132 g_object_unref (async_context->contact);
133
134 if (async_context->client_view != NULL)
135 g_object_unref (async_context->client_view);
136
137 if (async_context->client_cursor != NULL)
138 g_object_unref (async_context->client_cursor);
139
140 if (async_context->context)
141 g_main_context_unref (async_context->context);
142
143 g_slist_free_full (
144 async_context->object_list,
145 (GDestroyNotify) g_object_unref);
146
147 g_slist_free_full (
148 async_context->string_list,
149 (GDestroyNotify) g_free);
150
151 g_free (async_context->sort_fields);
152 g_free (async_context->sort_types);
153
154 g_free (async_context->sexp);
155 g_free (async_context->uid);
156
157 g_slice_free (AsyncContext, async_context);
158 }
159
160 static void
signal_closure_free(SignalClosure * signal_closure)161 signal_closure_free (SignalClosure *signal_closure)
162 {
163 g_weak_ref_clear (&signal_closure->client);
164
165 g_free (signal_closure->property_name);
166 g_free (signal_closure->property_value);
167 g_free (signal_closure->error_message);
168
169 g_slice_free (SignalClosure, signal_closure);
170 }
171
172 static void
connect_closure_free(ConnectClosure * connect_closure)173 connect_closure_free (ConnectClosure *connect_closure)
174 {
175 if (connect_closure->source != NULL)
176 g_object_unref (connect_closure->source);
177
178 if (connect_closure->cancellable != NULL)
179 g_object_unref (connect_closure->cancellable);
180
181 g_slice_free (ConnectClosure, connect_closure);
182 }
183
184 static void
run_in_thread_closure_free(RunInThreadClosure * run_in_thread_closure)185 run_in_thread_closure_free (RunInThreadClosure *run_in_thread_closure)
186 {
187 if (run_in_thread_closure->simple != NULL)
188 g_object_unref (run_in_thread_closure->simple);
189
190 if (run_in_thread_closure->cancellable != NULL)
191 g_object_unref (run_in_thread_closure->cancellable);
192
193 g_slice_free (RunInThreadClosure, run_in_thread_closure);
194 }
195
196 /*
197 * Well-known book backend properties:
198 * @E_BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS: Retrieves comma-separated list
199 * of required fields by the backend. Use e_client_util_parse_comma_strings()
200 * to parse returned string value into a #GSList. These fields are required
201 * to be filled in for all contacts.
202 * @E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS: Retrieves comma-separated list
203 * of supported fields by the backend. Use e_client_util_parse_comma_strings()
204 * to parse returned string value into a #GSList. These fields can be
205 * stored for contacts.
206 *
207 * See also: @CLIENT_BACKEND_PROPERTY_OPENED, @CLIENT_BACKEND_PROPERTY_OPENING,
208 * @CLIENT_BACKEND_PROPERTY_ONLINE, @CLIENT_BACKEND_PROPERTY_READONLY
209 * @CLIENT_BACKEND_PROPERTY_CACHE_DIR, @CLIENT_BACKEND_PROPERTY_CAPABILITIES
210 */
211
212 static EBookBackend *
book_client_load_direct_backend(ESourceRegistry * registry,ESource * source,const gchar * backend_path,const gchar * backend_name,const gchar * config,GError ** error)213 book_client_load_direct_backend (ESourceRegistry *registry,
214 ESource *source,
215 const gchar *backend_path,
216 const gchar *backend_name,
217 const gchar *config,
218 GError **error)
219 {
220 static GHashTable *modules_table = NULL;
221 G_LOCK_DEFINE_STATIC (modules_table);
222
223 EModule *module;
224 GType factory_type;
225 EBookBackend *backend;
226 EBookBackendFactoryClass *factory_class;
227
228 g_return_val_if_fail (backend_path != NULL, NULL);
229 g_return_val_if_fail (backend_name != NULL, NULL);
230
231 G_LOCK (modules_table);
232
233 if (modules_table == NULL)
234 modules_table = g_hash_table_new (
235 (GHashFunc) g_str_hash,
236 (GEqualFunc) g_str_equal);
237
238 module = g_hash_table_lookup (modules_table, backend_path);
239
240 if (module == NULL) {
241 module = e_module_new (backend_path);
242 g_hash_table_insert (
243 modules_table, g_strdup (backend_path), module);
244 }
245
246 G_UNLOCK (modules_table);
247
248 if (!g_type_module_use (G_TYPE_MODULE (module))) {
249 g_set_error (
250 error, E_CLIENT_ERROR,
251 E_CLIENT_ERROR_OTHER_ERROR,
252 "Failed to use EModule at path '%s'",
253 backend_path);
254 return NULL;
255 }
256
257 factory_type = g_type_from_name (backend_name);
258 if (factory_type == G_TYPE_INVALID) {
259 g_set_error (
260 error, E_CLIENT_ERROR,
261 E_CLIENT_ERROR_OTHER_ERROR,
262 "Failed to get backend factory '%s' "
263 "from EModule at path '%s'",
264 backend_name, backend_path);
265 g_type_module_unuse (G_TYPE_MODULE (module));
266 return NULL;
267 }
268
269 factory_class = g_type_class_ref (factory_type);
270
271 backend = g_object_new (
272 factory_class->backend_type,
273 "registry", registry,
274 "source", source, NULL);
275
276 /* The backend must be configured for direct access
277 * before calling g_initable_init(), since backends
278 * can access their content in initable_init(). */
279 e_book_backend_configure_direct (backend, config);
280
281 if (!g_initable_init (G_INITABLE (backend), NULL, error)) {
282 g_type_module_unuse (G_TYPE_MODULE (module));
283 g_object_unref (backend);
284 backend = NULL;
285 }
286
287 g_type_class_unref (factory_class);
288
289 /* XXX Until backend methods can be updated, EBookBackend
290 * still needs an EDataBook to catch "respond" calls. */
291 if (backend != NULL) {
292 EDataBook *data_book;
293
294 data_book = g_initable_new (
295 E_TYPE_DATA_BOOK, NULL, error,
296 "backend", backend, NULL);
297
298 if (data_book == NULL) {
299 g_type_module_unuse (G_TYPE_MODULE (module));
300 g_object_unref (backend);
301 backend = NULL;
302 }
303
304 g_clear_object (&data_book);
305
306 }
307
308 return backend;
309 }
310
311 static gpointer
book_client_dbus_thread(gpointer user_data)312 book_client_dbus_thread (gpointer user_data)
313 {
314 GMainContext *main_context = user_data;
315 GMainLoop *main_loop;
316
317 g_main_context_push_thread_default (main_context);
318
319 main_loop = g_main_loop_new (main_context, FALSE);
320 g_main_loop_run (main_loop);
321 g_main_loop_unref (main_loop);
322
323 g_main_context_pop_thread_default (main_context);
324
325 g_main_context_unref (main_context);
326
327 return NULL;
328 }
329
330 static gpointer
book_client_dbus_thread_init(gpointer unused)331 book_client_dbus_thread_init (gpointer unused)
332 {
333 GMainContext *main_context;
334
335 main_context = g_main_context_new ();
336
337 /* This thread terminates when the process itself terminates, so
338 * no need to worry about unreferencing the returned GThread. */
339 g_thread_new (
340 "book-client-dbus-thread",
341 book_client_dbus_thread,
342 g_main_context_ref (main_context));
343
344 return main_context;
345 }
346
347 static GMainContext *
book_client_ref_dbus_main_context(void)348 book_client_ref_dbus_main_context (void)
349 {
350 static GOnce book_client_dbus_thread_once = G_ONCE_INIT;
351
352 g_once (
353 &book_client_dbus_thread_once,
354 book_client_dbus_thread_init, NULL);
355
356 return g_main_context_ref (book_client_dbus_thread_once.retval);
357 }
358
359 static gboolean
book_client_run_in_dbus_thread_idle_cb(gpointer user_data)360 book_client_run_in_dbus_thread_idle_cb (gpointer user_data)
361 {
362 RunInThreadClosure *closure = user_data;
363 GObject *source_object;
364 GAsyncResult *result;
365
366 result = G_ASYNC_RESULT (closure->simple);
367 source_object = g_async_result_get_source_object (result);
368
369 closure->func (
370 closure->simple,
371 source_object,
372 closure->cancellable);
373
374 if (source_object != NULL)
375 g_object_unref (source_object);
376
377 g_simple_async_result_complete_in_idle (closure->simple);
378
379 return FALSE;
380 }
381
382 static void
book_client_run_in_dbus_thread(GSimpleAsyncResult * simple,GSimpleAsyncThreadFunc func,gint io_priority,GCancellable * cancellable)383 book_client_run_in_dbus_thread (GSimpleAsyncResult *simple,
384 GSimpleAsyncThreadFunc func,
385 gint io_priority,
386 GCancellable *cancellable)
387 {
388 RunInThreadClosure *closure;
389 GMainContext *main_context;
390 GSource *idle_source;
391
392 main_context = book_client_ref_dbus_main_context ();
393
394 closure = g_slice_new0 (RunInThreadClosure);
395 closure->func = func;
396 closure->simple = g_object_ref (simple);
397
398 if (G_IS_CANCELLABLE (cancellable))
399 closure->cancellable = g_object_ref (cancellable);
400
401 idle_source = g_idle_source_new ();
402 g_source_set_priority (idle_source, io_priority);
403 g_source_set_callback (
404 idle_source, book_client_run_in_dbus_thread_idle_cb,
405 closure, (GDestroyNotify) run_in_thread_closure_free);
406 g_source_attach (idle_source, main_context);
407 g_source_unref (idle_source);
408
409 g_main_context_unref (main_context);
410 }
411
412 static gboolean
book_client_emit_backend_died_idle_cb(gpointer user_data)413 book_client_emit_backend_died_idle_cb (gpointer user_data)
414 {
415 SignalClosure *signal_closure = user_data;
416 EClient *client;
417
418 client = g_weak_ref_get (&signal_closure->client);
419
420 if (client != NULL) {
421 g_signal_emit_by_name (client, "backend-died");
422 g_object_unref (client);
423 }
424
425 return FALSE;
426 }
427
428 static gboolean
book_client_emit_backend_error_idle_cb(gpointer user_data)429 book_client_emit_backend_error_idle_cb (gpointer user_data)
430 {
431 SignalClosure *signal_closure = user_data;
432 EClient *client;
433
434 client = g_weak_ref_get (&signal_closure->client);
435
436 if (client != NULL) {
437 g_signal_emit_by_name (
438 client, "backend-error",
439 signal_closure->error_message);
440 g_object_unref (client);
441 }
442
443 return FALSE;
444 }
445
446 static gboolean
book_client_emit_backend_property_changed_idle_cb(gpointer user_data)447 book_client_emit_backend_property_changed_idle_cb (gpointer user_data)
448 {
449 SignalClosure *signal_closure = user_data;
450 EClient *client;
451
452 client = g_weak_ref_get (&signal_closure->client);
453
454 if (client != NULL) {
455 gchar *prop_value = NULL;
456
457 /* Notify that the "locale" property has changed in the calling thread
458 */
459 if (g_str_equal (signal_closure->property_name, "locale")) {
460 EBookClient *book_client = E_BOOK_CLIENT (client);
461
462 book_client_set_locale (book_client, signal_closure->property_value);
463
464 } else {
465
466 /* XXX Despite appearances, this function does not block. */
467 e_client_get_backend_property_sync (
468 client,
469 signal_closure->property_name,
470 &prop_value, NULL, NULL);
471
472 if (prop_value != NULL) {
473 g_signal_emit_by_name (
474 client,
475 "backend-property-changed",
476 signal_closure->property_name,
477 prop_value);
478 g_free (prop_value);
479 }
480 }
481
482 g_object_unref (client);
483 }
484
485 return FALSE;
486 }
487
488 static void
book_client_dbus_proxy_error_cb(EDBusAddressBook * dbus_proxy,const gchar * error_message,GWeakRef * client_weak_ref)489 book_client_dbus_proxy_error_cb (EDBusAddressBook *dbus_proxy,
490 const gchar *error_message,
491 GWeakRef *client_weak_ref)
492 {
493 EClient *client;
494
495 client = g_weak_ref_get (client_weak_ref);
496
497 if (client != NULL) {
498 GSource *idle_source;
499 GMainContext *main_context;
500 SignalClosure *signal_closure;
501
502 signal_closure = g_slice_new0 (SignalClosure);
503 g_weak_ref_init (&signal_closure->client, client);
504 signal_closure->error_message = g_strdup (error_message);
505
506 main_context = e_client_ref_main_context (client);
507
508 idle_source = g_idle_source_new ();
509 g_source_set_callback (
510 idle_source,
511 book_client_emit_backend_error_idle_cb,
512 signal_closure,
513 (GDestroyNotify) signal_closure_free);
514 g_source_attach (idle_source, main_context);
515 g_source_unref (idle_source);
516
517 g_main_context_unref (main_context);
518
519 g_object_unref (client);
520 }
521 }
522
523 static void
book_client_dbus_proxy_property_changed(EClient * client,const gchar * property_name,const GValue * value,gboolean is_in_main_thread)524 book_client_dbus_proxy_property_changed (EClient *client,
525 const gchar *property_name,
526 const GValue *value,
527 gboolean is_in_main_thread)
528 {
529 const gchar *backend_prop_name = NULL;
530
531 g_return_if_fail (E_IS_BOOK_CLIENT (client));
532 g_return_if_fail (property_name != NULL);
533
534 if (g_str_equal (property_name, "cache-dir")) {
535 backend_prop_name = CLIENT_BACKEND_PROPERTY_CACHE_DIR;
536 }
537
538 if (g_str_equal (property_name, "capabilities")) {
539 gchar **strv;
540 gchar *csv = NULL;
541
542 backend_prop_name = CLIENT_BACKEND_PROPERTY_CAPABILITIES;
543
544 strv = g_value_get_boxed (value);
545 if (strv != NULL) {
546 csv = g_strjoinv (",", strv);
547 }
548 e_client_set_capabilities (client, csv);
549 g_free (csv);
550 }
551
552 if (g_str_equal (property_name, "online")) {
553 gboolean online;
554
555 backend_prop_name = CLIENT_BACKEND_PROPERTY_ONLINE;
556
557 online = g_value_get_boolean (value);
558 e_client_set_online (client, online);
559 }
560
561 if (g_str_equal (property_name, "required-fields")) {
562 backend_prop_name = E_BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS;
563 }
564
565 if (g_str_equal (property_name, "revision")) {
566 backend_prop_name = CLIENT_BACKEND_PROPERTY_REVISION;
567 }
568
569 if (g_str_equal (property_name, "supported-fields")) {
570 backend_prop_name = E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS;
571 }
572
573 if (g_str_equal (property_name, "writable")) {
574 gboolean writable;
575
576 backend_prop_name = CLIENT_BACKEND_PROPERTY_READONLY;
577
578 writable = g_value_get_boolean (value);
579 e_client_set_readonly (client, !writable);
580 }
581
582 if (g_str_equal (property_name, "locale")) {
583 backend_prop_name = "locale";
584 }
585
586 if (backend_prop_name != NULL) {
587 SignalClosure *signal_closure;
588
589 signal_closure = g_slice_new0 (SignalClosure);
590 g_weak_ref_init (&signal_closure->client, client);
591 signal_closure->property_name = g_strdup (backend_prop_name);
592
593 /* The 'locale' is not an EClient property, so just transport
594 * the value directly on the SignalClosure
595 */
596 if (g_str_equal (backend_prop_name, "locale"))
597 signal_closure->property_value = g_value_dup_string (value);
598
599 if (is_in_main_thread) {
600 book_client_emit_backend_property_changed_idle_cb (signal_closure);
601 signal_closure_free (signal_closure);
602 } else {
603 GSource *idle_source;
604 GMainContext *main_context;
605
606 main_context = e_client_ref_main_context (client);
607
608 idle_source = g_idle_source_new ();
609 g_source_set_callback (
610 idle_source,
611 book_client_emit_backend_property_changed_idle_cb,
612 signal_closure,
613 (GDestroyNotify) signal_closure_free);
614 g_source_attach (idle_source, main_context);
615 g_source_unref (idle_source);
616
617 g_main_context_unref (main_context);
618 }
619 }
620 }
621
622 typedef struct {
623 EClient *client;
624 gchar *property_name;
625 GValue property_value;
626 } IdleProxyNotifyData;
627
628 static void
idle_proxy_notify_data_free(gpointer ptr)629 idle_proxy_notify_data_free (gpointer ptr)
630 {
631 IdleProxyNotifyData *ipn = ptr;
632
633 if (ipn) {
634 g_clear_object (&ipn->client);
635 g_free (ipn->property_name);
636 g_value_unset (&ipn->property_value);
637 g_free (ipn);
638 }
639 }
640
641 static gboolean
book_client_proxy_notify_idle_cb(gpointer user_data)642 book_client_proxy_notify_idle_cb (gpointer user_data)
643 {
644 IdleProxyNotifyData *ipn = user_data;
645
646 g_return_val_if_fail (ipn != NULL, FALSE);
647
648 book_client_dbus_proxy_property_changed (ipn->client, ipn->property_name, &ipn->property_value, TRUE);
649
650 return FALSE;
651 }
652
653 static void
book_client_dbus_proxy_notify_cb(EDBusAddressBook * dbus_proxy,GParamSpec * pspec,GWeakRef * client_weak_ref)654 book_client_dbus_proxy_notify_cb (EDBusAddressBook *dbus_proxy,
655 GParamSpec *pspec,
656 GWeakRef *client_weak_ref)
657 {
658 EClient *client;
659 GSource *idle_source;
660 GMainContext *main_context;
661 IdleProxyNotifyData *ipn;
662
663 client = g_weak_ref_get (client_weak_ref);
664 if (client == NULL)
665 return;
666
667 ipn = g_new0 (IdleProxyNotifyData, 1);
668 ipn->client = g_object_ref (client);
669 ipn->property_name = g_strdup (pspec->name);
670 g_value_init (&ipn->property_value, pspec->value_type);
671 g_object_get_property (G_OBJECT (dbus_proxy), pspec->name, &ipn->property_value);
672
673 main_context = e_client_ref_main_context (client);
674
675 idle_source = g_idle_source_new ();
676 g_source_set_callback (idle_source, book_client_proxy_notify_idle_cb,
677 ipn, idle_proxy_notify_data_free);
678 g_source_attach (idle_source, main_context);
679 g_source_unref (idle_source);
680
681 g_main_context_unref (main_context);
682 g_object_unref (client);
683 }
684
685 static void
book_client_name_vanished_cb(GDBusConnection * connection,const gchar * name,GWeakRef * client_weak_ref)686 book_client_name_vanished_cb (GDBusConnection *connection,
687 const gchar *name,
688 GWeakRef *client_weak_ref)
689 {
690 EClient *client;
691
692 client = g_weak_ref_get (client_weak_ref);
693
694 if (client != NULL) {
695 GSource *idle_source;
696 GMainContext *main_context;
697 SignalClosure *signal_closure;
698
699 signal_closure = g_slice_new0 (SignalClosure);
700 g_weak_ref_init (&signal_closure->client, client);
701
702 main_context = e_client_ref_main_context (client);
703
704 idle_source = g_idle_source_new ();
705 g_source_set_callback (
706 idle_source,
707 book_client_emit_backend_died_idle_cb,
708 signal_closure,
709 (GDestroyNotify) signal_closure_free);
710 g_source_attach (idle_source, main_context);
711 g_source_unref (idle_source);
712
713 g_main_context_unref (main_context);
714
715 g_object_unref (client);
716 }
717 }
718
719 static void
book_client_dispose(GObject * object)720 book_client_dispose (GObject *object)
721 {
722 EBookClientPrivate *priv;
723
724 priv = E_BOOK_CLIENT (object)->priv;
725
726 if (priv->dbus_proxy_error_handler_id > 0) {
727 g_signal_handler_disconnect (
728 priv->dbus_proxy,
729 priv->dbus_proxy_error_handler_id);
730 priv->dbus_proxy_error_handler_id = 0;
731 }
732
733 if (priv->dbus_proxy_notify_handler_id > 0) {
734 g_signal_handler_disconnect (
735 priv->dbus_proxy,
736 priv->dbus_proxy_notify_handler_id);
737 priv->dbus_proxy_notify_handler_id = 0;
738 }
739
740 if (priv->dbus_proxy != NULL) {
741 /* Call close() asynchronously so we don't block dispose().
742 * Also omit a callback function, so the GDBusMessage uses
743 * G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED. */
744 e_dbus_address_book_call_close (
745 priv->dbus_proxy, NULL, NULL, NULL);
746 g_object_unref (priv->dbus_proxy);
747 priv->dbus_proxy = NULL;
748 }
749
750 g_clear_object (&priv->direct_backend);
751
752 /* Chain up to parent's dispose() method. */
753 G_OBJECT_CLASS (e_book_client_parent_class)->dispose (object);
754 }
755
756 static void
book_client_finalize(GObject * object)757 book_client_finalize (GObject *object)
758 {
759 EBookClientPrivate *priv;
760
761 priv = E_BOOK_CLIENT (object)->priv;
762
763 if (priv->name_watcher_id > 0)
764 g_bus_unwatch_name (priv->name_watcher_id);
765
766 g_free (priv->locale);
767
768 /* Chain up to parent's finalize() method. */
769 G_OBJECT_CLASS (e_book_client_parent_class)->finalize (object);
770 }
771
772 static void
book_client_process_properties(EBookClient * book_client,gchar * const * properties)773 book_client_process_properties (EBookClient *book_client,
774 gchar * const *properties)
775 {
776 GObject *dbus_proxy;
777 GObjectClass *object_class;
778 gint ii;
779
780 g_return_if_fail (E_IS_BOOK_CLIENT (book_client));
781
782 dbus_proxy = G_OBJECT (book_client->priv->dbus_proxy);
783 g_return_if_fail (G_IS_OBJECT (dbus_proxy));
784
785 if (!properties)
786 return;
787
788 object_class = G_OBJECT_GET_CLASS (dbus_proxy);
789
790 for (ii = 0; properties[ii]; ii++) {
791 if (!(ii & 1) && properties[ii + 1]) {
792 GParamSpec *param;
793 GVariant *expected = NULL;
794
795 param = g_object_class_find_property (object_class, properties[ii]);
796 if (param) {
797 #define WORKOUT(gvl, gvr) \
798 if (g_type_is_a (param->value_type, G_TYPE_ ## gvl)) { \
799 expected = g_variant_parse (G_VARIANT_TYPE_ ## gvr, properties[ii + 1], NULL, NULL, NULL); \
800 }
801
802 WORKOUT (BOOLEAN, BOOLEAN);
803 WORKOUT (STRING, STRING);
804 WORKOUT (STRV, STRING_ARRAY);
805 WORKOUT (UCHAR, BYTE);
806 WORKOUT (INT, INT32);
807 WORKOUT (UINT, UINT32);
808 WORKOUT (INT64, INT64);
809 WORKOUT (UINT64, UINT64);
810 WORKOUT (DOUBLE, DOUBLE);
811
812 #undef WORKOUT
813 }
814
815 /* Update the property always, even when the current value on the GDBusProxy
816 matches the expected value, because sometimes the proxy can have up-to-date
817 values, but still not propagated into EClient properties. */
818 if (expected) {
819 GValue value = G_VALUE_INIT;
820
821 g_dbus_gvariant_to_gvalue (expected, &value);
822
823 book_client_dbus_proxy_property_changed (E_CLIENT (book_client), param->name, &value, FALSE);
824
825 g_value_unset (&value);
826 g_variant_unref (expected);
827 }
828 }
829 }
830 }
831
832 static GDBusProxy *
book_client_get_dbus_proxy(EClient * client)833 book_client_get_dbus_proxy (EClient *client)
834 {
835 EBookClientPrivate *priv;
836
837 priv = E_BOOK_CLIENT (client)->priv;
838
839 return G_DBUS_PROXY (priv->dbus_proxy);
840 }
841
842 static gboolean
book_client_get_backend_property_sync(EClient * client,const gchar * prop_name,gchar ** prop_value,GCancellable * cancellable,GError ** error)843 book_client_get_backend_property_sync (EClient *client,
844 const gchar *prop_name,
845 gchar **prop_value,
846 GCancellable *cancellable,
847 GError **error)
848 {
849 EBookClient *book_client;
850 EDBusAddressBook *dbus_proxy;
851 gchar **strv;
852
853 book_client = E_BOOK_CLIENT (client);
854 dbus_proxy = book_client->priv->dbus_proxy;
855
856 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
857 *prop_value = g_strdup ("TRUE");
858 return TRUE;
859 }
860
861 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
862 *prop_value = g_strdup ("FALSE");
863 return TRUE;
864 }
865
866 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
867 if (e_dbus_address_book_get_online (dbus_proxy))
868 *prop_value = g_strdup ("TRUE");
869 else
870 *prop_value = g_strdup ("FALSE");
871 return TRUE;
872 }
873
874 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
875 if (e_dbus_address_book_get_writable (dbus_proxy))
876 *prop_value = g_strdup ("FALSE");
877 else
878 *prop_value = g_strdup ("TRUE");
879 return TRUE;
880 }
881
882 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
883 *prop_value = e_dbus_address_book_dup_cache_dir (dbus_proxy);
884 return TRUE;
885 }
886
887 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_REVISION)) {
888 *prop_value = e_dbus_address_book_dup_revision (dbus_proxy);
889 return TRUE;
890 }
891
892 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
893 strv = e_dbus_address_book_dup_capabilities (dbus_proxy);
894 if (strv != NULL)
895 *prop_value = g_strjoinv (",", strv);
896 else
897 *prop_value = g_strdup ("");
898 g_strfreev (strv);
899 return TRUE;
900 }
901
902 if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
903 strv = e_dbus_address_book_dup_required_fields (dbus_proxy);
904 if (strv != NULL)
905 *prop_value = g_strjoinv (",", strv);
906 else
907 *prop_value = g_strdup ("");
908 g_strfreev (strv);
909 return TRUE;
910 }
911
912 if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
913 strv = e_dbus_address_book_dup_supported_fields (dbus_proxy);
914 if (strv != NULL)
915 *prop_value = g_strjoinv (",", strv);
916 else
917 *prop_value = g_strdup ("");
918 g_strfreev (strv);
919 return TRUE;
920 }
921
922 g_set_error (
923 error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED,
924 _("Unknown book property “%s”"), prop_name);
925
926 return TRUE;
927 }
928
929 static gboolean
book_client_set_backend_property_sync(EClient * client,const gchar * prop_name,const gchar * prop_value,GCancellable * cancellable,GError ** error)930 book_client_set_backend_property_sync (EClient *client,
931 const gchar *prop_name,
932 const gchar *prop_value,
933 GCancellable *cancellable,
934 GError **error)
935 {
936 g_set_error (
937 error, E_CLIENT_ERROR,
938 E_CLIENT_ERROR_NOT_SUPPORTED,
939 _("Cannot change value of book property “%s”"),
940 prop_name);
941
942 return FALSE;
943 }
944
945 static gboolean
book_client_open_sync(EClient * client,gboolean only_if_exists,GCancellable * cancellable,GError ** error)946 book_client_open_sync (EClient *client,
947 gboolean only_if_exists,
948 GCancellable *cancellable,
949 GError **error)
950 {
951 EBookClient *book_client;
952 gchar **properties = NULL;
953 GError *local_error = NULL;
954
955 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
956
957 book_client = E_BOOK_CLIENT (client);
958
959 e_dbus_address_book_call_open_sync (
960 book_client->priv->dbus_proxy, &properties, cancellable, &local_error);
961
962 book_client_process_properties (book_client, properties);
963 g_strfreev (properties);
964
965 if (local_error != NULL) {
966 g_dbus_error_strip_remote_error (local_error);
967 g_propagate_error (error, local_error);
968 return FALSE;
969 }
970
971 return TRUE;
972 }
973
974 static gboolean
book_client_refresh_sync(EClient * client,GCancellable * cancellable,GError ** error)975 book_client_refresh_sync (EClient *client,
976 GCancellable *cancellable,
977 GError **error)
978 {
979 EBookClient *book_client;
980 GError *local_error = NULL;
981
982 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
983
984 book_client = E_BOOK_CLIENT (client);
985
986 e_dbus_address_book_call_refresh_sync (
987 book_client->priv->dbus_proxy, cancellable, &local_error);
988
989 if (local_error != NULL) {
990 g_dbus_error_strip_remote_error (local_error);
991 g_propagate_error (error, local_error);
992 return FALSE;
993 }
994
995 return TRUE;
996 }
997
998 static gboolean
book_client_retrieve_properties_sync(EClient * client,GCancellable * cancellable,GError ** error)999 book_client_retrieve_properties_sync (EClient *client,
1000 GCancellable *cancellable,
1001 GError **error)
1002 {
1003 EBookClient *book_client;
1004 gchar **properties = NULL;
1005 GError *local_error = NULL;
1006
1007 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
1008
1009 book_client = E_BOOK_CLIENT (client);
1010
1011 e_dbus_address_book_call_retrieve_properties_sync (
1012 book_client->priv->dbus_proxy, &properties, cancellable, &local_error);
1013
1014 book_client_process_properties (book_client, properties);
1015 g_strfreev (properties);
1016
1017 if (local_error != NULL) {
1018 g_dbus_error_strip_remote_error (local_error);
1019 g_propagate_error (error, local_error);
1020 return FALSE;
1021 }
1022
1023 return TRUE;
1024 }
1025
1026 static void
book_client_init_in_dbus_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1027 book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
1028 GObject *source_object,
1029 GCancellable *cancellable)
1030 {
1031 EBookClientPrivate *priv;
1032 EDBusAddressBookFactory *factory_proxy;
1033 GDBusConnection *connection;
1034 GDBusProxy *proxy;
1035 EClient *client;
1036 ESource *source;
1037 const gchar *uid;
1038 gchar *object_path = NULL;
1039 gchar *bus_name = NULL;
1040 gulong handler_id;
1041 GError *local_error = NULL;
1042
1043 priv = E_BOOK_CLIENT (source_object)->priv;
1044
1045 client = E_CLIENT (source_object);
1046 source = e_client_get_source (client);
1047 uid = e_source_get_uid (source);
1048
1049 connection = g_bus_get_sync (
1050 G_BUS_TYPE_SESSION, cancellable, &local_error);
1051
1052 /* Sanity check. */
1053 g_return_if_fail (
1054 ((connection != NULL) && (local_error == NULL)) ||
1055 ((connection == NULL) && (local_error != NULL)));
1056
1057 if (local_error != NULL) {
1058 g_dbus_error_strip_remote_error (local_error);
1059 g_simple_async_result_take_error (simple, local_error);
1060 return;
1061 }
1062
1063 factory_proxy = e_dbus_address_book_factory_proxy_new_sync (
1064 connection,
1065 G_DBUS_PROXY_FLAGS_NONE,
1066 ADDRESS_BOOK_DBUS_SERVICE_NAME,
1067 "/org/gnome/evolution/dataserver/AddressBookFactory",
1068 cancellable, &local_error);
1069
1070 /* Sanity check. */
1071 g_return_if_fail (
1072 ((factory_proxy != NULL) && (local_error == NULL)) ||
1073 ((factory_proxy == NULL) && (local_error != NULL)));
1074
1075 if (local_error != NULL) {
1076 g_dbus_error_strip_remote_error (local_error);
1077 g_simple_async_result_take_error (simple, local_error);
1078 g_object_unref (connection);
1079 return;
1080 }
1081
1082 e_dbus_address_book_factory_call_open_address_book_sync (
1083 factory_proxy, uid, &object_path, &bus_name, cancellable, &local_error);
1084
1085 g_object_unref (factory_proxy);
1086
1087 /* Sanity check. */
1088 g_return_if_fail (
1089 (((object_path != NULL) || (bus_name != NULL)) && (local_error == NULL)) ||
1090 (((object_path == NULL) || (bus_name == NULL)) && (local_error != NULL)));
1091
1092 if (local_error != NULL) {
1093 g_dbus_error_strip_remote_error (local_error);
1094 g_simple_async_result_take_error (simple, local_error);
1095 g_object_unref (connection);
1096 return;
1097 }
1098
1099 e_client_set_bus_name (client, bus_name);
1100
1101 priv->dbus_proxy = e_dbus_address_book_proxy_new_sync (
1102 connection,
1103 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
1104 bus_name, object_path, cancellable, &local_error);
1105
1106 g_free (object_path);
1107 g_free (bus_name);
1108
1109 /* Sanity check. */
1110 g_return_if_fail (
1111 ((priv->dbus_proxy != NULL) && (local_error == NULL)) ||
1112 ((priv->dbus_proxy == NULL) && (local_error != NULL)));
1113
1114 if (local_error != NULL) {
1115 g_dbus_error_strip_remote_error (local_error);
1116 g_simple_async_result_take_error (simple, local_error);
1117 g_object_unref (connection);
1118 return;
1119 }
1120
1121 /* Configure our new GDBusProxy. */
1122
1123 proxy = G_DBUS_PROXY (priv->dbus_proxy);
1124
1125 g_dbus_proxy_set_default_timeout (proxy, DBUS_PROXY_TIMEOUT_MS);
1126
1127 priv->name_watcher_id = g_bus_watch_name_on_connection (
1128 connection,
1129 g_dbus_proxy_get_name (proxy),
1130 G_BUS_NAME_WATCHER_FLAGS_NONE,
1131 (GBusNameAppearedCallback) NULL,
1132 (GBusNameVanishedCallback) book_client_name_vanished_cb,
1133 e_weak_ref_new (client),
1134 (GDestroyNotify) e_weak_ref_free);
1135
1136 handler_id = g_signal_connect_data (
1137 proxy, "error",
1138 G_CALLBACK (book_client_dbus_proxy_error_cb),
1139 e_weak_ref_new (client),
1140 (GClosureNotify) e_weak_ref_free,
1141 0);
1142 priv->dbus_proxy_error_handler_id = handler_id;
1143
1144 handler_id = g_signal_connect_data (
1145 proxy, "notify",
1146 G_CALLBACK (book_client_dbus_proxy_notify_cb),
1147 e_weak_ref_new (client),
1148 (GClosureNotify) e_weak_ref_free,
1149 0);
1150 priv->dbus_proxy_notify_handler_id = handler_id;
1151
1152 /* Initialize our public-facing GObject properties. */
1153 g_object_notify (G_OBJECT (proxy), "online");
1154 g_object_notify (G_OBJECT (proxy), "writable");
1155 g_object_notify (G_OBJECT (proxy), "capabilities");
1156
1157 book_client_set_locale (
1158 E_BOOK_CLIENT (client),
1159 e_dbus_address_book_get_locale (priv->dbus_proxy));
1160
1161 g_object_unref (connection);
1162 }
1163
1164 static gboolean
book_client_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)1165 book_client_initable_init (GInitable *initable,
1166 GCancellable *cancellable,
1167 GError **error)
1168 {
1169 EAsyncClosure *closure;
1170 GAsyncResult *result;
1171 gboolean success;
1172
1173 closure = e_async_closure_new ();
1174
1175 g_async_initable_init_async (
1176 G_ASYNC_INITABLE (initable),
1177 G_PRIORITY_DEFAULT, cancellable,
1178 e_async_closure_callback, closure);
1179
1180 result = e_async_closure_wait (closure);
1181
1182 success = g_async_initable_init_finish (
1183 G_ASYNC_INITABLE (initable), result, error);
1184
1185 e_async_closure_free (closure);
1186
1187 return success;
1188 }
1189
1190 static void
book_client_initable_init_async(GAsyncInitable * initable,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1191 book_client_initable_init_async (GAsyncInitable *initable,
1192 gint io_priority,
1193 GCancellable *cancellable,
1194 GAsyncReadyCallback callback,
1195 gpointer user_data)
1196 {
1197 GSimpleAsyncResult *simple;
1198
1199 simple = g_simple_async_result_new (
1200 G_OBJECT (initable), callback, user_data,
1201 book_client_initable_init_async);
1202
1203 g_simple_async_result_set_check_cancellable (simple, cancellable);
1204
1205 book_client_run_in_dbus_thread (
1206 simple, book_client_init_in_dbus_thread,
1207 io_priority, cancellable);
1208
1209 g_object_unref (simple);
1210 }
1211
1212 static gboolean
book_client_initable_init_finish(GAsyncInitable * initable,GAsyncResult * result,GError ** error)1213 book_client_initable_init_finish (GAsyncInitable *initable,
1214 GAsyncResult *result,
1215 GError **error)
1216 {
1217 GSimpleAsyncResult *simple;
1218
1219 g_return_val_if_fail (
1220 g_simple_async_result_is_valid (
1221 result, G_OBJECT (initable),
1222 book_client_initable_init_async), FALSE);
1223
1224 simple = G_SIMPLE_ASYNC_RESULT (result);
1225
1226 /* Assume success unless a GError is set. */
1227 return !g_simple_async_result_propagate_error (simple, error);
1228 }
1229
1230 static void
book_client_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1231 book_client_get_property (GObject *object,
1232 guint property_id,
1233 GValue *value,
1234 GParamSpec *pspec)
1235 {
1236 EBookClient *book_client;
1237
1238 book_client = E_BOOK_CLIENT (object);
1239
1240 switch (property_id) {
1241 case PROP_LOCALE:
1242 g_value_set_string (
1243 value,
1244 book_client->priv->locale);
1245 return;
1246
1247 }
1248
1249 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1250 }
1251
1252 static void
e_book_client_class_init(EBookClientClass * class)1253 e_book_client_class_init (EBookClientClass *class)
1254 {
1255 GObjectClass *object_class;
1256 EClientClass *client_class;
1257
1258 object_class = G_OBJECT_CLASS (class);
1259 object_class->dispose = book_client_dispose;
1260 object_class->finalize = book_client_finalize;
1261 object_class->get_property = book_client_get_property;
1262
1263 client_class = E_CLIENT_CLASS (class);
1264 client_class->get_dbus_proxy = book_client_get_dbus_proxy;
1265 client_class->get_backend_property_sync = book_client_get_backend_property_sync;
1266 client_class->set_backend_property_sync = book_client_set_backend_property_sync;
1267 client_class->open_sync = book_client_open_sync;
1268 client_class->refresh_sync = book_client_refresh_sync;
1269 client_class->retrieve_properties_sync = book_client_retrieve_properties_sync;
1270
1271 /**
1272 * EBookClient:locale:
1273 *
1274 * The currently active locale for this addressbook.
1275 *
1276 * Since: 3.12
1277 */
1278 g_object_class_install_property (
1279 object_class,
1280 PROP_LOCALE,
1281 g_param_spec_string (
1282 "locale",
1283 "Locale",
1284 "The currently active locale for this addressbook",
1285 NULL,
1286 G_PARAM_READABLE |
1287 G_PARAM_STATIC_STRINGS));
1288 }
1289
1290 static void
e_book_client_initable_init(GInitableIface * iface)1291 e_book_client_initable_init (GInitableIface *iface)
1292 {
1293 iface->init = book_client_initable_init;
1294 }
1295
1296 static void
e_book_client_async_initable_init(GAsyncInitableIface * iface)1297 e_book_client_async_initable_init (GAsyncInitableIface *iface)
1298 {
1299 iface->init_async = book_client_initable_init_async;
1300 iface->init_finish = book_client_initable_init_finish;
1301 }
1302
1303 static void
e_book_client_init(EBookClient * client)1304 e_book_client_init (EBookClient *client)
1305 {
1306 const gchar *default_locale;
1307
1308 client->priv = e_book_client_get_instance_private (client);
1309
1310 default_locale = setlocale (LC_COLLATE, NULL);
1311 client->priv->locale = g_strdup (default_locale);
1312 }
1313
1314 /**
1315 * e_book_client_connect_sync:
1316 * @source: an #ESource
1317 * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
1318 * @cancellable: optional #GCancellable object, or %NULL
1319 * @error: return location for a #GError, or %NULL
1320 *
1321 * Creates a new #EBookClient for @source. If an error occurs, the function
1322 * will set @error and return %FALSE.
1323 *
1324 * Unlike with e_book_client_new(), there is no need to call
1325 * e_client_open_sync() after obtaining the #EBookClient.
1326 *
1327 * The @wait_for_connected_seconds argument had been added since 3.16,
1328 * to let the caller decide how long to wait for the backend to fully
1329 * connect to its (possibly remote) data store. This is required due
1330 * to a change in the authentication process, which is fully asynchronous
1331 * and done on the client side, while not every client is supposed to
1332 * response to authentication requests. In case the backend will not connect
1333 * within the set interval, then it is opened in an offline mode. A special
1334 * value -1 can be used to not wait for the connected state at all.
1335 *
1336 * For error handling convenience, any error message returned by this
1337 * function will have a descriptive prefix that includes the display
1338 * name of @source.
1339 *
1340 * Returns: (transfer full) (type EBookClient): a new #EBookClient, or %NULL
1341 *
1342 * Since: 3.8
1343 **/
1344 EClient *
e_book_client_connect_sync(ESource * source,guint32 wait_for_connected_seconds,GCancellable * cancellable,GError ** error)1345 e_book_client_connect_sync (ESource *source,
1346 guint32 wait_for_connected_seconds,
1347 GCancellable *cancellable,
1348 GError **error)
1349 {
1350 EBookClient *client;
1351 GError *local_error = NULL;
1352
1353 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1354
1355 client = g_object_new (
1356 E_TYPE_BOOK_CLIENT,
1357 "source", source, NULL);
1358
1359 g_initable_init (G_INITABLE (client), cancellable, &local_error);
1360
1361 if (local_error == NULL) {
1362 gchar **properties = NULL;
1363
1364 e_dbus_address_book_call_open_sync (
1365 client->priv->dbus_proxy, &properties, cancellable, &local_error);
1366
1367 book_client_process_properties (client, properties);
1368 g_strfreev (properties);
1369 }
1370
1371 if (!local_error && wait_for_connected_seconds != (guint32) -1) {
1372 /* These errors are ignored, the book is left opened in an offline mode. */
1373 e_client_wait_for_connected_sync (E_CLIENT (client),
1374 wait_for_connected_seconds, cancellable, NULL);
1375 }
1376
1377 if (local_error != NULL) {
1378 g_dbus_error_strip_remote_error (local_error);
1379 g_propagate_error (error, local_error);
1380 g_prefix_error (
1381 error, _("Unable to connect to “%s”: "),
1382 e_source_get_display_name (source));
1383 g_object_unref (client);
1384 return NULL;
1385 }
1386
1387 return E_CLIENT (client);
1388 }
1389
1390 static void
book_client_connect_wait_for_connected_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1391 book_client_connect_wait_for_connected_cb (GObject *source_object,
1392 GAsyncResult *result,
1393 gpointer user_data)
1394 {
1395 GSimpleAsyncResult *simple;
1396
1397 simple = G_SIMPLE_ASYNC_RESULT (user_data);
1398
1399 /* These errors are ignored, the book is left opened in an offline mode. */
1400 e_client_wait_for_connected_finish (E_CLIENT (source_object), result, NULL);
1401
1402 g_simple_async_result_complete (simple);
1403
1404 g_object_unref (simple);
1405 }
1406
1407 /* Helper for e_book_client_connect() */
1408 static void
book_client_connect_open_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1409 book_client_connect_open_cb (GObject *source_object,
1410 GAsyncResult *result,
1411 gpointer user_data)
1412 {
1413 GSimpleAsyncResult *simple;
1414 gchar **properties = NULL;
1415 GObject *client_object;
1416 GError *local_error = NULL;
1417
1418 simple = G_SIMPLE_ASYNC_RESULT (user_data);
1419
1420 e_dbus_address_book_call_open_finish (
1421 E_DBUS_ADDRESS_BOOK (source_object), &properties, result, &local_error);
1422
1423 client_object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
1424 if (client_object) {
1425 book_client_process_properties (E_BOOK_CLIENT (client_object), properties);
1426
1427 if (!local_error) {
1428 ConnectClosure *closure;
1429
1430 closure = g_simple_async_result_get_op_res_gpointer (simple);
1431 if (closure->wait_for_connected_seconds != (guint32) -1) {
1432 e_client_wait_for_connected (E_CLIENT (client_object),
1433 closure->wait_for_connected_seconds,
1434 closure->cancellable,
1435 book_client_connect_wait_for_connected_cb, g_object_ref (simple));
1436
1437 g_clear_object (&client_object);
1438 g_object_unref (simple);
1439 g_strfreev (properties);
1440 return;
1441 }
1442 }
1443
1444 g_clear_object (&client_object);
1445 }
1446
1447 if (local_error != NULL) {
1448 g_dbus_error_strip_remote_error (local_error);
1449 g_simple_async_result_take_error (simple, local_error);
1450 }
1451
1452 g_simple_async_result_complete (simple);
1453
1454 g_object_unref (simple);
1455 g_strfreev (properties);
1456 }
1457
1458 /* Helper for e_book_client_connect() */
1459 static void
book_client_connect_init_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1460 book_client_connect_init_cb (GObject *source_object,
1461 GAsyncResult *result,
1462 gpointer user_data)
1463 {
1464 GSimpleAsyncResult *simple;
1465 EBookClientPrivate *priv;
1466 ConnectClosure *closure;
1467 GError *local_error = NULL;
1468
1469 simple = G_SIMPLE_ASYNC_RESULT (user_data);
1470
1471 g_async_initable_init_finish (
1472 G_ASYNC_INITABLE (source_object), result, &local_error);
1473
1474 if (local_error != NULL) {
1475 g_simple_async_result_take_error (simple, local_error);
1476 g_simple_async_result_complete (simple);
1477 goto exit;
1478 }
1479
1480 /* Note, we're repurposing some function parameters. */
1481
1482 result = G_ASYNC_RESULT (simple);
1483 source_object = g_async_result_get_source_object (result);
1484 closure = g_simple_async_result_get_op_res_gpointer (simple);
1485
1486 priv = E_BOOK_CLIENT (source_object)->priv;
1487
1488 e_dbus_address_book_call_open (
1489 priv->dbus_proxy,
1490 closure->cancellable,
1491 book_client_connect_open_cb,
1492 g_object_ref (simple));
1493
1494 g_object_unref (source_object);
1495
1496 exit:
1497 g_object_unref (simple);
1498 }
1499
1500 /**
1501 * e_book_client_connect:
1502 * @source: an #ESource
1503 * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
1504 * @cancellable: optional #GCancellable object, or %NULL
1505 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1506 * @user_data: data to pass to the callback function
1507 *
1508 * Asynchronously creates a new #EBookClient for @source.
1509 *
1510 * The @wait_for_connected_seconds argument had been added since 3.16,
1511 * to let the caller decide how long to wait for the backend to fully
1512 * connect to its (possibly remote) data store. This is required due
1513 * to a change in the authentication process, which is fully asynchronous
1514 * and done on the client side, while not every client is supposed to
1515 * response to authentication requests. In case the backend will not connect
1516 * within the set interval, then it is opened in an offline mode. A special
1517 * value -1 can be used to not wait for the connected state at all.
1518 *
1519 * Unlike with e_book_client_new(), there is no need to call e_client_open()
1520 * after obtaining the #EBookClient.
1521 *
1522 * When the operation is finished, @callback will be called. You can then
1523 * call e_book_client_connect_finish() to get the result of the operation.
1524 *
1525 * Since: 3.8
1526 **/
1527 void
e_book_client_connect(ESource * source,guint32 wait_for_connected_seconds,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1528 e_book_client_connect (ESource *source,
1529 guint32 wait_for_connected_seconds,
1530 GCancellable *cancellable,
1531 GAsyncReadyCallback callback,
1532 gpointer user_data)
1533 {
1534 GSimpleAsyncResult *simple;
1535 ConnectClosure *closure;
1536 EBookClient *client;
1537
1538 g_return_if_fail (E_IS_SOURCE (source));
1539
1540 /* Two things with this: 1) instantiate the client object
1541 * immediately to make sure the thread-default GMainContext
1542 * gets plucked, and 2) do not call the D-Bus open() method
1543 * from our designated D-Bus thread -- it may take a long
1544 * time and block other clients from receiving signals. */
1545
1546 closure = g_slice_new0 (ConnectClosure);
1547 closure->source = g_object_ref (source);
1548 closure->wait_for_connected_seconds = wait_for_connected_seconds;
1549
1550 if (G_IS_CANCELLABLE (cancellable))
1551 closure->cancellable = g_object_ref (cancellable);
1552
1553 client = g_object_new (
1554 E_TYPE_BOOK_CLIENT,
1555 "source", source, NULL);
1556
1557 simple = g_simple_async_result_new (
1558 G_OBJECT (client), callback,
1559 user_data, e_book_client_connect);
1560
1561 g_simple_async_result_set_check_cancellable (simple, cancellable);
1562
1563 g_simple_async_result_set_op_res_gpointer (
1564 simple, closure, (GDestroyNotify) connect_closure_free);
1565
1566 g_async_initable_init_async (
1567 G_ASYNC_INITABLE (client),
1568 G_PRIORITY_DEFAULT, cancellable,
1569 book_client_connect_init_cb,
1570 g_object_ref (simple));
1571
1572 g_object_unref (simple);
1573 g_object_unref (client);
1574 }
1575
1576 /**
1577 * e_book_client_connect_finish:
1578 * @result: a #GAsyncResult
1579 * @error: return location for a #GError, or %NULL
1580 *
1581 * Finishes the operation started with e_book_client_connect(). If an
1582 * error occurs in connecting to the D-Bus service, the function sets
1583 * @error and returns %NULL.
1584 *
1585 * For error handling convenience, any error message returned by this
1586 * function will have a descriptive prefix that includes the display
1587 * name of the #ESource passed to e_book_client_connect().
1588 *
1589 * Returns: (transfer full) (type EBookClient): a new #EBookClient, or %NULL
1590 *
1591 * Since: 3.8
1592 **/
1593 EClient *
e_book_client_connect_finish(GAsyncResult * result,GError ** error)1594 e_book_client_connect_finish (GAsyncResult *result,
1595 GError **error)
1596 {
1597 GSimpleAsyncResult *simple;
1598 ConnectClosure *closure;
1599 gpointer source_tag;
1600
1601 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
1602
1603 simple = G_SIMPLE_ASYNC_RESULT (result);
1604 closure = g_simple_async_result_get_op_res_gpointer (simple);
1605
1606 source_tag = g_simple_async_result_get_source_tag (simple);
1607 g_return_val_if_fail (source_tag == e_book_client_connect, NULL);
1608
1609 if (g_simple_async_result_propagate_error (simple, error)) {
1610 g_prefix_error (
1611 error, _("Unable to connect to “%s”: "),
1612 e_source_get_display_name (closure->source));
1613 return NULL;
1614 }
1615
1616 return E_CLIENT (g_async_result_get_source_object (result));
1617 }
1618
1619 /**
1620 * e_book_client_new:
1621 * @source: An #ESource pointer
1622 * @error: A #GError pointer
1623 *
1624 * Creates a new #EBookClient corresponding to the given source. There are
1625 * only two operations that are valid on this book at this point:
1626 * e_client_open(), and e_client_remove().
1627 *
1628 * Returns: a new but unopened #EBookClient.
1629 *
1630 * Since: 3.2
1631 *
1632 * Deprecated: 3.8: It covertly makes synchronous D-Bus calls, with no
1633 * way to cancel. Use e_book_client_connect() instead,
1634 * which combines e_book_client_new() and e_client_open()
1635 * into one step.
1636 **/
1637 EBookClient *
e_book_client_new(ESource * source,GError ** error)1638 e_book_client_new (ESource *source,
1639 GError **error)
1640 {
1641 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1642
1643 return g_initable_new (
1644 E_TYPE_BOOK_CLIENT, NULL, error,
1645 "source", source, NULL);
1646 }
1647
1648 /* Direct Read Access connect helper */
1649 static void
connect_direct(EBookClient * client,GCancellable * cancellable,ESourceRegistry * registry)1650 connect_direct (EBookClient *client,
1651 GCancellable *cancellable,
1652 ESourceRegistry *registry)
1653 {
1654 EBookClientPrivate *priv;
1655 EDBusDirectBook *direct_config;
1656 const gchar *backend_name, *backend_path, *config;
1657 gchar *bus_name;
1658
1659 priv = E_BOOK_CLIENT (client)->priv;
1660
1661 if (registry)
1662 g_object_ref (registry);
1663 else {
1664 registry = e_source_registry_new_sync (cancellable, NULL);
1665
1666 if (!registry)
1667 return;
1668 }
1669
1670 bus_name = e_client_dup_bus_name (E_CLIENT (client));
1671
1672 direct_config = e_dbus_direct_book_proxy_new_sync (
1673 g_dbus_proxy_get_connection (G_DBUS_PROXY (priv->dbus_proxy)),
1674 G_DBUS_PROXY_FLAGS_NONE,
1675 bus_name,
1676 g_dbus_proxy_get_object_path (G_DBUS_PROXY (priv->dbus_proxy)),
1677 NULL, NULL);
1678
1679 g_free (bus_name);
1680
1681 backend_path = e_dbus_direct_book_get_backend_path (direct_config);
1682 backend_name = e_dbus_direct_book_get_backend_name (direct_config);
1683 config = e_dbus_direct_book_get_backend_config (direct_config);
1684
1685 if (backend_path != NULL && *backend_path != '\0' &&
1686 backend_name != NULL && *backend_name != '\0') {
1687 priv->direct_backend = book_client_load_direct_backend (
1688 registry, e_client_get_source (E_CLIENT (client)),
1689 backend_path,
1690 backend_name,
1691 config, NULL);
1692
1693 }
1694
1695 g_object_unref (direct_config);
1696 g_object_unref (registry);
1697
1698 /* We have to perform the opening of the direct backend separately
1699 * from the EClient->open() implementation, because the direct
1700 * backend does not exist yet. */
1701 if (priv->direct_backend != NULL &&
1702 !e_book_backend_open_sync (priv->direct_backend,
1703 cancellable,
1704 NULL))
1705 g_clear_object (&priv->direct_backend);
1706 }
1707
1708 /**
1709 * e_book_client_connect_direct_sync:
1710 * @registry: an #ESourceRegistry
1711 * @source: an #ESource
1712 * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
1713 * @cancellable: optional #GCancellable object, or %NULL
1714 * @error: return location for a #GError, or %NULL
1715 *
1716 * Like e_book_client_connect_sync(), except creates the book client for
1717 * direct read access to the underlying addressbook.
1718 *
1719 * Returns: (transfer full) (type EBookClient): a new but unopened #EBookClient.
1720 *
1721 * Since: 3.8
1722 **/
1723 EClient *
e_book_client_connect_direct_sync(ESourceRegistry * registry,ESource * source,guint32 wait_for_connected_seconds,GCancellable * cancellable,GError ** error)1724 e_book_client_connect_direct_sync (ESourceRegistry *registry,
1725 ESource *source,
1726 guint32 wait_for_connected_seconds,
1727 GCancellable *cancellable,
1728 GError **error)
1729 {
1730 EClient *client;
1731
1732 client = e_book_client_connect_sync (source, wait_for_connected_seconds, cancellable, error);
1733
1734 if (!client)
1735 return NULL;
1736
1737 /* Connect the direct EDataBook connection */
1738 connect_direct (E_BOOK_CLIENT (client), cancellable, registry);
1739
1740 return client;
1741
1742 }
1743
1744 /* Helper for e_book_client_connect_direct() */
1745 static void
book_client_connect_direct_init_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1746 book_client_connect_direct_init_cb (GObject *source_object,
1747 GAsyncResult *result,
1748 gpointer user_data)
1749 {
1750 GSimpleAsyncResult *simple;
1751 EBookClientPrivate *priv;
1752 ConnectClosure *closure;
1753 GError *error = NULL;
1754
1755 simple = G_SIMPLE_ASYNC_RESULT (user_data);
1756
1757 g_async_initable_init_finish (
1758 G_ASYNC_INITABLE (source_object), result, &error);
1759
1760 if (error != NULL) {
1761 g_simple_async_result_take_error (simple, error);
1762 g_simple_async_result_complete (simple);
1763 goto exit;
1764 }
1765
1766 /* Note, we're repurposing some function parameters. */
1767 result = G_ASYNC_RESULT (simple);
1768 source_object = g_async_result_get_source_object (result);
1769 closure = g_simple_async_result_get_op_res_gpointer (simple);
1770
1771 priv = E_BOOK_CLIENT (source_object)->priv;
1772
1773 e_dbus_address_book_call_open (
1774 priv->dbus_proxy,
1775 closure->cancellable,
1776 book_client_connect_open_cb,
1777 g_object_ref (simple));
1778
1779 /* Make the DRA connection */
1780 connect_direct (E_BOOK_CLIENT (source_object), closure->cancellable, NULL);
1781
1782 g_object_unref (source_object);
1783
1784 exit:
1785 g_object_unref (simple);
1786 }
1787
1788 /**
1789 * e_book_client_connect_direct:
1790 * @source: an #ESource
1791 * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
1792 * @cancellable: optional #GCancellable object, or %NULL
1793 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1794 * @user_data: data to pass to the callback function
1795 *
1796 * Like e_book_client_connect(), except creates the book client for
1797 * direct read access to the underlying addressbook.
1798 *
1799 * When the operation is finished, @callback will be called. You can then
1800 * call e_book_client_connect_direct_finish() to get the result of the operation.
1801 *
1802 * Since: 3.12
1803 **/
1804 void
e_book_client_connect_direct(ESource * source,guint32 wait_for_connected_seconds,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1805 e_book_client_connect_direct (ESource *source,
1806 guint32 wait_for_connected_seconds,
1807 GCancellable *cancellable,
1808 GAsyncReadyCallback callback,
1809 gpointer user_data)
1810 {
1811 GSimpleAsyncResult *simple;
1812 ConnectClosure *closure;
1813 EBookClient *client;
1814
1815 g_return_if_fail (E_IS_SOURCE (source));
1816
1817 /* Two things with this: 1) instantiate the client object
1818 * immediately to make sure the thread-default GMainContext
1819 * gets plucked, and 2) do not call the D-Bus open() method
1820 * from our designated D-Bus thread -- it may take a long
1821 * time and block other clients from receiving signals. */
1822 closure = g_slice_new0 (ConnectClosure);
1823 closure->source = g_object_ref (source);
1824 closure->wait_for_connected_seconds = wait_for_connected_seconds;
1825
1826 if (G_IS_CANCELLABLE (cancellable))
1827 closure->cancellable = g_object_ref (cancellable);
1828
1829 client = g_object_new (
1830 E_TYPE_BOOK_CLIENT,
1831 "source", source, NULL);
1832
1833 simple = g_simple_async_result_new (
1834 G_OBJECT (client), callback,
1835 user_data, e_book_client_connect_direct);
1836
1837 g_simple_async_result_set_check_cancellable (simple, cancellable);
1838
1839 g_simple_async_result_set_op_res_gpointer (
1840 simple, closure, (GDestroyNotify) connect_closure_free);
1841
1842 g_async_initable_init_async (
1843 G_ASYNC_INITABLE (client),
1844 G_PRIORITY_DEFAULT, cancellable,
1845 book_client_connect_direct_init_cb,
1846 g_object_ref (simple));
1847
1848 g_object_unref (simple);
1849 g_object_unref (client);
1850 }
1851
1852 /**
1853 * e_book_client_connect_direct_finish:
1854 * @result: a #GAsyncResult
1855 * @error: return location for a #GError, or %NULL
1856 *
1857 * Finishes the operation started with e_book_client_connect_direct().
1858 * If an error occurs in connecting to the D-Bus service, the function sets
1859 * @error and returns %NULL.
1860 *
1861 * For error handling convenience, any error message returned by this
1862 * function will have a descriptive prefix that includes the display
1863 * name of the #ESource passed to e_book_client_connect_direct().
1864 *
1865 * Returns: (transfer full) (type EBookClient): a new #EBookClient, or %NULL
1866 *
1867 * Since: 3.12
1868 **/
1869 EClient *
e_book_client_connect_direct_finish(GAsyncResult * result,GError ** error)1870 e_book_client_connect_direct_finish (GAsyncResult *result,
1871 GError **error)
1872 {
1873 GSimpleAsyncResult *simple;
1874 ConnectClosure *closure;
1875 gpointer source_tag;
1876
1877 g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
1878
1879 simple = G_SIMPLE_ASYNC_RESULT (result);
1880 closure = g_simple_async_result_get_op_res_gpointer (simple);
1881
1882 source_tag = g_simple_async_result_get_source_tag (simple);
1883 g_return_val_if_fail (source_tag == e_book_client_connect_direct, NULL);
1884
1885 if (g_simple_async_result_propagate_error (simple, error)) {
1886 g_prefix_error (
1887 error, _("Unable to connect to “%s”: "),
1888 e_source_get_display_name (closure->source));
1889 return NULL;
1890 }
1891
1892 return E_CLIENT (g_async_result_get_source_object (result));
1893 }
1894
1895 #define SELF_UID_PATH_ID "org.gnome.evolution-data-server.addressbook"
1896 #define SELF_UID_KEY "self-contact-uid"
1897
1898 static EContact *
make_me_card(void)1899 make_me_card (void)
1900 {
1901 GString *vcard;
1902 const gchar *s;
1903 EContact *contact;
1904
1905 vcard = g_string_new ("BEGIN:VCARD\nVERSION:3.0\n");
1906
1907 s = g_get_user_name ();
1908 if (s)
1909 g_string_append_printf (vcard, "NICKNAME:%s\n", s);
1910
1911 s = g_get_real_name ();
1912 if (s && strcmp (s, "Unknown") != 0) {
1913 ENameWestern *western;
1914
1915 g_string_append_printf (vcard, "FN:%s\n", s);
1916
1917 western = e_name_western_parse (s);
1918 g_string_append_printf (
1919 vcard, "N:%s;%s;%s;%s;%s\n",
1920 western->last ? western->last : "",
1921 western->first ? western->first : "",
1922 western->middle ? western->middle : "",
1923 western->prefix ? western->prefix : "",
1924 western->suffix ? western->suffix : "");
1925 e_name_western_free (western);
1926 }
1927 g_string_append (vcard, "END:VCARD");
1928
1929 contact = e_contact_new_from_vcard (vcard->str);
1930
1931 g_string_free (vcard, TRUE);
1932
1933 return contact;
1934 }
1935
1936 /**
1937 * e_book_client_get_self:
1938 * @registry: an #ESourceRegistry
1939 * @out_contact: (out): an #EContact pointer to set
1940 * @out_client: (out): an #EBookClient pointer to set
1941 * @error: a #GError to set on failure
1942 *
1943 * Get the #EContact referring to the user of the address book
1944 * and set it in @out_contact and @out_client.
1945 *
1946 * Returns: %TRUE if successful, otherwise %FALSE.
1947 *
1948 * Since: 3.2
1949 **/
1950 gboolean
e_book_client_get_self(ESourceRegistry * registry,EContact ** out_contact,EBookClient ** out_client,GError ** error)1951 e_book_client_get_self (ESourceRegistry *registry,
1952 EContact **out_contact,
1953 EBookClient **out_client,
1954 GError **error)
1955 {
1956 EBookClient *book_client;
1957 ESource *source;
1958 EContact *contact = NULL;
1959 GSettings *settings;
1960 gchar *uid;
1961 gboolean success;
1962
1963 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
1964 g_return_val_if_fail (out_contact != NULL, FALSE);
1965 g_return_val_if_fail (out_client != NULL, FALSE);
1966
1967 source = e_source_registry_ref_builtin_address_book (registry);
1968 book_client = e_book_client_new (source, error);
1969 g_object_unref (source);
1970
1971 if (book_client == NULL)
1972 return FALSE;
1973
1974 success = e_client_open_sync (
1975 E_CLIENT (book_client), FALSE, NULL, error);
1976 if (!success) {
1977 g_object_unref (book_client);
1978 return FALSE;
1979 }
1980
1981 *out_client = book_client;
1982
1983 settings = g_settings_new (SELF_UID_PATH_ID);
1984 uid = g_settings_get_string (settings, SELF_UID_KEY);
1985 g_object_unref (settings);
1986
1987 if (uid) {
1988 /* Don't care about errors because
1989 * we'll create a new card on failure. */
1990 /* coverity[unchecked_value] */
1991 if (!e_book_client_get_contact_sync (book_client, uid, &contact, NULL, NULL))
1992 contact = NULL;
1993
1994 g_free (uid);
1995
1996 if (contact != NULL) {
1997 *out_client = book_client;
1998 *out_contact = contact;
1999 return TRUE;
2000 }
2001 }
2002
2003 uid = NULL;
2004 contact = make_me_card ();
2005 success = e_book_client_add_contact_sync (
2006 book_client, contact, E_BOOK_OPERATION_FLAG_NONE, &uid, NULL, error);
2007 if (!success) {
2008 g_object_unref (book_client);
2009 g_object_unref (contact);
2010 return FALSE;
2011 }
2012
2013 if (uid != NULL) {
2014 e_contact_set (contact, E_CONTACT_UID, uid);
2015 g_free (uid);
2016 }
2017
2018 e_book_client_set_self (book_client, contact, NULL);
2019
2020 *out_client = book_client;
2021 *out_contact = contact;
2022
2023 return TRUE;
2024 }
2025
2026 /**
2027 * e_book_client_set_self:
2028 * @client: an #EBookClient
2029 * @contact: an #EContact
2030 * @error: a #GError to set on failure
2031 *
2032 * Specify that @contact residing in @client is the #EContact that
2033 * refers to the user of the address book.
2034 *
2035 * Returns: %TRUE if successful, %FALSE otherwise.
2036 *
2037 * Since: 3.2
2038 **/
2039 gboolean
e_book_client_set_self(EBookClient * client,EContact * contact,GError ** error)2040 e_book_client_set_self (EBookClient *client,
2041 EContact *contact,
2042 GError **error)
2043 {
2044 GSettings *settings;
2045
2046 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2047 g_return_val_if_fail (contact != NULL, FALSE);
2048 g_return_val_if_fail (
2049 e_contact_get_const (contact, E_CONTACT_UID) != NULL, FALSE);
2050
2051 settings = g_settings_new (SELF_UID_PATH_ID);
2052 g_settings_set_string (
2053 settings, SELF_UID_KEY,
2054 e_contact_get_const (contact, E_CONTACT_UID));
2055 g_object_unref (settings);
2056
2057 return TRUE;
2058 }
2059
2060 /**
2061 * e_book_client_is_self:
2062 * @contact: an #EContact
2063 *
2064 * Check if @contact is the user of the address book.
2065 *
2066 * Returns: %TRUE if @contact is the user, %FALSE otherwise.
2067 *
2068 * Since: 3.2
2069 **/
2070 gboolean
e_book_client_is_self(EContact * contact)2071 e_book_client_is_self (EContact *contact)
2072 {
2073 static GSettings *settings;
2074 static GMutex mutex;
2075 const gchar *contact_uid;
2076 gchar *uid;
2077 gboolean is_self;
2078
2079 g_return_val_if_fail (contact && E_IS_CONTACT (contact), FALSE);
2080
2081 /*
2082 * It would be nice to attach this instance to the EBookClient
2083 * instance so that it can be free again later, but
2084 * unfortunately the API doesn't allow that.
2085 */
2086 g_mutex_lock (&mutex);
2087 if (!settings)
2088 settings = g_settings_new (SELF_UID_PATH_ID);
2089 uid = g_settings_get_string (settings, SELF_UID_KEY);
2090 g_mutex_unlock (&mutex);
2091
2092 contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
2093 is_self = (uid != NULL) && (g_strcmp0 (uid, contact_uid) == 0);
2094
2095 g_free (uid);
2096
2097 return is_self;
2098 }
2099
2100 /* Helper for e_book_client_add_contact() */
2101 static void
book_client_add_contact_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2102 book_client_add_contact_thread (GSimpleAsyncResult *simple,
2103 GObject *source_object,
2104 GCancellable *cancellable)
2105 {
2106 AsyncContext *async_context;
2107 GError *local_error = NULL;
2108
2109 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2110
2111 if (!e_book_client_add_contact_sync (
2112 E_BOOK_CLIENT (source_object),
2113 async_context->contact,
2114 async_context->opflags,
2115 &async_context->uid,
2116 cancellable, &local_error)) {
2117
2118 if (!local_error)
2119 local_error = g_error_new_literal (
2120 E_CLIENT_ERROR,
2121 E_CLIENT_ERROR_OTHER_ERROR,
2122 _("Unknown error"));
2123 }
2124
2125 if (local_error != NULL)
2126 g_simple_async_result_take_error (simple, local_error);
2127 }
2128
2129 /**
2130 * e_book_client_add_contact:
2131 * @client: an #EBookClient
2132 * @contact: an #EContact
2133 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2134 * @cancellable: a #GCancellable; can be %NULL
2135 * @callback: callback to call when a result is ready
2136 * @user_data: user data for the @callback
2137 *
2138 * Adds @contact to @client.
2139 * The call is finished by e_book_client_add_contact_finish()
2140 * from the @callback.
2141 *
2142 * Since: 3.2
2143 **/
2144 void
e_book_client_add_contact(EBookClient * client,EContact * contact,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2145 e_book_client_add_contact (EBookClient *client,
2146 EContact *contact,
2147 guint32 opflags,
2148 GCancellable *cancellable,
2149 GAsyncReadyCallback callback,
2150 gpointer user_data)
2151 {
2152 GSimpleAsyncResult *simple;
2153 AsyncContext *async_context;
2154
2155 g_return_if_fail (E_IS_BOOK_CLIENT (client));
2156 g_return_if_fail (E_IS_CONTACT (contact));
2157
2158 async_context = g_slice_new0 (AsyncContext);
2159 async_context->contact = g_object_ref (contact);
2160 async_context->opflags = opflags;
2161
2162 simple = g_simple_async_result_new (
2163 G_OBJECT (client), callback, user_data,
2164 e_book_client_add_contact);
2165
2166 g_simple_async_result_set_check_cancellable (simple, cancellable);
2167
2168 g_simple_async_result_set_op_res_gpointer (
2169 simple, async_context, (GDestroyNotify) async_context_free);
2170
2171 g_simple_async_result_run_in_thread (
2172 simple, book_client_add_contact_thread,
2173 G_PRIORITY_DEFAULT, cancellable);
2174
2175 g_object_unref (simple);
2176 }
2177
2178 /**
2179 * e_book_client_add_contact_finish:
2180 * @client: an #EBookClient
2181 * @result: a #GAsyncResult
2182 * @out_added_uid: (out) (optional): UID of a newly added contact; can be %NULL
2183 * @error: a #GError to set an error, if any
2184 *
2185 * Finishes previous call of e_book_client_add_contact() and
2186 * sets @out_added_uid to a UID of a newly added contact.
2187 * This string should be freed with g_free().
2188 *
2189 * Note: This is not modifying original #EContact.
2190 *
2191 * Returns: %TRUE if successful, %FALSE otherwise.
2192 *
2193 * Since: 3.2
2194 **/
2195 gboolean
e_book_client_add_contact_finish(EBookClient * client,GAsyncResult * result,gchar ** out_added_uid,GError ** error)2196 e_book_client_add_contact_finish (EBookClient *client,
2197 GAsyncResult *result,
2198 gchar **out_added_uid,
2199 GError **error)
2200 {
2201 GSimpleAsyncResult *simple;
2202 AsyncContext *async_context;
2203
2204 g_return_val_if_fail (
2205 g_simple_async_result_is_valid (
2206 result, G_OBJECT (client),
2207 e_book_client_add_contact), FALSE);
2208
2209 simple = G_SIMPLE_ASYNC_RESULT (result);
2210 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2211
2212 if (g_simple_async_result_propagate_error (simple, error))
2213 return FALSE;
2214
2215 g_return_val_if_fail (async_context->uid != NULL, FALSE);
2216
2217 if (out_added_uid != NULL) {
2218 *out_added_uid = async_context->uid;
2219 async_context->uid = NULL;
2220 }
2221
2222 return TRUE;
2223 }
2224
2225 /**
2226 * e_book_client_add_contact_sync:
2227 * @client: an #EBookClient
2228 * @contact: an #EContact
2229 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2230 * @out_added_uid: (out) (optional): UID of a newly added contact; can be %NULL
2231 * @cancellable: a #GCancellable; can be %NULL
2232 * @error: a #GError to set an error, if any
2233 *
2234 * Adds @contact to @client and
2235 * sets @out_added_uid to a UID of a newly added contact.
2236 * This string should be freed with g_free().
2237 *
2238 * Note: This is not modifying original @contact, thus if it's needed,
2239 * then use e_contact_set (contact, E_CONTACT_UID, new_uid).
2240 *
2241 * Returns: %TRUE if successful, %FALSE otherwise.
2242 *
2243 * Since: 3.2
2244 **/
2245 gboolean
e_book_client_add_contact_sync(EBookClient * client,EContact * contact,guint32 opflags,gchar ** out_added_uid,GCancellable * cancellable,GError ** error)2246 e_book_client_add_contact_sync (EBookClient *client,
2247 EContact *contact,
2248 guint32 opflags,
2249 gchar **out_added_uid,
2250 GCancellable *cancellable,
2251 GError **error)
2252 {
2253 GSList link = { contact, NULL };
2254 GSList *uids = NULL;
2255 gboolean success;
2256
2257 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2258 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
2259
2260 success = e_book_client_add_contacts_sync (
2261 client, &link, opflags, &uids, cancellable, error);
2262
2263 /* Sanity check. */
2264 g_return_val_if_fail (
2265 (success && (uids != NULL)) ||
2266 (!success && (uids == NULL)), FALSE);
2267
2268 if (uids != NULL) {
2269 if (out_added_uid != NULL)
2270 *out_added_uid = g_strdup (uids->data);
2271
2272 g_slist_free_full (uids, (GDestroyNotify) g_free);
2273 }
2274
2275 return success;
2276 }
2277
2278 /* Helper for e_book_client_add_contacts() */
2279 static void
book_client_add_contacts_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2280 book_client_add_contacts_thread (GSimpleAsyncResult *simple,
2281 GObject *source_object,
2282 GCancellable *cancellable)
2283 {
2284 AsyncContext *async_context;
2285 GError *local_error = NULL;
2286
2287 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2288
2289 if (!e_book_client_add_contacts_sync (
2290 E_BOOK_CLIENT (source_object),
2291 async_context->object_list,
2292 async_context->opflags,
2293 &async_context->string_list,
2294 cancellable, &local_error)) {
2295
2296 if (!local_error)
2297 local_error = g_error_new_literal (
2298 E_CLIENT_ERROR,
2299 E_CLIENT_ERROR_OTHER_ERROR,
2300 _("Unknown error"));
2301 }
2302
2303 if (local_error != NULL)
2304 g_simple_async_result_take_error (simple, local_error);
2305 }
2306
2307 /**
2308 * e_book_client_add_contacts:
2309 * @client: an #EBookClient
2310 * @contacts: (element-type EContact): a #GSList of #EContact objects to add
2311 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2312 * @cancellable: a #GCancellable; can be %NULL
2313 * @callback: callback to call when a result is ready
2314 * @user_data: user data for the @callback
2315 *
2316 * Adds @contacts to @client.
2317 * The call is finished by e_book_client_add_contacts_finish()
2318 * from the @callback.
2319 *
2320 * Since: 3.4
2321 **/
2322 void
e_book_client_add_contacts(EBookClient * client,GSList * contacts,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2323 e_book_client_add_contacts (EBookClient *client,
2324 GSList *contacts,
2325 guint32 opflags,
2326 GCancellable *cancellable,
2327 GAsyncReadyCallback callback,
2328 gpointer user_data)
2329 {
2330 GSimpleAsyncResult *simple;
2331 AsyncContext *async_context;
2332
2333 g_return_if_fail (E_IS_BOOK_CLIENT (client));
2334 g_return_if_fail (contacts != NULL);
2335
2336 async_context = g_slice_new0 (AsyncContext);
2337 async_context->object_list = g_slist_copy_deep (
2338 contacts, (GCopyFunc) g_object_ref, NULL);
2339 async_context->opflags = opflags;
2340
2341 simple = g_simple_async_result_new (
2342 G_OBJECT (client), callback, user_data,
2343 e_book_client_add_contacts);
2344
2345 g_simple_async_result_set_check_cancellable (simple, cancellable);
2346
2347 g_simple_async_result_set_op_res_gpointer (
2348 simple, async_context, (GDestroyNotify) async_context_free);
2349
2350 g_simple_async_result_run_in_thread (
2351 simple, book_client_add_contacts_thread,
2352 G_PRIORITY_DEFAULT, cancellable);
2353
2354 g_object_unref (simple);
2355 }
2356
2357 /**
2358 * e_book_client_add_contacts_finish:
2359 * @client: an #EBookClient
2360 * @result: a #GAsyncResult
2361 * @out_added_uids: (out) (element-type utf8) (optional): UIDs of
2362 * newly added contacts; can be %NULL
2363 * @error: a #GError to set an error, if any
2364 *
2365 * Finishes previous call of e_book_client_add_contacts() and
2366 * sets @out_added_uids to the UIDs of newly added contacts if successful.
2367 * This #GSList should be freed with e_client_util_free_string_slist().
2368 *
2369 * If any of the contacts cannot be inserted, all of the insertions will be
2370 * reverted and this method will return %FALSE.
2371 *
2372 * Note: This is not modifying original #EContact objects.
2373 *
2374 * Returns: %TRUE if successful, %FALSE otherwise.
2375 *
2376 * Since: 3.4
2377 **/
2378 gboolean
e_book_client_add_contacts_finish(EBookClient * client,GAsyncResult * result,GSList ** out_added_uids,GError ** error)2379 e_book_client_add_contacts_finish (EBookClient *client,
2380 GAsyncResult *result,
2381 GSList **out_added_uids,
2382 GError **error)
2383 {
2384 GSimpleAsyncResult *simple;
2385 AsyncContext *async_context;
2386
2387 g_return_val_if_fail (
2388 g_simple_async_result_is_valid (
2389 result, G_OBJECT (client),
2390 e_book_client_add_contacts), FALSE);
2391
2392 simple = G_SIMPLE_ASYNC_RESULT (result);
2393 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2394
2395 if (g_simple_async_result_propagate_error (simple, error))
2396 return FALSE;
2397
2398 if (out_added_uids != NULL) {
2399 *out_added_uids = async_context->string_list;
2400 async_context->string_list = NULL;
2401 }
2402
2403 return TRUE;
2404 }
2405
2406 /**
2407 * e_book_client_add_contacts_sync:
2408 * @client: an #EBookClient
2409 * @contacts: (element-type EContact): a #GSList of #EContact objects to add
2410 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2411 * @out_added_uids: (out) (element-type utf8) (optional): UIDs of newly
2412 * added contacts; can be %NULL
2413 * @cancellable: a #GCancellable; can be %NULL
2414 * @error: a #GError to set an error, if any
2415 *
2416 * Adds @contacts to @client and
2417 * sets @out_added_uids to the UIDs of newly added contacts if successful.
2418 * This #GSList should be freed with e_client_util_free_string_slist().
2419 *
2420 * If any of the contacts cannot be inserted, all of the insertions will be
2421 * reverted and this method will return %FALSE.
2422 *
2423 * Note: This is not modifying original @contacts, thus if it's needed,
2424 * then use e_contact_set (contact, E_CONTACT_UID, new_uid).
2425 *
2426 * Returns: %TRUE if successful, %FALSE otherwise.
2427 *
2428 * Since: 3.4
2429 **/
2430 gboolean
e_book_client_add_contacts_sync(EBookClient * client,GSList * contacts,guint32 opflags,GSList ** out_added_uids,GCancellable * cancellable,GError ** error)2431 e_book_client_add_contacts_sync (EBookClient *client,
2432 GSList *contacts,
2433 guint32 opflags,
2434 GSList **out_added_uids,
2435 GCancellable *cancellable,
2436 GError **error)
2437 {
2438 GSList *link;
2439 gchar **strv;
2440 gchar **uids = NULL;
2441 gint ii = 0;
2442 GError *local_error = NULL;
2443
2444 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2445 g_return_val_if_fail (contacts != NULL, FALSE);
2446
2447 /* Build a string array, ensuring each element is valid UTF-8. */
2448 strv = g_new0 (gchar *, g_slist_length (contacts) + 1);
2449 for (link = contacts; link != NULL; link = g_slist_next (link)) {
2450 EVCard *vcard;
2451 gchar *string;
2452
2453 vcard = E_VCARD (link->data);
2454 string = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
2455 strv[ii++] = e_util_utf8_make_valid (string);
2456 g_free (string);
2457 }
2458
2459 e_dbus_address_book_call_create_contacts_sync (
2460 client->priv->dbus_proxy,
2461 (const gchar * const *) strv, opflags,
2462 &uids, cancellable, &local_error);
2463
2464 g_strfreev (strv);
2465
2466 /* Sanity check. */
2467 g_return_val_if_fail (
2468 ((uids != NULL) && (local_error == NULL)) ||
2469 ((uids == NULL) && (local_error != NULL)), FALSE);
2470
2471 if (local_error != NULL) {
2472 g_dbus_error_strip_remote_error (local_error);
2473 g_propagate_error (error, local_error);
2474 return FALSE;
2475 }
2476
2477 /* XXX We should have passed the string array directly
2478 * back to the caller instead of building a linked
2479 * list. This is unnecessary work. */
2480 if (out_added_uids != NULL) {
2481 GSList *tmp = NULL;
2482 gint ii;
2483
2484 /* Take ownership of the string array elements. */
2485 for (ii = 0; uids[ii] != NULL; ii++) {
2486 tmp = g_slist_prepend (tmp, uids[ii]);
2487 uids[ii] = NULL;
2488 }
2489
2490 *out_added_uids = g_slist_reverse (tmp);
2491 }
2492
2493 g_strfreev (uids);
2494
2495 return TRUE;
2496 }
2497
2498 /* Helper for e_book_client_modify_contact() */
2499 static void
book_client_modify_contact_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2500 book_client_modify_contact_thread (GSimpleAsyncResult *simple,
2501 GObject *source_object,
2502 GCancellable *cancellable)
2503 {
2504 AsyncContext *async_context;
2505 GError *local_error = NULL;
2506
2507 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2508
2509 if (!e_book_client_modify_contact_sync (
2510 E_BOOK_CLIENT (source_object),
2511 async_context->contact,
2512 async_context->opflags,
2513 cancellable, &local_error)) {
2514
2515 if (!local_error)
2516 local_error = g_error_new_literal (
2517 E_CLIENT_ERROR,
2518 E_CLIENT_ERROR_OTHER_ERROR,
2519 _("Unknown error"));
2520 }
2521
2522 if (local_error != NULL)
2523 g_simple_async_result_take_error (simple, local_error);
2524 }
2525
2526 /**
2527 * e_book_client_modify_contact:
2528 * @client: an #EBookClient
2529 * @contact: an #EContact
2530 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2531 * @cancellable: a #GCancellable; can be %NULL
2532 * @callback: callback to call when a result is ready
2533 * @user_data: user data for the @callback
2534 *
2535 * Applies the changes made to @contact to the stored version in @client.
2536 * The call is finished by e_book_client_modify_contact_finish()
2537 * from the @callback.
2538 *
2539 * Since: 3.2
2540 **/
2541 void
e_book_client_modify_contact(EBookClient * client,EContact * contact,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2542 e_book_client_modify_contact (EBookClient *client,
2543 EContact *contact,
2544 guint32 opflags,
2545 GCancellable *cancellable,
2546 GAsyncReadyCallback callback,
2547 gpointer user_data)
2548 {
2549 GSimpleAsyncResult *simple;
2550 AsyncContext *async_context;
2551
2552 g_return_if_fail (E_IS_BOOK_CLIENT (client));
2553 g_return_if_fail (E_IS_CONTACT (contact));
2554
2555 async_context = g_slice_new0 (AsyncContext);
2556 async_context->contact = g_object_ref (contact);
2557 async_context->opflags = opflags;
2558
2559 simple = g_simple_async_result_new (
2560 G_OBJECT (client), callback, user_data,
2561 e_book_client_modify_contact);
2562
2563 g_simple_async_result_set_check_cancellable (simple, cancellable);
2564
2565 g_simple_async_result_set_op_res_gpointer (
2566 simple, async_context, (GDestroyNotify) async_context_free);
2567
2568 g_simple_async_result_run_in_thread (
2569 simple, book_client_modify_contact_thread,
2570 G_PRIORITY_DEFAULT, cancellable);
2571
2572 g_object_unref (simple);
2573 }
2574
2575 /**
2576 * e_book_client_modify_contact_finish:
2577 * @client: an #EBookClient
2578 * @result: a #GAsyncResult
2579 * @error: a #GError to set an error, if any
2580 *
2581 * Finishes previous call of e_book_client_modify_contact().
2582 *
2583 * Returns: %TRUE if successful, %FALSE otherwise.
2584 *
2585 * Since: 3.2
2586 **/
2587 gboolean
e_book_client_modify_contact_finish(EBookClient * client,GAsyncResult * result,GError ** error)2588 e_book_client_modify_contact_finish (EBookClient *client,
2589 GAsyncResult *result,
2590 GError **error)
2591 {
2592 GSimpleAsyncResult *simple;
2593
2594 g_return_val_if_fail (
2595 g_simple_async_result_is_valid (
2596 result, G_OBJECT (client),
2597 e_book_client_modify_contact), FALSE);
2598
2599 simple = G_SIMPLE_ASYNC_RESULT (result);
2600
2601 /* Assume success unless a GError is set. */
2602 return !g_simple_async_result_propagate_error (simple, error);
2603 }
2604
2605 /**
2606 * e_book_client_modify_contact_sync:
2607 * @client: an #EBookClient
2608 * @contact: an #EContact
2609 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2610 * @cancellable: a #GCancellable; can be %NULL
2611 * @error: a #GError to set an error, if any
2612 *
2613 * Applies the changes made to @contact to the stored version in @client.
2614 *
2615 * Returns: %TRUE if successful, %FALSE otherwise.
2616 *
2617 * Since: 3.2
2618 **/
2619 gboolean
e_book_client_modify_contact_sync(EBookClient * client,EContact * contact,guint32 opflags,GCancellable * cancellable,GError ** error)2620 e_book_client_modify_contact_sync (EBookClient *client,
2621 EContact *contact,
2622 guint32 opflags,
2623 GCancellable *cancellable,
2624 GError **error)
2625 {
2626 GSList link = { contact, NULL };
2627
2628 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2629 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
2630
2631 return e_book_client_modify_contacts_sync (
2632 client, &link, opflags, cancellable, error);
2633 }
2634
2635 /* Helper for e_book_client_modify_contacts() */
2636 static void
book_client_modify_contacts_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2637 book_client_modify_contacts_thread (GSimpleAsyncResult *simple,
2638 GObject *source_object,
2639 GCancellable *cancellable)
2640 {
2641 AsyncContext *async_context;
2642 GError *local_error = NULL;
2643
2644 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2645
2646 if (!e_book_client_modify_contacts_sync (
2647 E_BOOK_CLIENT (source_object),
2648 async_context->object_list,
2649 async_context->opflags,
2650 cancellable, &local_error)) {
2651
2652 if (!local_error)
2653 local_error = g_error_new_literal (
2654 E_CLIENT_ERROR,
2655 E_CLIENT_ERROR_OTHER_ERROR,
2656 _("Unknown error"));
2657 }
2658
2659 if (local_error != NULL)
2660 g_simple_async_result_take_error (simple, local_error);
2661 }
2662
2663 /**
2664 * e_book_client_modify_contacts:
2665 * @client: an #EBookClient
2666 * @contacts: (element-type EContact): a #GSList of #EContact objects
2667 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2668 * @cancellable: a #GCancellable; can be %NULL
2669 * @callback: callback to call when a result is ready
2670 * @user_data: user data for the @callback
2671 *
2672 * Applies the changes made to @contacts to the stored versions in @client.
2673 * The call is finished by e_book_client_modify_contacts_finish()
2674 * from the @callback.
2675 *
2676 * Since: 3.4
2677 **/
2678 void
e_book_client_modify_contacts(EBookClient * client,GSList * contacts,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2679 e_book_client_modify_contacts (EBookClient *client,
2680 GSList *contacts,
2681 guint32 opflags,
2682 GCancellable *cancellable,
2683 GAsyncReadyCallback callback,
2684 gpointer user_data)
2685 {
2686 GSimpleAsyncResult *simple;
2687 AsyncContext *async_context;
2688
2689 g_return_if_fail (E_IS_BOOK_CLIENT (client));
2690 g_return_if_fail (contacts != NULL);
2691
2692 async_context = g_slice_new0 (AsyncContext);
2693 async_context->object_list = g_slist_copy_deep (
2694 contacts, (GCopyFunc) g_object_ref, NULL);
2695 async_context->opflags = opflags;
2696
2697 simple = g_simple_async_result_new (
2698 G_OBJECT (client), callback, user_data,
2699 e_book_client_modify_contacts);
2700
2701 g_simple_async_result_set_check_cancellable (simple, cancellable);
2702
2703 g_simple_async_result_set_op_res_gpointer (
2704 simple, async_context, (GDestroyNotify) async_context_free);
2705
2706 g_simple_async_result_run_in_thread (
2707 simple, book_client_modify_contacts_thread,
2708 G_PRIORITY_DEFAULT, cancellable);
2709
2710 g_object_unref (simple);
2711 }
2712
2713 /**
2714 * e_book_client_modify_contacts_finish:
2715 * @client: an #EBookClient
2716 * @result: a #GAsyncResult
2717 * @error: a #GError to set an error, if any
2718 *
2719 * Finishes previous call of e_book_client_modify_contacts().
2720 *
2721 * Returns: %TRUE if successful, %FALSE otherwise.
2722 *
2723 * Since: 3.4
2724 **/
2725 gboolean
e_book_client_modify_contacts_finish(EBookClient * client,GAsyncResult * result,GError ** error)2726 e_book_client_modify_contacts_finish (EBookClient *client,
2727 GAsyncResult *result,
2728 GError **error)
2729 {
2730 GSimpleAsyncResult *simple;
2731
2732 g_return_val_if_fail (
2733 g_simple_async_result_is_valid (
2734 result, G_OBJECT (client),
2735 e_book_client_modify_contacts), FALSE);
2736
2737 simple = G_SIMPLE_ASYNC_RESULT (result);
2738
2739 /* Assume success unless a GError is set. */
2740 return !g_simple_async_result_propagate_error (simple, error);
2741 }
2742
2743 /**
2744 * e_book_client_modify_contacts_sync:
2745 * @client: an #EBookClient
2746 * @contacts: (element-type EContact): a #GSList of #EContact objects
2747 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2748 * @cancellable: a #GCancellable; can be %NULL
2749 * @error: a #GError to set an error, if any
2750 *
2751 * Applies the changes made to @contacts to the stored versions in @client.
2752 *
2753 * Returns: %TRUE if successful, %FALSE otherwise.
2754 *
2755 * Since: 3.4
2756 **/
2757 gboolean
e_book_client_modify_contacts_sync(EBookClient * client,GSList * contacts,guint32 opflags,GCancellable * cancellable,GError ** error)2758 e_book_client_modify_contacts_sync (EBookClient *client,
2759 GSList *contacts,
2760 guint32 opflags,
2761 GCancellable *cancellable,
2762 GError **error)
2763 {
2764 GSList *link;
2765 gchar **strv;
2766 gint ii = 0;
2767 GError *local_error = NULL;
2768
2769 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2770 g_return_val_if_fail (contacts != NULL, FALSE);
2771
2772 /* Build a string array, ensuring each element is valid UTF-8. */
2773 strv = g_new0 (gchar *, g_slist_length (contacts) + 1);
2774 for (link = contacts; link != NULL; link = g_slist_next (link)) {
2775 EVCard *vcard;
2776 gchar *string;
2777
2778 vcard = E_VCARD (link->data);
2779 string = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
2780 strv[ii++] = e_util_utf8_make_valid (string);
2781 g_free (string);
2782 }
2783
2784 e_dbus_address_book_call_modify_contacts_sync (
2785 client->priv->dbus_proxy,
2786 (const gchar * const *) strv, opflags,
2787 cancellable, &local_error);
2788
2789 g_strfreev (strv);
2790
2791 if (local_error != NULL) {
2792 g_dbus_error_strip_remote_error (local_error);
2793 g_propagate_error (error, local_error);
2794 return FALSE;
2795 }
2796
2797 return TRUE;
2798 }
2799
2800 /* Helper for e_book_client_remove_contact() */
2801 static void
book_client_remove_contact_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2802 book_client_remove_contact_thread (GSimpleAsyncResult *simple,
2803 GObject *source_object,
2804 GCancellable *cancellable)
2805 {
2806 AsyncContext *async_context;
2807 GError *local_error = NULL;
2808
2809 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2810
2811 if (!e_book_client_remove_contact_sync (
2812 E_BOOK_CLIENT (source_object),
2813 async_context->contact,
2814 async_context->opflags,
2815 cancellable, &local_error)) {
2816
2817 if (!local_error)
2818 local_error = g_error_new_literal (
2819 E_CLIENT_ERROR,
2820 E_CLIENT_ERROR_OTHER_ERROR,
2821 _("Unknown error"));
2822 }
2823
2824 if (local_error != NULL)
2825 g_simple_async_result_take_error (simple, local_error);
2826 }
2827
2828 /**
2829 * e_book_client_remove_contact:
2830 * @client: an #EBookClient
2831 * @contact: an #EContact
2832 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2833 * @cancellable: a #GCancellable; can be %NULL
2834 * @callback: callback to call when a result is ready
2835 * @user_data: user data for the @callback
2836 *
2837 * Removes @contact from the @client.
2838 * The call is finished by e_book_client_remove_contact_finish()
2839 * from the @callback.
2840 *
2841 * Since: 3.2
2842 **/
2843 void
e_book_client_remove_contact(EBookClient * client,EContact * contact,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2844 e_book_client_remove_contact (EBookClient *client,
2845 /* const */ EContact *contact,
2846 guint32 opflags,
2847 GCancellable *cancellable,
2848 GAsyncReadyCallback callback,
2849 gpointer user_data)
2850 {
2851 GSimpleAsyncResult *simple;
2852 AsyncContext *async_context;
2853
2854 g_return_if_fail (E_IS_BOOK_CLIENT (client));
2855 g_return_if_fail (E_IS_CONTACT (contact));
2856
2857 async_context = g_slice_new0 (AsyncContext);
2858 async_context->contact = g_object_ref (contact);
2859 async_context->opflags = opflags;
2860
2861 simple = g_simple_async_result_new (
2862 G_OBJECT (client), callback, user_data,
2863 e_book_client_remove_contact);
2864
2865 g_simple_async_result_set_check_cancellable (simple, cancellable);
2866
2867 g_simple_async_result_set_op_res_gpointer (
2868 simple, async_context, (GDestroyNotify) async_context_free);
2869
2870 g_simple_async_result_run_in_thread (
2871 simple, book_client_remove_contact_thread,
2872 G_PRIORITY_DEFAULT, cancellable);
2873
2874 g_object_unref (simple);
2875 }
2876
2877 /**
2878 * e_book_client_remove_contact_finish:
2879 * @client: an #EBookClient
2880 * @result: a #GAsyncResult
2881 * @error: a #GError to set an error, if any
2882 *
2883 * Finishes previous call of e_book_client_remove_contact().
2884 *
2885 * Returns: %TRUE if successful, %FALSE otherwise.
2886 *
2887 * Since: 3.2
2888 **/
2889 gboolean
e_book_client_remove_contact_finish(EBookClient * client,GAsyncResult * result,GError ** error)2890 e_book_client_remove_contact_finish (EBookClient *client,
2891 GAsyncResult *result,
2892 GError **error)
2893 {
2894 GSimpleAsyncResult *simple;
2895
2896 g_return_val_if_fail (
2897 g_simple_async_result_is_valid (
2898 result, G_OBJECT (client),
2899 e_book_client_remove_contact), FALSE);
2900
2901 simple = G_SIMPLE_ASYNC_RESULT (result);
2902
2903 /* Assume success unless a GError is set. */
2904 return !g_simple_async_result_propagate_error (simple, error);
2905 }
2906
2907 /**
2908 * e_book_client_remove_contact_sync:
2909 * @client: an #EBookClient
2910 * @contact: an #EContact
2911 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2912 * @cancellable: a #GCancellable; can be %NULL
2913 * @error: a #GError to set an error, if any
2914 *
2915 * Removes @contact from the @client.
2916 *
2917 * Returns: %TRUE if successful, %FALSE otherwise.
2918 *
2919 * Since: 3.2
2920 **/
2921 gboolean
e_book_client_remove_contact_sync(EBookClient * client,EContact * contact,guint32 opflags,GCancellable * cancellable,GError ** error)2922 e_book_client_remove_contact_sync (EBookClient *client,
2923 EContact *contact,
2924 guint32 opflags,
2925 GCancellable *cancellable,
2926 GError **error)
2927 {
2928 const gchar *uid;
2929
2930 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
2931 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
2932
2933 uid = e_contact_get_const (contact, E_CONTACT_UID);
2934 g_return_val_if_fail (uid != NULL, FALSE);
2935
2936 return e_book_client_remove_contact_by_uid_sync (
2937 client, uid, opflags, cancellable, error);
2938 }
2939
2940 /* Helper for e_book_client_remove_contact_by_uid() */
2941 static void
book_client_remove_contact_by_uid_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2942 book_client_remove_contact_by_uid_thread (GSimpleAsyncResult *simple,
2943 GObject *source_object,
2944 GCancellable *cancellable)
2945 {
2946 AsyncContext *async_context;
2947 GError *local_error = NULL;
2948
2949 async_context = g_simple_async_result_get_op_res_gpointer (simple);
2950
2951 if (!e_book_client_remove_contact_by_uid_sync (
2952 E_BOOK_CLIENT (source_object),
2953 async_context->uid,
2954 async_context->opflags,
2955 cancellable, &local_error)) {
2956
2957 if (!local_error)
2958 local_error = g_error_new_literal (
2959 E_CLIENT_ERROR,
2960 E_CLIENT_ERROR_OTHER_ERROR,
2961 _("Unknown error"));
2962 }
2963
2964 if (local_error != NULL)
2965 g_simple_async_result_take_error (simple, local_error);
2966 }
2967
2968 /**
2969 * e_book_client_remove_contact_by_uid:
2970 * @client: an #EBookClient
2971 * @uid: a UID of a contact to remove
2972 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
2973 * @cancellable: a #GCancellable; can be %NULL
2974 * @callback: callback to call when a result is ready
2975 * @user_data: user data for the @callback
2976 *
2977 * Removes contact with @uid from the @client.
2978 * The call is finished by e_book_client_remove_contact_by_uid_finish()
2979 * from the @callback.
2980 *
2981 * Since: 3.2
2982 **/
2983 void
e_book_client_remove_contact_by_uid(EBookClient * client,const gchar * uid,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2984 e_book_client_remove_contact_by_uid (EBookClient *client,
2985 const gchar *uid,
2986 guint32 opflags,
2987 GCancellable *cancellable,
2988 GAsyncReadyCallback callback,
2989 gpointer user_data)
2990 {
2991 GSimpleAsyncResult *simple;
2992 AsyncContext *async_context;
2993
2994 g_return_if_fail (E_IS_BOOK_CLIENT (client));
2995 g_return_if_fail (uid != NULL);
2996
2997 async_context = g_slice_new0 (AsyncContext);
2998 async_context->uid = g_strdup (uid);
2999 async_context->opflags = opflags;
3000
3001 simple = g_simple_async_result_new (
3002 G_OBJECT (client), callback, user_data,
3003 e_book_client_remove_contact_by_uid);
3004
3005 g_simple_async_result_set_check_cancellable (simple, cancellable);
3006
3007 g_simple_async_result_set_op_res_gpointer (
3008 simple, async_context, (GDestroyNotify) async_context_free);
3009
3010 g_simple_async_result_run_in_thread (
3011 simple, book_client_remove_contact_by_uid_thread,
3012 G_PRIORITY_DEFAULT, cancellable);
3013
3014 g_object_unref (simple);
3015 }
3016
3017 /**
3018 * e_book_client_remove_contact_by_uid_finish:
3019 * @client: an #EBookClient
3020 * @result: a #GAsyncResult
3021 * @error: a #GError to set an error, if any
3022 *
3023 * Finishes previous call of e_book_client_remove_contact_by_uid().
3024 *
3025 * Returns: %TRUE if successful, %FALSE otherwise.
3026 *
3027 * Since: 3.2
3028 **/
3029 gboolean
e_book_client_remove_contact_by_uid_finish(EBookClient * client,GAsyncResult * result,GError ** error)3030 e_book_client_remove_contact_by_uid_finish (EBookClient *client,
3031 GAsyncResult *result,
3032 GError **error)
3033 {
3034 GSimpleAsyncResult *simple;
3035
3036 g_return_val_if_fail (
3037 g_simple_async_result_is_valid (
3038 result, G_OBJECT (client),
3039 e_book_client_remove_contact_by_uid), FALSE);
3040
3041 simple = G_SIMPLE_ASYNC_RESULT (result);
3042
3043 /* Assume success unless a GError is set. */
3044 return !g_simple_async_result_propagate_error (simple, error);
3045 }
3046
3047 /**
3048 * e_book_client_remove_contact_by_uid_sync:
3049 * @client: an #EBookClient
3050 * @uid: a UID of a contact to remove
3051 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
3052 * @cancellable: a #GCancellable; can be %NULL
3053 * @error: a #GError to set an error, if any
3054 *
3055 * Removes contact with @uid from the @client.
3056 *
3057 * Returns: %TRUE if successful, %FALSE otherwise.
3058 *
3059 * Since: 3.2
3060 **/
3061 gboolean
e_book_client_remove_contact_by_uid_sync(EBookClient * client,const gchar * uid,guint32 opflags,GCancellable * cancellable,GError ** error)3062 e_book_client_remove_contact_by_uid_sync (EBookClient *client,
3063 const gchar *uid,
3064 guint32 opflags,
3065 GCancellable *cancellable,
3066 GError **error)
3067 {
3068 GSList link = { (gpointer) uid, NULL };
3069
3070 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
3071 g_return_val_if_fail (uid != NULL, FALSE);
3072
3073 return e_book_client_remove_contacts_sync (
3074 client, &link, opflags, cancellable, error);
3075 }
3076
3077 /* Helper for e_book_client_remove_contacts() */
3078 static void
book_client_remove_contacts_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3079 book_client_remove_contacts_thread (GSimpleAsyncResult *simple,
3080 GObject *source_object,
3081 GCancellable *cancellable)
3082 {
3083 AsyncContext *async_context;
3084 GError *local_error = NULL;
3085
3086 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3087
3088 if (!e_book_client_remove_contacts_sync (
3089 E_BOOK_CLIENT (source_object),
3090 async_context->string_list,
3091 async_context->opflags,
3092 cancellable, &local_error)) {
3093
3094 if (!local_error)
3095 local_error = g_error_new_literal (
3096 E_CLIENT_ERROR,
3097 E_CLIENT_ERROR_OTHER_ERROR,
3098 _("Unknown error"));
3099 }
3100
3101 if (local_error != NULL)
3102 g_simple_async_result_take_error (simple, local_error);
3103 }
3104
3105 /**
3106 * e_book_client_remove_contacts:
3107 * @client: an #EBookClient
3108 * @uids: (element-type utf8): a #GSList of UIDs to remove
3109 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
3110 * @cancellable: a #GCancellable; can be %NULL
3111 * @callback: callback to call when a result is ready
3112 * @user_data: user data for the @callback
3113 *
3114 * Removes the contacts with uids from the list @uids from @client. This is
3115 * always more efficient than calling e_book_client_remove_contact() if you
3116 * have more than one uid to remove, as some backends can implement it
3117 * as a batch request.
3118 * The call is finished by e_book_client_remove_contacts_finish()
3119 * from the @callback.
3120 *
3121 * Since: 3.2
3122 **/
3123 void
e_book_client_remove_contacts(EBookClient * client,const GSList * uids,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3124 e_book_client_remove_contacts (EBookClient *client,
3125 const GSList *uids,
3126 guint32 opflags,
3127 GCancellable *cancellable,
3128 GAsyncReadyCallback callback,
3129 gpointer user_data)
3130 {
3131 GSimpleAsyncResult *simple;
3132 AsyncContext *async_context;
3133
3134 g_return_if_fail (E_IS_BOOK_CLIENT (client));
3135 g_return_if_fail (uids != NULL);
3136
3137 async_context = g_slice_new0 (AsyncContext);
3138 async_context->string_list = g_slist_copy_deep (
3139 (GSList *) uids, (GCopyFunc) g_strdup, NULL);
3140 async_context->opflags = opflags;
3141
3142 simple = g_simple_async_result_new (
3143 G_OBJECT (client), callback, user_data,
3144 e_book_client_remove_contacts);
3145
3146 g_simple_async_result_set_check_cancellable (simple, cancellable);
3147
3148 g_simple_async_result_set_op_res_gpointer (
3149 simple, async_context, (GDestroyNotify) async_context_free);
3150
3151 g_simple_async_result_run_in_thread (
3152 simple, book_client_remove_contacts_thread,
3153 G_PRIORITY_DEFAULT, cancellable);
3154
3155 g_object_unref (simple);
3156 }
3157
3158 /**
3159 * e_book_client_remove_contacts_finish:
3160 * @client: an #EBookClient
3161 * @result: a #GAsyncResult
3162 * @error: a #GError to set an error, if any
3163 *
3164 * Finishes previous call of e_book_client_remove_contacts().
3165 *
3166 * Returns: %TRUE if successful, %FALSE otherwise.
3167 *
3168 * Since: 3.2
3169 **/
3170 gboolean
e_book_client_remove_contacts_finish(EBookClient * client,GAsyncResult * result,GError ** error)3171 e_book_client_remove_contacts_finish (EBookClient *client,
3172 GAsyncResult *result,
3173 GError **error)
3174 {
3175 GSimpleAsyncResult *simple;
3176
3177 g_return_val_if_fail (
3178 g_simple_async_result_is_valid (
3179 result, G_OBJECT (client),
3180 e_book_client_remove_contacts), FALSE);
3181
3182 simple = G_SIMPLE_ASYNC_RESULT (result);
3183
3184 /* Assume success unless a GError is set. */
3185 return !g_simple_async_result_propagate_error (simple, error);
3186 }
3187
3188 /**
3189 * e_book_client_remove_contacts_sync:
3190 * @client: an #EBookClient
3191 * @uids: (element-type utf8): a #GSList of UIDs to remove
3192 * @opflags: (type EBookOperationFlags): bit-or of #EBookOperationFlags
3193 * @cancellable: a #GCancellable; can be %NULL
3194 * @error: a #GError to set an error, if any
3195 *
3196 * Removes the contacts with uids from the list @uids from @client. This is
3197 * always more efficient than calling e_book_client_remove_contact() if you
3198 * have more than one uid to remove, as some backends can implement it
3199 * as a batch request.
3200 *
3201 * Returns: %TRUE if successful, %FALSE otherwise.
3202 *
3203 * Since: 3.2
3204 **/
3205 gboolean
e_book_client_remove_contacts_sync(EBookClient * client,const GSList * uids,guint32 opflags,GCancellable * cancellable,GError ** error)3206 e_book_client_remove_contacts_sync (EBookClient *client,
3207 const GSList *uids,
3208 guint32 opflags,
3209 GCancellable *cancellable,
3210 GError **error)
3211 {
3212 gchar **strv;
3213 gint ii = 0;
3214 GError *local_error = NULL;
3215
3216 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
3217 g_return_val_if_fail (uids != NULL, FALSE);
3218
3219 strv = g_new0 (gchar *, g_slist_length ((GSList *) uids) + 1);
3220 while (uids != NULL) {
3221 strv[ii++] = e_util_utf8_make_valid (uids->data);
3222 uids = g_slist_next (uids);
3223 }
3224
3225 e_dbus_address_book_call_remove_contacts_sync (
3226 client->priv->dbus_proxy,
3227 (const gchar * const *) strv, opflags,
3228 cancellable, &local_error);
3229
3230 g_strfreev (strv);
3231
3232 if (local_error != NULL) {
3233 g_dbus_error_strip_remote_error (local_error);
3234 g_propagate_error (error, local_error);
3235 return FALSE;
3236 }
3237
3238 return TRUE;
3239 }
3240
3241 /* Helper for e_book_client_get_contact() */
3242 static void
book_client_get_contact_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3243 book_client_get_contact_thread (GSimpleAsyncResult *simple,
3244 GObject *source_object,
3245 GCancellable *cancellable)
3246 {
3247 AsyncContext *async_context;
3248 GError *local_error = NULL;
3249
3250 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3251
3252 if (!e_book_client_get_contact_sync (
3253 E_BOOK_CLIENT (source_object),
3254 async_context->uid,
3255 &async_context->contact,
3256 cancellable, &local_error)) {
3257 if (!local_error)
3258 local_error = g_error_new_literal (
3259 E_CLIENT_ERROR,
3260 E_CLIENT_ERROR_OTHER_ERROR,
3261 _("Unknown error"));
3262 }
3263
3264 if (local_error != NULL)
3265 g_simple_async_result_take_error (simple, local_error);
3266 }
3267
3268 /**
3269 * e_book_client_get_contact:
3270 * @client: an #EBookClient
3271 * @uid: a unique string ID specifying the contact
3272 * @cancellable: a #GCancellable; can be %NULL
3273 * @callback: callback to call when a result is ready
3274 * @user_data: user data for the @callback
3275 *
3276 * Receive #EContact from the @client for the gived @uid.
3277 * The call is finished by e_book_client_get_contact_finish()
3278 * from the @callback.
3279 *
3280 * Since: 3.2
3281 **/
3282 void
e_book_client_get_contact(EBookClient * client,const gchar * uid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3283 e_book_client_get_contact (EBookClient *client,
3284 const gchar *uid,
3285 GCancellable *cancellable,
3286 GAsyncReadyCallback callback,
3287 gpointer user_data)
3288 {
3289 GSimpleAsyncResult *simple;
3290 AsyncContext *async_context;
3291
3292 g_return_if_fail (E_IS_BOOK_CLIENT (client));
3293 g_return_if_fail (uid != NULL);
3294
3295 async_context = g_slice_new0 (AsyncContext);
3296 async_context->uid = g_strdup (uid);
3297
3298 simple = g_simple_async_result_new (
3299 G_OBJECT (client), callback, user_data,
3300 e_book_client_get_contact);
3301
3302 g_simple_async_result_set_check_cancellable (simple, cancellable);
3303
3304 g_simple_async_result_set_op_res_gpointer (
3305 simple, async_context, (GDestroyNotify) async_context_free);
3306
3307 g_simple_async_result_run_in_thread (
3308 simple, book_client_get_contact_thread,
3309 G_PRIORITY_DEFAULT, cancellable);
3310
3311 g_object_unref (simple);
3312 }
3313
3314 /**
3315 * e_book_client_get_contact_finish:
3316 * @client: an #EBookClient
3317 * @result: a #GAsyncResult
3318 * @out_contact: (out) (optional): an #EContact for previously given uid
3319 * @error: a #GError to set an error, if any
3320 *
3321 * Finishes previous call of e_book_client_get_contact().
3322 * If successful, then the @out_contact is set to newly allocated
3323 * #EContact, which should be freed with g_object_unref().
3324 *
3325 * Returns: %TRUE if successful, %FALSE otherwise.
3326 *
3327 * Since: 3.2
3328 **/
3329 gboolean
e_book_client_get_contact_finish(EBookClient * client,GAsyncResult * result,EContact ** out_contact,GError ** error)3330 e_book_client_get_contact_finish (EBookClient *client,
3331 GAsyncResult *result,
3332 EContact **out_contact,
3333 GError **error)
3334 {
3335 GSimpleAsyncResult *simple;
3336 AsyncContext *async_context;
3337
3338 g_return_val_if_fail (
3339 g_simple_async_result_is_valid (
3340 result, G_OBJECT (client),
3341 e_book_client_get_contact), FALSE);
3342
3343 simple = G_SIMPLE_ASYNC_RESULT (result);
3344 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3345
3346 if (g_simple_async_result_propagate_error (simple, error))
3347 return FALSE;
3348
3349 g_return_val_if_fail (async_context->contact != NULL, FALSE);
3350
3351 if (out_contact != NULL)
3352 *out_contact = g_object_ref (async_context->contact);
3353
3354 return TRUE;
3355 }
3356
3357 /**
3358 * e_book_client_get_contact_sync:
3359 * @client: an #EBookClient
3360 * @uid: a unique string ID specifying the contact
3361 * @out_contact: (out): an #EContact for given @uid
3362 * @cancellable: a #GCancellable; can be %NULL
3363 * @error: a #GError to set an error, if any
3364 *
3365 * Receive #EContact from the @client for the gived @uid.
3366 * If successful, then the @out_contact is set to newly allocated
3367 * #EContact, which should be freed with g_object_unref().
3368 *
3369 * Returns: %TRUE if successful, %FALSE otherwise.
3370 *
3371 * Since: 3.2
3372 **/
3373 gboolean
e_book_client_get_contact_sync(EBookClient * client,const gchar * uid,EContact ** out_contact,GCancellable * cancellable,GError ** error)3374 e_book_client_get_contact_sync (EBookClient *client,
3375 const gchar *uid,
3376 EContact **out_contact,
3377 GCancellable *cancellable,
3378 GError **error)
3379 {
3380 gchar *utf8_uid;
3381 gchar *vcard = NULL;
3382 GError *local_error = NULL;
3383
3384 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
3385 g_return_val_if_fail (uid != NULL, FALSE);
3386 g_return_val_if_fail (out_contact != NULL, FALSE);
3387
3388 if (client->priv->direct_backend != NULL) {
3389 EContact *contact;
3390 gboolean success = FALSE;
3391
3392 /* Direct backend is not using D-Bus (obviously),
3393 * so no need to strip D-Bus info from the error. */
3394 contact = e_book_backend_get_contact_sync (
3395 client->priv->direct_backend,
3396 uid, cancellable, error);
3397
3398 if (contact != NULL) {
3399 *out_contact = g_object_ref (contact);
3400 g_object_unref (contact);
3401 success = TRUE;
3402 }
3403
3404 return success;
3405 }
3406
3407 utf8_uid = e_util_utf8_make_valid (uid);
3408
3409 e_dbus_address_book_call_get_contact_sync (
3410 client->priv->dbus_proxy, utf8_uid,
3411 &vcard, cancellable, &local_error);
3412
3413 /* Sanity check. */
3414 g_return_val_if_fail (
3415 ((vcard != NULL) && (local_error == NULL)) ||
3416 ((vcard == NULL) && (local_error != NULL)), FALSE);
3417
3418 if (vcard != NULL) {
3419 *out_contact =
3420 e_contact_new_from_vcard_with_uid (vcard, utf8_uid);
3421 g_free (vcard);
3422 }
3423
3424 g_free (utf8_uid);
3425
3426 if (local_error != NULL) {
3427 g_dbus_error_strip_remote_error (local_error);
3428 g_propagate_error (error, local_error);
3429 return FALSE;
3430 }
3431
3432 return TRUE;
3433 }
3434
3435 /* Helper for e_book_client_get_contacts() */
3436 static void
book_client_get_contacts_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3437 book_client_get_contacts_thread (GSimpleAsyncResult *simple,
3438 GObject *source_object,
3439 GCancellable *cancellable)
3440 {
3441 AsyncContext *async_context;
3442 GError *local_error = NULL;
3443
3444 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3445
3446 if (!e_book_client_get_contacts_sync (
3447 E_BOOK_CLIENT (source_object),
3448 async_context->sexp,
3449 &async_context->object_list,
3450 cancellable, &local_error)) {
3451
3452 if (!local_error)
3453 local_error = g_error_new_literal (
3454 E_CLIENT_ERROR,
3455 E_CLIENT_ERROR_OTHER_ERROR,
3456 _("Unknown error"));
3457 }
3458
3459 if (local_error != NULL)
3460 g_simple_async_result_take_error (simple, local_error);
3461 }
3462
3463 /**
3464 * e_book_client_get_contacts:
3465 * @client: an #EBookClient
3466 * @sexp: an S-expression representing the query
3467 * @cancellable: a #GCancellable; can be %NULL
3468 * @callback: callback to call when a result is ready
3469 * @user_data: user data for the @callback
3470 *
3471 * Query @client with @sexp, receiving a list of contacts which
3472 * matched. The call is finished by e_book_client_get_contacts_finish()
3473 * from the @callback.
3474 *
3475 * Note: @sexp can be obtained through #EBookQuery, by converting it
3476 * to a string with e_book_query_to_string().
3477 *
3478 * Since: 3.2
3479 **/
3480 void
e_book_client_get_contacts(EBookClient * client,const gchar * sexp,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3481 e_book_client_get_contacts (EBookClient *client,
3482 const gchar *sexp,
3483 GCancellable *cancellable,
3484 GAsyncReadyCallback callback,
3485 gpointer user_data)
3486 {
3487 GSimpleAsyncResult *simple;
3488 AsyncContext *async_context;
3489
3490 g_return_if_fail (E_IS_BOOK_CLIENT (client));
3491 g_return_if_fail (sexp != NULL);
3492
3493 async_context = g_slice_new0 (AsyncContext);
3494 async_context->sexp = g_strdup (sexp);
3495
3496 simple = g_simple_async_result_new (
3497 G_OBJECT (client), callback, user_data,
3498 e_book_client_get_contacts);
3499
3500 g_simple_async_result_set_check_cancellable (simple, cancellable);
3501
3502 g_simple_async_result_set_op_res_gpointer (
3503 simple, async_context, (GDestroyNotify) async_context_free);
3504
3505 g_simple_async_result_run_in_thread (
3506 simple, book_client_get_contacts_thread,
3507 G_PRIORITY_DEFAULT, cancellable);
3508
3509 g_object_unref (simple);
3510 }
3511
3512 /**
3513 * e_book_client_get_contacts_finish:
3514 * @client: an #EBookClient
3515 * @result: a #GAsyncResult
3516 * @out_contacts: (element-type EContact) (out) (transfer full): a #GSList
3517 * of matched #EContact(s)
3518 * @error: a #GError to set an error, if any
3519 *
3520 * Finishes previous call of e_book_client_get_contacts().
3521 * If successful, then the @out_contacts is set to newly allocated list of
3522 * #EContact(s), which should be freed with e_client_util_free_object_slist().
3523 *
3524 * Returns: %TRUE if successful, %FALSE otherwise.
3525 *
3526 * Since: 3.2
3527 **/
3528 gboolean
e_book_client_get_contacts_finish(EBookClient * client,GAsyncResult * result,GSList ** out_contacts,GError ** error)3529 e_book_client_get_contacts_finish (EBookClient *client,
3530 GAsyncResult *result,
3531 GSList **out_contacts,
3532 GError **error)
3533 {
3534 GSimpleAsyncResult *simple;
3535 AsyncContext *async_context;
3536
3537 g_return_val_if_fail (
3538 g_simple_async_result_is_valid (
3539 result, G_OBJECT (client),
3540 e_book_client_get_contacts), FALSE);
3541
3542 simple = G_SIMPLE_ASYNC_RESULT (result);
3543 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3544
3545 if (g_simple_async_result_propagate_error (simple, error))
3546 return FALSE;
3547
3548 if (out_contacts != NULL) {
3549 *out_contacts = async_context->object_list;
3550 async_context->object_list = NULL;
3551 }
3552
3553 return TRUE;
3554 }
3555
3556 /**
3557 * e_book_client_get_contacts_sync:
3558 * @client: an #EBookClient
3559 * @sexp: an S-expression representing the query
3560 * @out_contacts: (element-type EContact) (out): a #GSList of matched
3561 * #EContact(s)
3562 * @cancellable: a #GCancellable; can be %NULL
3563 * @error: a #GError to set an error, if any
3564 *
3565 * Query @client with @sexp, receiving a list of contacts which matched.
3566 * If successful, then the @out_contacts is set to newly allocated #GSList of
3567 * #EContact(s), which should be freed with e_client_util_free_object_slist().
3568 *
3569 * Note: @sexp can be obtained through #EBookQuery, by converting it
3570 * to a string with e_book_query_to_string().
3571 *
3572 * Returns: %TRUE if successful, %FALSE otherwise.
3573 *
3574 * Since: 3.2
3575 **/
3576 gboolean
e_book_client_get_contacts_sync(EBookClient * client,const gchar * sexp,GSList ** out_contacts,GCancellable * cancellable,GError ** error)3577 e_book_client_get_contacts_sync (EBookClient *client,
3578 const gchar *sexp,
3579 GSList **out_contacts,
3580 GCancellable *cancellable,
3581 GError **error)
3582 {
3583 gchar *utf8_sexp;
3584 gchar **vcards = NULL;
3585 GError *local_error = NULL;
3586
3587 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
3588 g_return_val_if_fail (sexp != NULL, FALSE);
3589 g_return_val_if_fail (out_contacts != NULL, FALSE);
3590
3591 if (client->priv->direct_backend != NULL) {
3592 GQueue queue = G_QUEUE_INIT;
3593 GSList *list = NULL;
3594 gboolean success;
3595
3596 /* Direct backend is not using D-Bus (obviously),
3597 * so no need to strip D-Bus info from the error. */
3598 success = e_book_backend_get_contact_list_sync (
3599 client->priv->direct_backend,
3600 sexp, &queue, cancellable, error);
3601
3602 if (success) {
3603 while (!g_queue_is_empty (&queue)) {
3604 EContact *contact;
3605
3606 contact = g_queue_pop_head (&queue);
3607 list = g_slist_prepend (list, contact);
3608 }
3609
3610 *out_contacts = g_slist_reverse (list);
3611 }
3612
3613 return success;
3614 }
3615
3616 utf8_sexp = e_util_utf8_make_valid (sexp);
3617
3618 e_dbus_address_book_call_get_contact_list_sync (
3619 client->priv->dbus_proxy, utf8_sexp,
3620 &vcards, cancellable, &local_error);
3621
3622 g_free (utf8_sexp);
3623
3624 /* Sanity check. */
3625 g_return_val_if_fail (
3626 ((vcards != NULL) && (local_error == NULL)) ||
3627 ((vcards == NULL) && (local_error != NULL)), FALSE);
3628
3629 if (vcards != NULL) {
3630 EContact *contact;
3631 GSList *tmp = NULL;
3632 gint ii;
3633
3634 for (ii = 0; vcards[ii] != NULL; ii++) {
3635 contact = e_contact_new_from_vcard (vcards[ii]);
3636 tmp = g_slist_prepend (tmp, contact);
3637 }
3638
3639 *out_contacts = g_slist_reverse (tmp);
3640
3641 g_strfreev (vcards);
3642 }
3643
3644 if (local_error != NULL) {
3645 g_dbus_error_strip_remote_error (local_error);
3646 g_propagate_error (error, local_error);
3647 return FALSE;
3648 }
3649
3650 return TRUE;
3651 }
3652
3653 /* Helper for e_book_client_get_contacts_uids() */
3654 static void
book_client_get_contacts_uids_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3655 book_client_get_contacts_uids_thread (GSimpleAsyncResult *simple,
3656 GObject *source_object,
3657 GCancellable *cancellable)
3658 {
3659 AsyncContext *async_context;
3660 GError *local_error = NULL;
3661
3662 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3663
3664 if (!e_book_client_get_contacts_uids_sync (
3665 E_BOOK_CLIENT (source_object),
3666 async_context->sexp,
3667 &async_context->string_list,
3668 cancellable, &local_error)) {
3669
3670 if (!local_error)
3671 local_error = g_error_new_literal (
3672 E_CLIENT_ERROR,
3673 E_CLIENT_ERROR_OTHER_ERROR,
3674 _("Unknown error"));
3675 }
3676
3677 if (local_error != NULL)
3678 g_simple_async_result_take_error (simple, local_error);
3679 }
3680
3681 /**
3682 * e_book_client_get_contacts_uids:
3683 * @client: an #EBookClient
3684 * @sexp: an S-expression representing the query
3685 * @cancellable: a #GCancellable; can be %NULL
3686 * @callback: callback to call when a result is ready
3687 * @user_data: user data for the @callback
3688 *
3689 * Query @client with @sexp, receiving a list of contacts UIDs which
3690 * matched. The call is finished by e_book_client_get_contacts_uids_finish()
3691 * from the @callback.
3692 *
3693 * Note: @sexp can be obtained through #EBookQuery, by converting it
3694 * to a string with e_book_query_to_string().
3695 *
3696 * Since: 3.2
3697 **/
3698 void
e_book_client_get_contacts_uids(EBookClient * client,const gchar * sexp,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3699 e_book_client_get_contacts_uids (EBookClient *client,
3700 const gchar *sexp,
3701 GCancellable *cancellable,
3702 GAsyncReadyCallback callback,
3703 gpointer user_data)
3704 {
3705 GSimpleAsyncResult *simple;
3706 AsyncContext *async_context;
3707
3708 g_return_if_fail (E_IS_BOOK_CLIENT (client));
3709 g_return_if_fail (sexp != NULL);
3710
3711 async_context = g_slice_new0 (AsyncContext);
3712 async_context->sexp = g_strdup (sexp);
3713
3714 simple = g_simple_async_result_new (
3715 G_OBJECT (client), callback, user_data,
3716 e_book_client_get_contacts_uids);
3717
3718 g_simple_async_result_set_check_cancellable (simple, cancellable);
3719
3720 g_simple_async_result_set_op_res_gpointer (
3721 simple, async_context, (GDestroyNotify) async_context_free);
3722
3723 g_simple_async_result_run_in_thread (
3724 simple, book_client_get_contacts_uids_thread,
3725 G_PRIORITY_DEFAULT, cancellable);
3726
3727 g_object_unref (simple);
3728 }
3729
3730 /**
3731 * e_book_client_get_contacts_uids_finish:
3732 * @client: an #EBookClient
3733 * @result: a #GAsyncResult
3734 * @out_contact_uids: (element-type utf8) (out): a #GSList of matched
3735 * contact UIDs stored as strings
3736 * @error: a #GError to set an error, if any
3737 *
3738 * Finishes previous call of e_book_client_get_contacts_uids().
3739 * If successful, then the @out_contact_uids is set to newly allocated list
3740 * of UID strings, which should be freed with e_client_util_free_string_slist().
3741 *
3742 * Returns: %TRUE if successful, %FALSE otherwise.
3743 *
3744 * Since: 3.2
3745 **/
3746 gboolean
e_book_client_get_contacts_uids_finish(EBookClient * client,GAsyncResult * result,GSList ** out_contact_uids,GError ** error)3747 e_book_client_get_contacts_uids_finish (EBookClient *client,
3748 GAsyncResult *result,
3749 GSList **out_contact_uids,
3750 GError **error)
3751 {
3752 GSimpleAsyncResult *simple;
3753 AsyncContext *async_context;
3754
3755 g_return_val_if_fail (
3756 g_simple_async_result_is_valid (
3757 result, G_OBJECT (client),
3758 e_book_client_get_contacts_uids), FALSE);
3759
3760 simple = G_SIMPLE_ASYNC_RESULT (result);
3761 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3762
3763 if (g_simple_async_result_propagate_error (simple, error))
3764 return FALSE;
3765
3766 if (out_contact_uids != NULL) {
3767 *out_contact_uids = async_context->string_list;
3768 async_context->string_list = NULL;
3769 }
3770
3771 return TRUE;
3772 }
3773
3774 /**
3775 * e_book_client_get_contacts_uids_sync:
3776 * @client: an #EBookClient
3777 * @sexp: an S-expression representing the query
3778 * @out_contact_uids: (element-type utf8) (out): a #GSList of matched
3779 * contacts UIDs stored as strings
3780 * @cancellable: a #GCancellable; can be %NULL
3781 * @error: a #GError to set an error, if any
3782 *
3783 * Query @client with @sexp, receiving a list of contacts UIDs which matched.
3784 * If successful, then the @out_contact_uids is set to newly allocated list
3785 * of UID strings, which should be freed with e_client_util_free_string_slist().
3786 *
3787 * Note: @sexp can be obtained through #EBookQuery, by converting it
3788 * to a string with e_book_query_to_string().
3789 *
3790 * Returns: %TRUE if successful, %FALSE otherwise.
3791 *
3792 * Since: 3.2
3793 **/
3794 gboolean
e_book_client_get_contacts_uids_sync(EBookClient * client,const gchar * sexp,GSList ** out_contact_uids,GCancellable * cancellable,GError ** error)3795 e_book_client_get_contacts_uids_sync (EBookClient *client,
3796 const gchar *sexp,
3797 GSList **out_contact_uids,
3798 GCancellable *cancellable,
3799 GError **error)
3800 {
3801 gchar *utf8_sexp;
3802 gchar **uids = NULL;
3803 GError *local_error = NULL;
3804
3805 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
3806 g_return_val_if_fail (sexp != NULL, FALSE);
3807 g_return_val_if_fail (out_contact_uids != NULL, FALSE);
3808
3809 if (client->priv->direct_backend != NULL) {
3810 GQueue queue = G_QUEUE_INIT;
3811 GSList *list = NULL;
3812 gboolean success;
3813
3814 /* Direct backend is not using D-Bus (obviously),
3815 * so no need to strip D-Bus info from the error. */
3816 success = e_book_backend_get_contact_list_uids_sync (
3817 client->priv->direct_backend,
3818 sexp, &queue, cancellable, error);
3819
3820 if (success) {
3821 while (!g_queue_is_empty (&queue)) {
3822 gchar *uid;
3823
3824 uid = g_queue_pop_head (&queue);
3825 list = g_slist_prepend (list, uid);
3826 }
3827
3828 *out_contact_uids = g_slist_reverse (list);
3829 }
3830
3831 return success;
3832 }
3833
3834 utf8_sexp = e_util_utf8_make_valid (sexp);
3835
3836 e_dbus_address_book_call_get_contact_list_uids_sync (
3837 client->priv->dbus_proxy, utf8_sexp,
3838 &uids, cancellable, &local_error);
3839
3840 g_free (utf8_sexp);
3841
3842 /* Sanity check. */
3843 g_return_val_if_fail (
3844 ((uids != NULL) && (local_error == NULL)) ||
3845 ((uids == NULL) && (local_error != NULL)), FALSE);
3846
3847 /* XXX We should have passed the string array directly
3848 * back to the caller instead of building a linked
3849 * list. This is unnecessary work. */
3850 if (uids != NULL) {
3851 GSList *tmp = NULL;
3852 gint ii;
3853
3854 /* Take ownership of the string array elements. */
3855 for (ii = 0; uids[ii] != NULL; ii++) {
3856 tmp = g_slist_prepend (tmp, uids[ii]);
3857 uids[ii] = NULL;
3858 }
3859
3860 *out_contact_uids = g_slist_reverse (tmp);
3861
3862 g_free (uids);
3863 }
3864
3865 if (local_error != NULL) {
3866 g_dbus_error_strip_remote_error (local_error);
3867 g_propagate_error (error, local_error);
3868 return FALSE;
3869 }
3870
3871 return TRUE;
3872 }
3873
3874 /* Helper for e_book_client_get_view() */
3875 static void
book_client_get_view_in_dbus_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)3876 book_client_get_view_in_dbus_thread (GSimpleAsyncResult *simple,
3877 GObject *source_object,
3878 GCancellable *cancellable)
3879 {
3880 EBookClient *client = E_BOOK_CLIENT (source_object);
3881 AsyncContext *async_context;
3882 gchar *utf8_sexp;
3883 gchar *object_path = NULL;
3884 GError *local_error = NULL;
3885
3886 async_context = g_simple_async_result_get_op_res_gpointer (simple);
3887
3888 utf8_sexp = e_util_utf8_make_valid (async_context->sexp);
3889
3890 e_dbus_address_book_call_get_view_sync (
3891 client->priv->dbus_proxy, utf8_sexp,
3892 &object_path, cancellable, &local_error);
3893
3894 g_free (utf8_sexp);
3895
3896 /* Sanity check. */
3897 g_return_if_fail (
3898 ((object_path != NULL) && (local_error == NULL)) ||
3899 ((object_path == NULL) && (local_error != NULL)));
3900
3901 if (object_path != NULL) {
3902 GDBusConnection *connection;
3903 EBookClientView *client_view;
3904
3905 connection = g_dbus_proxy_get_connection (
3906 G_DBUS_PROXY (client->priv->dbus_proxy));
3907
3908 client_view = g_initable_new (
3909 E_TYPE_BOOK_CLIENT_VIEW,
3910 cancellable, &local_error,
3911 "client", client,
3912 "connection", connection,
3913 "object-path", object_path,
3914 "direct-backend", client->priv->direct_backend,
3915 NULL);
3916
3917 /* Sanity check. */
3918 g_return_if_fail (
3919 ((client_view != NULL) && (local_error == NULL)) ||
3920 ((client_view == NULL) && (local_error != NULL)));
3921
3922 async_context->client_view = client_view;
3923
3924 g_free (object_path);
3925 }
3926
3927 if (local_error != NULL) {
3928 g_dbus_error_strip_remote_error (local_error);
3929 g_simple_async_result_take_error (simple, local_error);
3930 }
3931 }
3932
3933 /**
3934 * e_book_client_get_view:
3935 * @client: an #EBookClient
3936 * @sexp: an S-expression representing the query
3937 * @cancellable: a #GCancellable; can be %NULL
3938 * @callback: callback to call when a result is ready
3939 * @user_data: user data for the @callback
3940 *
3941 * Query @client with @sexp, creating an #EBookClientView.
3942 * The call is finished by e_book_client_get_view_finish()
3943 * from the @callback.
3944 *
3945 * Note: @sexp can be obtained through #EBookQuery, by converting it
3946 * to a string with e_book_query_to_string().
3947 *
3948 * Since: 3.2
3949 **/
3950 void
e_book_client_get_view(EBookClient * client,const gchar * sexp,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3951 e_book_client_get_view (EBookClient *client,
3952 const gchar *sexp,
3953 GCancellable *cancellable,
3954 GAsyncReadyCallback callback,
3955 gpointer user_data)
3956 {
3957 GSimpleAsyncResult *simple;
3958 AsyncContext *async_context;
3959
3960 g_return_if_fail (E_IS_BOOK_CLIENT (client));
3961 g_return_if_fail (sexp != NULL);
3962
3963 async_context = g_slice_new0 (AsyncContext);
3964 async_context->sexp = g_strdup (sexp);
3965
3966 simple = g_simple_async_result_new (
3967 G_OBJECT (client), callback, user_data,
3968 e_book_client_get_view);
3969
3970 g_simple_async_result_set_check_cancellable (simple, cancellable);
3971
3972 g_simple_async_result_set_op_res_gpointer (
3973 simple, async_context, (GDestroyNotify) async_context_free);
3974
3975 book_client_run_in_dbus_thread (
3976 simple, book_client_get_view_in_dbus_thread,
3977 G_PRIORITY_DEFAULT, cancellable);
3978
3979 g_object_unref (simple);
3980 }
3981
3982 /**
3983 * e_book_client_get_view_finish:
3984 * @client: an #EBookClient
3985 * @result: a #GAsyncResult
3986 * @out_view: (out): an #EBookClientView
3987 * @error: a #GError to set an error, if any
3988 *
3989 * Finishes previous call of e_book_client_get_view().
3990 * If successful, then the @out_view is set to newly allocated
3991 * #EBookClientView, which should be freed with g_object_unref().
3992 *
3993 * Returns: %TRUE if successful, %FALSE otherwise.
3994 *
3995 * Since: 3.2
3996 **/
3997 gboolean
e_book_client_get_view_finish(EBookClient * client,GAsyncResult * result,EBookClientView ** out_view,GError ** error)3998 e_book_client_get_view_finish (EBookClient *client,
3999 GAsyncResult *result,
4000 EBookClientView **out_view,
4001 GError **error)
4002 {
4003 GSimpleAsyncResult *simple;
4004 AsyncContext *async_context;
4005
4006 g_return_val_if_fail (
4007 g_simple_async_result_is_valid (
4008 result, G_OBJECT (client),
4009 e_book_client_get_view), FALSE);
4010
4011 simple = G_SIMPLE_ASYNC_RESULT (result);
4012 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4013
4014 if (g_simple_async_result_propagate_error (simple, error))
4015 return FALSE;
4016
4017 g_return_val_if_fail (async_context->client_view != NULL, FALSE);
4018
4019 if (out_view != NULL)
4020 *out_view = g_object_ref (async_context->client_view);
4021
4022 return TRUE;
4023 }
4024
4025 /**
4026 * e_book_client_get_view_sync:
4027 * @client: an #EBookClient
4028 * @sexp: an S-expression representing the query
4029 * @out_view: (out): an #EBookClientView
4030 * @cancellable: a #GCancellable; can be %NULL
4031 * @error: a #GError to set an error, if any
4032 *
4033 * Query @client with @sexp, creating an #EBookClientView.
4034 * If successful, then the @out_view is set to newly allocated
4035 * #EBookClientView, which should be freed with g_object_unref().
4036 *
4037 * Note: @sexp can be obtained through #EBookQuery, by converting it
4038 * to a string with e_book_query_to_string().
4039 *
4040 * Returns: %TRUE if successful, %FALSE otherwise.
4041 *
4042 * Since: 3.2
4043 **/
4044 gboolean
e_book_client_get_view_sync(EBookClient * client,const gchar * sexp,EBookClientView ** out_view,GCancellable * cancellable,GError ** error)4045 e_book_client_get_view_sync (EBookClient *client,
4046 const gchar *sexp,
4047 EBookClientView **out_view,
4048 GCancellable *cancellable,
4049 GError **error)
4050 {
4051 EAsyncClosure *closure;
4052 GAsyncResult *result;
4053 gboolean success;
4054
4055 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
4056 g_return_val_if_fail (sexp != NULL, FALSE);
4057 g_return_val_if_fail (out_view != NULL, FALSE);
4058
4059 closure = e_async_closure_new ();
4060
4061 e_book_client_get_view (
4062 client, sexp, cancellable,
4063 e_async_closure_callback, closure);
4064
4065 result = e_async_closure_wait (closure);
4066
4067 success = e_book_client_get_view_finish (
4068 client, result, out_view, error);
4069
4070 e_async_closure_free (closure);
4071
4072 return success;
4073 }
4074
4075 /* Helper for e_book_client_get_cursor() */
4076 static const gchar **
sort_param_to_strv(gpointer param,gint n_fields,gboolean keys)4077 sort_param_to_strv (gpointer param,
4078 gint n_fields,
4079 gboolean keys)
4080 {
4081 const gchar **array;
4082 gint i;
4083
4084 array = (const gchar **) g_new0 (gchar *, n_fields + 1);
4085
4086 /* string arrays are shallow allocated, the strings themselves
4087 * are intern strings and don't need to be dupped.
4088 */
4089 for (i = 0; i < n_fields; i++) {
4090
4091 if (keys) {
4092 EContactField *fields = (EContactField *) param;
4093
4094 array[i] = e_contact_field_name (fields[i]);
4095 } else {
4096 EBookCursorSortType *types = (EBookCursorSortType *) param;
4097
4098 array[i] = e_enum_to_string (
4099 E_TYPE_BOOK_CURSOR_SORT_TYPE,
4100 types[i]);
4101 }
4102 }
4103
4104 return array;
4105 }
4106
4107 /* This is ugly and should change yes, currently EBookClientCursor
4108 * needs to keep a strong reference to the EBookClient to keep it alive
4109 * long enough to ask the EBookClient to delete a direct cursor on
4110 * the EBookClientCursor's behalf, otherwise direct cursors are leaked.
4111 */
4112 void book_client_delete_direct_cursor (EBookClient *client,
4113 EDataBookCursor *cursor);
4114
4115 void
book_client_delete_direct_cursor(EBookClient * client,EDataBookCursor * cursor)4116 book_client_delete_direct_cursor (EBookClient *client,
4117 EDataBookCursor *cursor)
4118 {
4119 g_return_if_fail (E_IS_BOOK_CLIENT (client));
4120 g_return_if_fail (E_IS_DATA_BOOK_CURSOR (cursor));
4121
4122 if (!client->priv->direct_backend) {
4123 g_warning ("Tried to delete a cursor in DRA mode but the direct backend is missing");
4124 return;
4125 }
4126
4127 e_book_backend_delete_cursor (
4128 client->priv->direct_backend,
4129 cursor, NULL);
4130 }
4131
4132 static void
book_client_get_cursor_in_dbus_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)4133 book_client_get_cursor_in_dbus_thread (GSimpleAsyncResult *simple,
4134 GObject *source_object,
4135 GCancellable *cancellable)
4136 {
4137 EBookClient *client = E_BOOK_CLIENT (source_object);
4138 AsyncContext *async_context;
4139 gchar *utf8_sexp;
4140 gchar *object_path = NULL;
4141 GError *local_error = NULL;
4142 const gchar **sort_fields;
4143 const gchar **sort_types;
4144
4145 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4146
4147 sort_fields = sort_param_to_strv (
4148 async_context->sort_fields,
4149 async_context->n_sort_fields, TRUE);
4150 sort_types = sort_param_to_strv (
4151 async_context->sort_types,
4152 async_context->n_sort_fields, FALSE);
4153
4154 /* Direct Read Access cursor don't need any D-Bus connection
4155 * themselves, just give them an EDataBookCursor directly. */
4156 if (client->priv->direct_backend) {
4157 EDataBookCursor *cursor;
4158
4159 cursor = e_book_backend_create_cursor (
4160 client->priv->direct_backend,
4161 async_context->sort_fields,
4162 async_context->sort_types,
4163 async_context->n_sort_fields,
4164 &local_error);
4165
4166 if (cursor != NULL) {
4167 e_data_book_cursor_set_sexp (
4168 cursor,
4169 async_context->sexp,
4170 cancellable, &local_error);
4171
4172 if (local_error != NULL) {
4173 e_book_backend_delete_cursor (
4174 client->priv->direct_backend,
4175 cursor,
4176 NULL);
4177 cursor = NULL;
4178 }
4179 }
4180
4181 if (cursor != NULL) {
4182 EBookClientCursor *client_cursor;
4183
4184 /* The client cursor will take a ref, but
4185 * e_book_backend_create_cursor() returns
4186 * a pointer to a cursor owned by the backend,
4187 * don't unref the returned pointer here.
4188 */
4189 client_cursor = g_initable_new (
4190 E_TYPE_BOOK_CLIENT_CURSOR,
4191 cancellable, &local_error,
4192 "sort-fields", sort_fields,
4193 "client", client,
4194 "context", async_context->context,
4195 "direct-cursor", cursor,
4196 NULL);
4197
4198 /* Sanity check. */
4199 g_return_if_fail (
4200 ((client_cursor != NULL) && (local_error == NULL)) ||
4201 ((client_cursor == NULL) && (local_error != NULL)));
4202
4203 async_context->client_cursor = client_cursor;
4204 }
4205
4206 } else {
4207 utf8_sexp = e_util_utf8_make_valid (async_context->sexp);
4208
4209 e_dbus_address_book_call_get_cursor_sync (
4210 client->priv->dbus_proxy, utf8_sexp,
4211 (const gchar *const *) sort_fields,
4212 (const gchar *const *) sort_types,
4213 &object_path, cancellable, &local_error);
4214
4215 g_free (utf8_sexp);
4216
4217 /* Sanity check. */
4218 g_return_if_fail (
4219 ((object_path != NULL) && (local_error == NULL)) ||
4220 ((object_path == NULL) && (local_error != NULL)));
4221
4222 if (object_path != NULL) {
4223 GDBusConnection *connection;
4224 EBookClientCursor *client_cursor;
4225
4226 connection = g_dbus_proxy_get_connection (
4227 G_DBUS_PROXY (client->priv->dbus_proxy));
4228
4229 client_cursor = g_initable_new (
4230 E_TYPE_BOOK_CLIENT_CURSOR,
4231 cancellable, &local_error,
4232 "sort-fields", sort_fields,
4233 "client", client,
4234 "context", async_context->context,
4235 "connection", connection,
4236 "object-path", object_path,
4237 NULL);
4238
4239 /* Sanity check. */
4240 g_return_if_fail (
4241 ((client_cursor != NULL) && (local_error == NULL)) ||
4242 ((client_cursor == NULL) && (local_error != NULL)));
4243
4244 async_context->client_cursor = client_cursor;
4245
4246 g_free (object_path);
4247 }
4248 }
4249
4250 if (local_error != NULL) {
4251 g_dbus_error_strip_remote_error (local_error);
4252 g_simple_async_result_take_error (simple, local_error);
4253 }
4254
4255 g_free (sort_fields);
4256 g_free (sort_types);
4257 }
4258
4259 /* Need to catch the GMainContext of the actual caller,
4260 * we do this dance because e_async_closure_new ()
4261 * steps on the thread default main context (so we
4262 * can use this in the sync call as well).
4263 */
4264 static void
e_book_client_get_cursor_with_context(EBookClient * client,const gchar * sexp,const EContactField * sort_fields,const EBookCursorSortType * sort_types,guint n_fields,GMainContext * context,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4265 e_book_client_get_cursor_with_context (EBookClient *client,
4266 const gchar *sexp,
4267 const EContactField *sort_fields,
4268 const EBookCursorSortType *sort_types,
4269 guint n_fields,
4270 GMainContext *context,
4271 GCancellable *cancellable,
4272 GAsyncReadyCallback callback,
4273 gpointer user_data)
4274 {
4275 GSimpleAsyncResult *simple;
4276 AsyncContext *async_context;
4277
4278 g_return_if_fail (E_IS_BOOK_CLIENT (client));
4279 g_return_if_fail (sort_fields != NULL);
4280 g_return_if_fail (sort_types != NULL);
4281 g_return_if_fail (n_fields > 0);
4282
4283 async_context = g_slice_new0 (AsyncContext);
4284 async_context->sexp = g_strdup (sexp);
4285 async_context->sort_fields = g_memdup (sort_fields, sizeof (EContactField) * n_fields);
4286 async_context->sort_types = g_memdup (sort_types, sizeof (EBookCursorSortType) * n_fields);
4287 async_context->n_sort_fields = n_fields;
4288 async_context->context = g_main_context_ref (context);
4289
4290 simple = g_simple_async_result_new (
4291 G_OBJECT (client), callback, user_data,
4292 e_book_client_get_cursor);
4293
4294 g_simple_async_result_set_check_cancellable (simple, cancellable);
4295
4296 g_simple_async_result_set_op_res_gpointer (
4297 simple, async_context, (GDestroyNotify) async_context_free);
4298
4299 book_client_run_in_dbus_thread (
4300 simple, book_client_get_cursor_in_dbus_thread,
4301 G_PRIORITY_DEFAULT, cancellable);
4302
4303 g_object_unref (simple);
4304 }
4305
4306 /**
4307 * e_book_client_get_cursor:
4308 * @client: an #EBookClient
4309 * @sexp: an S-expression representing the query
4310 * @sort_fields: an array of #EContactFields to sort the cursor with
4311 * @sort_types: an array of #EBookCursorSortTypes to complement @sort_fields
4312 * @n_fields: the length of the input @sort_fields and @sort_types arrays
4313 * @cancellable: a #GCancellable; can be %NULL
4314 * @callback: callback to call when a result is ready
4315 * @user_data: user data for the @callback
4316 *
4317 * Create an #EBookClientCursor.
4318 * The call is finished by e_book_client_get_view_finish()
4319 * from the @callback.
4320 *
4321 * Note: @sexp can be obtained through #EBookQuery, by converting it
4322 * to a string with e_book_query_to_string().
4323 *
4324 * Since: 3.12
4325 */
4326 void
e_book_client_get_cursor(EBookClient * client,const gchar * sexp,const EContactField * sort_fields,const EBookCursorSortType * sort_types,guint n_fields,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4327 e_book_client_get_cursor (EBookClient *client,
4328 const gchar *sexp,
4329 const EContactField *sort_fields,
4330 const EBookCursorSortType *sort_types,
4331 guint n_fields,
4332 GCancellable *cancellable,
4333 GAsyncReadyCallback callback,
4334 gpointer user_data)
4335 {
4336 GMainContext *context;
4337
4338 context = g_main_context_ref_thread_default ();
4339 e_book_client_get_cursor_with_context (
4340 client, sexp,
4341 sort_fields,
4342 sort_types,
4343 n_fields,
4344 context,
4345 cancellable,
4346 callback,
4347 user_data);
4348 g_main_context_unref (context);
4349 }
4350
4351 /**
4352 * e_book_client_get_cursor_finish:
4353 * @client: an #EBookClient
4354 * @result: a #GAsyncResult
4355 * @out_cursor: (out): return location for an #EBookClientCursor
4356 * @error: a #GError to set an error, if any
4357 *
4358 * Finishes previous call of e_book_client_get_cursor().
4359 * If successful, then the @out_cursor is set to newly create
4360 * #EBookClientCursor, the cursor should be freed with g_object_unref()
4361 * when no longer needed.
4362 *
4363 * Returns: %TRUE if successful, %FALSE otherwise.
4364 *
4365 * Since: 3.12
4366 */
4367 gboolean
e_book_client_get_cursor_finish(EBookClient * client,GAsyncResult * result,EBookClientCursor ** out_cursor,GError ** error)4368 e_book_client_get_cursor_finish (EBookClient *client,
4369 GAsyncResult *result,
4370 EBookClientCursor **out_cursor,
4371 GError **error)
4372 {
4373 GSimpleAsyncResult *simple;
4374 AsyncContext *async_context;
4375
4376 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
4377 g_return_val_if_fail (
4378 g_simple_async_result_is_valid (
4379 result, G_OBJECT (client),
4380 e_book_client_get_cursor), FALSE);
4381
4382 simple = G_SIMPLE_ASYNC_RESULT (result);
4383 async_context = g_simple_async_result_get_op_res_gpointer (simple);
4384
4385 if (g_simple_async_result_propagate_error (simple, error))
4386 return FALSE;
4387
4388 g_return_val_if_fail (async_context->client_cursor != NULL, FALSE);
4389
4390 if (out_cursor != NULL)
4391 *out_cursor = g_object_ref (async_context->client_cursor);
4392
4393 return TRUE;
4394 }
4395
4396 /**
4397 * e_book_client_get_cursor_sync:
4398 * @client: an #EBookClient
4399 * @sexp: an S-expression representing the query
4400 * @sort_fields: an array of #EContactFields to sort the cursor with
4401 * @sort_types: an array of #EBookCursorSortTypes to complement @sort_fields
4402 * @n_fields: the length of the input @sort_fields and @sort_types arrays
4403 * @out_cursor: (out): return location for an #EBookClientCursor
4404 * @cancellable: a #GCancellable; can be %NULL
4405 * @error: a #GError to set an error, if any
4406 *
4407 * Create an #EBookClientCursor. If successful, then the @out_cursor is set
4408 * to newly allocated #EBookClientCursor, the cursor should be freed with g_object_unref()
4409 * when no longer needed.
4410 *
4411 * Note: @sexp can be obtained through #EBookQuery, by converting it
4412 * to a string with e_book_query_to_string().
4413 *
4414 * Returns: %TRUE if successful, %FALSE otherwise.
4415 *
4416 * Since: 3.12
4417 */
4418 gboolean
e_book_client_get_cursor_sync(EBookClient * client,const gchar * sexp,const EContactField * sort_fields,const EBookCursorSortType * sort_types,guint n_fields,EBookClientCursor ** out_cursor,GCancellable * cancellable,GError ** error)4419 e_book_client_get_cursor_sync (EBookClient *client,
4420 const gchar *sexp,
4421 const EContactField *sort_fields,
4422 const EBookCursorSortType *sort_types,
4423 guint n_fields,
4424 EBookClientCursor **out_cursor,
4425 GCancellable *cancellable,
4426 GError **error)
4427 {
4428 EAsyncClosure *closure;
4429 GAsyncResult *result;
4430 gboolean success;
4431 GMainContext *context;
4432
4433 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
4434 g_return_val_if_fail (sort_fields != NULL, FALSE);
4435 g_return_val_if_fail (sort_types != NULL, FALSE);
4436 g_return_val_if_fail (n_fields > 0, FALSE);
4437 g_return_val_if_fail (out_cursor != NULL, FALSE);
4438
4439 /* Get the main context before e_async_closure_new () steps on it */
4440 context = g_main_context_ref_thread_default ();
4441
4442 closure = e_async_closure_new ();
4443
4444 e_book_client_get_cursor_with_context (
4445 client, sexp,
4446 sort_fields,
4447 sort_types,
4448 n_fields,
4449 context,
4450 cancellable,
4451 e_async_closure_callback, closure);
4452
4453 g_main_context_unref (context);
4454
4455 result = e_async_closure_wait (closure);
4456
4457 success = e_book_client_get_cursor_finish (
4458 client, result, out_cursor, error);
4459
4460 e_async_closure_free (closure);
4461
4462 return success;
4463 }
4464
4465 static void
book_client_set_locale(EBookClient * client,const gchar * locale)4466 book_client_set_locale (EBookClient *client,
4467 const gchar *locale)
4468 {
4469 if (g_strcmp0 (client->priv->locale, locale) != 0) {
4470 g_free (client->priv->locale);
4471 client->priv->locale = g_strdup (locale);
4472
4473 g_object_notify (G_OBJECT (client), "locale");
4474 }
4475 }
4476
4477 /**
4478 * e_book_client_get_locale:
4479 * @client: an #EBookClient
4480 *
4481 * Reports the locale in use for @client. The addressbook might sort contacts
4482 * in different orders, or store and compare phone numbers in different ways
4483 * depending on the currently set locale.
4484 *
4485 * Locales can change dynamically if systemd decides to change the locale, so
4486 * it's important to listen for notifications on the #EBookClient:locale property
4487 * if you depend on sorted result lists. Ordered results should be reloaded
4488 * after a locale change is detected.
4489 *
4490 * Returns: (transfer none): The currently set locale for @client
4491 *
4492 * Since: 3.12
4493 */
4494 const gchar *
e_book_client_get_locale(EBookClient * client)4495 e_book_client_get_locale (EBookClient *client)
4496 {
4497 g_return_val_if_fail (E_IS_BOOK_CLIENT (client), NULL);
4498
4499 return client->priv->locale;
4500 }
4501