39181527fd2a287d084f31bb31c566e7823d7dec
[platform/upstream/evolution-data-server.git] / addressbook / libedata-book / e-data-book.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  * Copyright (C) 2006 OpenedHand Ltd
5  * Copyright (C) 2009 Intel Corporation
6  *
7  * This library is free software; you can redistribute it and/or modify it under
8  * the terms of version 2.1 of the GNU Lesser General Public License as
9  * published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  * Author: Ross Burton <ross@linux.intel.com>
21  */
22
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <glib/gi18n.h>
26 #include <gio/gio.h>
27
28 /* Private D-Bus classes. */
29 #include <e-dbus-address-book.h>
30
31 #include <libebook-contacts/libebook-contacts.h>
32
33 #include "e-data-book-factory.h"
34 #include "e-data-book.h"
35 #include "e-data-book-view.h"
36 #include "e-book-backend.h"
37 #include "e-book-backend-sexp.h"
38
39 #define E_DATA_BOOK_GET_PRIVATE(obj) \
40         (G_TYPE_INSTANCE_GET_PRIVATE \
41         ((obj), E_TYPE_DATA_BOOK, EDataBookPrivate))
42
43 struct _EDataBookPrivate {
44         GDBusConnection *connection;
45         EDBusAddressBook *dbus_interface;
46         EBookBackend *backend;
47         gchar *object_path;
48
49         GRecMutex pending_ops_lock;
50         GHashTable *pending_ops; /* opid -> OperationData */
51
52         /* Operations are queued while an
53          * open operation is in progress. */
54         GMutex open_lock;
55         guint32 open_opid;
56         GQueue open_queue;
57 };
58
59 enum {
60         PROP_0,
61         PROP_BACKEND,
62         PROP_CONNECTION,
63         PROP_OBJECT_PATH
64 };
65
66 static EOperationPool *ops_pool = NULL;
67
68 typedef enum {
69         OP_OPEN,
70         OP_REFRESH,
71         OP_GET_CONTACT,
72         OP_GET_CONTACTS,
73         OP_GET_CONTACTS_UIDS,
74         OP_ADD_CONTACTS,
75         OP_REMOVE_CONTACTS,
76         OP_MODIFY_CONTACTS,
77         OP_GET_BACKEND_PROPERTY,
78         OP_GET_VIEW,
79         OP_CLOSE
80 } OperationID;
81
82 typedef struct {
83         volatile gint ref_count;
84
85         OperationID op;
86         guint32 id; /* operation id */
87         EDataBook *book; /* book */
88         GCancellable *cancellable;
89         GDBusMethodInvocation *invocation;
90         guint watcher_id;
91
92         union {
93                 /* OP_GET_CONTACT */
94                 gchar *uid;
95                 /* OP_REMOVE_CONTACTS */
96                 GSList *ids;
97                 /* OP_ADD_CONTACTS */
98                 /* OP_MODIFY_CONTACTS */
99                 GSList *vcards;
100                 /* OP_GET_VIEW */
101                 /* OP_GET_CONTACTS */
102                 /* OP_GET_CONTACTS_UIDS */
103                 gchar *query;
104                 /* OP_GET_BACKEND_PROPERTY */
105                 const gchar *prop_name;
106
107                 /* OP_REFRESH */
108                 /* OP_CLOSE */
109         } d;
110 } OperationData;
111
112 /* Forward Declarations */
113 static void     e_data_book_initable_init       (GInitableIface *interface);
114
115 G_DEFINE_TYPE_WITH_CODE (
116         EDataBook,
117         e_data_book,
118         G_TYPE_OBJECT,
119         G_IMPLEMENT_INTERFACE (
120                 G_TYPE_INITABLE,
121                 e_data_book_initable_init))
122
123 static gchar *
124 construct_bookview_path (void)
125 {
126         static volatile gint counter = 1;
127
128         g_atomic_int_inc (&counter);
129
130         return g_strdup_printf (
131                 "/org/gnome/evolution/dataserver/AddressBookView/%d/%d",
132                 getpid (), counter);
133 }
134
135 static void
136 op_sender_vanished_cb (GDBusConnection *connection,
137                        const gchar *sender,
138                        GCancellable *cancellable)
139 {
140         g_cancellable_cancel (cancellable);
141 }
142
143 static OperationData *
144 op_ref (OperationData *data)
145 {
146         g_return_val_if_fail (data != NULL, data);
147         g_return_val_if_fail (data->ref_count > 0, data);
148
149         g_atomic_int_inc (&data->ref_count);
150
151         return data;
152 }
153
154 static OperationData *
155 op_new (OperationID op,
156         EDataBook *book,
157         GDBusMethodInvocation *invocation)
158 {
159         OperationData *data;
160
161         data = g_slice_new0 (OperationData);
162         data->ref_count = 1;
163         data->op = op;
164         data->id = e_operation_pool_reserve_opid (ops_pool);
165         data->book = g_object_ref (book);
166         data->cancellable = g_cancellable_new ();
167
168         /* This is optional so we can fake client requests. */
169         if (invocation != NULL) {
170                 GDBusConnection *connection;
171                 const gchar *sender;
172
173                 data->invocation = g_object_ref (invocation);
174
175                 connection = e_data_book_get_connection (book);
176                 sender = g_dbus_method_invocation_get_sender (invocation);
177
178                 data->watcher_id = g_bus_watch_name_on_connection (
179                         connection, sender,
180                         G_BUS_NAME_WATCHER_FLAGS_NONE,
181                         (GBusNameAppearedCallback) NULL,
182                         (GBusNameVanishedCallback) op_sender_vanished_cb,
183                         g_object_ref (data->cancellable),
184                         (GDestroyNotify) g_object_unref);
185         }
186
187         g_rec_mutex_lock (&book->priv->pending_ops_lock);
188         g_hash_table_insert (
189                 book->priv->pending_ops,
190                 GUINT_TO_POINTER (data->id),
191                 op_ref (data));
192         g_rec_mutex_unlock (&book->priv->pending_ops_lock);
193
194         return data;
195 }
196
197 static void
198 op_unref (OperationData *data)
199 {
200         g_return_if_fail (data != NULL);
201         g_return_if_fail (data->ref_count > 0);
202
203         if (g_atomic_int_dec_and_test (&data->ref_count)) {
204
205                 switch (data->op) {
206                         case OP_GET_CONTACT:
207                                 g_free (data->d.uid);
208                                 break;
209                         case OP_REMOVE_CONTACTS:
210                                 g_slist_free_full (
211                                         data->d.ids,
212                                         (GDestroyNotify) g_free);
213                                 break;
214                         case OP_ADD_CONTACTS:
215                         case OP_MODIFY_CONTACTS:
216                                 g_slist_free_full (
217                                         data->d.vcards,
218                                         (GDestroyNotify) g_free);
219                                 break;
220                         case OP_GET_VIEW:
221                         case OP_GET_CONTACTS:
222                         case OP_GET_CONTACTS_UIDS:
223                                 g_free (data->d.query);
224                                 break;
225                         default:
226                                 break;
227                 }
228
229                 g_object_unref (data->book);
230                 g_object_unref (data->cancellable);
231
232                 if (data->invocation != NULL)
233                         g_object_unref (data->invocation);
234
235                 if (data->watcher_id > 0)
236                         g_bus_unwatch_name (data->watcher_id);
237
238                 g_slice_free (OperationData, data);
239         }
240 }
241
242 static void
243 op_dispatch (EDataBook *book,
244              OperationData *data)
245 {
246         g_mutex_lock (&book->priv->open_lock);
247
248         /* If an open operation is currently in progress, queue this
249          * operation to be dispatched when the open operation finishes. */
250         if (book->priv->open_opid > 0) {
251                 g_queue_push_tail (&book->priv->open_queue, data);
252         } else {
253                 if (data->op == OP_OPEN)
254                         book->priv->open_opid = data->id;
255                 e_operation_pool_push (ops_pool, data);
256         }
257
258         g_mutex_unlock (&book->priv->open_lock);
259 }
260
261 static OperationData *
262 op_claim (EDataBook *book,
263           guint32 opid)
264 {
265         OperationData *data;
266
267         g_return_val_if_fail (E_IS_DATA_BOOK (book), NULL);
268
269         e_operation_pool_release_opid (ops_pool, opid);
270
271         g_rec_mutex_lock (&book->priv->pending_ops_lock);
272         data = g_hash_table_lookup (
273                 book->priv->pending_ops,
274                 GUINT_TO_POINTER (opid));
275         if (data != NULL) {
276                 /* Steal the hash table's reference. */
277                 g_hash_table_steal (
278                         book->priv->pending_ops,
279                         GUINT_TO_POINTER (opid));
280         }
281         g_rec_mutex_unlock (&book->priv->pending_ops_lock);
282
283         return data;
284 }
285
286 static void
287 op_complete (EDataBook *book,
288              guint32 opid)
289 {
290         g_return_if_fail (E_IS_DATA_BOOK (book));
291
292         e_operation_pool_release_opid (ops_pool, opid);
293
294         g_rec_mutex_lock (&book->priv->pending_ops_lock);
295         g_hash_table_remove (
296                 book->priv->pending_ops,
297                 GUINT_TO_POINTER (opid));
298         g_rec_mutex_unlock (&book->priv->pending_ops_lock);
299 }
300
301 static void
302 data_book_convert_to_client_error (GError *error)
303 {
304         g_return_if_fail (error != NULL);
305
306         if (error->domain != E_DATA_BOOK_ERROR)
307                 return;
308
309         switch (error->code) {
310                 case E_DATA_BOOK_STATUS_REPOSITORY_OFFLINE:
311                         error->domain = E_CLIENT_ERROR;
312                         error->code = E_CLIENT_ERROR_REPOSITORY_OFFLINE;
313                         break;
314
315                 case E_DATA_BOOK_STATUS_PERMISSION_DENIED:
316                         error->domain = E_CLIENT_ERROR;
317                         error->code = E_CLIENT_ERROR_PERMISSION_DENIED;
318                         break;
319
320                 case E_DATA_BOOK_STATUS_CONTACT_NOT_FOUND:
321                         error->domain = E_BOOK_CLIENT_ERROR;
322                         error->code = E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND;
323                         break;
324
325                 case E_DATA_BOOK_STATUS_CONTACTID_ALREADY_EXISTS:
326                         error->domain = E_BOOK_CLIENT_ERROR;
327                         error->code = E_BOOK_CLIENT_ERROR_CONTACT_ID_ALREADY_EXISTS;
328                         break;
329
330                 case E_DATA_BOOK_STATUS_AUTHENTICATION_FAILED:
331                         error->domain = E_CLIENT_ERROR;
332                         error->code = E_CLIENT_ERROR_AUTHENTICATION_FAILED;
333                         break;
334
335                 case E_DATA_BOOK_STATUS_UNSUPPORTED_AUTHENTICATION_METHOD:
336                         error->domain = E_CLIENT_ERROR;
337                         error->code = E_CLIENT_ERROR_UNSUPPORTED_AUTHENTICATION_METHOD;
338                         break;
339
340                 case E_DATA_BOOK_STATUS_TLS_NOT_AVAILABLE:
341                         error->domain = E_CLIENT_ERROR;
342                         error->code = E_CLIENT_ERROR_TLS_NOT_AVAILABLE;
343                         break;
344
345                 case E_DATA_BOOK_STATUS_NO_SUCH_BOOK:
346                         error->domain = E_BOOK_CLIENT_ERROR;
347                         error->code = E_BOOK_CLIENT_ERROR_NO_SUCH_BOOK;
348                         break;
349
350                 case E_DATA_BOOK_STATUS_BOOK_REMOVED:
351                         error->domain = E_BOOK_CLIENT_ERROR;
352                         error->code = E_BOOK_CLIENT_ERROR_NO_SUCH_SOURCE;
353                         break;
354
355                 case E_DATA_BOOK_STATUS_OFFLINE_UNAVAILABLE:
356                         error->domain = E_CLIENT_ERROR;
357                         error->code = E_CLIENT_ERROR_OFFLINE_UNAVAILABLE;
358                         break;
359
360                 case E_DATA_BOOK_STATUS_SEARCH_SIZE_LIMIT_EXCEEDED:
361                         error->domain = E_CLIENT_ERROR;
362                         error->code = E_CLIENT_ERROR_SEARCH_SIZE_LIMIT_EXCEEDED;
363                         break;
364
365                 case E_DATA_BOOK_STATUS_SEARCH_TIME_LIMIT_EXCEEDED:
366                         error->domain = E_CLIENT_ERROR;
367                         error->code = E_CLIENT_ERROR_SEARCH_TIME_LIMIT_EXCEEDED;
368                         break;
369
370                 case E_DATA_BOOK_STATUS_INVALID_QUERY:
371                         error->domain = E_CLIENT_ERROR;
372                         error->code = E_CLIENT_ERROR_INVALID_QUERY;
373                         break;
374
375                 case E_DATA_BOOK_STATUS_QUERY_REFUSED:
376                         error->domain = E_CLIENT_ERROR;
377                         error->code = E_CLIENT_ERROR_QUERY_REFUSED;
378                         break;
379
380                 case E_DATA_BOOK_STATUS_COULD_NOT_CANCEL:
381                         error->domain = E_CLIENT_ERROR;
382                         error->code = E_CLIENT_ERROR_COULD_NOT_CANCEL;
383                         break;
384
385                 case E_DATA_BOOK_STATUS_NO_SPACE:
386                         error->domain = E_BOOK_CLIENT_ERROR;
387                         error->code = E_BOOK_CLIENT_ERROR_NO_SPACE;
388                         break;
389
390                 case E_DATA_BOOK_STATUS_INVALID_ARG:
391                         error->domain = E_CLIENT_ERROR;
392                         error->code = E_CLIENT_ERROR_INVALID_ARG;
393                         break;
394
395                 case E_DATA_BOOK_STATUS_NOT_SUPPORTED:
396                         error->domain = E_CLIENT_ERROR;
397                         error->code = E_CLIENT_ERROR_NOT_SUPPORTED;
398                         break;
399
400                 case E_DATA_BOOK_STATUS_NOT_OPENED:
401                         error->domain = E_CLIENT_ERROR;
402                         error->code = E_CLIENT_ERROR_NOT_OPENED;
403                         break;
404
405                 case E_DATA_BOOK_STATUS_OUT_OF_SYNC:
406                         error->domain = E_CLIENT_ERROR;
407                         error->code = E_CLIENT_ERROR_OUT_OF_SYNC;
408                         break;
409
410                 case E_DATA_BOOK_STATUS_UNSUPPORTED_FIELD:
411                 case E_DATA_BOOK_STATUS_OTHER_ERROR:
412                 case E_DATA_BOOK_STATUS_INVALID_SERVER_VERSION:
413                         error->domain = E_CLIENT_ERROR;
414                         error->code = E_CLIENT_ERROR_OTHER_ERROR;
415                         break;
416
417                 default:
418                         g_warn_if_reached ();
419         }
420 }
421
422 static void
423 operation_thread (gpointer data,
424                   gpointer user_data)
425 {
426         OperationData *op = data;
427         EBookBackend *backend;
428         GHashTableIter iter;
429         gpointer value;
430
431         backend = e_data_book_get_backend (op->book);
432
433         switch (op->op) {
434         case OP_OPEN:
435                 e_book_backend_open (
436                         backend, op->book, op->id,
437                         op->cancellable, FALSE);
438                 break;
439
440         case OP_ADD_CONTACTS:
441                 e_book_backend_create_contacts (
442                         backend, op->book, op->id,
443                         op->cancellable, op->d.vcards);
444                 break;
445
446         case OP_GET_CONTACT:
447                 e_book_backend_get_contact (
448                         backend, op->book, op->id,
449                         op->cancellable, op->d.uid);
450                 break;
451
452         case OP_GET_CONTACTS:
453                 e_book_backend_get_contact_list (
454                         backend, op->book, op->id,
455                         op->cancellable, op->d.query);
456                 break;
457
458         case OP_GET_CONTACTS_UIDS:
459                 e_book_backend_get_contact_list_uids (
460                         backend, op->book, op->id,
461                         op->cancellable, op->d.query);
462                 break;
463
464         case OP_MODIFY_CONTACTS:
465                 e_book_backend_modify_contacts (
466                         backend, op->book, op->id,
467                         op->cancellable, op->d.vcards);
468                 break;
469
470         case OP_REMOVE_CONTACTS:
471                 e_book_backend_remove_contacts (
472                         backend, op->book, op->id,
473                         op->cancellable, op->d.ids);
474                 break;
475
476         case OP_REFRESH:
477                 e_book_backend_refresh (
478                         backend, op->book, op->id, op->cancellable);
479                 break;
480
481         case OP_GET_BACKEND_PROPERTY:
482                 e_book_backend_get_backend_property (
483                         backend, op->book, op->id,
484                         op->cancellable, op->d.prop_name);
485                 break;
486
487         case OP_GET_VIEW:
488                 if (op->d.query) {
489                         EDataBookView *view;
490                         EBookBackendSExp *card_sexp;
491                         GDBusConnection *connection;
492                         gchar *object_path;
493                         GError *error = NULL;
494
495                         card_sexp = e_book_backend_sexp_new (op->d.query);
496                         if (!card_sexp) {
497                                 g_dbus_method_invocation_return_error_literal (
498                                         op->invocation,
499                                         E_CLIENT_ERROR,
500                                         E_CLIENT_ERROR_INVALID_QUERY,
501                                         _("Invalid query"));
502
503                                 op_complete (op->book, op->id);
504                                 break;
505                         }
506
507                         object_path = construct_bookview_path ();
508                         connection = e_data_book_get_connection (op->book);
509
510                         view = e_data_book_view_new (
511                                 op->book, card_sexp,
512                                 connection, object_path, &error);
513
514                         g_object_unref (card_sexp);
515
516                         /* Sanity check. */
517                         g_return_if_fail (
518                                 ((view != NULL) && (error == NULL)) ||
519                                 ((view == NULL) && (error != NULL)));
520
521                         if (error != NULL) {
522                                 /* Translators: This is prefix to a detailed error message */
523                                 g_prefix_error (&error, "%s", _("Invalid query: "));
524                                 data_book_convert_to_client_error (error);
525                                 g_dbus_method_invocation_take_error (
526                                         op->invocation, error);
527
528                                 op_complete (op->book, op->id);
529                                 g_free (object_path);
530                                 break;
531                         }
532
533                         e_book_backend_add_view (backend, view);
534
535                         e_dbus_address_book_complete_get_view (
536                                 op->book->priv->dbus_interface,
537                                 op->invocation,
538                                 object_path);
539
540                         op_complete (op->book, op->id);
541                         g_free (object_path);
542                 }
543                 break;
544
545         case OP_CLOSE:
546                 /* close just cancels all pending ops and frees data book */
547                 e_book_backend_remove_client (backend, op->book);
548
549                 g_rec_mutex_lock (&op->book->priv->pending_ops_lock);
550
551                 g_hash_table_iter_init (&iter, op->book->priv->pending_ops);
552                 while (g_hash_table_iter_next (&iter, NULL, &value)) {
553                         OperationData *cancel_op = value;
554                         g_cancellable_cancel (cancel_op->cancellable);
555                 }
556
557                 g_rec_mutex_unlock (&op->book->priv->pending_ops_lock);
558
559                 e_dbus_address_book_complete_close (
560                         op->book->priv->dbus_interface,
561                         op->invocation);
562
563                 op_complete (op->book, op->id);
564                 break;
565         }
566
567         op_unref (op);
568 }
569
570 /**
571  * e_data_book_status_to_string:
572  *
573  * Since: 2.32
574  **/
575 const gchar *
576 e_data_book_status_to_string (EDataBookStatus status)
577 {
578         gint i;
579         static struct _statuses {
580                 EDataBookStatus status;
581                 const gchar *msg;
582         } statuses[] = {
583                 { E_DATA_BOOK_STATUS_SUCCESS,                           N_("Success") },
584                 { E_DATA_BOOK_STATUS_BUSY,                              N_("Backend is busy") },
585                 { E_DATA_BOOK_STATUS_REPOSITORY_OFFLINE,                N_("Repository offline") },
586                 { E_DATA_BOOK_STATUS_PERMISSION_DENIED,                 N_("Permission denied") },
587                 { E_DATA_BOOK_STATUS_CONTACT_NOT_FOUND,                 N_("Contact not found") },
588                 { E_DATA_BOOK_STATUS_CONTACTID_ALREADY_EXISTS,          N_("Contact ID already exists") },
589                 { E_DATA_BOOK_STATUS_AUTHENTICATION_FAILED,             N_("Authentication Failed") },
590                 { E_DATA_BOOK_STATUS_AUTHENTICATION_REQUIRED,           N_("Authentication Required") },
591                 { E_DATA_BOOK_STATUS_UNSUPPORTED_FIELD,                 N_("Unsupported field") },
592                 { E_DATA_BOOK_STATUS_UNSUPPORTED_AUTHENTICATION_METHOD, N_("Unsupported authentication method") },
593                 { E_DATA_BOOK_STATUS_TLS_NOT_AVAILABLE,                 N_("TLS not available") },
594                 { E_DATA_BOOK_STATUS_NO_SUCH_BOOK,                      N_("Address book does not exist") },
595                 { E_DATA_BOOK_STATUS_BOOK_REMOVED,                      N_("Book removed") },
596                 { E_DATA_BOOK_STATUS_OFFLINE_UNAVAILABLE,               N_("Not available in offline mode") },
597                 { E_DATA_BOOK_STATUS_SEARCH_SIZE_LIMIT_EXCEEDED,        N_("Search size limit exceeded") },
598                 { E_DATA_BOOK_STATUS_SEARCH_TIME_LIMIT_EXCEEDED,        N_("Search time limit exceeded") },
599                 { E_DATA_BOOK_STATUS_INVALID_QUERY,                     N_("Invalid query") },
600                 { E_DATA_BOOK_STATUS_QUERY_REFUSED,                     N_("Query refused") },
601                 { E_DATA_BOOK_STATUS_COULD_NOT_CANCEL,                  N_("Could not cancel") },
602                 /* { E_DATA_BOOK_STATUS_OTHER_ERROR,                    N_("Other error") }, */
603                 { E_DATA_BOOK_STATUS_INVALID_SERVER_VERSION,            N_("Invalid server version") },
604                 { E_DATA_BOOK_STATUS_NO_SPACE,                          N_("No space") },
605                 { E_DATA_BOOK_STATUS_INVALID_ARG,                       N_("Invalid argument") },
606                 /* Translators: The string for NOT_SUPPORTED error */
607                 { E_DATA_BOOK_STATUS_NOT_SUPPORTED,                     N_("Not supported") },
608                 { E_DATA_BOOK_STATUS_NOT_OPENED,                        N_("Backend is not opened yet") },
609                 { E_DATA_BOOK_STATUS_OUT_OF_SYNC,                       N_("Object is out of sync") }
610         };
611
612         for (i = 0; i < G_N_ELEMENTS (statuses); i++) {
613                 if (statuses[i].status == status)
614                         return _(statuses[i].msg);
615         }
616
617         return _("Other error");
618 }
619
620 /* Create the EDataBook error quark */
621 GQuark
622 e_data_book_error_quark (void)
623 {
624         #define ERR_PREFIX "org.gnome.evolution.dataserver.AddressBook."
625
626         static const GDBusErrorEntry entries[] = {
627                 { E_DATA_BOOK_STATUS_SUCCESS,                           ERR_PREFIX "Success" },
628                 { E_DATA_BOOK_STATUS_BUSY,                              ERR_PREFIX "Busy" },
629                 { E_DATA_BOOK_STATUS_REPOSITORY_OFFLINE,                ERR_PREFIX "RepositoryOffline" },
630                 { E_DATA_BOOK_STATUS_PERMISSION_DENIED,                 ERR_PREFIX "PermissionDenied" },
631                 { E_DATA_BOOK_STATUS_CONTACT_NOT_FOUND,                 ERR_PREFIX "ContactNotFound" },
632                 { E_DATA_BOOK_STATUS_CONTACTID_ALREADY_EXISTS,          ERR_PREFIX "ContactIDAlreadyExists" },
633                 { E_DATA_BOOK_STATUS_AUTHENTICATION_FAILED,             ERR_PREFIX "AuthenticationFailed" },
634                 { E_DATA_BOOK_STATUS_AUTHENTICATION_REQUIRED,           ERR_PREFIX "AuthenticationRequired" },
635                 { E_DATA_BOOK_STATUS_UNSUPPORTED_FIELD,                 ERR_PREFIX "UnsupportedField" },
636                 { E_DATA_BOOK_STATUS_UNSUPPORTED_AUTHENTICATION_METHOD, ERR_PREFIX "UnsupportedAuthenticationMethod" },
637                 { E_DATA_BOOK_STATUS_TLS_NOT_AVAILABLE,                 ERR_PREFIX "TLSNotAvailable" },
638                 { E_DATA_BOOK_STATUS_NO_SUCH_BOOK,                      ERR_PREFIX "NoSuchBook" },
639                 { E_DATA_BOOK_STATUS_BOOK_REMOVED,                      ERR_PREFIX "BookRemoved" },
640                 { E_DATA_BOOK_STATUS_OFFLINE_UNAVAILABLE,               ERR_PREFIX "OfflineUnavailable" },
641                 { E_DATA_BOOK_STATUS_SEARCH_SIZE_LIMIT_EXCEEDED,        ERR_PREFIX "SearchSizeLimitExceeded" },
642                 { E_DATA_BOOK_STATUS_SEARCH_TIME_LIMIT_EXCEEDED,        ERR_PREFIX "SearchTimeLimitExceeded" },
643                 { E_DATA_BOOK_STATUS_INVALID_QUERY,                     ERR_PREFIX "InvalidQuery" },
644                 { E_DATA_BOOK_STATUS_QUERY_REFUSED,                     ERR_PREFIX "QueryRefused" },
645                 { E_DATA_BOOK_STATUS_COULD_NOT_CANCEL,                  ERR_PREFIX "CouldNotCancel" },
646                 { E_DATA_BOOK_STATUS_OTHER_ERROR,                       ERR_PREFIX "OtherError" },
647                 { E_DATA_BOOK_STATUS_INVALID_SERVER_VERSION,            ERR_PREFIX "InvalidServerVersion" },
648                 { E_DATA_BOOK_STATUS_NO_SPACE,                          ERR_PREFIX "NoSpace" },
649                 { E_DATA_BOOK_STATUS_INVALID_ARG,                       ERR_PREFIX "InvalidArg" },
650                 { E_DATA_BOOK_STATUS_NOT_SUPPORTED,                     ERR_PREFIX "NotSupported" },
651                 { E_DATA_BOOK_STATUS_NOT_OPENED,                        ERR_PREFIX "NotOpened" },
652                 { E_DATA_BOOK_STATUS_OUT_OF_SYNC,                       ERR_PREFIX "OutOfSync" }
653         };
654
655         #undef ERR_PREFIX
656
657         static volatile gsize quark_volatile = 0;
658
659         g_dbus_error_register_error_domain ("e-data-book-error", &quark_volatile, entries, G_N_ELEMENTS (entries));
660
661         return (GQuark) quark_volatile;
662 }
663
664 /**
665  * e_data_book_create_error:
666  *
667  * Since: 2.32
668  **/
669 GError *
670 e_data_book_create_error (EDataBookStatus status,
671                           const gchar *custom_msg)
672 {
673         if (status == E_DATA_BOOK_STATUS_SUCCESS)
674                 return NULL;
675
676         return g_error_new_literal (E_DATA_BOOK_ERROR, status, custom_msg ? custom_msg : e_data_book_status_to_string (status));
677 }
678
679 /**
680  * e_data_book_create_error_fmt:
681  *
682  * Since: 2.32
683  **/
684 GError *
685 e_data_book_create_error_fmt (EDataBookStatus status,
686                               const gchar *custom_msg_fmt,
687                               ...)
688 {
689         GError *error;
690         gchar *custom_msg;
691         va_list ap;
692
693         if (!custom_msg_fmt)
694                 return e_data_book_create_error (status, NULL);
695
696         va_start (ap, custom_msg_fmt);
697         custom_msg = g_strdup_vprintf (custom_msg_fmt, ap);
698         va_end (ap);
699
700         error = e_data_book_create_error (status, custom_msg);
701
702         g_free (custom_msg);
703
704         return error;
705 }
706
707 /**
708  * e_data_book_string_slist_to_comma_string:
709  *
710  * Takes a list of strings and converts it to a comma-separated string of
711  * values; free returned pointer with g_free()
712  *
713  * Since: 3.2
714  **/
715 gchar *
716 e_data_book_string_slist_to_comma_string (const GSList *strings)
717 {
718         GString *tmp;
719         gchar *res;
720         const GSList *l;
721
722         tmp = g_string_new ("");
723         for (l = strings; l != NULL; l = l->next) {
724                 const gchar *str = l->data;
725
726                 if (!str)
727                         continue;
728
729                 if (strchr (str, ',')) {
730                         g_warning ("%s: String cannot contain comma; skipping value '%s'\n", G_STRFUNC, str);
731                         continue;
732                 }
733
734                 if (tmp->len)
735                         g_string_append_c (tmp, ',');
736                 g_string_append (tmp, str);
737         }
738
739         res = e_util_utf8_make_valid (tmp->str);
740
741         g_string_free (tmp, TRUE);
742
743         return res;
744 }
745
746 static gboolean
747 data_book_handle_open_cb (EDBusAddressBook *interface,
748                           GDBusMethodInvocation *invocation,
749                           EDataBook *book)
750 {
751         OperationData *op;
752
753         op = op_new (OP_OPEN, book, invocation);
754
755         op_dispatch (book, op);
756
757         return TRUE;
758 }
759
760 static gboolean
761 data_book_handle_refresh_cb (EDBusAddressBook *interface,
762                              GDBusMethodInvocation *invocation,
763                              EDataBook *book)
764 {
765         OperationData *op;
766
767         op = op_new (OP_REFRESH, book, invocation);
768
769         op_dispatch (book, op);
770
771         return TRUE;
772 }
773
774 static gboolean
775 data_book_handle_get_contact_cb (EDBusAddressBook *interface,
776                                  GDBusMethodInvocation *invocation,
777                                  const gchar *in_uid,
778                                  EDataBook *book)
779 {
780         OperationData *op;
781
782         op = op_new (OP_GET_CONTACT, book, invocation);
783         op->d.uid = g_strdup (in_uid);
784
785         op_dispatch (book, op);
786
787         return TRUE;
788 }
789
790 static gboolean
791 data_book_handle_get_contact_list_cb (EDBusAddressBook *interface,
792                                       GDBusMethodInvocation *invocation,
793                                       const gchar *in_query,
794                                       EDataBook *book)
795 {
796         OperationData *op;
797
798         op = op_new (OP_GET_CONTACTS, book, invocation);
799         op->d.query = g_strdup (in_query);
800
801         op_dispatch (book, op);
802
803         return TRUE;
804 }
805
806 static gboolean
807 data_book_handle_get_contact_list_uids_cb (EDBusAddressBook *interface,
808                                            GDBusMethodInvocation *invocation,
809                                            const gchar *in_query,
810                                            EDataBook *book)
811 {
812         OperationData *op;
813
814         op = op_new (OP_GET_CONTACTS_UIDS, book, invocation);
815         op->d.query = g_strdup (in_query);
816
817         op_dispatch (book, op);
818
819         return TRUE;
820 }
821
822 static gboolean
823 data_book_handle_create_contacts_cb (EDBusAddressBook *interface,
824                                      GDBusMethodInvocation *invocation,
825                                      const gchar * const *in_vcards,
826                                      EDataBook *book)
827 {
828         OperationData *op;
829
830         op = op_new (OP_ADD_CONTACTS, book, invocation);
831         op->d.vcards = e_util_strv_to_slist (in_vcards);
832
833         op_dispatch (book, op);
834
835         return TRUE;
836 }
837
838 static gboolean
839 data_book_handle_modify_contacts_cb (EDBusAddressBook *interface,
840                                      GDBusMethodInvocation *invocation,
841                                      const gchar * const *in_vcards,
842                                      EDataBook *book)
843 {
844         OperationData *op;
845
846         op = op_new (OP_MODIFY_CONTACTS, book, invocation);
847         op->d.vcards = e_util_strv_to_slist (in_vcards);
848
849         op_dispatch (book, op);
850
851         return TRUE;
852 }
853
854 static gboolean
855 data_book_handle_remove_contacts_cb (EDBusAddressBook *interface,
856                                      GDBusMethodInvocation *invocation,
857                                      const gchar * const *in_uids,
858                                      EDataBook *book)
859 {
860         OperationData *op;
861
862         op = op_new (OP_REMOVE_CONTACTS, book, invocation);
863
864         /* Allow an empty array to be removed */
865         for (; in_uids && *in_uids; in_uids++) {
866                 op->d.ids = g_slist_prepend (op->d.ids, g_strdup (*in_uids));
867         }
868
869         op_dispatch (book, op);
870
871         return TRUE;
872 }
873
874 static gboolean
875 data_book_handle_get_view_cb (EDBusAddressBook *interface,
876                               GDBusMethodInvocation *invocation,
877                               const gchar *in_query,
878                               EDataBook *book)
879 {
880         OperationData *op;
881
882         op = op_new (OP_GET_VIEW, book, invocation);
883         op->d.query = g_strdup (in_query);
884
885         /* This operation is never queued. */
886         e_operation_pool_push (ops_pool, op);
887
888         return TRUE;
889 }
890
891 static gboolean
892 data_book_handle_close_cb (EDBusAddressBook *interface,
893                            GDBusMethodInvocation *invocation,
894                            EDataBook *book)
895 {
896         OperationData *op;
897
898         op = op_new (OP_CLOSE, book, invocation);
899         /* unref here makes sure the book is freed in a separate thread */
900         g_object_unref (book);
901
902         /* This operation is never queued. */
903         e_operation_pool_push (ops_pool, op);
904
905         return TRUE;
906 }
907
908 void
909 e_data_book_respond_open (EDataBook *book,
910                           guint opid,
911                           GError *error)
912 {
913         OperationData *data;
914         GError *copy = NULL;
915
916         g_return_if_fail (E_IS_DATA_BOOK (book));
917
918         data = op_claim (book, opid);
919         g_return_if_fail (data != NULL);
920
921         /* Translators: This is prefix to a detailed error message */
922         g_prefix_error (&error, "%s", _("Cannot open book: "));
923
924         /* This function is deprecated, but it's the only way to
925          * set EBookBackend's internal 'opened' flag.  We should
926          * be the only ones calling this. */
927         if (error != NULL)
928                 copy = g_error_copy (error);
929         e_book_backend_notify_opened (book->priv->backend, copy);
930
931         if (error == NULL) {
932                 e_dbus_address_book_complete_open (
933                         book->priv->dbus_interface,
934                         data->invocation);
935         } else {
936                 data_book_convert_to_client_error (error);
937                 g_dbus_method_invocation_take_error (
938                         data->invocation, error);
939         }
940
941         op_unref (data);
942
943         /* Dispatch any pending operations. */
944
945         g_mutex_lock (&book->priv->open_lock);
946
947         if (opid == book->priv->open_opid) {
948                 OperationData *op;
949
950                 book->priv->open_opid = 0;
951
952                 while (!g_queue_is_empty (&book->priv->open_queue)) {
953                         op = g_queue_pop_head (&book->priv->open_queue);
954                         e_operation_pool_push (ops_pool, op);
955                 }
956         }
957
958         g_mutex_unlock (&book->priv->open_lock);
959 }
960
961 /**
962  * e_data_book_respond_refresh:
963  * @book: An addressbook client interface.
964  * @error: Operation error, if any, automatically freed if passed it.
965  *
966  * Notifies listeners of the completion of the refresh method call.
967  *
968  * Since: 3.2
969  */
970 void
971 e_data_book_respond_refresh (EDataBook *book,
972                              guint32 opid,
973                              GError *error)
974 {
975         OperationData *data;
976
977         g_return_if_fail (E_IS_DATA_BOOK (book));
978
979         data = op_claim (book, opid);
980         g_return_if_fail (data != NULL);
981
982         /* Translators: This is prefix to a detailed error message */
983         g_prefix_error (&error, "%s", _("Cannot refresh address book: "));
984
985         if (error == NULL) {
986                 e_dbus_address_book_complete_refresh (
987                         book->priv->dbus_interface,
988                         data->invocation);
989         } else {
990                 data_book_convert_to_client_error (error);
991                 g_dbus_method_invocation_take_error (
992                         data->invocation, error);
993         }
994
995         op_unref (data);
996 }
997
998 /**
999  * e_data_book_respond_get_backend_property:
1000  *
1001  * FIXME: Document me.
1002  *
1003  * Since: 3.2
1004  **/
1005 void
1006 e_data_book_respond_get_backend_property (EDataBook *book,
1007                                           guint32 opid,
1008                                           GError *error,
1009                                           const gchar *prop_value)
1010 {
1011         OperationData *data;
1012
1013         g_return_if_fail (E_IS_DATA_BOOK (book));
1014
1015         data = op_claim (book, opid);
1016         g_return_if_fail (data != NULL);
1017
1018         if (error == NULL) {
1019                 e_data_book_report_backend_property_changed (
1020                         book, data->d.prop_name, prop_value);
1021         } else {
1022                 /* This should never happen, since all backend property
1023                  * requests now originate from our constructed() method. */
1024                 g_warning ("%s: %s", G_STRFUNC, error->message);
1025                 g_error_free (error);
1026         }
1027
1028         op_unref (data);
1029 }
1030
1031 /**
1032  * e_data_book_respond_set_backend_property:
1033  *
1034  * FIXME: Document me.
1035  *
1036  * Since: 3.2
1037  *
1038  * Deprecated: 3.8: This function no longer does anything.
1039  **/
1040 void
1041 e_data_book_respond_set_backend_property (EDataBook *book,
1042                                           guint32 opid,
1043                                           GError *error)
1044 {
1045         /* Do nothing. */
1046 }
1047
1048 void
1049 e_data_book_respond_get_contact (EDataBook *book,
1050                                  guint32 opid,
1051                                  GError *error,
1052                                  const gchar *vcard)
1053 {
1054         OperationData *data;
1055
1056         g_return_if_fail (E_IS_DATA_BOOK (book));
1057
1058         data = op_claim (book, opid);
1059         g_return_if_fail (data != NULL);
1060
1061         /* Translators: This is prefix to a detailed error message */
1062         g_prefix_error (&error, "%s", _("Cannot get contact: "));
1063
1064         if (error == NULL) {
1065                 gchar *utf8_vcard;
1066
1067                 utf8_vcard = e_util_utf8_make_valid (vcard);
1068
1069                 e_dbus_address_book_complete_get_contact (
1070                         book->priv->dbus_interface,
1071                         data->invocation,
1072                         utf8_vcard);
1073
1074                 g_free (utf8_vcard);
1075         } else {
1076                 data_book_convert_to_client_error (error);
1077                 g_dbus_method_invocation_take_error (
1078                         data->invocation, error);
1079         }
1080
1081         op_unref (data);
1082 }
1083
1084 void
1085 e_data_book_respond_get_contact_list (EDataBook *book,
1086                                       guint32 opid,
1087                                       GError *error,
1088                                       const GSList *cards)
1089 {
1090         OperationData *data;
1091
1092         g_return_if_fail (E_IS_DATA_BOOK (book));
1093
1094         data = op_claim (book, opid);
1095         g_return_if_fail (data != NULL);
1096
1097         /* Translators: This is prefix to a detailed error message */
1098         g_prefix_error (&error, "%s", _("Cannot get contact list: "));
1099
1100         if (error == NULL) {
1101                 gchar **strv;
1102                 guint length;
1103                 gint ii = 0;
1104
1105                 length = g_slist_length ((GSList *) cards);
1106                 strv = g_new0 (gchar *, length + 1);
1107
1108                 while (cards != NULL) {
1109                         strv[ii++] = e_util_utf8_make_valid (cards->data);
1110                         cards = g_slist_next ((GSList *) cards);
1111                 }
1112
1113                 e_dbus_address_book_complete_get_contact_list (
1114                         book->priv->dbus_interface,
1115                         data->invocation,
1116                         (const gchar * const *) strv);
1117
1118                 g_strfreev (strv);
1119         } else {
1120                 data_book_convert_to_client_error (error);
1121                 g_dbus_method_invocation_take_error (
1122                         data->invocation, error);
1123         }
1124
1125         op_unref (data);
1126 }
1127
1128 /**
1129  * e_data_book_respond_get_contact_list_uids:
1130  *
1131  * FIXME: Document me.
1132  *
1133  * Since: 3.2
1134  **/
1135 void
1136 e_data_book_respond_get_contact_list_uids (EDataBook *book,
1137                                            guint32 opid,
1138                                            GError *error,
1139                                            const GSList *uids)
1140 {
1141         OperationData *data;
1142
1143         g_return_if_fail (E_IS_DATA_BOOK (book));
1144
1145         data = op_claim (book, opid);
1146         g_return_if_fail (data != NULL);
1147
1148         /* Translators: This is prefix to a detailed error message */
1149         g_prefix_error (&error, "%s", _("Cannot get contact list uids: "));
1150
1151         if (error == NULL) {
1152                 gchar **strv;
1153                 guint length;
1154                 gint ii = 0;
1155
1156                 length = g_slist_length ((GSList *) uids);
1157                 strv = g_new0 (gchar *, length + 1);
1158
1159                 while (uids != NULL) {
1160                         strv[ii++] = e_util_utf8_make_valid (uids->data);
1161                         uids = g_slist_next ((GSList *) uids);
1162                 }
1163
1164                 e_dbus_address_book_complete_get_contact_list_uids (
1165                         book->priv->dbus_interface,
1166                         data->invocation,
1167                         (const gchar * const *) strv);
1168
1169                 g_strfreev (strv);
1170         } else {
1171                 data_book_convert_to_client_error (error);
1172                 g_dbus_method_invocation_take_error (
1173                         data->invocation, error);
1174         }
1175
1176         op_unref (data);
1177 }
1178
1179 /**
1180  * e_data_book_respond_create_contacts:
1181  *
1182  * FIXME: Document me!
1183  *
1184  * Since: 3.4
1185  **/
1186 void
1187 e_data_book_respond_create_contacts (EDataBook *book,
1188                                      guint32 opid,
1189                                      GError *error,
1190                                      const GSList *contacts)
1191 {
1192         OperationData *data;
1193
1194         g_return_if_fail (E_IS_DATA_BOOK (book));
1195
1196         data = op_claim (book, opid);
1197         g_return_if_fail (data != NULL);
1198
1199         /* Translators: This is prefix to a detailed error message */
1200         g_prefix_error (&error, "%s", _("Cannot add contact: "));
1201
1202         if (error == NULL) {
1203                 EBookBackend *backend;
1204                 gchar **strv;
1205                 guint length;
1206                 gint ii = 0;
1207
1208                 backend = e_data_book_get_backend (book);
1209
1210                 length = g_slist_length ((GSList *) contacts);
1211                 strv = g_new0 (gchar *, length + 1);
1212
1213                 while (contacts != NULL) {
1214                         EContact *contact = E_CONTACT (contacts->data);
1215                         const gchar *uid;
1216
1217                         uid = e_contact_get_const (contact, E_CONTACT_UID);
1218                         strv[ii++] = e_util_utf8_make_valid (uid);
1219
1220                         e_book_backend_notify_update (backend, contact);
1221
1222                         contacts = g_slist_next ((GSList *) contacts);
1223                 }
1224
1225                 e_dbus_address_book_complete_create_contacts (
1226                         book->priv->dbus_interface,
1227                         data->invocation,
1228                         (const gchar * const *) strv);
1229
1230                 e_book_backend_notify_complete (backend);
1231
1232                 g_strfreev (strv);
1233         } else {
1234                 data_book_convert_to_client_error (error);
1235                 g_dbus_method_invocation_take_error (
1236                         data->invocation, error);
1237         }
1238
1239         op_unref (data);
1240 }
1241
1242 /**
1243  * e_data_book_respond_modify_contacts:
1244  *
1245  * FIXME: Document me!
1246  *
1247  * Since: 3.4
1248  **/
1249 void
1250 e_data_book_respond_modify_contacts (EDataBook *book,
1251                                      guint32 opid,
1252                                      GError *error,
1253                                      const GSList *contacts)
1254 {
1255         OperationData *data;
1256
1257         g_return_if_fail (E_IS_DATA_BOOK (book));
1258
1259         data = op_claim (book, opid);
1260         g_return_if_fail (data != NULL);
1261
1262         /* Translators: This is prefix to a detailed error message */
1263         g_prefix_error (&error, "%s", _("Cannot modify contacts: "));
1264
1265         if (error == NULL) {
1266                 EBookBackend *backend;
1267
1268                 backend = e_data_book_get_backend (book);
1269
1270                 e_dbus_address_book_complete_modify_contacts (
1271                         book->priv->dbus_interface,
1272                         data->invocation);
1273
1274                 while (contacts != NULL) {
1275                         EContact *contact = E_CONTACT (contacts->data);
1276                         e_book_backend_notify_update (backend, contact);
1277                         contacts = g_slist_next ((GSList *) contacts);
1278                 }
1279
1280                 e_book_backend_notify_complete (backend);
1281         } else {
1282                 data_book_convert_to_client_error (error);
1283                 g_dbus_method_invocation_take_error (
1284                         data->invocation, error);
1285         }
1286
1287         op_unref (data);
1288 }
1289
1290 void
1291 e_data_book_respond_remove_contacts (EDataBook *book,
1292                                      guint32 opid,
1293                                      GError *error,
1294                                      const GSList *ids)
1295 {
1296         OperationData *data;
1297
1298         g_return_if_fail (E_IS_DATA_BOOK (book));
1299
1300         data = op_claim (book, opid);
1301         g_return_if_fail (data != NULL);
1302
1303         /* Translators: This is prefix to a detailed error message */
1304         g_prefix_error (&error, "%s", _("Cannot remove contacts: "));
1305
1306         if (error == NULL) {
1307                 EBookBackend *backend;
1308
1309                 backend = e_data_book_get_backend (book);
1310
1311                 e_dbus_address_book_complete_remove_contacts (
1312                         book->priv->dbus_interface,
1313                         data->invocation);
1314
1315                 while (ids != NULL) {
1316                         e_book_backend_notify_remove (backend, ids->data);
1317                         ids = g_slist_next ((GSList *) ids);
1318                 }
1319
1320                 e_book_backend_notify_complete (backend);
1321         } else {
1322                 data_book_convert_to_client_error (error);
1323                 g_dbus_method_invocation_take_error (
1324                         data->invocation, error);
1325         }
1326
1327         op_unref (data);
1328 }
1329
1330 /**
1331  * e_data_book_report_error:
1332  *
1333  * FIXME: Document me.
1334  *
1335  * Since: 3.2
1336  **/
1337 void
1338 e_data_book_report_error (EDataBook *book,
1339                           const gchar *message)
1340 {
1341         g_return_if_fail (E_IS_DATA_BOOK (book));
1342         g_return_if_fail (message != NULL);
1343
1344         e_dbus_address_book_emit_error (book->priv->dbus_interface, message);
1345 }
1346
1347 /**
1348  * e_data_book_report_readonly:
1349  *
1350  * FIXME: Document me.
1351  *
1352  * Since: 3.2
1353  *
1354  * Deprecated: 3.8: Use e_book_backend_set_writable() instead.
1355  **/
1356 void
1357 e_data_book_report_readonly (EDataBook *book,
1358                              gboolean readonly)
1359 {
1360         g_return_if_fail (E_IS_DATA_BOOK (book));
1361
1362         e_book_backend_set_writable (book->priv->backend, !readonly);
1363 }
1364
1365 /**
1366  * e_data_book_report_online:
1367  *
1368  * FIXME: Document me.
1369  *
1370  * Since: 3.2
1371  *
1372  * Deprecated: 3.8: Use e_backend_set_online() instead.
1373  **/
1374 void
1375 e_data_book_report_online (EDataBook *book,
1376                            gboolean is_online)
1377 {
1378         g_return_if_fail (E_IS_DATA_BOOK (book));
1379
1380         e_backend_set_online (E_BACKEND (book->priv->backend), is_online);
1381 }
1382
1383 /**
1384  * e_data_book_report_opened:
1385  *
1386  * Reports to associated client that opening phase of the book is finished.
1387  * error being NULL means successfully, otherwise reports an error which
1388  * happened during opening phase. By opening phase is meant a process
1389  * including successfull authentication to the server/storage.
1390  *
1391  * Since: 3.2
1392  *
1393  * Deprecated: 3.8: This function no longer does anything.
1394  **/
1395 void
1396 e_data_book_report_opened (EDataBook *book,
1397                            const GError *error)
1398 {
1399         /* Do nothing. */
1400 }
1401
1402 /**
1403  * e_data_book_report_backend_property_changed:
1404  *
1405  * FIXME: Document me.
1406  *
1407  * Since: 3.2
1408  **/
1409 void
1410 e_data_book_report_backend_property_changed (EDataBook *book,
1411                                              const gchar *prop_name,
1412                                              const gchar *prop_value)
1413 {
1414         EDBusAddressBook *dbus_interface;
1415         gchar **strv;
1416
1417         g_return_if_fail (E_IS_DATA_BOOK (book));
1418         g_return_if_fail (prop_name != NULL);
1419
1420         if (prop_value == NULL)
1421                 prop_value = "";
1422
1423         dbus_interface = book->priv->dbus_interface;
1424
1425         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
1426                 strv = g_strsplit (prop_value, ",", -1);
1427                 e_dbus_address_book_set_capabilities (
1428                         dbus_interface, (const gchar * const *) strv);
1429                 g_strfreev (strv);
1430         }
1431
1432         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_REVISION))
1433                 e_dbus_address_book_set_revision (dbus_interface, prop_value);
1434
1435         if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
1436                 strv = g_strsplit (prop_value, ",", -1);
1437                 e_dbus_address_book_set_required_fields (
1438                         dbus_interface, (const gchar * const *) strv);
1439                 g_strfreev (strv);
1440         }
1441
1442         if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
1443                 strv = g_strsplit (prop_value, ",", -1);
1444                 e_dbus_address_book_set_supported_fields (
1445                         dbus_interface, (const gchar * const *) strv);
1446                 g_strfreev (strv);
1447         }
1448
1449         /* Disregard anything else. */
1450 }
1451
1452 static void
1453 data_book_set_backend (EDataBook *book,
1454                        EBookBackend *backend)
1455 {
1456         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1457         g_return_if_fail (book->priv->backend == NULL);
1458
1459         book->priv->backend = g_object_ref (backend);
1460 }
1461
1462 static void
1463 data_book_set_connection (EDataBook *book,
1464                           GDBusConnection *connection)
1465 {
1466         g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
1467         g_return_if_fail (book->priv->connection == NULL);
1468
1469         book->priv->connection = g_object_ref (connection);
1470 }
1471
1472 static void
1473 data_book_set_object_path (EDataBook *book,
1474                            const gchar *object_path)
1475 {
1476         g_return_if_fail (object_path != NULL);
1477         g_return_if_fail (book->priv->object_path == NULL);
1478
1479         book->priv->object_path = g_strdup (object_path);
1480 }
1481
1482 static void
1483 data_book_set_property (GObject *object,
1484                         guint property_id,
1485                         const GValue *value,
1486                         GParamSpec *pspec)
1487 {
1488         switch (property_id) {
1489                 case PROP_BACKEND:
1490                         data_book_set_backend (
1491                                 E_DATA_BOOK (object),
1492                                 g_value_get_object (value));
1493                         return;
1494
1495                 case PROP_CONNECTION:
1496                         data_book_set_connection (
1497                                 E_DATA_BOOK (object),
1498                                 g_value_get_object (value));
1499                         return;
1500
1501                 case PROP_OBJECT_PATH:
1502                         data_book_set_object_path (
1503                                 E_DATA_BOOK (object),
1504                                 g_value_get_string (value));
1505                         return;
1506         }
1507
1508         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1509 }
1510
1511 static void
1512 data_book_get_property (GObject *object,
1513                         guint property_id,
1514                         GValue *value,
1515                         GParamSpec *pspec)
1516 {
1517         switch (property_id) {
1518                 case PROP_BACKEND:
1519                         g_value_set_object (
1520                                 value,
1521                                 e_data_book_get_backend (
1522                                 E_DATA_BOOK (object)));
1523                         return;
1524
1525                 case PROP_CONNECTION:
1526                         g_value_set_object (
1527                                 value,
1528                                 e_data_book_get_connection (
1529                                 E_DATA_BOOK (object)));
1530                         return;
1531
1532                 case PROP_OBJECT_PATH:
1533                         g_value_set_string (
1534                                 value,
1535                                 e_data_book_get_object_path (
1536                                 E_DATA_BOOK (object)));
1537                         return;
1538         }
1539
1540         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1541 }
1542
1543 static void
1544 data_book_dispose (GObject *object)
1545 {
1546         EDataBookPrivate *priv;
1547
1548         priv = E_DATA_BOOK_GET_PRIVATE (object);
1549
1550         if (priv->connection != NULL) {
1551                 g_object_unref (priv->connection);
1552                 priv->connection = NULL;
1553         }
1554
1555         if (priv->backend != NULL) {
1556                 g_object_unref (priv->backend);
1557                 priv->backend = NULL;
1558         }
1559
1560         /* Chain up to parent's dispose() metnod. */
1561         G_OBJECT_CLASS (e_data_book_parent_class)->dispose (object);
1562 }
1563
1564 static void
1565 data_book_finalize (GObject *object)
1566 {
1567         EDataBookPrivate *priv;
1568
1569         priv = E_DATA_BOOK_GET_PRIVATE (object);
1570
1571         g_free (priv->object_path);
1572
1573         if (priv->pending_ops) {
1574                 g_hash_table_destroy (priv->pending_ops);
1575                 priv->pending_ops = NULL;
1576         }
1577
1578         g_rec_mutex_clear (&priv->pending_ops_lock);
1579
1580         if (priv->dbus_interface) {
1581                 g_object_unref (priv->dbus_interface);
1582                 priv->dbus_interface = NULL;
1583         }
1584
1585         g_mutex_clear (&priv->open_lock);
1586
1587         /* This should be empty now, else we leak memory. */
1588         g_warn_if_fail (g_queue_is_empty (&priv->open_queue));
1589
1590         /* Chain up to parent's finalize() method. */
1591         G_OBJECT_CLASS (e_data_book_parent_class)->finalize (object);
1592 }
1593
1594 static void
1595 data_book_constructed (GObject *object)
1596 {
1597         EDataBook *book = E_DATA_BOOK (object);
1598         OperationData *op;
1599
1600         /* Chain up to parent's constructed() method. */
1601         G_OBJECT_CLASS (e_data_book_parent_class)->constructed (object);
1602
1603         g_object_bind_property (
1604                 book->priv->backend, "cache-dir",
1605                 book->priv->dbus_interface, "cache-dir",
1606                 G_BINDING_SYNC_CREATE);
1607
1608         g_object_bind_property (
1609                 book->priv->backend, "online",
1610                 book->priv->dbus_interface, "online",
1611                 G_BINDING_SYNC_CREATE);
1612
1613         g_object_bind_property (
1614                 book->priv->backend, "writable",
1615                 book->priv->dbus_interface, "writable",
1616                 G_BINDING_SYNC_CREATE);
1617
1618         /* XXX Initialize the rest of the properties by faking client
1619          *     requests.  At present it's the only way to fish values
1620          *     from EBookBackend's antiquated API. */
1621
1622         op = op_new (OP_GET_BACKEND_PROPERTY, book, NULL);
1623         op->d.prop_name = CLIENT_BACKEND_PROPERTY_CAPABILITIES;
1624         e_book_backend_get_backend_property (
1625                 book->priv->backend, book, op->id,
1626                 op->cancellable, op->d.prop_name);
1627         op_unref (op);
1628
1629         op = op_new (OP_GET_BACKEND_PROPERTY, book, NULL);
1630         op->d.prop_name = CLIENT_BACKEND_PROPERTY_REVISION;
1631         e_book_backend_get_backend_property (
1632                 book->priv->backend, book, op->id,
1633                 op->cancellable, op->d.prop_name);
1634         op_unref (op);
1635
1636         op = op_new (OP_GET_BACKEND_PROPERTY, book, NULL);
1637         op->d.prop_name = BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS;
1638         e_book_backend_get_backend_property (
1639                 book->priv->backend, book, op->id,
1640                 op->cancellable, op->d.prop_name);
1641         op_unref (op);
1642
1643         op = op_new (OP_GET_BACKEND_PROPERTY, book, NULL);
1644         op->d.prop_name = BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS;
1645         e_book_backend_get_backend_property (
1646                 book->priv->backend, book, op->id,
1647                 op->cancellable, op->d.prop_name);
1648         op_unref (op);
1649 }
1650
1651 static gboolean
1652 data_book_initable_init (GInitable *initable,
1653                          GCancellable *cancellable,
1654                          GError **error)
1655 {
1656         EDataBook *book;
1657
1658         book = E_DATA_BOOK (initable);
1659
1660         return g_dbus_interface_skeleton_export (
1661                 G_DBUS_INTERFACE_SKELETON (book->priv->dbus_interface),
1662                 book->priv->connection,
1663                 book->priv->object_path,
1664                 error);
1665 }
1666
1667 static void
1668 e_data_book_class_init (EDataBookClass *class)
1669 {
1670         GObjectClass *object_class;
1671
1672         g_type_class_add_private (class, sizeof (EDataBookPrivate));
1673
1674         object_class = G_OBJECT_CLASS (class);
1675         object_class->set_property = data_book_set_property;
1676         object_class->get_property = data_book_get_property;
1677         object_class->dispose = data_book_dispose;
1678         object_class->finalize = data_book_finalize;
1679         object_class->constructed = data_book_constructed;
1680
1681         g_object_class_install_property (
1682                 object_class,
1683                 PROP_BACKEND,
1684                 g_param_spec_object (
1685                         "backend",
1686                         "Backend",
1687                         "The backend driving this connection",
1688                         E_TYPE_BOOK_BACKEND,
1689                         G_PARAM_READWRITE |
1690                         G_PARAM_CONSTRUCT_ONLY |
1691                         G_PARAM_STATIC_STRINGS));
1692
1693         g_object_class_install_property (
1694                 object_class,
1695                 PROP_CONNECTION,
1696                 g_param_spec_object (
1697                         "connection",
1698                         "Connection",
1699                         "The GDBusConnection on which to "
1700                         "export the address book interface",
1701                         G_TYPE_DBUS_CONNECTION,
1702                         G_PARAM_READWRITE |
1703                         G_PARAM_CONSTRUCT_ONLY |
1704                         G_PARAM_STATIC_STRINGS));
1705
1706         g_object_class_install_property (
1707                 object_class,
1708                 PROP_OBJECT_PATH,
1709                 g_param_spec_string (
1710                         "object-path",
1711                         "Object Path",
1712                         "The object path at which to "
1713                         "export the address book interface",
1714                         NULL,
1715                         G_PARAM_READWRITE |
1716                         G_PARAM_CONSTRUCT_ONLY |
1717                         G_PARAM_STATIC_STRINGS));
1718
1719         if (!ops_pool)
1720                 ops_pool = e_operation_pool_new (10, operation_thread, NULL);
1721 }
1722
1723 static void
1724 e_data_book_initable_init (GInitableIface *interface)
1725 {
1726         interface->init = data_book_initable_init;
1727 }
1728
1729 static void
1730 e_data_book_init (EDataBook *ebook)
1731 {
1732         EDBusAddressBook *dbus_interface;
1733
1734         ebook->priv = E_DATA_BOOK_GET_PRIVATE (ebook);
1735
1736         dbus_interface = e_dbus_address_book_skeleton_new ();
1737         ebook->priv->dbus_interface = dbus_interface;
1738
1739         ebook->priv->pending_ops = g_hash_table_new_full (
1740                 (GHashFunc) g_direct_hash,
1741                 (GEqualFunc) g_direct_equal,
1742                 (GDestroyNotify) NULL,
1743                 (GDestroyNotify) op_unref);
1744         g_rec_mutex_init (&ebook->priv->pending_ops_lock);
1745
1746         g_mutex_init (&ebook->priv->open_lock);
1747
1748         g_signal_connect (
1749                 dbus_interface, "handle-open",
1750                 G_CALLBACK (data_book_handle_open_cb), ebook);
1751         g_signal_connect (
1752                 dbus_interface, "handle-refresh",
1753                 G_CALLBACK (data_book_handle_refresh_cb), ebook);
1754         g_signal_connect (
1755                 dbus_interface, "handle-get-contact",
1756                 G_CALLBACK (data_book_handle_get_contact_cb), ebook);
1757         g_signal_connect (
1758                 dbus_interface, "handle-get-contact-list",
1759                 G_CALLBACK (data_book_handle_get_contact_list_cb), ebook);
1760         g_signal_connect (
1761                 dbus_interface, "handle-get-contact-list-uids",
1762                 G_CALLBACK (data_book_handle_get_contact_list_uids_cb), ebook);
1763         g_signal_connect (
1764                 dbus_interface, "handle-create-contacts",
1765                 G_CALLBACK (data_book_handle_create_contacts_cb), ebook);
1766         g_signal_connect (
1767                 dbus_interface, "handle-modify-contacts",
1768                 G_CALLBACK (data_book_handle_modify_contacts_cb), ebook);
1769         g_signal_connect (
1770                 dbus_interface, "handle-remove-contacts",
1771                 G_CALLBACK (data_book_handle_remove_contacts_cb), ebook);
1772         g_signal_connect (
1773                 dbus_interface, "handle-get-view",
1774                 G_CALLBACK (data_book_handle_get_view_cb), ebook);
1775         g_signal_connect (
1776                 dbus_interface, "handle-close",
1777                 G_CALLBACK (data_book_handle_close_cb), ebook);
1778 }
1779
1780 /**
1781  * e_data_book_new:
1782  * @backend: an #EBookBackend
1783  * @connection: a #GDBusConnection
1784  * @object_path: object path for the D-Bus interface
1785  * @error: return location for a #GError, or %NULL
1786  *
1787  * Creates a new #EDataBook and exports the AddressBook D-Bus interface
1788  * on @connection at @object_path.  The #EDataBook handles incoming remote
1789  * method invocations and forwards them to the @backend.  If the AddressBook
1790  * interface fails to export, the function sets @error and returns %NULL.
1791  *
1792  * Returns: an #EDataBook, or %NULL on error
1793  **/
1794 EDataBook *
1795 e_data_book_new (EBookBackend *backend,
1796                  GDBusConnection *connection,
1797                  const gchar *object_path,
1798                  GError **error)
1799 {
1800         g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
1801         g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
1802         g_return_val_if_fail (object_path != NULL, NULL);
1803
1804         return g_initable_new (
1805                 E_TYPE_DATA_BOOK, NULL, error,
1806                 "backend", backend,
1807                 "connection", connection,
1808                 "object-path", object_path,
1809                 NULL);
1810 }
1811
1812 /**
1813  * e_data_book_get_backend:
1814  * @book: an #EDataBook
1815  *
1816  * Returns the #EBookBackend to which incoming remote method invocations
1817  * are being forwarded.
1818  *
1819  * Returns: the #EBookBackend
1820  **/
1821 EBookBackend *
1822 e_data_book_get_backend (EDataBook *book)
1823 {
1824         g_return_val_if_fail (E_IS_DATA_BOOK (book), NULL);
1825
1826         return book->priv->backend;
1827 }
1828
1829 /**
1830  * e_data_book_get_connection:
1831  * @book: an #EDataBook
1832  *
1833  * Returns the #GDBusConnection on which the AddressBook D-Bus interface
1834  * is exported.
1835  *
1836  * Returns: the #GDBusConnection
1837  *
1838  * Since: 3.8
1839  **/
1840 GDBusConnection *
1841 e_data_book_get_connection (EDataBook *book)
1842 {
1843         g_return_val_if_fail (E_IS_DATA_BOOK (book), NULL);
1844
1845         return book->priv->connection;
1846 }
1847
1848 /**
1849  * e_data_book_get_object_path:
1850  * @book: an #EDataBook
1851  *
1852  * Returns the object path at which the AddressBook D-Bus interface is
1853  * exported.
1854  *
1855  * Returns: the object path
1856  *
1857  * Since: 3.8
1858  **/
1859 const gchar *
1860 e_data_book_get_object_path (EDataBook *book)
1861 {
1862         g_return_val_if_fail (E_IS_DATA_BOOK (book), NULL);
1863
1864         return book->priv->object_path;
1865 }
1866