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