1 /*
2  * e-book-backend.c
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.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  * Authors: Nat Friedman (nat@ximian.com)
20  *          Tristan Van Berkom <tristanvb@openismus.com>
21  */
22 
23 /**
24  * SECTION: e-book-backend
25  * @include: libedata-book/libedata-book.h
26  * @short_description: An abstract class for implementing addressbook backends
27  *
28  * This is the main server facing API for interfacing with addressbook backends,
29  * addressbook backends must implement methods on this class.
30  **/
31 
32 #include "evolution-data-server-config.h"
33 
34 #include <glib/gi18n-lib.h>
35 
36 #include "e-data-book-view.h"
37 #include "e-data-book.h"
38 #include "e-book-backend.h"
39 
40 typedef struct _AsyncContext AsyncContext;
41 typedef struct _DispatchNode DispatchNode;
42 
43 struct _EBookBackendPrivate {
44 	ESourceRegistry *registry;
45 	EDataBook *data_book;
46 
47 	gboolean opened;
48 
49 	GMutex views_mutex;
50 	GList *views;
51 
52 	GMutex property_lock;
53 	GProxyResolver *proxy_resolver;
54 	gchar *cache_dir;
55 	gboolean writable;
56 
57 	ESource *authentication_source;
58 	gulong auth_source_changed_handler_id;
59 
60 	GMutex operation_lock;
61 	GThreadPool *thread_pool;
62 	GHashTable *operation_ids;
63 	GQueue pending_operations;
64 	guint32 next_operation_id;
65 	GSimpleAsyncResult *blocked;
66 	gboolean blocked_by_custom_op;
67 };
68 
69 struct _AsyncContext {
70 	/* Inputs */
71 	gchar *uid;
72 	gchar *query;
73 	gchar **strv;
74 
75 	/* Outputs */
76 	EContact *contact;
77 	GQueue result_queue;
78 
79 	/* One of these should point to result_queue
80 	 * so any leftover resources can be released. */
81 	GQueue *object_queue;
82 	GQueue *string_queue;
83 
84 	guint32 opflags; /* bit-or of EBookOperationFlags */
85 };
86 
87 struct _DispatchNode {
88 	/* This is the dispatch function
89 	 * that invokes the class method. */
90 	GSimpleAsyncThreadFunc dispatch_func;
91 	gboolean blocking_operation;
92 
93 	GSimpleAsyncResult *simple;
94 	GCancellable *cancellable;
95 
96 	GWeakRef *book_backend_weak_ref;
97 	EBookBackendCustomOpFunc custom_func;
98 	gpointer custom_func_user_data;
99 	GDestroyNotify custom_func_user_data_free;
100 };
101 
102 enum {
103 	PROP_0,
104 	PROP_CACHE_DIR,
105 	PROP_PROXY_RESOLVER,
106 	PROP_REGISTRY,
107 	PROP_WRITABLE
108 };
109 
110 enum {
111 	CLOSED,
112 	SHUTDOWN,
113 	LAST_SIGNAL
114 };
115 
116 static guint signals[LAST_SIGNAL];
117 
G_DEFINE_TYPE_WITH_PRIVATE(EBookBackend,e_book_backend,E_TYPE_BACKEND)118 G_DEFINE_TYPE_WITH_PRIVATE (EBookBackend, e_book_backend, E_TYPE_BACKEND)
119 
120 static void
121 async_context_free (AsyncContext *async_context)
122 {
123 	GQueue *queue;
124 
125 	g_free (async_context->uid);
126 	g_free (async_context->query);
127 	g_strfreev (async_context->strv);
128 
129 	g_clear_object (&async_context->contact);
130 
131 	queue = async_context->object_queue;
132 	while (queue != NULL && !g_queue_is_empty (queue))
133 		g_object_unref (g_queue_pop_head (queue));
134 
135 	queue = async_context->string_queue;
136 	while (queue != NULL && !g_queue_is_empty (queue))
137 		g_free (g_queue_pop_head (queue));
138 
139 	g_slice_free (AsyncContext, async_context);
140 }
141 
142 static void
dispatch_node_free(DispatchNode * dispatch_node)143 dispatch_node_free (DispatchNode *dispatch_node)
144 {
145 	g_clear_object (&dispatch_node->simple);
146 	g_clear_object (&dispatch_node->cancellable);
147 
148 	if (dispatch_node->custom_func_user_data_free)
149 		dispatch_node->custom_func_user_data_free (dispatch_node->custom_func_user_data);
150 
151 	if (dispatch_node->book_backend_weak_ref)
152 		e_weak_ref_free (dispatch_node->book_backend_weak_ref);
153 
154 	g_slice_free (DispatchNode, dispatch_node);
155 }
156 
157 static void
book_backend_push_operation(EBookBackend * backend,GSimpleAsyncResult * simple,GCancellable * cancellable,gboolean blocking_operation,GSimpleAsyncThreadFunc dispatch_func)158 book_backend_push_operation (EBookBackend *backend,
159                              GSimpleAsyncResult *simple,
160                              GCancellable *cancellable,
161                              gboolean blocking_operation,
162                              GSimpleAsyncThreadFunc dispatch_func)
163 {
164 	DispatchNode *node;
165 
166 	g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
167 	g_return_if_fail (dispatch_func != NULL);
168 
169 	g_mutex_lock (&backend->priv->operation_lock);
170 
171 	node = g_slice_new0 (DispatchNode);
172 	node->dispatch_func = dispatch_func;
173 	node->blocking_operation = blocking_operation;
174 	node->simple = g_object_ref (simple);
175 
176 	if (G_IS_CANCELLABLE (cancellable))
177 		node->cancellable = g_object_ref (cancellable);
178 
179 	g_queue_push_tail (&backend->priv->pending_operations, node);
180 
181 	g_mutex_unlock (&backend->priv->operation_lock);
182 }
183 
184 static void book_backend_unblock_operations (EBookBackend *backend, GSimpleAsyncResult *simple);
185 
186 static void
book_backend_dispatch_thread(DispatchNode * node)187 book_backend_dispatch_thread (DispatchNode *node)
188 {
189 	GCancellable *cancellable = node->cancellable;
190 	GError *local_error = NULL;
191 
192 	if (node->custom_func) {
193 		EBookBackend *book_backend;
194 
195 		book_backend = g_weak_ref_get (node->book_backend_weak_ref);
196 		if (book_backend &&
197 		    !g_cancellable_is_cancelled (cancellable)) {
198 			node->custom_func (book_backend, node->custom_func_user_data, cancellable, &local_error);
199 
200 			if (local_error) {
201 				if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
202 					e_book_backend_notify_error (book_backend, local_error->message);
203 
204 				g_clear_error (&local_error);
205 			}
206 		}
207 
208 		if (book_backend) {
209 			book_backend_unblock_operations (book_backend, NULL);
210 			e_util_unref_in_thread (book_backend);
211 		}
212 	} else if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
213 		g_simple_async_result_take_error (node->simple, local_error);
214 		g_simple_async_result_complete_in_idle (node->simple);
215 	} else {
216 		GAsyncResult *result;
217 		GObject *source_object;
218 
219 		result = G_ASYNC_RESULT (node->simple);
220 		source_object = g_async_result_get_source_object (result);
221 		node->dispatch_func (node->simple, source_object, cancellable);
222 		g_object_unref (source_object);
223 	}
224 
225 	dispatch_node_free (node);
226 }
227 
228 static gboolean
book_backend_dispatch_next_operation(EBookBackend * backend)229 book_backend_dispatch_next_operation (EBookBackend *backend)
230 {
231 	DispatchNode *node;
232 
233 	g_mutex_lock (&backend->priv->operation_lock);
234 
235 	/* We can't dispatch additional operations
236 	 * while a blocking operation is in progress. */
237 	if (backend->priv->blocked != NULL ||
238 	    backend->priv->blocked_by_custom_op) {
239 		g_mutex_unlock (&backend->priv->operation_lock);
240 		return FALSE;
241 	}
242 
243 	/* Pop the next DispatchNode off the queue. */
244 	node = g_queue_pop_head (&backend->priv->pending_operations);
245 	if (node == NULL) {
246 		g_mutex_unlock (&backend->priv->operation_lock);
247 		return FALSE;
248 	}
249 
250 	/* If this a blocking operation, block any
251 	 * further dispatching until this finishes. */
252 	if (node->blocking_operation) {
253 		if (node->simple)
254 			backend->priv->blocked = g_object_ref (node->simple);
255 		else
256 			backend->priv->blocked_by_custom_op = TRUE;
257 	}
258 
259 	g_mutex_unlock (&backend->priv->operation_lock);
260 
261 	/* An error here merely indicates a thread could not be
262 	 * created, and so the node was queued.  We don't care. */
263 	g_thread_pool_push (backend->priv->thread_pool, node, NULL);
264 
265 	return TRUE;
266 }
267 
268 static void
book_backend_unblock_operations(EBookBackend * backend,GSimpleAsyncResult * simple)269 book_backend_unblock_operations (EBookBackend *backend,
270                                  GSimpleAsyncResult *simple)
271 {
272 	/* If the GSimpleAsyncResult was blocking the dispatch queue,
273 	 * unblock the dispatch queue.  Then dispatch as many waiting
274 	 * operations as we can. */
275 
276 	g_mutex_lock (&backend->priv->operation_lock);
277 	if (backend->priv->blocked == simple)
278 		g_clear_object (&backend->priv->blocked);
279 	backend->priv->blocked_by_custom_op = FALSE;
280 	g_mutex_unlock (&backend->priv->operation_lock);
281 
282 	while (book_backend_dispatch_next_operation (backend))
283 		;
284 }
285 
286 static guint32
book_backend_stash_operation(EBookBackend * backend,GSimpleAsyncResult * simple)287 book_backend_stash_operation (EBookBackend *backend,
288                               GSimpleAsyncResult *simple)
289 {
290 	guint32 opid;
291 
292 	g_mutex_lock (&backend->priv->operation_lock);
293 
294 	if (backend->priv->next_operation_id == 0)
295 		backend->priv->next_operation_id = 1;
296 
297 	opid = backend->priv->next_operation_id++;
298 
299 	g_hash_table_insert (
300 		backend->priv->operation_ids,
301 		GUINT_TO_POINTER (opid),
302 		g_object_ref (simple));
303 
304 	g_mutex_unlock (&backend->priv->operation_lock);
305 
306 	return opid;
307 }
308 
309 static GSimpleAsyncResult *
book_backend_claim_operation(EBookBackend * backend,guint32 opid)310 book_backend_claim_operation (EBookBackend *backend,
311                               guint32 opid)
312 {
313 	GSimpleAsyncResult *simple;
314 
315 	g_return_val_if_fail (opid > 0, NULL);
316 
317 	g_mutex_lock (&backend->priv->operation_lock);
318 
319 	simple = g_hash_table_lookup (
320 		backend->priv->operation_ids,
321 		GUINT_TO_POINTER (opid));
322 
323 	if (simple != NULL) {
324 		/* Steal the hash table's reference. */
325 		g_hash_table_steal (
326 			backend->priv->operation_ids,
327 			GUINT_TO_POINTER (opid));
328 	}
329 
330 	g_mutex_unlock (&backend->priv->operation_lock);
331 
332 	return simple;
333 }
334 
335 static void
book_backend_set_default_cache_dir(EBookBackend * backend)336 book_backend_set_default_cache_dir (EBookBackend *backend)
337 {
338 	ESource *source;
339 	const gchar *user_cache_dir;
340 	const gchar *uid;
341 	gchar *filename;
342 
343 	user_cache_dir = e_get_user_cache_dir ();
344 	source = e_backend_get_source (E_BACKEND (backend));
345 
346 	uid = e_source_get_uid (source);
347 	g_return_if_fail (uid != NULL);
348 
349 	filename = g_build_filename (
350 		user_cache_dir, "addressbook", uid, NULL);
351 	e_book_backend_set_cache_dir (backend, filename);
352 	g_free (filename);
353 }
354 
355 static void
book_backend_update_proxy_resolver(EBookBackend * backend)356 book_backend_update_proxy_resolver (EBookBackend *backend)
357 {
358 	GProxyResolver *proxy_resolver = NULL;
359 	ESourceAuthentication *extension;
360 	ESource *source = NULL;
361 	gboolean notify = FALSE;
362 	gchar *uid;
363 
364 	extension = e_source_get_extension (
365 		backend->priv->authentication_source,
366 		E_SOURCE_EXTENSION_AUTHENTICATION);
367 
368 	uid = e_source_authentication_dup_proxy_uid (extension);
369 	if (uid != NULL) {
370 		ESourceRegistry *registry;
371 
372 		registry = e_book_backend_get_registry (backend);
373 		source = e_source_registry_ref_source (registry, uid);
374 		g_free (uid);
375 	}
376 
377 	if (source != NULL) {
378 		proxy_resolver = G_PROXY_RESOLVER (source);
379 		if (!g_proxy_resolver_is_supported (proxy_resolver))
380 			proxy_resolver = NULL;
381 	}
382 
383 	g_mutex_lock (&backend->priv->property_lock);
384 
385 	/* Emitting a "notify" signal unnecessarily might have
386 	 * unwanted side effects like cancelling a SoupMessage.
387 	 * Only emit if we now have a different GProxyResolver. */
388 
389 	if (proxy_resolver != backend->priv->proxy_resolver) {
390 		g_clear_object (&backend->priv->proxy_resolver);
391 		backend->priv->proxy_resolver = proxy_resolver;
392 
393 		if (proxy_resolver != NULL)
394 			g_object_ref (proxy_resolver);
395 
396 		notify = TRUE;
397 	}
398 
399 	g_mutex_unlock (&backend->priv->property_lock);
400 
401 	if (notify)
402 		g_object_notify (G_OBJECT (backend), "proxy-resolver");
403 
404 	g_clear_object (&source);
405 }
406 
407 static void
book_backend_auth_source_changed_cb(ESource * authentication_source,GWeakRef * backend_weak_ref)408 book_backend_auth_source_changed_cb (ESource *authentication_source,
409                                      GWeakRef *backend_weak_ref)
410 {
411 	EBookBackend *backend;
412 
413 	backend = g_weak_ref_get (backend_weak_ref);
414 
415 	if (backend != NULL) {
416 		book_backend_update_proxy_resolver (backend);
417 		g_object_unref (backend);
418 	}
419 }
420 
421 static void
book_backend_set_registry(EBookBackend * backend,ESourceRegistry * registry)422 book_backend_set_registry (EBookBackend *backend,
423                            ESourceRegistry *registry)
424 {
425 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
426 	g_return_if_fail (backend->priv->registry == NULL);
427 
428 	backend->priv->registry = g_object_ref (registry);
429 }
430 
431 static void
book_backend_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)432 book_backend_set_property (GObject *object,
433                            guint property_id,
434                            const GValue *value,
435                            GParamSpec *pspec)
436 {
437 	switch (property_id) {
438 		case PROP_CACHE_DIR:
439 			e_book_backend_set_cache_dir (
440 				E_BOOK_BACKEND (object),
441 				g_value_get_string (value));
442 			return;
443 
444 		case PROP_REGISTRY:
445 			book_backend_set_registry (
446 				E_BOOK_BACKEND (object),
447 				g_value_get_object (value));
448 			return;
449 
450 		case PROP_WRITABLE:
451 			e_book_backend_set_writable (
452 				E_BOOK_BACKEND (object),
453 				g_value_get_boolean (value));
454 			return;
455 	}
456 
457 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
458 }
459 
460 static void
book_backend_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)461 book_backend_get_property (GObject *object,
462                            guint property_id,
463                            GValue *value,
464                            GParamSpec *pspec)
465 {
466 	switch (property_id) {
467 		case PROP_CACHE_DIR:
468 			g_value_take_string (
469 				value, e_book_backend_dup_cache_dir (
470 				E_BOOK_BACKEND (object)));
471 			return;
472 
473 		case PROP_PROXY_RESOLVER:
474 			g_value_take_object (
475 				value, e_book_backend_ref_proxy_resolver (
476 				E_BOOK_BACKEND (object)));
477 			return;
478 
479 		case PROP_REGISTRY:
480 			g_value_set_object (
481 				value, e_book_backend_get_registry (
482 				E_BOOK_BACKEND (object)));
483 			return;
484 
485 		case PROP_WRITABLE:
486 			g_value_set_boolean (
487 				value, e_book_backend_get_writable (
488 				E_BOOK_BACKEND (object)));
489 			return;
490 	}
491 
492 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
493 }
494 
495 static void
book_backend_dispose(GObject * object)496 book_backend_dispose (GObject *object)
497 {
498 	EBookBackendPrivate *priv;
499 
500 	priv = E_BOOK_BACKEND (object)->priv;
501 
502 	if (priv->auth_source_changed_handler_id > 0) {
503 		g_signal_handler_disconnect (
504 			priv->authentication_source,
505 			priv->auth_source_changed_handler_id);
506 		priv->auth_source_changed_handler_id = 0;
507 	}
508 
509 	g_clear_object (&priv->registry);
510 	g_clear_object (&priv->data_book);
511 	g_clear_object (&priv->proxy_resolver);
512 	g_clear_object (&priv->authentication_source);
513 
514 	g_mutex_lock (&priv->views_mutex);
515 	g_list_free_full (priv->views, g_object_unref);
516 	priv->views = NULL;
517 	g_mutex_unlock (&priv->views_mutex);
518 
519 	g_mutex_lock (&priv->operation_lock);
520 
521 	g_hash_table_remove_all (priv->operation_ids);
522 
523 	while (!g_queue_is_empty (&priv->pending_operations))
524 		dispatch_node_free (g_queue_pop_head (&priv->pending_operations));
525 
526 	g_mutex_unlock (&priv->operation_lock);
527 
528 	g_clear_object (&priv->blocked);
529 
530 	/* Chain up to parent's dispose() method. */
531 	G_OBJECT_CLASS (e_book_backend_parent_class)->dispose (object);
532 }
533 
534 static void
book_backend_finalize(GObject * object)535 book_backend_finalize (GObject *object)
536 {
537 	EBookBackendPrivate *priv;
538 
539 	priv = E_BOOK_BACKEND (object)->priv;
540 
541 	g_mutex_clear (&priv->views_mutex);
542 	g_mutex_clear (&priv->property_lock);
543 
544 	g_free (priv->cache_dir);
545 
546 	g_warn_if_fail (g_queue_is_empty (&priv->pending_operations));
547 	g_mutex_clear (&priv->operation_lock);
548 	g_hash_table_destroy (priv->operation_ids);
549 
550 	/* Return immediately, do not wait. */
551 	g_thread_pool_free (priv->thread_pool, TRUE, FALSE);
552 
553 	/* Chain up to parent's finalize() method. */
554 	G_OBJECT_CLASS (e_book_backend_parent_class)->finalize (object);
555 }
556 
557 static void
book_backend_constructed(GObject * object)558 book_backend_constructed (GObject *object)
559 {
560 	EBookBackend *backend;
561 	EBookBackendClass *class;
562 	ESourceRegistry *registry;
563 	ESource *source;
564 	gint max_threads = -1;
565 	gboolean exclusive = FALSE;
566 
567 	/* Chain up to parent's constructed() method. */
568 	G_OBJECT_CLASS (e_book_backend_parent_class)->constructed (object);
569 
570 	backend = E_BOOK_BACKEND (object);
571 	class = E_BOOK_BACKEND_GET_CLASS (backend);
572 	g_return_if_fail (class != NULL);
573 
574 	registry = e_book_backend_get_registry (backend);
575 	source = e_backend_get_source (E_BACKEND (backend));
576 
577 	/* If the backend specifies a serial dispatch queue, create
578 	 * a thread pool with one exclusive thread.  The thread pool
579 	 * will serialize operations for us. */
580 	if (class->use_serial_dispatch_queue) {
581 		max_threads = 1;
582 		exclusive = TRUE;
583 	}
584 
585 	/* XXX If creating an exclusive thread pool, technically there's
586 	 *     a small chance of error here but we'll risk it since it's
587 	 *     only for one exclusive thread. */
588 	backend->priv->thread_pool = g_thread_pool_new (
589 		(GFunc) book_backend_dispatch_thread,
590 		NULL, max_threads, exclusive, NULL);
591 
592 	/* Initialize the "cache-dir" property. */
593 	book_backend_set_default_cache_dir (backend);
594 
595 	/* Track the proxy resolver for this backend. */
596 	backend->priv->authentication_source =
597 		e_source_registry_find_extension (
598 		registry, source, E_SOURCE_EXTENSION_AUTHENTICATION);
599 	if (backend->priv->authentication_source != NULL) {
600 		gulong handler_id;
601 
602 		handler_id = g_signal_connect_data (
603 			backend->priv->authentication_source, "changed",
604 			G_CALLBACK (book_backend_auth_source_changed_cb),
605 			e_weak_ref_new (backend),
606 			(GClosureNotify) e_weak_ref_free, 0);
607 		backend->priv->auth_source_changed_handler_id = handler_id;
608 
609 		book_backend_update_proxy_resolver (backend);
610 	}
611 }
612 
613 static void
book_backend_prepare_shutdown(EBackend * backend)614 book_backend_prepare_shutdown (EBackend *backend)
615 {
616 	GList *list, *l;
617 
618 	list = e_book_backend_list_views (E_BOOK_BACKEND (backend));
619 
620 	for (l = list; l != NULL; l = g_list_next (l)) {
621 		EDataBookView *view = l->data;
622 
623 		e_book_backend_remove_view (E_BOOK_BACKEND (backend), view);
624 	}
625 
626 	g_list_free_full (list, g_object_unref);
627 
628 	/* Chain up to parent's prepare_shutdown() method. */
629 	E_BACKEND_CLASS (e_book_backend_parent_class)->prepare_shutdown (backend);
630 }
631 
632 static gchar *
book_backend_get_backend_property(EBookBackend * backend,const gchar * prop_name)633 book_backend_get_backend_property (EBookBackend *backend,
634                                    const gchar *prop_name)
635 {
636 	gchar *prop_value = NULL;
637 
638 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
639 	g_return_val_if_fail (prop_name != NULL, NULL);
640 
641 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
642 		prop_value = g_strdup ("TRUE");
643 
644 	} else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
645 		prop_value = g_strdup ("FALSE");
646 
647 	} else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_REVISION)) {
648 		prop_value = g_strdup ("0");
649 
650 	} else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
651 		gboolean online;
652 
653 		online = e_backend_get_online (E_BACKEND (backend));
654 		prop_value = g_strdup (online ? "TRUE" : "FALSE");
655 
656 	} else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
657 		gboolean readonly;
658 
659 		readonly = e_book_backend_is_readonly (backend);
660 		prop_value = g_strdup (readonly ? "TRUE" : "FALSE");
661 
662 	} else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
663 		prop_value = e_book_backend_dup_cache_dir (backend);
664 	}
665 
666 	return prop_value;
667 }
668 
669 static void
book_backend_notify_update(EBookBackend * backend,const EContact * contact)670 book_backend_notify_update (EBookBackend *backend,
671                             const EContact *contact)
672 {
673 	GList *list, *link;
674 
675 	list = e_book_backend_list_views (backend);
676 
677 	for (link = list; link != NULL; link = g_list_next (link)) {
678 		EDataBookView *view = E_DATA_BOOK_VIEW (link->data);
679 		e_data_book_view_notify_update (view, contact);
680 	}
681 
682 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
683 }
684 
685 static void
book_backend_shutdown(EBookBackend * backend)686 book_backend_shutdown (EBookBackend *backend)
687 {
688 	ESource *source;
689 
690 	source = e_backend_get_source (E_BACKEND (backend));
691 
692 	e_source_registry_debug_print (
693 		"The %s instance for \"%s\" is shutting down.\n",
694 		G_OBJECT_TYPE_NAME (backend),
695 		e_source_get_display_name (source));
696 }
697 
698 static void
e_book_backend_class_init(EBookBackendClass * class)699 e_book_backend_class_init (EBookBackendClass *class)
700 {
701 	GObjectClass *object_class;
702 	EBackendClass *backend_class;
703 
704 	object_class = G_OBJECT_CLASS (class);
705 	object_class->set_property = book_backend_set_property;
706 	object_class->get_property = book_backend_get_property;
707 	object_class->dispose = book_backend_dispose;
708 	object_class->finalize = book_backend_finalize;
709 	object_class->constructed = book_backend_constructed;
710 
711 	backend_class = E_BACKEND_CLASS (class);
712 	backend_class->prepare_shutdown = book_backend_prepare_shutdown;
713 
714 	class->use_serial_dispatch_queue = TRUE;
715 	class->impl_get_backend_property = book_backend_get_backend_property;
716 	class->impl_notify_update = book_backend_notify_update;
717 	class->shutdown = book_backend_shutdown;
718 
719 	g_object_class_install_property (
720 		object_class,
721 		PROP_CACHE_DIR,
722 		g_param_spec_string (
723 			"cache-dir",
724 			"Cache Dir",
725 			"The backend's cache directory",
726 			NULL,
727 			G_PARAM_READWRITE |
728 			G_PARAM_EXPLICIT_NOTIFY |
729 			G_PARAM_STATIC_STRINGS));
730 
731 	g_object_class_install_property (
732 		object_class,
733 		PROP_PROXY_RESOLVER,
734 		g_param_spec_object (
735 			"proxy-resolver",
736 			"Proxy Resolver",
737 			"The proxy resolver for this backend",
738 			G_TYPE_PROXY_RESOLVER,
739 			G_PARAM_READABLE |
740 			G_PARAM_STATIC_STRINGS));
741 
742 	g_object_class_install_property (
743 		object_class,
744 		PROP_REGISTRY,
745 		g_param_spec_object (
746 			"registry",
747 			"Registry",
748 			"Data source registry",
749 			E_TYPE_SOURCE_REGISTRY,
750 			G_PARAM_READWRITE |
751 			G_PARAM_CONSTRUCT_ONLY |
752 			G_PARAM_STATIC_STRINGS));
753 
754 	g_object_class_install_property (
755 		object_class,
756 		PROP_WRITABLE,
757 		g_param_spec_boolean (
758 			"writable",
759 			"Writable",
760 			"Whether the backend will accept changes",
761 			FALSE,
762 			G_PARAM_READWRITE |
763 			G_PARAM_EXPLICIT_NOTIFY |
764 			G_PARAM_STATIC_STRINGS));
765 
766 	/**
767 	 * EBookBackend::closed:
768 	 * @backend: the #EBookBackend which emitted the signal
769 	 * @sender: the bus name that invoked the "close" method
770 	 *
771 	 * Emitted when a client destroys its #EBookClient for @backend.
772 	 *
773 	 * Since: 3.10
774 	 **/
775 	signals[CLOSED] = g_signal_new (
776 		"closed",
777 		G_OBJECT_CLASS_TYPE (object_class),
778 		G_SIGNAL_RUN_LAST,
779 		G_STRUCT_OFFSET (EBookBackendClass, closed),
780 		NULL, NULL, NULL,
781 		G_TYPE_NONE, 1,
782 		G_TYPE_STRING);
783 
784 	/**
785 	 * EBookBackend::shutdown:
786 	 * @backend: the #EBookBackend which emitted the signal
787 	 *
788 	 * Emitted when the last client destroys its #EBookClient for
789 	 * @backend.  This signals the @backend to begin final cleanup
790 	 * tasks such as synchronizing data to permanent storage.
791 	 *
792 	 * Since: 3.10
793 	 **/
794 	signals[SHUTDOWN] = g_signal_new (
795 		"shutdown",
796 		G_OBJECT_CLASS_TYPE (object_class),
797 		G_SIGNAL_RUN_LAST,
798 		G_STRUCT_OFFSET (EBookBackendClass, shutdown),
799 		NULL, NULL, NULL,
800 		G_TYPE_NONE, 0);
801 }
802 
803 static void
e_book_backend_init(EBookBackend * backend)804 e_book_backend_init (EBookBackend *backend)
805 {
806 	backend->priv = e_book_backend_get_instance_private (backend);
807 
808 	backend->priv->views = NULL;
809 	g_mutex_init (&backend->priv->views_mutex);
810 	g_mutex_init (&backend->priv->property_lock);
811 	g_mutex_init (&backend->priv->operation_lock);
812 
813 	backend->priv->operation_ids = g_hash_table_new_full (
814 		(GHashFunc) g_direct_hash,
815 		(GEqualFunc) g_direct_equal,
816 		(GDestroyNotify) NULL,
817 		(GDestroyNotify) g_object_unref);
818 }
819 
820 /**
821  * e_book_backend_get_cache_dir:
822  * @backend: an #EBookBackend
823  *
824  * Returns the cache directory path used by @backend.
825  *
826  * Returns: the cache directory path
827  *
828  * Since: 2.32
829  **/
830 const gchar *
e_book_backend_get_cache_dir(EBookBackend * backend)831 e_book_backend_get_cache_dir (EBookBackend *backend)
832 {
833 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
834 
835 	return backend->priv->cache_dir;
836 }
837 
838 /**
839  * e_book_backend_dup_cache_dir:
840  * @backend: an #EBookBackend
841  *
842  * Thread-safe variation of e_book_backend_get_cache_dir().
843  * Use this function when accessing @backend from multiple threads.
844  *
845  * The returned string should be freed with g_free() when no longer needed.
846  *
847  * Returns: a newly-allocated copy of #EBookBackend:cache-dir
848  *
849  * Since: 3.10
850  **/
851 gchar *
e_book_backend_dup_cache_dir(EBookBackend * backend)852 e_book_backend_dup_cache_dir (EBookBackend *backend)
853 {
854 	const gchar *protected;
855 	gchar *duplicate;
856 
857 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
858 
859 	g_mutex_lock (&backend->priv->property_lock);
860 
861 	protected = e_book_backend_get_cache_dir (backend);
862 	duplicate = g_strdup (protected);
863 
864 	g_mutex_unlock (&backend->priv->property_lock);
865 
866 	return duplicate;
867 }
868 
869 /**
870  * e_book_backend_set_cache_dir:
871  * @backend: an #EBookBackend
872  * @cache_dir: a local cache directory path
873  *
874  * Sets the cache directory path for use by @backend.
875  *
876  * Note that #EBookBackend is initialized with a default cache directory
877  * path which should suffice for most cases.  Backends should not override
878  * the default path without good reason.
879  *
880  * Since: 2.32
881  **/
882 void
e_book_backend_set_cache_dir(EBookBackend * backend,const gchar * cache_dir)883 e_book_backend_set_cache_dir (EBookBackend *backend,
884                               const gchar *cache_dir)
885 {
886 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
887 	g_return_if_fail (cache_dir != NULL);
888 
889 	g_mutex_lock (&backend->priv->property_lock);
890 
891 	if (g_strcmp0 (backend->priv->cache_dir, cache_dir) == 0) {
892 		g_mutex_unlock (&backend->priv->property_lock);
893 		return;
894 	}
895 
896 	g_free (backend->priv->cache_dir);
897 	backend->priv->cache_dir = g_strdup (cache_dir);
898 
899 	g_mutex_unlock (&backend->priv->property_lock);
900 
901 	g_object_notify (G_OBJECT (backend), "cache-dir");
902 }
903 
904 /**
905  * e_book_backend_ref_data_book:
906  * @backend: an #EBookBackend
907  *
908  * Returns the #EDataBook for @backend.  The #EDataBook is essentially
909  * the glue between incoming D-Bus requests and @backend's native API.
910  *
911  * An #EDataBook should be set only once after @backend is first created.
912  * If an #EDataBook has not yet been set, the function returns %NULL.
913  *
914  * The returned #EDataBook is referenced for thread-safety and must be
915  * unreferenced with g_object_unref() when finished with it.
916  *
917  * Returns: (transfer full) (nullable): an #EDataBook, or %NULL
918  *
919  * Since: 3.10
920  **/
921 EDataBook *
e_book_backend_ref_data_book(EBookBackend * backend)922 e_book_backend_ref_data_book (EBookBackend *backend)
923 {
924 	EDataBook *data_book = NULL;
925 
926 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
927 
928 	if (backend->priv->data_book != NULL)
929 		data_book = g_object_ref (backend->priv->data_book);
930 
931 	return data_book;
932 }
933 
934 /**
935  * e_book_backend_set_data_book:
936  * @backend: an #EBookBackend
937  * @data_book: an #EDataBook
938  *
939  * Sets the #EDataBook for @backend.  The #EDataBook is essentially the
940  * glue between incoming D-Bus requests and @backend's native API.
941  *
942  * An #EDataBook should be set only once after @backend is first created.
943  *
944  * Since: 3.10
945  **/
946 void
e_book_backend_set_data_book(EBookBackend * backend,EDataBook * data_book)947 e_book_backend_set_data_book (EBookBackend *backend,
948                               EDataBook *data_book)
949 {
950 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
951 	g_return_if_fail (E_IS_DATA_BOOK (data_book));
952 
953 	/* This should be set only once.  Warn if not. */
954 	g_warn_if_fail (backend->priv->data_book == NULL);
955 
956 	backend->priv->data_book = g_object_ref (data_book);
957 }
958 
959 /**
960  * e_book_backend_ref_proxy_resolver:
961  * @backend: an #EBookBackend
962  *
963  * Returns the #GProxyResolver for @backend (if applicable), as indicated
964  * by the #ESourceAuthentication:proxy-uid of @backend's #EBackend:source
965  * or one of its ancestors.
966  *
967  * The returned #GProxyResolver is referenced for thread-safety and must
968  * be unreferenced with g_object_unref() when finished with it.
969  *
970  * Returns: (transfer full) (nullable): a #GProxyResolver, or %NULL
971  *
972  * Since: 3.12
973  **/
974 GProxyResolver *
e_book_backend_ref_proxy_resolver(EBookBackend * backend)975 e_book_backend_ref_proxy_resolver (EBookBackend *backend)
976 {
977 	GProxyResolver *proxy_resolver = NULL;
978 
979 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
980 
981 	g_mutex_lock (&backend->priv->property_lock);
982 
983 	if (backend->priv->proxy_resolver != NULL)
984 		proxy_resolver = g_object_ref (backend->priv->proxy_resolver);
985 
986 	g_mutex_unlock (&backend->priv->property_lock);
987 
988 	return proxy_resolver;
989 }
990 
991 /**
992  * e_book_backend_get_registry:
993  * @backend: an #EBookBackend
994  *
995  * Returns the data source registry to which #EBackend:source belongs.
996  *
997  * Returns: (transfer none): an #ESourceRegistry
998  *
999  * Since: 3.6
1000  **/
1001 ESourceRegistry *
e_book_backend_get_registry(EBookBackend * backend)1002 e_book_backend_get_registry (EBookBackend *backend)
1003 {
1004 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
1005 
1006 	return backend->priv->registry;
1007 }
1008 
1009 /**
1010  * e_book_backend_get_writable:
1011  * @backend: an #EBookBackend
1012  *
1013  * Returns whether @backend will accept changes to its data content.
1014  *
1015  * Returns: whether @backend is writable
1016  *
1017  * Since: 3.8
1018  **/
1019 gboolean
e_book_backend_get_writable(EBookBackend * backend)1020 e_book_backend_get_writable (EBookBackend *backend)
1021 {
1022 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
1023 
1024 	return backend->priv->writable;
1025 }
1026 
1027 /**
1028  * e_book_backend_set_writable:
1029  * @backend: an #EBookBackend
1030  * @writable: whether @backend is writable
1031  *
1032  * Sets whether @backend will accept changes to its data content.
1033  *
1034  * Since: 3.8
1035  **/
1036 void
e_book_backend_set_writable(EBookBackend * backend,gboolean writable)1037 e_book_backend_set_writable (EBookBackend *backend,
1038                              gboolean writable)
1039 {
1040 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1041 
1042 	if (writable == backend->priv->writable)
1043 		return;
1044 
1045 	backend->priv->writable = writable;
1046 
1047 	g_object_notify (G_OBJECT (backend), "writable");
1048 }
1049 
1050 /**
1051  * e_book_backend_open_sync:
1052  * @backend: an #EBookBackend
1053  * @cancellable: optional #GCancellable object, or %NULL
1054  * @error: return location for a #GError, or %NULL
1055  *
1056  * "Opens" the @backend.  Opening a backend is something of an outdated
1057  * concept, but the operation is hanging around for a little while longer.
1058  * This usually involves some custom initialization logic, and testing of
1059  * remote authentication if applicable.
1060  *
1061  * If an error occurs, the function will set @error and return %FALSE.
1062  *
1063  * Returns: %TRUE on success, %FALSE on failure
1064  *
1065  * Since: 3.10
1066  **/
1067 gboolean
e_book_backend_open_sync(EBookBackend * backend,GCancellable * cancellable,GError ** error)1068 e_book_backend_open_sync (EBookBackend *backend,
1069                           GCancellable *cancellable,
1070                           GError **error)
1071 {
1072 	EAsyncClosure *closure;
1073 	GAsyncResult *result;
1074 	gboolean success;
1075 
1076 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
1077 
1078 	closure = e_async_closure_new ();
1079 
1080 	e_book_backend_open (
1081 		backend, cancellable,
1082 		e_async_closure_callback, closure);
1083 
1084 	result = e_async_closure_wait (closure);
1085 
1086 	success = e_book_backend_open_finish (backend, result, error);
1087 
1088 	e_async_closure_free (closure);
1089 
1090 	return success;
1091 }
1092 
1093 /* Helper for e_book_backend_open() */
1094 static void
book_backend_open_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1095 book_backend_open_thread (GSimpleAsyncResult *simple,
1096 			  GObject *source_object,
1097 			  GCancellable *cancellable)
1098 {
1099 	EBookBackend *backend;
1100 	EBookBackendClass *class;
1101 	EDataBook *data_book;
1102 
1103 	backend = E_BOOK_BACKEND (source_object);
1104 
1105 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1106 	g_return_if_fail (class != NULL);
1107 	g_return_if_fail (class->impl_open != NULL);
1108 
1109 	data_book = e_book_backend_ref_data_book (backend);
1110 	g_return_if_fail (data_book != NULL);
1111 
1112 	if (e_book_backend_is_opened (backend)) {
1113 		g_simple_async_result_complete_in_idle (simple);
1114 
1115 	} else {
1116 		guint32 opid;
1117 
1118 		opid = book_backend_stash_operation (backend, simple);
1119 
1120 		e_backend_ensure_online_state_updated (E_BACKEND (backend), cancellable);
1121 
1122 		class->impl_open (backend, data_book, opid, cancellable);
1123 	}
1124 
1125 	g_object_unref (data_book);
1126 }
1127 
1128 /**
1129  * e_book_backend_open:
1130  * @backend: an #EBookBackend
1131  * @cancellable: optional #GCancellable object, or %NULL
1132  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1133  * @user_data: data to pass to the callback function
1134  *
1135  * Asynchronously "opens" the @backend.  Opening a backend is something of
1136  * an outdated concept, but the operation is hanging around for a little
1137  * while longer.  This usually involves some custom initialization logic,
1138  * and testing of remote authentication if applicable.
1139  *
1140  * When the operation is finished, @callback will be called.  You can then
1141  * call e_book_backend_open_finish() to get the result of the operation.
1142  *
1143  * Since: 3.10
1144  **/
1145 void
e_book_backend_open(EBookBackend * backend,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1146 e_book_backend_open (EBookBackend *backend,
1147                      GCancellable *cancellable,
1148                      GAsyncReadyCallback callback,
1149                      gpointer user_data)
1150 {
1151 	EBookBackendClass *class;
1152 	GSimpleAsyncResult *simple;
1153 
1154 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1155 
1156 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1157 	g_return_if_fail (class != NULL);
1158 
1159 	simple = g_simple_async_result_new (
1160 		G_OBJECT (backend), callback,
1161 		user_data, e_book_backend_open);
1162 
1163 	g_simple_async_result_set_check_cancellable (simple, cancellable);
1164 
1165 	if (class->impl_open != NULL) {
1166 		book_backend_push_operation (
1167 			backend, simple, cancellable, TRUE,
1168 			book_backend_open_thread);
1169 		book_backend_dispatch_next_operation (backend);
1170 
1171 	} else {
1172 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
1173 		g_simple_async_result_complete_in_idle (simple);
1174 	}
1175 
1176 	g_object_unref (simple);
1177 }
1178 
1179 /**
1180  * e_book_backend_open_finish:
1181  * @backend: an #EBookBackend
1182  * @result: a #GAsyncResult
1183  * @error: return location for a #GError, or %NULL
1184  *
1185  * Finishes the operation started with e_book_backend_open().
1186  *
1187  * If an error occurred, the function will set @error and return %FALSE.
1188  *
1189  * Returns: %TRUE on success, %FALSE on failure
1190  *
1191  * Since: 3.10
1192  **/
1193 gboolean
e_book_backend_open_finish(EBookBackend * backend,GAsyncResult * result,GError ** error)1194 e_book_backend_open_finish (EBookBackend *backend,
1195                             GAsyncResult *result,
1196                             GError **error)
1197 {
1198 	GSimpleAsyncResult *simple;
1199 
1200 	g_return_val_if_fail (
1201 		g_simple_async_result_is_valid (
1202 		result, G_OBJECT (backend),
1203 		e_book_backend_open), FALSE);
1204 
1205 	simple = G_SIMPLE_ASYNC_RESULT (result);
1206 
1207 	book_backend_unblock_operations (backend, simple);
1208 
1209 	if (g_simple_async_result_propagate_error (simple, error))
1210 		return FALSE;
1211 
1212 	backend->priv->opened = TRUE;
1213 
1214 	return TRUE;
1215 }
1216 
1217 /**
1218  * e_book_backend_refresh_sync:
1219  * @backend: an #EBookBackend
1220  * @cancellable: optional #GCancellable object, or %NULL
1221  * @error: return location for a #GError, or %NULL
1222  *
1223  * Initiates a refresh for @backend, if the @backend supports refreshing.
1224  * The actual refresh operation completes on its own time.  This function
1225  * merely initiates the operation.
1226  *
1227  * If an error occurs while initiating the refresh, the function will set
1228  * @error and return %FALSE.  If the @backend does not support refreshing,
1229  * the function will set an %E_CLIENT_ERROR_NOT_SUPPORTED error and return
1230  * %FALSE.
1231  *
1232  * Returns: %TRUE on success, %FALSE on failure
1233  *
1234  * Since: 3.10
1235  **/
1236 gboolean
e_book_backend_refresh_sync(EBookBackend * backend,GCancellable * cancellable,GError ** error)1237 e_book_backend_refresh_sync (EBookBackend *backend,
1238                              GCancellable *cancellable,
1239                              GError **error)
1240 {
1241 	EAsyncClosure *closure;
1242 	GAsyncResult *result;
1243 	gboolean success;
1244 
1245 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
1246 
1247 	closure = e_async_closure_new ();
1248 
1249 	e_book_backend_refresh (
1250 		backend, cancellable,
1251 		e_async_closure_callback, closure);
1252 
1253 	result = e_async_closure_wait (closure);
1254 
1255 	success = e_book_backend_refresh_finish (backend, result, error);
1256 
1257 	e_async_closure_free (closure);
1258 
1259 	return success;
1260 }
1261 
1262 /* Helper for e_book_backend_refresh() */
1263 static void
book_backend_refresh_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1264 book_backend_refresh_thread (GSimpleAsyncResult *simple,
1265 			     GObject *source_object,
1266 			     GCancellable *cancellable)
1267 {
1268 	EBookBackend *backend;
1269 	EBookBackendClass *class;
1270 	EDataBook *data_book;
1271 
1272 	backend = E_BOOK_BACKEND (source_object);
1273 
1274 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1275 	g_return_if_fail (class != NULL);
1276 	g_return_if_fail (class->impl_refresh != NULL);
1277 
1278 	data_book = e_book_backend_ref_data_book (backend);
1279 	g_return_if_fail (data_book != NULL);
1280 
1281 	if (!e_book_backend_is_opened (backend)) {
1282 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_OPENED, NULL));
1283 		g_simple_async_result_complete_in_idle (simple);
1284 
1285 	} else {
1286 		guint32 opid;
1287 
1288 		opid = book_backend_stash_operation (backend, simple);
1289 
1290 		class->impl_refresh (backend, data_book, opid, cancellable);
1291 	}
1292 
1293 	g_object_unref (data_book);
1294 }
1295 
1296 /**
1297  * e_book_backend_refresh:
1298  * @backend: an #EBookBackend
1299  * @cancellable: optional #GCancellable object, or %NULL
1300  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1301  * @user_data: data to pass to the callback function
1302  *
1303  * Asynchronously initiates a refresh for @backend, if the @backend supports
1304  * refreshing.  The actual refresh operation completes on its own time.  This
1305  * function, along with e_book_backend_refresh_finish(), merely initiates the
1306  * operation.
1307  *
1308  * Once the refresh is initiated, @callback will be called.  You can then
1309  * call e_book_backend_refresh_finish() to get the result of the initiation.
1310  *
1311  * Since: 3.10
1312  **/
1313 void
e_book_backend_refresh(EBookBackend * backend,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1314 e_book_backend_refresh (EBookBackend *backend,
1315                         GCancellable *cancellable,
1316                         GAsyncReadyCallback callback,
1317                         gpointer user_data)
1318 {
1319 	EBookBackendClass *class;
1320 	GSimpleAsyncResult *simple;
1321 
1322 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1323 
1324 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1325 	g_return_if_fail (class != NULL);
1326 
1327 	simple = g_simple_async_result_new (
1328 		G_OBJECT (backend), callback,
1329 		user_data, e_book_backend_refresh);
1330 
1331 	g_simple_async_result_set_check_cancellable (simple, cancellable);
1332 
1333 	if (class->impl_refresh != NULL) {
1334 		book_backend_push_operation (
1335 			backend, simple, cancellable, FALSE,
1336 			book_backend_refresh_thread);
1337 		book_backend_dispatch_next_operation (backend);
1338 
1339 	} else {
1340 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
1341 		g_simple_async_result_complete_in_idle (simple);
1342 	}
1343 
1344 	g_object_unref (simple);
1345 }
1346 
1347 /**
1348  * e_book_backend_refresh_finish:
1349  * @backend: an #EBookBackend
1350  * @result: a #GAsyncResult
1351  * @error: return location for a #GError, or %NULL
1352  *
1353  * Finishes the refresh initiation started with e_book_backend_refresh().
1354  *
1355  * If an error occurred while initiating the refresh, the function will set
1356  * @error and return %FALSE.  If the @backend does not support refreshing,
1357  * the function will set an %E_CLIENT_ERROR_NOT_SUPPORTED error and return
1358  * %FALSE.
1359  *
1360  * Returns: %TRUE on success, %FALSE on failure
1361  *
1362  * Since: 3.10
1363  **/
1364 gboolean
e_book_backend_refresh_finish(EBookBackend * backend,GAsyncResult * result,GError ** error)1365 e_book_backend_refresh_finish (EBookBackend *backend,
1366                                GAsyncResult *result,
1367                                GError **error)
1368 {
1369 	GSimpleAsyncResult *simple;
1370 
1371 	g_return_val_if_fail (
1372 		g_simple_async_result_is_valid (
1373 		result, G_OBJECT (backend),
1374 		e_book_backend_refresh), FALSE);
1375 
1376 	simple = G_SIMPLE_ASYNC_RESULT (result);
1377 
1378 	book_backend_unblock_operations (backend, simple);
1379 
1380 	/* Assume success unless a GError is set. */
1381 	return !g_simple_async_result_propagate_error (simple, error);
1382 }
1383 
1384 /**
1385  * e_book_backend_create_contacts_sync:
1386  * @backend: an #EBookBackend
1387  * @vcards: a %NULL-terminated array of vCard strings
1388  * @opflags: bit-or of #EBookOperationFlags
1389  * @out_contacts: a #GQueue in which to deposit results
1390  * @cancellable: optional #GCancellable object, or %NULL
1391  * @error: return location for a #GError, or %NULL
1392  *
1393  * Creates one or more new contacts from @vcards, and deposits an #EContact
1394  * instance for each newly-created contact in @out_contacts.
1395  *
1396  * The returned #EContact instances are referenced for thread-safety and
1397  * must be unreferenced with g_object_unref() when finished with them.
1398  *
1399  * If an error occurs, the function will set @error and return %FALSE.
1400  *
1401  * Returns: %TRUE on success, %FALSE on failure
1402  *
1403  * Since: 3.10
1404  **/
1405 gboolean
e_book_backend_create_contacts_sync(EBookBackend * backend,const gchar * const * vcards,guint32 opflags,GQueue * out_contacts,GCancellable * cancellable,GError ** error)1406 e_book_backend_create_contacts_sync (EBookBackend *backend,
1407 				     const gchar * const *vcards,
1408 				     guint32 opflags,
1409 				     GQueue *out_contacts,
1410 				     GCancellable *cancellable,
1411 				     GError **error)
1412 {
1413 	EAsyncClosure *closure;
1414 	GAsyncResult *result;
1415 	gboolean success;
1416 
1417 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
1418 	g_return_val_if_fail (vcards != NULL, FALSE);
1419 	g_return_val_if_fail (out_contacts != NULL, FALSE);
1420 
1421 	closure = e_async_closure_new ();
1422 
1423 	e_book_backend_create_contacts (
1424 		backend, vcards, opflags, cancellable,
1425 		e_async_closure_callback, closure);
1426 
1427 	result = e_async_closure_wait (closure);
1428 
1429 	success = e_book_backend_create_contacts_finish (
1430 		backend, result, out_contacts, error);
1431 
1432 	e_async_closure_free (closure);
1433 
1434 	return success;
1435 }
1436 
1437 /* Helper for e_book_backend_create_contacts() */
1438 static void
book_backend_create_contacts_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1439 book_backend_create_contacts_thread (GSimpleAsyncResult *simple,
1440 				     GObject *source_object,
1441 				     GCancellable *cancellable)
1442 {
1443 	EBookBackend *backend;
1444 	EBookBackendClass *class;
1445 	EDataBook *data_book;
1446 	AsyncContext *async_context;
1447 
1448 	backend = E_BOOK_BACKEND (source_object);
1449 
1450 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1451 	g_return_if_fail (class != NULL);
1452 	g_return_if_fail (class->impl_create_contacts != NULL);
1453 
1454 	data_book = e_book_backend_ref_data_book (backend);
1455 	g_return_if_fail (data_book != NULL);
1456 
1457 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
1458 
1459 	if (!e_book_backend_is_opened (backend)) {
1460 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_OPENED, NULL));
1461 		g_simple_async_result_complete_in_idle (simple);
1462 
1463 	} else {
1464 		guint32 opid;
1465 
1466 		opid = book_backend_stash_operation (backend, simple);
1467 
1468 		class->impl_create_contacts (
1469 			backend, data_book, opid, cancellable, (const gchar * const *) async_context->strv, async_context->opflags);
1470 	}
1471 
1472 	g_object_unref (data_book);
1473 }
1474 
1475 /**
1476  * e_book_backend_create_contacts:
1477  * @backend: an #EBookBackend
1478  * @vcards: a %NULL-terminated array of vCard strings
1479  * @opflags: bit-or of #EBookOperationFlags
1480  * @cancellable: optional #GCancellable object, or %NULL
1481  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1482  * @user_data: data to pass to the callback function
1483  *
1484  * Asynchronously creates one or more new contacts from @vcards.
1485  *
1486  * When the operation is finished, @callback will be called.  You can then
1487  * call e_book_backend_create_contacts_finish() to get the result of the
1488  * operation.
1489  *
1490  * Since: 3.10
1491  **/
1492 void
e_book_backend_create_contacts(EBookBackend * backend,const gchar * const * vcards,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1493 e_book_backend_create_contacts (EBookBackend *backend,
1494 				const gchar * const *vcards,
1495 				guint32 opflags,
1496 				GCancellable *cancellable,
1497 				GAsyncReadyCallback callback,
1498 				gpointer user_data)
1499 {
1500 	EBookBackendClass *class;
1501 	GSimpleAsyncResult *simple;
1502 	AsyncContext *async_context;
1503 
1504 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1505 	g_return_if_fail (vcards != NULL);
1506 
1507 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1508 	g_return_if_fail (class != NULL);
1509 
1510 	async_context = g_slice_new0 (AsyncContext);
1511 	async_context->strv = g_strdupv ((gchar **) vcards);
1512 	async_context->opflags = opflags;
1513 	async_context->object_queue = &async_context->result_queue;
1514 
1515 	simple = g_simple_async_result_new (
1516 		G_OBJECT (backend), callback, user_data,
1517 		e_book_backend_create_contacts);
1518 
1519 	g_simple_async_result_set_check_cancellable (simple, cancellable);
1520 
1521 	g_simple_async_result_set_op_res_gpointer (
1522 		simple, async_context, (GDestroyNotify) async_context_free);
1523 
1524 	if (class->impl_create_contacts != NULL) {
1525 		book_backend_push_operation (
1526 			backend, simple, cancellable, FALSE,
1527 			book_backend_create_contacts_thread);
1528 		book_backend_dispatch_next_operation (backend);
1529 
1530 	} else {
1531 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
1532 		g_simple_async_result_complete_in_idle (simple);
1533 	}
1534 
1535 	g_object_unref (simple);
1536 }
1537 
1538 /**
1539  * e_book_backend_create_contacts_finish:
1540  * @backend: an #EBookBackend
1541  * @result: a #GAsyncResult
1542  * @out_contacts: a #GQueue in which to deposit results
1543  * @error: return location for a #GError, or %NULL
1544  *
1545  * Finishes the operation started with e_book_backend_create_contacts().
1546  *
1547  * An #EContact instance for each newly-created contact is deposited in
1548  * @out_contacts.  The returned #EContact instances are referenced for
1549  * thread-safety and must be unreferenced with g_object_unref() when
1550  * finished with them.
1551  *
1552  * If an error occurred, the function will set @error and return %FALSE.
1553  *
1554  * Returns: %TRUE on success, %FALSE on failure
1555  *
1556  * Since: 3.10
1557  **/
1558 gboolean
e_book_backend_create_contacts_finish(EBookBackend * backend,GAsyncResult * result,GQueue * out_contacts,GError ** error)1559 e_book_backend_create_contacts_finish (EBookBackend *backend,
1560                                        GAsyncResult *result,
1561                                        GQueue *out_contacts,
1562                                        GError **error)
1563 {
1564 	GSimpleAsyncResult *simple;
1565 	AsyncContext *async_context;
1566 
1567 	g_return_val_if_fail (
1568 		g_simple_async_result_is_valid (
1569 		result, G_OBJECT (backend),
1570 		e_book_backend_create_contacts), FALSE);
1571 	g_return_val_if_fail (out_contacts != NULL, FALSE);
1572 
1573 	simple = G_SIMPLE_ASYNC_RESULT (result);
1574 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
1575 
1576 	book_backend_unblock_operations (backend, simple);
1577 
1578 	if (g_simple_async_result_propagate_error (simple, error))
1579 		return FALSE;
1580 
1581 	while (!g_queue_is_empty (async_context->object_queue)) {
1582 		EContact *contact;
1583 
1584 		contact = g_queue_pop_head (async_context->object_queue);
1585 		g_queue_push_tail (out_contacts, g_object_ref (contact));
1586 		e_book_backend_notify_update (backend, contact);
1587 		g_object_unref (contact);
1588 	}
1589 
1590 	e_book_backend_notify_complete (backend);
1591 
1592 	return TRUE;
1593 }
1594 
1595 /**
1596  * e_book_backend_modify_contacts_sync:
1597  * @backend: an #EBookBackend
1598  * @vcards: a %NULL-terminated array of vCard strings
1599  * @opflags: bit-or of #EBookOperationFlags
1600  * @cancellable: optional #GCancellable object, or %NULL
1601  * @error: return location for a #GError, or %NULL
1602  *
1603  * Modifies one or more contacts according to @vcards.
1604  *
1605  * If an error occurs, the function will set @error and return %FALSE.
1606  *
1607  * Returns: %TRUE on success, %FALSE on failure
1608  *
1609  * Since: 3.10
1610  **/
1611 gboolean
e_book_backend_modify_contacts_sync(EBookBackend * backend,const gchar * const * vcards,guint32 opflags,GCancellable * cancellable,GError ** error)1612 e_book_backend_modify_contacts_sync (EBookBackend *backend,
1613 				     const gchar * const *vcards,
1614 				     guint32 opflags,
1615 				     GCancellable *cancellable,
1616 				     GError **error)
1617 {
1618 	EAsyncClosure *closure;
1619 	GAsyncResult *result;
1620 	gboolean success;
1621 
1622 	closure = e_async_closure_new ();
1623 
1624 	e_book_backend_modify_contacts (
1625 		backend, vcards, opflags, cancellable,
1626 		e_async_closure_callback, closure);
1627 
1628 	result = e_async_closure_wait (closure);
1629 
1630 	success = e_book_backend_modify_contacts_finish (
1631 		backend, result, error);
1632 
1633 	e_async_closure_free (closure);
1634 
1635 	return success;
1636 }
1637 
1638 /* Helper for e_book_backend_modify_contacts() */
1639 static void
book_backend_modify_contacts_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1640 book_backend_modify_contacts_thread (GSimpleAsyncResult *simple,
1641 				     GObject *source_object,
1642 				     GCancellable *cancellable)
1643 {
1644 	EBookBackend *backend;
1645 	EBookBackendClass *class;
1646 	EDataBook *data_book;
1647 	AsyncContext *async_context;
1648 
1649 	backend = E_BOOK_BACKEND (source_object);
1650 
1651 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1652 	g_return_if_fail (class != NULL);
1653 	g_return_if_fail (class->impl_modify_contacts != NULL);
1654 
1655 	data_book = e_book_backend_ref_data_book (backend);
1656 	g_return_if_fail (data_book != NULL);
1657 
1658 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
1659 
1660 	if (!e_book_backend_is_opened (backend)) {
1661 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_OPENED, NULL));
1662 		g_simple_async_result_complete_in_idle (simple);
1663 
1664 	} else {
1665 		guint32 opid;
1666 
1667 		opid = book_backend_stash_operation (backend, simple);
1668 
1669 		class->impl_modify_contacts (
1670 			backend, data_book, opid, cancellable, (const gchar * const *) async_context->strv, async_context->opflags);
1671 	}
1672 
1673 	g_object_unref (data_book);
1674 }
1675 
1676 /**
1677  * e_book_backend_modify_contacts:
1678  * @backend: an #EBookBackend
1679  * @vcards: a %NULL-terminated array of vCard strings
1680  * @opflags: bit-or of #EBookOperationFlags
1681  * @cancellable: optional #GCancellable object, or %NULL
1682  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1683  * @user_data: data to pass to the callback function
1684  *
1685  * Asynchronously modifies one or more contacts according to @vcards.
1686  *
1687  * When the operation is finished, @callback will be called.  You can then
1688  * call e_book_backend_modify_contacts_finish() to get the result of the
1689  * operation.
1690  *
1691  * Since: 3.10
1692  **/
1693 void
e_book_backend_modify_contacts(EBookBackend * backend,const gchar * const * vcards,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1694 e_book_backend_modify_contacts (EBookBackend *backend,
1695 				const gchar * const *vcards,
1696 				guint32 opflags,
1697 				GCancellable *cancellable,
1698 				GAsyncReadyCallback callback,
1699 				gpointer user_data)
1700 {
1701 	EBookBackendClass *class;
1702 	GSimpleAsyncResult *simple;
1703 	AsyncContext *async_context;
1704 
1705 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1706 	g_return_if_fail (vcards != NULL);
1707 
1708 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1709 	g_return_if_fail (class != NULL);
1710 
1711 	async_context = g_slice_new0 (AsyncContext);
1712 	async_context->strv = g_strdupv ((gchar **) vcards);
1713 	async_context->opflags = opflags;
1714 	async_context->object_queue = &async_context->result_queue;
1715 
1716 	simple = g_simple_async_result_new (
1717 		G_OBJECT (backend), callback, user_data,
1718 		e_book_backend_modify_contacts);
1719 
1720 	g_simple_async_result_set_check_cancellable (simple, cancellable);
1721 
1722 	g_simple_async_result_set_op_res_gpointer (
1723 		simple, async_context, (GDestroyNotify) async_context_free);
1724 
1725 	if (class->impl_modify_contacts != NULL) {
1726 		book_backend_push_operation (
1727 			backend, simple, cancellable, FALSE,
1728 			book_backend_modify_contacts_thread);
1729 		book_backend_dispatch_next_operation (backend);
1730 
1731 	} else {
1732 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
1733 		g_simple_async_result_complete_in_idle (simple);
1734 	}
1735 
1736 	g_object_unref (simple);
1737 }
1738 
1739 /**
1740  * e_book_backend_modify_contacts_finish:
1741  * @backend: an #EBookBackend
1742  * @result: a #GAsyncResult
1743  * @error: return location for a #GError, or %NULL
1744  *
1745  * Finishes the operation started with e_book_backend_modify_contacts().
1746  *
1747  * If an error occurred, the function will set @error and return %FALSE.
1748  *
1749  * Returns: %TRUE on success, %FALSE on failure
1750  *
1751  * Since: 3.10
1752  **/
1753 gboolean
e_book_backend_modify_contacts_finish(EBookBackend * backend,GAsyncResult * result,GError ** error)1754 e_book_backend_modify_contacts_finish (EBookBackend *backend,
1755                                        GAsyncResult *result,
1756                                        GError **error)
1757 {
1758 	GSimpleAsyncResult *simple;
1759 	AsyncContext *async_context;
1760 
1761 	g_return_val_if_fail (
1762 		g_simple_async_result_is_valid (
1763 		result, G_OBJECT (backend),
1764 		e_book_backend_modify_contacts), FALSE);
1765 
1766 	simple = G_SIMPLE_ASYNC_RESULT (result);
1767 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
1768 
1769 	book_backend_unblock_operations (backend, simple);
1770 
1771 	if (g_simple_async_result_propagate_error (simple, error))
1772 		return FALSE;
1773 
1774 	while (!g_queue_is_empty (async_context->object_queue)) {
1775 		EContact *contact;
1776 
1777 		contact = g_queue_pop_head (async_context->object_queue);
1778 		e_book_backend_notify_update (backend, contact);
1779 		g_object_unref (contact);
1780 	}
1781 
1782 	e_book_backend_notify_complete (backend);
1783 
1784 	return TRUE;
1785 }
1786 
1787 /**
1788  * e_book_backend_remove_contacts_sync:
1789  * @backend: an #EBookBackend
1790  * @uids: a %NULL-terminated array of contact ID strings
1791  * @opflags: bit-or of #EBookOperationFlags
1792  * @cancellable: optional #GCancellable object, or %NULL
1793  * @error: return location for a #GError, or %NULL
1794  *
1795  * Removes one or more contacts according to @uids.
1796  *
1797  * If an error occurs, the function will set @error and return %FALSE.
1798  *
1799  * Returns: %TRUE on success, %FALSE on failure
1800  *
1801  * Since: 3.10
1802  **/
1803 gboolean
e_book_backend_remove_contacts_sync(EBookBackend * backend,const gchar * const * uids,guint32 opflags,GCancellable * cancellable,GError ** error)1804 e_book_backend_remove_contacts_sync (EBookBackend *backend,
1805 				     const gchar * const *uids,
1806 				     guint32 opflags,
1807 				     GCancellable *cancellable,
1808 				     GError **error)
1809 {
1810 	EAsyncClosure *closure;
1811 	GAsyncResult *result;
1812 	gboolean success;
1813 
1814 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
1815 	g_return_val_if_fail (uids != NULL, FALSE);
1816 
1817 	closure = e_async_closure_new ();
1818 
1819 	e_book_backend_remove_contacts (
1820 		backend, uids, opflags, cancellable,
1821 		e_async_closure_callback, closure);
1822 
1823 	result = e_async_closure_wait (closure);
1824 
1825 	success = e_book_backend_remove_contacts_finish (
1826 		backend, result, error);
1827 
1828 	e_async_closure_free (closure);
1829 
1830 	return success;
1831 }
1832 
1833 /* Helper for e_book_backend_remove_contacts() */
1834 static void
book_backend_remove_contacts_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)1835 book_backend_remove_contacts_thread (GSimpleAsyncResult *simple,
1836 				     GObject *source_object,
1837 				     GCancellable *cancellable)
1838 {
1839 	EBookBackend *backend;
1840 	EBookBackendClass *class;
1841 	EDataBook *data_book;
1842 	AsyncContext *async_context;
1843 
1844 	backend = E_BOOK_BACKEND (source_object);
1845 
1846 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1847 	g_return_if_fail (class != NULL);
1848 	g_return_if_fail (class->impl_remove_contacts != NULL);
1849 
1850 	data_book = e_book_backend_ref_data_book (backend);
1851 	g_return_if_fail (data_book != NULL);
1852 
1853 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
1854 
1855 	if (!e_book_backend_is_opened (backend)) {
1856 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_OPENED, NULL));
1857 		g_simple_async_result_complete_in_idle (simple);
1858 
1859 	} else {
1860 		guint32 opid;
1861 
1862 		opid = book_backend_stash_operation (backend, simple);
1863 
1864 		class->impl_remove_contacts (
1865 			backend, data_book, opid, cancellable, (const gchar * const *) async_context->strv, async_context->opflags);
1866 	}
1867 
1868 	g_object_unref (data_book);
1869 }
1870 
1871 /**
1872  * e_book_backend_remove_contacts:
1873  * @backend: an #EBookBackend
1874  * @uids: (array zero-terminated=1): a %NULL-terminated array of contact ID strings
1875  * @opflags: bit-or of #EBookOperationFlags
1876  * @cancellable: optional #GCancellable object, or %NULL
1877  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1878  * @user_data: data to pass to the callback function
1879  *
1880  * Asynchronously removes one or more contacts according to @uids.
1881  *
1882  * When the operation is finished, @callback will be called.  You can then
1883  * call e_book_backend_remove_contacts_finish() to get the result of the
1884  * operation.
1885  *
1886  * Since: 3.10
1887  **/
1888 void
e_book_backend_remove_contacts(EBookBackend * backend,const gchar * const * uids,guint32 opflags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1889 e_book_backend_remove_contacts (EBookBackend *backend,
1890 				const gchar * const *uids,
1891 				guint32 opflags,
1892 				GCancellable *cancellable,
1893 				GAsyncReadyCallback callback,
1894 				gpointer user_data)
1895 {
1896 	EBookBackendClass *class;
1897 	GSimpleAsyncResult *simple;
1898 	AsyncContext *async_context;
1899 
1900 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1901 	g_return_if_fail (uids != NULL);
1902 
1903 	class = E_BOOK_BACKEND_GET_CLASS (backend);
1904 	g_return_if_fail (class != NULL);
1905 
1906 	async_context = g_slice_new0 (AsyncContext);
1907 	async_context->strv = g_strdupv ((gchar **) uids);
1908 	async_context->opflags = opflags;
1909 	async_context->string_queue = &async_context->result_queue;
1910 
1911 	simple = g_simple_async_result_new (
1912 		G_OBJECT (backend), callback, user_data,
1913 		e_book_backend_remove_contacts);
1914 
1915 	g_simple_async_result_set_check_cancellable (simple, cancellable);
1916 
1917 	g_simple_async_result_set_op_res_gpointer (
1918 		simple, async_context, (GDestroyNotify) async_context_free);
1919 
1920 	if (class->impl_remove_contacts != NULL) {
1921 		book_backend_push_operation (
1922 			backend, simple, cancellable, FALSE,
1923 			book_backend_remove_contacts_thread);
1924 		book_backend_dispatch_next_operation (backend);
1925 
1926 	} else {
1927 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
1928 		g_simple_async_result_complete_in_idle (simple);
1929 	}
1930 
1931 	g_object_unref (simple);
1932 }
1933 
1934 /**
1935  * e_book_backend_remove_contacts_finish:
1936  * @backend: an #EBookBackend
1937  * @result: a #GAsyncResult
1938  * @error: return location for a #GError, or %NULL
1939  *
1940  * Finishes the operation started with e_book_backend_remove_contacts().
1941  *
1942  * If an error occurred, the function will set @error and return %FALSE.
1943  *
1944  * Returns: %TRUE on success, %FALSE on failure
1945  *
1946  * Since: 3.10
1947  **/
1948 gboolean
e_book_backend_remove_contacts_finish(EBookBackend * backend,GAsyncResult * result,GError ** error)1949 e_book_backend_remove_contacts_finish (EBookBackend *backend,
1950                                        GAsyncResult *result,
1951                                        GError **error)
1952 {
1953 	GSimpleAsyncResult *simple;
1954 	AsyncContext *async_context;
1955 	guint ii;
1956 
1957 	g_return_val_if_fail (
1958 		g_simple_async_result_is_valid (
1959 		result, G_OBJECT (backend),
1960 		e_book_backend_remove_contacts), FALSE);
1961 
1962 	simple = G_SIMPLE_ASYNC_RESULT (result);
1963 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
1964 
1965 	book_backend_unblock_operations (backend, simple);
1966 
1967 	if (g_simple_async_result_propagate_error (simple, error))
1968 		return FALSE;
1969 
1970 	for (ii = 0; async_context->strv[ii] != NULL; ii++) {
1971 		const gchar *uid = async_context->strv[ii];
1972 		e_book_backend_notify_remove (backend, uid);
1973 	}
1974 
1975 	e_book_backend_notify_complete (backend);
1976 
1977 	return TRUE;
1978 }
1979 
1980 /**
1981  * e_book_backend_get_contact_sync:
1982  * @backend: an #EBookBackend
1983  * @uid: a contact ID
1984  * @cancellable: optional #GCancellable object, or %NULL
1985  * @error: return location for a #GError, or %NULL
1986  *
1987  * Obtains an #EContact for @uid.
1988  *
1989  * The returned #EContact is referenced for thread-safety and must be
1990  * unreferenced with g_object_unref() when finished with it.
1991  *
1992  * If an error occurs, the function will set @error and return %NULL.
1993  *
1994  * Returns: (transfer full): an #EContact, or %NULL
1995  *
1996  * Since: 3.10
1997  **/
1998 EContact *
e_book_backend_get_contact_sync(EBookBackend * backend,const gchar * uid,GCancellable * cancellable,GError ** error)1999 e_book_backend_get_contact_sync (EBookBackend *backend,
2000                                  const gchar *uid,
2001                                  GCancellable *cancellable,
2002                                  GError **error)
2003 {
2004 	EAsyncClosure *closure;
2005 	GAsyncResult *result;
2006 	EContact *contact;
2007 
2008 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
2009 	g_return_val_if_fail (uid != NULL, NULL);
2010 
2011 	closure = e_async_closure_new ();
2012 
2013 	e_book_backend_get_contact (
2014 		backend, uid, cancellable,
2015 		e_async_closure_callback, closure);
2016 
2017 	result = e_async_closure_wait (closure);
2018 
2019 	contact = e_book_backend_get_contact_finish (
2020 		backend, result, error);
2021 
2022 	e_async_closure_free (closure);
2023 
2024 	return contact;
2025 }
2026 
2027 /* Helper for e_book_backend_get_contact() */
2028 static void
book_backend_get_contact_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2029 book_backend_get_contact_thread (GSimpleAsyncResult *simple,
2030 				 GObject *source_object,
2031 				 GCancellable *cancellable)
2032 {
2033 	EBookBackend *backend;
2034 	EBookBackendClass *class;
2035 	EDataBook *data_book;
2036 	AsyncContext *async_context;
2037 
2038 	backend = E_BOOK_BACKEND (source_object);
2039 
2040 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2041 	g_return_if_fail (class != NULL);
2042 	g_return_if_fail (class->impl_get_contact != NULL);
2043 
2044 	data_book = e_book_backend_ref_data_book (backend);
2045 	g_return_if_fail (data_book != NULL);
2046 
2047 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
2048 
2049 	if (!e_book_backend_is_opened (backend)) {
2050 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_OPENED, NULL));
2051 		g_simple_async_result_complete_in_idle (simple);
2052 
2053 	} else {
2054 		guint32 opid;
2055 
2056 		opid = book_backend_stash_operation (backend, simple);
2057 
2058 		class->impl_get_contact (
2059 			backend, data_book, opid, cancellable,
2060 			async_context->uid);
2061 	}
2062 
2063 	g_object_unref (data_book);
2064 }
2065 
2066 /**
2067  * e_book_backend_get_contact:
2068  * @backend: an #EBookBackend
2069  * @uid: a contact ID
2070  * @cancellable: optional #GCancellable object, or %NULL
2071  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2072  * @user_data: data to pass to the callback function
2073  *
2074  * Asynchronously obtains an #EContact for @uid.
2075  *
2076  * When the operation is finished, @callback will be called.  You can
2077  * then call e_book_backend_get_contact_finish() to get the result of the
2078  * operation.
2079  *
2080  * Since: 3.10
2081  **/
2082 void
e_book_backend_get_contact(EBookBackend * backend,const gchar * uid,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2083 e_book_backend_get_contact (EBookBackend *backend,
2084                             const gchar *uid,
2085                             GCancellable *cancellable,
2086                             GAsyncReadyCallback callback,
2087                             gpointer user_data)
2088 {
2089 	EBookBackendClass *class;
2090 	GSimpleAsyncResult *simple;
2091 	AsyncContext *async_context;
2092 
2093 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2094 	g_return_if_fail (uid != NULL);
2095 
2096 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2097 	g_return_if_fail (class != NULL);
2098 
2099 	async_context = g_slice_new0 (AsyncContext);
2100 	async_context->uid = g_strdup (uid);
2101 	async_context->object_queue = &async_context->result_queue;
2102 
2103 	simple = g_simple_async_result_new (
2104 		G_OBJECT (backend), callback, user_data,
2105 		e_book_backend_get_contact);
2106 
2107 	g_simple_async_result_set_check_cancellable (simple, cancellable);
2108 
2109 	g_simple_async_result_set_op_res_gpointer (
2110 		simple, async_context, (GDestroyNotify) async_context_free);
2111 
2112 	if (class->impl_get_contact != NULL) {
2113 		book_backend_push_operation (
2114 			backend, simple, cancellable, FALSE,
2115 			book_backend_get_contact_thread);
2116 		book_backend_dispatch_next_operation (backend);
2117 
2118 	} else {
2119 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
2120 		g_simple_async_result_complete_in_idle (simple);
2121 	}
2122 
2123 	g_object_unref (simple);
2124 }
2125 
2126 /**
2127  * e_book_backend_get_contact_finish:
2128  * @backend: an #EBookBackend
2129  * @result: a #GAsyncResult
2130  * @error: return location for a #GError, or %NULL
2131  *
2132  * Finishes the operation started with e_book_backend_get_contact_finish().
2133  *
2134  * The returned #EContact is referenced for thread-safety and must be
2135  * unreferenced with g_object_unref() when finished with it.
2136  *
2137  * If an error occurred, the function will set @error and return %NULL.
2138  *
2139  * Returns: (transfer full): an #EContact, or %NULL
2140  *
2141  * Since: 3.10
2142  **/
2143 EContact *
e_book_backend_get_contact_finish(EBookBackend * backend,GAsyncResult * result,GError ** error)2144 e_book_backend_get_contact_finish (EBookBackend *backend,
2145                                    GAsyncResult *result,
2146                                    GError **error)
2147 {
2148 	GSimpleAsyncResult *simple;
2149 	GQueue *queue;
2150 	AsyncContext *async_context;
2151 
2152 	g_return_val_if_fail (
2153 		g_simple_async_result_is_valid (
2154 		result, G_OBJECT (backend),
2155 		e_book_backend_get_contact), NULL);
2156 
2157 	simple = G_SIMPLE_ASYNC_RESULT (result);
2158 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
2159 
2160 	book_backend_unblock_operations (backend, simple);
2161 
2162 	if (g_simple_async_result_propagate_error (simple, error))
2163 		return NULL;
2164 
2165 	/* XXX e_data_book_respond_get_contact() stuffs the
2166 	 *     resulting EContact into the object queue. */
2167 	queue = async_context->object_queue;
2168 	g_warn_if_fail (async_context->contact == NULL);
2169 	async_context->contact = g_queue_pop_head (queue);
2170 	g_warn_if_fail (g_queue_is_empty (queue));
2171 
2172 	g_return_val_if_fail (E_IS_CONTACT (async_context->contact), NULL);
2173 
2174 	return g_object_ref (async_context->contact);
2175 }
2176 
2177 /**
2178  * e_book_backend_get_contact_list_sync:
2179  * @backend: an #EBookBackend
2180  * @query: a search query in S-expression format
2181  * @out_contacts: a #GQueue in which to deposit results
2182  * @cancellable: optional #GCancellable object, or %NULL
2183  * @error: return location for a #GError, or %NULL
2184  *
2185  * Obtains a set of #EContact instances which satisfy the criteria specified
2186  * in @query, and deposits them in @out_contacts.
2187  *
2188  * The returned #EContact instances are referenced for thread-safety and
2189  * must be unreferenced with g_object_unref() when finished with them.
2190  *
2191  * If an error occurs, the function will set @error and return %FALSE.
2192  * Note that an empty result set does not necessarily imply an error.
2193  *
2194  * Returns: %TRUE on success, %FALSE on failure
2195  *
2196  * Since: 3.10
2197  **/
2198 gboolean
e_book_backend_get_contact_list_sync(EBookBackend * backend,const gchar * query,GQueue * out_contacts,GCancellable * cancellable,GError ** error)2199 e_book_backend_get_contact_list_sync (EBookBackend *backend,
2200                                       const gchar *query,
2201                                       GQueue *out_contacts,
2202                                       GCancellable *cancellable,
2203                                       GError **error)
2204 {
2205 	EAsyncClosure *closure;
2206 	GAsyncResult *result;
2207 	gboolean success;
2208 
2209 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
2210 	g_return_val_if_fail (query != NULL, FALSE);
2211 	g_return_val_if_fail (out_contacts != NULL, FALSE);
2212 
2213 	closure = e_async_closure_new ();
2214 
2215 	e_book_backend_get_contact_list (
2216 		backend, query, cancellable,
2217 		e_async_closure_callback, closure);
2218 
2219 	result = e_async_closure_wait (closure);
2220 
2221 	success = e_book_backend_get_contact_list_finish (
2222 		backend, result, out_contacts, error);
2223 
2224 	e_async_closure_free (closure);
2225 
2226 	return success;
2227 }
2228 
2229 /* Helper for e_book_backend_get_contact_list() */
2230 static void
book_backend_get_contact_list_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2231 book_backend_get_contact_list_thread (GSimpleAsyncResult *simple,
2232 				      GObject *source_object,
2233 				      GCancellable *cancellable)
2234 {
2235 	EBookBackend *backend;
2236 	EBookBackendClass *class;
2237 	EDataBook *data_book;
2238 	AsyncContext *async_context;
2239 
2240 	backend = E_BOOK_BACKEND (source_object);
2241 
2242 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2243 	g_return_if_fail (class != NULL);
2244 	g_return_if_fail (class->impl_get_contact_list != NULL);
2245 
2246 	data_book = e_book_backend_ref_data_book (backend);
2247 	g_return_if_fail (data_book != NULL);
2248 
2249 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
2250 
2251 	if (!e_book_backend_is_opened (backend)) {
2252 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_OPENED, NULL));
2253 		g_simple_async_result_complete_in_idle (simple);
2254 
2255 	} else {
2256 		guint32 opid;
2257 
2258 		opid = book_backend_stash_operation (backend, simple);
2259 
2260 		class->impl_get_contact_list (
2261 			backend, data_book, opid, cancellable,
2262 			async_context->query);
2263 	}
2264 
2265 	g_object_unref (data_book);
2266 }
2267 
2268 /**
2269  * e_book_backend_get_contact_list:
2270  * @backend: an #EBookBackend
2271  * @query: a search query in S-expression format
2272  * @cancellable: optional #GCancellable object, or %NULL
2273  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2274  * @user_data: data to pass to the callback function
2275  *
2276  * Asynchronously obtains a set of #EContact instances which satisfy the
2277  * criteria specified in @query.
2278  *
2279  * When the operation is finished, @callback will be called.  You can then
2280  * call e_book_backend_get_contact_list_finish() to get the result of the
2281  * operation.
2282  *
2283  * Since: 3.10
2284  **/
2285 void
e_book_backend_get_contact_list(EBookBackend * backend,const gchar * query,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2286 e_book_backend_get_contact_list (EBookBackend *backend,
2287                                  const gchar *query,
2288                                  GCancellable *cancellable,
2289                                  GAsyncReadyCallback callback,
2290                                  gpointer user_data)
2291 {
2292 	EBookBackendClass *class;
2293 	GSimpleAsyncResult *simple;
2294 	AsyncContext *async_context;
2295 
2296 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2297 	g_return_if_fail (query != NULL);
2298 
2299 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2300 	g_return_if_fail (class != NULL);
2301 
2302 	async_context = g_slice_new0 (AsyncContext);
2303 	async_context->query = g_strdup (query);
2304 	async_context->object_queue = &async_context->result_queue;
2305 
2306 	simple = g_simple_async_result_new (
2307 		G_OBJECT (backend), callback, user_data,
2308 		e_book_backend_get_contact_list);
2309 
2310 	g_simple_async_result_set_check_cancellable (simple, cancellable);
2311 
2312 	g_simple_async_result_set_op_res_gpointer (
2313 		simple, async_context, (GDestroyNotify) async_context_free);
2314 
2315 	if (class->impl_get_contact_list != NULL) {
2316 		book_backend_push_operation (
2317 			backend, simple, cancellable, FALSE,
2318 			book_backend_get_contact_list_thread);
2319 		book_backend_dispatch_next_operation (backend);
2320 
2321 	} else {
2322 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
2323 		g_simple_async_result_complete_in_idle (simple);
2324 	}
2325 
2326 	g_object_unref (simple);
2327 }
2328 
2329 /**
2330  * e_book_backend_get_contact_list_finish:
2331  * @backend: an #EBookBackend
2332  * @result: a #GAsyncResult
2333  * @out_contacts: a #GQueue in which to deposit results
2334  * @error: return location for a #GError, or %NULL
2335  *
2336  * Finishes the operation started with e_book_backend_get_contact_list().
2337  *
2338  * The matching #EContact instances are deposited in @out_contacts.  The
2339  * returned #EContact instances are referenced for thread-safety and must
2340  * be unreferenced with g_object_unref() when finished with them.
2341  *
2342  * If an error occurred, the function will set @error and return %FALSE.
2343  * Note that an empty result set does not necessarily imply an error.
2344  *
2345  * Returns: %TRUE on success, %FALSE on failure
2346  *
2347  * Since: 3.10
2348  **/
2349 gboolean
e_book_backend_get_contact_list_finish(EBookBackend * backend,GAsyncResult * result,GQueue * out_contacts,GError ** error)2350 e_book_backend_get_contact_list_finish (EBookBackend *backend,
2351                                         GAsyncResult *result,
2352                                         GQueue *out_contacts,
2353                                         GError **error)
2354 {
2355 	GSimpleAsyncResult *simple;
2356 	AsyncContext *async_context;
2357 
2358 	g_return_val_if_fail (
2359 		g_simple_async_result_is_valid (
2360 		result, G_OBJECT (backend),
2361 		e_book_backend_get_contact_list), FALSE);
2362 	g_return_val_if_fail (out_contacts != NULL, FALSE);
2363 
2364 	simple = G_SIMPLE_ASYNC_RESULT (result);
2365 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
2366 
2367 	book_backend_unblock_operations (backend, simple);
2368 
2369 	if (g_simple_async_result_propagate_error (simple, error))
2370 		return FALSE;
2371 
2372 	e_queue_transfer (async_context->object_queue, out_contacts);
2373 
2374 	return TRUE;
2375 }
2376 
2377 /**
2378  * e_book_backend_get_contact_list_uids_sync:
2379  * @backend: an #EBookBackend
2380  * @query: a search query in S-expression format
2381  * @out_uids: a #GQueue in which to deposit results
2382  * @cancellable: optional #GCancellable object, or %NULL
2383  * @error: return location for a #GError, or %NULL
2384  *
2385  * Obtains a set of ID strings for contacts which satisfy the criteria
2386  * specified in @query, and deposits them in @out_uids.
2387  *
2388  * The returned ID strings must be freed with g_free() with finished
2389  * with them.
2390  *
2391  * If an error occurs, the function will set @error and return %FALSE.
2392  * Note that an empty result set does not necessarily imply an error.
2393  *
2394  * Returns: %TRUE on success, %FALSE on failure
2395  *
2396  * Since: 3.10
2397  **/
2398 gboolean
e_book_backend_get_contact_list_uids_sync(EBookBackend * backend,const gchar * query,GQueue * out_uids,GCancellable * cancellable,GError ** error)2399 e_book_backend_get_contact_list_uids_sync (EBookBackend *backend,
2400                                            const gchar *query,
2401                                            GQueue *out_uids,
2402                                            GCancellable *cancellable,
2403                                            GError **error)
2404 {
2405 	EAsyncClosure *closure;
2406 	GAsyncResult *result;
2407 	gboolean success;
2408 
2409 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
2410 	g_return_val_if_fail (query != NULL, FALSE);
2411 	g_return_val_if_fail (out_uids != NULL, FALSE);
2412 
2413 	closure = e_async_closure_new ();
2414 
2415 	e_book_backend_get_contact_list_uids (
2416 		backend, query, cancellable,
2417 		e_async_closure_callback, closure);
2418 
2419 	result = e_async_closure_wait (closure);
2420 
2421 	success = e_book_backend_get_contact_list_uids_finish (
2422 		backend, result, out_uids, error);
2423 
2424 	e_async_closure_free (closure);
2425 
2426 	return success;
2427 }
2428 
2429 /* Helper for e_book_backend_get_contact_list_uids() */
2430 static void
book_backend_get_contact_list_uids_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)2431 book_backend_get_contact_list_uids_thread (GSimpleAsyncResult *simple,
2432 					   GObject *source_object,
2433 					   GCancellable *cancellable)
2434 {
2435 	EBookBackend *backend;
2436 	EBookBackendClass *class;
2437 	EDataBook *data_book;
2438 	AsyncContext *async_context;
2439 
2440 	backend = E_BOOK_BACKEND (source_object);
2441 
2442 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2443 	g_return_if_fail (class != NULL);
2444 	g_return_if_fail (class->impl_get_contact_list_uids != NULL);
2445 
2446 	data_book = e_book_backend_ref_data_book (backend);
2447 	g_return_if_fail (data_book != NULL);
2448 
2449 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
2450 
2451 	if (!e_book_backend_is_opened (backend)) {
2452 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_OPENED, NULL));
2453 		g_simple_async_result_complete_in_idle (simple);
2454 
2455 	} else {
2456 		guint32 opid;
2457 
2458 		opid = book_backend_stash_operation (backend, simple);
2459 
2460 		class->impl_get_contact_list_uids (
2461 			backend, data_book, opid, cancellable,
2462 			async_context->query);
2463 	}
2464 
2465 	g_object_unref (data_book);
2466 }
2467 
2468 /**
2469  * e_book_backend_get_contact_list_uids:
2470  * @backend: an #EBookBackend
2471  * @query: a search query in S-expression format
2472  * @cancellable: optional #GCancellable object, or %NULL
2473  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2474  * @user_data: data to pass to the callback function
2475  *
2476  * Asynchronously obtains a set of ID strings for contacts which satisfy
2477  * the criteria specified in @query.
2478  *
2479  * When the operation is finished, @callback will be called.  You can then
2480  * call e_book_backend_get_contact_list_uids_finish() to get the result of
2481  * the operation.
2482  *
2483  * Since: 3.10
2484  **/
2485 void
e_book_backend_get_contact_list_uids(EBookBackend * backend,const gchar * query,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2486 e_book_backend_get_contact_list_uids (EBookBackend *backend,
2487                                       const gchar *query,
2488                                       GCancellable *cancellable,
2489                                       GAsyncReadyCallback callback,
2490                                       gpointer user_data)
2491 {
2492 	EBookBackendClass *class;
2493 	GSimpleAsyncResult *simple;
2494 	AsyncContext *async_context;
2495 
2496 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2497 	g_return_if_fail (query != NULL);
2498 
2499 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2500 	g_return_if_fail (class != NULL);
2501 
2502 	async_context = g_slice_new0 (AsyncContext);
2503 	async_context->query = g_strdup (query);
2504 	async_context->string_queue = &async_context->result_queue;
2505 
2506 	simple = g_simple_async_result_new (
2507 		G_OBJECT (backend), callback, user_data,
2508 		e_book_backend_get_contact_list_uids);
2509 
2510 	g_simple_async_result_set_check_cancellable (simple, cancellable);
2511 
2512 	g_simple_async_result_set_op_res_gpointer (
2513 		simple, async_context, (GDestroyNotify) async_context_free);
2514 
2515 	if (class->impl_get_contact_list_uids != NULL) {
2516 		book_backend_push_operation (
2517 			backend, simple, cancellable, FALSE,
2518 			book_backend_get_contact_list_uids_thread);
2519 		book_backend_dispatch_next_operation (backend);
2520 
2521 	} else {
2522 		g_simple_async_result_take_error (simple, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
2523 		g_simple_async_result_complete_in_idle (simple);
2524 	}
2525 
2526 	g_object_unref (simple);
2527 }
2528 
2529 /**
2530  * e_book_backend_get_contact_list_uids_finish:
2531  * @backend: an #EBookBackend
2532  * @result: a #GAsyncResult
2533  * @out_uids: a #GQueue in which to deposit results
2534  * @error: return location for a #GError, or %NULL
2535  *
2536  * Finishes the operation started with
2537  * e_book_backend_get_contact_list_uids_finish().
2538  *
2539  * ID strings for the matching contacts are deposited in @out_uids, and
2540  * must be freed with g_free() when finished with them.
2541  *
2542  * If an error occurs, the function will set @error and return %FALSE.
2543  * Note that an empty result set does not necessarily imply an error.
2544  *
2545  * Returns: %TRUE on success, %FALSE on failure
2546  *
2547  * Since: 3.10
2548  **/
2549 gboolean
e_book_backend_get_contact_list_uids_finish(EBookBackend * backend,GAsyncResult * result,GQueue * out_uids,GError ** error)2550 e_book_backend_get_contact_list_uids_finish (EBookBackend *backend,
2551                                              GAsyncResult *result,
2552                                              GQueue *out_uids,
2553                                              GError **error)
2554 {
2555 	GSimpleAsyncResult *simple;
2556 	AsyncContext *async_context;
2557 
2558 	g_return_val_if_fail (
2559 		g_simple_async_result_is_valid (
2560 		result, G_OBJECT (backend),
2561 		e_book_backend_get_contact_list_uids), FALSE);
2562 	g_return_val_if_fail (out_uids != NULL, FALSE);
2563 
2564 	simple = G_SIMPLE_ASYNC_RESULT (result);
2565 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
2566 
2567 	book_backend_unblock_operations (backend, simple);
2568 
2569 	if (g_simple_async_result_propagate_error (simple, error))
2570 		return FALSE;
2571 
2572 	e_queue_transfer (async_context->string_queue, out_uids);
2573 
2574 	return TRUE;
2575 }
2576 
2577 /**
2578  * e_book_backend_start_view:
2579  * @backend: an #EBookBackend
2580  * @view: the #EDataBookView to start
2581  *
2582  * Starts running the query specified by @view, emitting signals for
2583  * matching contacts.
2584  **/
2585 void
e_book_backend_start_view(EBookBackend * backend,EDataBookView * view)2586 e_book_backend_start_view (EBookBackend *backend,
2587                            EDataBookView *view)
2588 {
2589 	EBookBackendClass *class;
2590 
2591 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2592 	g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
2593 
2594 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2595 	g_return_if_fail (class != NULL);
2596 	g_return_if_fail (class->impl_start_view);
2597 
2598 	class->impl_start_view (backend, view);
2599 }
2600 
2601 /**
2602  * e_book_backend_stop_view:
2603  * @backend: an #EBookBackend
2604  * @view: the #EDataBookView to stop
2605  *
2606  * Stops running the query specified by @view, emitting no more signals.
2607  **/
2608 void
e_book_backend_stop_view(EBookBackend * backend,EDataBookView * view)2609 e_book_backend_stop_view (EBookBackend *backend,
2610                           EDataBookView *view)
2611 {
2612 	EBookBackendClass *class;
2613 
2614 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2615 	g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
2616 
2617 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2618 	g_return_if_fail (class != NULL);
2619 	g_return_if_fail (class->impl_stop_view != NULL);
2620 
2621 	class->impl_stop_view (backend, view);
2622 }
2623 
2624 /**
2625  * e_book_backend_add_view:
2626  * @backend: an #EBookBackend
2627  * @view: an #EDataBookView
2628  *
2629  * Adds @view to @backend for querying.
2630  **/
2631 void
e_book_backend_add_view(EBookBackend * backend,EDataBookView * view)2632 e_book_backend_add_view (EBookBackend *backend,
2633                          EDataBookView *view)
2634 {
2635 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2636 
2637 	g_mutex_lock (&backend->priv->views_mutex);
2638 
2639 	g_object_ref (view);
2640 	backend->priv->views = g_list_append (backend->priv->views, view);
2641 
2642 	g_mutex_unlock (&backend->priv->views_mutex);
2643 }
2644 
2645 /**
2646  * e_book_backend_remove_view:
2647  * @backend: an #EBookBackend
2648  * @view: an #EDataBookView
2649  *
2650  * Removes @view from @backend.
2651  **/
2652 void
e_book_backend_remove_view(EBookBackend * backend,EDataBookView * view)2653 e_book_backend_remove_view (EBookBackend *backend,
2654                             EDataBookView *view)
2655 {
2656 	GList *list, *link;
2657 
2658 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2659 
2660 	/* In case the view holds the last reference to backend */
2661 	g_object_ref (backend);
2662 
2663 	g_mutex_lock (&backend->priv->views_mutex);
2664 
2665 	list = backend->priv->views;
2666 
2667 	link = g_list_find (list, view);
2668 	if (link != NULL) {
2669 		g_object_unref (view);
2670 		list = g_list_delete_link (list, link);
2671 	}
2672 
2673 	backend->priv->views = list;
2674 
2675 	g_mutex_unlock (&backend->priv->views_mutex);
2676 
2677 	g_object_unref (backend);
2678 }
2679 
2680 /**
2681  * e_book_backend_list_views:
2682  * @backend: an #EBookBackend
2683  *
2684  * Returns a list of #EDataBookView instances added with
2685  * e_book_backend_add_view().
2686  *
2687  * The views returned in the list are referenced for thread-safety.
2688  * They must each be unreferenced with g_object_unref() when finished
2689  * with them.  Free the returned list itself with g_list_free().
2690  *
2691  * An easy way to free the list properly in one step is as follows:
2692  *
2693  * |[
2694  *   g_list_free_full (list, g_object_unref);
2695  * ]|
2696  *
2697  * Returns: (transfer full) (element-type EDataBookView): a list of book views
2698  *
2699  * Since: 3.8
2700  **/
2701 GList *
e_book_backend_list_views(EBookBackend * backend)2702 e_book_backend_list_views (EBookBackend *backend)
2703 {
2704 	GList *list;
2705 
2706 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
2707 
2708 	g_mutex_lock (&backend->priv->views_mutex);
2709 
2710 	/* XXX Use g_list_copy_deep() once we require GLib >= 2.34. */
2711 	list = g_list_copy (backend->priv->views);
2712 	g_list_foreach (list, (GFunc) g_object_ref, NULL);
2713 
2714 	g_mutex_unlock (&backend->priv->views_mutex);
2715 
2716 	return list;
2717 }
2718 
2719 /**
2720  * EBookBackendForeachViewFunc:
2721  * @backend: an #EBookBackend
2722  * @view: an #EDataBookView
2723  * @user_data: user data for the function
2724  *
2725  * Callback function used by e_book_backend_foreach_view().
2726  *
2727  * Returns: %TRUE, to continue, %FALSE to stop further processing.
2728  *
2729  * Since: 3.34
2730  **/
2731 
2732 /**
2733  * e_book_backend_foreach_view:
2734  * @backend: an #EBookBackend
2735  * @func: (scope call): an #EBookBackendForeachViewFunc function to call
2736  * @user_data: (closure func): user data to pass to @func
2737  *
2738  * Calls @func for each existing view (as returned by e_book_backend_list_views()).
2739  * The @func can return %FALSE to stop early.
2740  *
2741  * Returns: whether the call had been stopped by @func
2742  *
2743  * Since: 3.34
2744  **/
2745 gboolean
e_book_backend_foreach_view(EBookBackend * backend,EBookBackendForeachViewFunc func,gpointer user_data)2746 e_book_backend_foreach_view (EBookBackend *backend,
2747 			     EBookBackendForeachViewFunc func,
2748 			     gpointer user_data)
2749 {
2750 	GList *views, *link;
2751 	gboolean stopped = FALSE;
2752 
2753 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
2754 	g_return_val_if_fail (func != NULL, FALSE);
2755 
2756 	views = e_book_backend_list_views (backend);
2757 
2758 	for (link = views; link && !stopped; link = g_list_next (link)) {
2759 		stopped = !func (backend, link->data, user_data);
2760 	}
2761 
2762 	g_list_free_full (views, g_object_unref);
2763 
2764 	return stopped;
2765 }
2766 
2767 struct NotifyProgressData {
2768 	gboolean only_completed_views;
2769 	gint percent;
2770 	const gchar *message;
2771 };
2772 
2773 static gboolean
ebb_notify_progress_cb(EBookBackend * backend,EDataBookView * view,gpointer user_data)2774 ebb_notify_progress_cb (EBookBackend *backend,
2775 			EDataBookView *view,
2776 			gpointer user_data)
2777 {
2778 	struct NotifyProgressData *npd = user_data;
2779 
2780 	g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (view), FALSE);
2781 	g_return_val_if_fail (npd != NULL, FALSE);
2782 
2783 	if (!npd->only_completed_views || e_data_book_view_is_completed (view))
2784 		e_data_book_view_notify_progress (view, npd->percent, npd->message);
2785 
2786 	return TRUE;
2787 }
2788 
2789 /**
2790  * e_book_backend_foreach_view_notify_progress:
2791  * @backend: an #EBookBackend
2792  * @only_completed_views: whether notify in completed views only
2793  * @percent: percent complete
2794  * @message: (nullable): message describing the operation in progress, or %NULL
2795  *
2796  * Notifies each view of the @backend about progress. When @only_completed_views
2797  * is %TRUE, notifies only completed views.
2798  *
2799  * Since: 3.34
2800  **/
2801 void
e_book_backend_foreach_view_notify_progress(EBookBackend * backend,gboolean only_completed_views,gint percent,const gchar * message)2802 e_book_backend_foreach_view_notify_progress (EBookBackend *backend,
2803 					     gboolean only_completed_views,
2804 					     gint percent,
2805 					     const gchar *message)
2806 {
2807 	struct NotifyProgressData npd;
2808 
2809 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2810 
2811 	npd.only_completed_views = only_completed_views;
2812 	npd.percent = percent;
2813 	npd.message = message;
2814 
2815 	e_book_backend_foreach_view (backend, ebb_notify_progress_cb, &npd);
2816 }
2817 
2818 /**
2819  * e_book_backend_get_backend_property:
2820  * @backend: an #EBookBackend
2821  * @prop_name: a backend property name
2822  *
2823  * Obtains the value of the backend property named @prop_name.
2824  * Freed the returned string with g_free() when finished with it.
2825  *
2826  * Returns: the value for @prop_name
2827  *
2828  * Since: 3.10
2829  **/
2830 gchar *
e_book_backend_get_backend_property(EBookBackend * backend,const gchar * prop_name)2831 e_book_backend_get_backend_property (EBookBackend *backend,
2832                                      const gchar *prop_name)
2833 {
2834 	EBookBackendClass *class;
2835 
2836 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
2837 	g_return_val_if_fail (prop_name != NULL, NULL);
2838 
2839 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2840 	g_return_val_if_fail (class != NULL, NULL);
2841 	g_return_val_if_fail (class->impl_get_backend_property != NULL, NULL);
2842 
2843 	return class->impl_get_backend_property (backend, prop_name);
2844 }
2845 
2846 /**
2847  * e_book_backend_is_opened:
2848  * @backend: an #EBookBackend
2849  *
2850  * Checks if @backend's storage has been opened (and
2851  * authenticated, if necessary) and the backend itself
2852  * is ready for accessing. This property is changed automatically
2853  * after the @backend is successfully opened.
2854  *
2855  * Returns: %TRUE if fully opened, %FALSE otherwise.
2856  *
2857  * Since: 3.2
2858  **/
2859 gboolean
e_book_backend_is_opened(EBookBackend * backend)2860 e_book_backend_is_opened (EBookBackend *backend)
2861 {
2862 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
2863 
2864 	return backend->priv->opened;
2865 }
2866 
2867 /**
2868  * e_book_backend_is_readonly:
2869  * @backend: an #EBookBackend
2870  *
2871  * Checks if we can write to @backend.
2872  *
2873  * Returns: %TRUE if read-only, %FALSE if not.
2874  *
2875  * Since: 3.2
2876  **/
2877 gboolean
e_book_backend_is_readonly(EBookBackend * backend)2878 e_book_backend_is_readonly (EBookBackend *backend)
2879 {
2880 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
2881 
2882 	return !e_book_backend_get_writable (backend);
2883 }
2884 
2885 /**
2886  * e_book_backend_get_direct_book:
2887  * @backend: an #EBookBackend
2888  *
2889  * Tries to create an #EDataBookDirect for @backend if
2890  * backend supports direct read access.
2891  *
2892  * Returns: (transfer full) (nullable): A new #EDataBookDirect object, or %NULL if
2893  *          @backend does not support direct access
2894  *
2895  * Since: 3.8
2896  */
2897 EDataBookDirect *
e_book_backend_get_direct_book(EBookBackend * backend)2898 e_book_backend_get_direct_book (EBookBackend *backend)
2899 {
2900 	EBookBackendClass *class;
2901 	EDataBookDirect *direct_book = NULL;
2902 
2903 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
2904 
2905 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2906 	g_return_val_if_fail (class != NULL, NULL);
2907 
2908 	if (class->impl_get_direct_book != NULL)
2909 		direct_book = class->impl_get_direct_book (backend);
2910 
2911 	return direct_book;
2912 }
2913 
2914 /**
2915  * e_book_backend_configure_direct:
2916  * @backend: an #EBookBackend
2917  * @config: The configuration string for the given backend
2918  *
2919  * This method is called on @backend in direct read access mode.
2920  * The @config argument is the same configuration string which
2921  * the same backend reported in the #EDataBookDirect returned
2922  * by e_book_backend_get_direct_book().
2923  *
2924  * The configuration string is optional and is used to ensure
2925  * that direct access backends are properly configured to
2926  * interface with the same data as the running server side backend.
2927  *
2928  * Since: 3.8
2929  */
2930 void
e_book_backend_configure_direct(EBookBackend * backend,const gchar * config)2931 e_book_backend_configure_direct (EBookBackend *backend,
2932                                  const gchar *config)
2933 {
2934 	EBookBackendClass *class;
2935 
2936 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
2937 
2938 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2939 	g_return_if_fail (class != NULL);
2940 
2941 	if (class->impl_configure_direct)
2942 		class->impl_configure_direct (backend, config);
2943 }
2944 
2945 /**
2946  * e_book_backend_set_locale:
2947  * @backend: an #EBookBackend
2948  * @locale: the new locale for the addressbook
2949  * @cancellable: optional #GCancellable object, or %NULL
2950  * @error: return location for a #GError, or %NULL
2951  *
2952  * Notify the addressbook backend that the current locale has
2953  * changed, this is important for backends which support
2954  * ordered result lists which are locale sensitive.
2955  *
2956  * Returns: %TRUE on success, %FALSE on failure
2957  *
2958  * Since: 3.12
2959  */
2960 gboolean
e_book_backend_set_locale(EBookBackend * backend,const gchar * locale,GCancellable * cancellable,GError ** error)2961 e_book_backend_set_locale (EBookBackend *backend,
2962                            const gchar *locale,
2963                            GCancellable *cancellable,
2964                            GError **error)
2965 {
2966 	EBookBackendClass *class;
2967 	/* If the backend does not support locales, just happily return */
2968 	gboolean success = TRUE;
2969 
2970 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
2971 
2972 	class = E_BOOK_BACKEND_GET_CLASS (backend);
2973 	g_return_val_if_fail (class != NULL, FALSE);
2974 
2975 	if (class->impl_set_locale) {
2976 		g_object_ref (backend);
2977 
2978 		success = class->impl_set_locale (backend, locale, cancellable, error);
2979 
2980 		if (success)
2981 			e_book_backend_notify_complete (backend);
2982 
2983 		g_object_unref (backend);
2984 	}
2985 
2986 	return success;
2987 }
2988 
2989 /**
2990  * e_book_backend_dup_locale:
2991  * @backend: an #EBookBackend
2992  *
2993  * Fetches a copy of the currently configured locale for the addressbook
2994  *
2995  * Returns: A copy of the currently configured locale for the addressbook.
2996  *   Free with g_free() when done with it.
2997  *
2998  * Since: 3.12
2999  */
3000 gchar *
e_book_backend_dup_locale(EBookBackend * backend)3001 e_book_backend_dup_locale (EBookBackend *backend)
3002 {
3003 	EBookBackendClass *class;
3004 	gchar *locale = NULL;
3005 
3006 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
3007 
3008 	class = E_BOOK_BACKEND_GET_CLASS (backend);
3009 	g_return_val_if_fail (class != NULL, NULL);
3010 
3011 	if (class->impl_dup_locale) {
3012 		g_object_ref (backend);
3013 
3014 		locale = class->impl_dup_locale (backend);
3015 
3016 		g_object_unref (backend);
3017 	}
3018 
3019 	return locale;
3020 }
3021 
3022 /**
3023  * e_book_backend_notify_update:
3024  * @backend: an #EBookBackend
3025  * @contact: a new or modified contact
3026  *
3027  * Notifies all of @backend's book views about the new or modified
3028  * contacts @contact.
3029  *
3030  * e_data_book_respond_create_contacts() and e_data_book_respond_modify_contacts() call this
3031  * function for you. You only need to call this from your backend if
3032  * contacts are created or modified by another (non-PAS-using) client.
3033  **/
3034 void
e_book_backend_notify_update(EBookBackend * backend,const EContact * contact)3035 e_book_backend_notify_update (EBookBackend *backend,
3036                               const EContact *contact)
3037 {
3038 	EBookBackendClass *class;
3039 
3040 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
3041 	g_return_if_fail (E_IS_CONTACT (contact));
3042 
3043 	class = E_BOOK_BACKEND_GET_CLASS (backend);
3044 	g_return_if_fail (class != NULL);
3045 	g_return_if_fail (class->impl_notify_update != NULL);
3046 
3047 	class->impl_notify_update (backend, contact);
3048 }
3049 
3050 /**
3051  * e_book_backend_notify_remove:
3052  * @backend: an #EBookBackend
3053  * @id: a contact id
3054  *
3055  * Notifies all of @backend's book views that the contact with UID
3056  * @id has been removed.
3057  *
3058  * e_data_book_respond_remove_contacts() calls this function for you. You
3059  * only need to call this from your backend if contacts are removed by
3060  * another (non-PAS-using) client.
3061  **/
3062 void
e_book_backend_notify_remove(EBookBackend * backend,const gchar * id)3063 e_book_backend_notify_remove (EBookBackend *backend,
3064                               const gchar *id)
3065 {
3066 	GList *list, *link;
3067 
3068 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
3069 	g_return_if_fail (id != NULL);
3070 
3071 	list = e_book_backend_list_views (backend);
3072 
3073 	for (link = list; link != NULL; link = g_list_next (link)) {
3074 		EDataBookView *view = E_DATA_BOOK_VIEW (link->data);
3075 		e_data_book_view_notify_remove (view, id);
3076 	}
3077 
3078 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3079 }
3080 
3081 /**
3082  * e_book_backend_notify_complete:
3083  * @backend: an #EBookBackend
3084  *
3085  * Notifies all of @backend's book views that the current set of
3086  * notifications is complete; use this after a series of
3087  * e_book_backend_notify_update() and e_book_backend_notify_remove() calls.
3088  **/
3089 void
e_book_backend_notify_complete(EBookBackend * backend)3090 e_book_backend_notify_complete (EBookBackend *backend)
3091 {
3092 	GList *list, *link;
3093 
3094 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
3095 
3096 	list = e_book_backend_list_views (backend);
3097 
3098 	for (link = list; link != NULL; link = g_list_next (link)) {
3099 		EDataBookView *view = E_DATA_BOOK_VIEW (link->data);
3100 		e_data_book_view_notify_complete (view, NULL /* SUCCESS */);
3101 	}
3102 
3103 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3104 }
3105 
3106 /**
3107  * e_book_backend_notify_error:
3108  * @backend: an #EBookBackend
3109  * @message: an error message
3110  *
3111  * Notifies each backend listener about an error. This is meant to be used
3112  * for cases where is no GError return possibility, to notify user about
3113  * an issue.
3114  *
3115  * Since: 3.2
3116  **/
3117 void
e_book_backend_notify_error(EBookBackend * backend,const gchar * message)3118 e_book_backend_notify_error (EBookBackend *backend,
3119                              const gchar *message)
3120 {
3121 	EDataBook *data_book;
3122 
3123 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
3124 	g_return_if_fail (message != NULL);
3125 
3126 	data_book = e_book_backend_ref_data_book (backend);
3127 
3128 	if (data_book != NULL) {
3129 		e_data_book_report_error (data_book, message);
3130 		g_object_unref (data_book);
3131 	}
3132 }
3133 
3134 /**
3135  * e_book_backend_notify_property_changed:
3136  * @backend: an #EBookBackend
3137  * @prop_name: property name, which changed
3138  * @prop_value: (nullable): new property value
3139  *
3140  * Notifies clients about property value change.
3141  *
3142  * Since: 3.2
3143  **/
3144 void
e_book_backend_notify_property_changed(EBookBackend * backend,const gchar * prop_name,const gchar * prop_value)3145 e_book_backend_notify_property_changed (EBookBackend *backend,
3146                                         const gchar *prop_name,
3147                                         const gchar *prop_value)
3148 {
3149 	EDataBook *data_book;
3150 
3151 	g_return_if_fail (E_IS_BOOK_BACKEND (backend));
3152 	g_return_if_fail (prop_name != NULL);
3153 
3154 	data_book = e_book_backend_ref_data_book (backend);
3155 
3156 	if (data_book != NULL) {
3157 		e_data_book_report_backend_property_changed (data_book, prop_name, prop_value ? prop_value : "");
3158 		g_object_unref (data_book);
3159 	}
3160 }
3161 
3162 /**
3163  * e_book_backend_prepare_for_completion:
3164  * @backend: an #EBookBackend
3165  * @opid: an operation ID given to #EDataBook
3166  * @result_queue: return location for a #GQueue, or %NULL
3167  *
3168  * Obtains the #GSimpleAsyncResult for @opid and sets @result_queue as a
3169  * place to deposit results prior to completing the #GSimpleAsyncResult.
3170  *
3171  * <note>
3172  *   <para>
3173  *     This is a temporary function to serve #EDataBook's "respond"
3174  *     functions until they can be removed.  Nothing else should be
3175  *     calling this function.
3176  *   </para>
3177  * </note>
3178  *
3179  * Returns: (transfer full): a #GSimpleAsyncResult
3180  *
3181  * Since: 3.10
3182  **/
3183 GSimpleAsyncResult *
e_book_backend_prepare_for_completion(EBookBackend * backend,guint32 opid,GQueue ** result_queue)3184 e_book_backend_prepare_for_completion (EBookBackend *backend,
3185                                        guint32 opid,
3186                                        GQueue **result_queue)
3187 {
3188 	GSimpleAsyncResult *simple;
3189 	AsyncContext *async_context;
3190 
3191 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
3192 	g_return_val_if_fail (opid > 0, NULL);
3193 
3194 	simple = book_backend_claim_operation (backend, opid);
3195 	g_return_val_if_fail (simple != NULL, NULL);
3196 
3197 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
3198 
3199 	if (result_queue != NULL) {
3200 		if (async_context != NULL)
3201 			*result_queue = &async_context->result_queue;
3202 		else
3203 			*result_queue = NULL;
3204 	}
3205 
3206 	return simple;
3207 }
3208 
3209 /**
3210  * e_book_backend_create_cursor:
3211  * @backend: an #EBookBackend
3212  * @sort_fields: the #EContactFields to sort by
3213  * @sort_types: the #EBookCursorSortTypes for the sorted fields
3214  * @n_fields: the number of fields in the @sort_fields and @sort_types
3215  * @error: return location for a #GError, or %NULL
3216  *
3217  * Creates a new #EDataBookCursor for the given backend if the backend
3218  * has cursor support. If the backend does not support cursors then
3219  * an %E_CLIENT_ERROR_NOT_SUPPORTED error will be set in @error.
3220  *
3221  * Backends can also refuse to create cursors for some values of @sort_fields
3222  * and report more specific errors.
3223  *
3224  * The returned cursor belongs to @backend and should be destroyed
3225  * with e_book_backend_delete_cursor() when no longer needed.
3226  *
3227  * Returns: (transfer none): A newly created cursor, the cursor belongs
3228  * to the backend and should not be unreffed, or %NULL
3229  *
3230  * Since: 3.12
3231  */
3232 EDataBookCursor *
e_book_backend_create_cursor(EBookBackend * backend,EContactField * sort_fields,EBookCursorSortType * sort_types,guint n_fields,GError ** error)3233 e_book_backend_create_cursor (EBookBackend *backend,
3234                               EContactField *sort_fields,
3235                               EBookCursorSortType *sort_types,
3236                               guint n_fields,
3237                               GError **error)
3238 {
3239 	EBookBackendClass *class;
3240 	EDataBookCursor *cursor = NULL;
3241 
3242 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
3243 
3244 	class = E_BOOK_BACKEND_GET_CLASS (backend);
3245 	g_return_val_if_fail (class != NULL, NULL);
3246 
3247 	if (class->impl_create_cursor) {
3248 		g_object_ref (backend);
3249 
3250 		cursor = class->impl_create_cursor (backend, sort_fields, sort_types, n_fields, error);
3251 
3252 		g_object_unref (backend);
3253 	} else {
3254 		g_set_error (
3255 			error,
3256 			E_CLIENT_ERROR,
3257 			E_CLIENT_ERROR_NOT_SUPPORTED,
3258 			_("Addressbook backend does not support cursors"));
3259 	}
3260 
3261 	return cursor;
3262 }
3263 
3264 /**
3265  * e_book_backend_delete_cursor:
3266  * @backend: an #EBookBackend
3267  * @cursor: the #EDataBookCursor to destroy
3268  * @error: return location for a #GError, or %NULL
3269  *
3270  * Requests @backend to release and destroy @cursor, this
3271  * will trigger an %E_CLIENT_ERROR_INVALID_ARG error if @cursor
3272  * is not owned by @backend.
3273  *
3274  * Returns: Whether @cursor was successfully deleted.
3275  *
3276  * Since: 3.12
3277  */
3278 gboolean
e_book_backend_delete_cursor(EBookBackend * backend,EDataBookCursor * cursor,GError ** error)3279 e_book_backend_delete_cursor (EBookBackend *backend,
3280                               EDataBookCursor *cursor,
3281                               GError **error)
3282 {
3283 	EBookBackendClass *class;
3284 	gboolean success = FALSE;
3285 
3286 	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
3287 
3288 	class = E_BOOK_BACKEND_GET_CLASS (backend);
3289 	g_return_val_if_fail (class != NULL, FALSE);
3290 
3291 	g_object_ref (backend);
3292 
3293 	if (class->impl_delete_cursor)
3294 		success = class->impl_delete_cursor (backend, cursor, error);
3295 	else
3296 		g_warning ("Backend asked to delete a cursor, but does not support cursors");
3297 
3298 	g_object_unref (backend);
3299 
3300 	return success;
3301 }
3302 
3303 /**
3304  * e_book_backend_schedule_custom_operation:
3305  * @book_backend: an #EBookBackend
3306  * @use_cancellable: (nullable): an optional #GCancellable to use for @func
3307  * @func: a function to call in a dedicated thread
3308  * @user_data: user data being passed to @func
3309  * @user_data_free: (nullable): optional destroy call back for @user_data
3310  *
3311  * Schedules user function @func to be run in a dedicated thread as
3312  * a blocking operation.
3313  *
3314  * The function adds its own reference to @use_cancellable, if not %NULL.
3315  *
3316  * The error returned from @func is propagated to client using
3317  * e_book_backend_notify_error() function. If it's not desired,
3318  * then left the error unchanged and notify about errors manually.
3319  *
3320  * Since: 3.26
3321  **/
3322 void
e_book_backend_schedule_custom_operation(EBookBackend * book_backend,GCancellable * use_cancellable,EBookBackendCustomOpFunc func,gpointer user_data,GDestroyNotify user_data_free)3323 e_book_backend_schedule_custom_operation (EBookBackend *book_backend,
3324 					  GCancellable *use_cancellable,
3325 					  EBookBackendCustomOpFunc func,
3326 					  gpointer user_data,
3327 					  GDestroyNotify user_data_free)
3328 {
3329 	DispatchNode *node;
3330 
3331 	g_return_if_fail (E_IS_BOOK_BACKEND (book_backend));
3332 	g_return_if_fail (func != NULL);
3333 
3334 	g_mutex_lock (&book_backend->priv->operation_lock);
3335 
3336 	node = g_slice_new0 (DispatchNode);
3337 	node->blocking_operation = TRUE;
3338 	node->book_backend_weak_ref = e_weak_ref_new (book_backend);
3339 	node->custom_func = func;
3340 	node->custom_func_user_data = user_data;
3341 	node->custom_func_user_data_free = user_data_free;
3342 
3343 	if (G_IS_CANCELLABLE (use_cancellable))
3344 		node->cancellable = g_object_ref (use_cancellable);
3345 
3346 	g_queue_push_tail (&book_backend->priv->pending_operations, node);
3347 
3348 	g_mutex_unlock (&book_backend->priv->operation_lock);
3349 
3350 	book_backend_dispatch_next_operation (book_backend);
3351 }
3352