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