Camel: Allow SSL certificate resave and use detailed errors from SSL stream
[platform/upstream/evolution-data-server.git] / addressbook / libedata-book / e-book-backend.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Author:
4  *   Nat Friedman (nat@ximian.com)
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  */
8
9 #include <config.h>
10
11 #include <glib/gi18n-lib.h>
12
13 #include <libedataserver/e-data-server-util.h>
14
15 #include "e-data-book-view.h"
16 #include "e-data-book.h"
17 #include "e-book-backend.h"
18
19 #define EDB_OPENING_ERROR       e_data_book_create_error (E_DATA_BOOK_STATUS_BUSY, _("Cannot process, book backend is opening"))
20 #define EDB_NOT_OPENED_ERROR    e_data_book_create_error (E_DATA_BOOK_STATUS_NOT_OPENED, NULL)
21
22 #define E_BOOK_BACKEND_GET_PRIVATE(obj) \
23         (G_TYPE_INSTANCE_GET_PRIVATE \
24         ((obj), E_TYPE_BOOK_BACKEND, EBookBackendPrivate))
25
26 struct _EBookBackendPrivate {
27         GMutex *clients_mutex;
28         GSList *clients;
29
30         gboolean opening, opened, readonly, removed, online;
31
32         GMutex *views_mutex;
33         GSList *views;
34
35         gchar *cache_dir;
36 };
37
38 /* Property IDs */
39 enum {
40         PROP_0,
41         PROP_CACHE_DIR
42 };
43
44 G_DEFINE_TYPE (EBookBackend, e_book_backend, E_TYPE_BACKEND)
45
46 static void
47 book_backend_set_default_cache_dir (EBookBackend *backend)
48 {
49         ESource *source;
50         const gchar *user_cache_dir;
51         gchar *mangled_uri;
52         gchar *filename;
53
54         user_cache_dir = e_get_user_cache_dir ();
55         source = e_backend_get_source (E_BACKEND (backend));
56
57         /* Mangle the URI to not contain invalid characters. */
58         mangled_uri = g_strdelimit (e_source_get_uri (source), ":/", '_');
59
60         filename = g_build_filename (
61                 user_cache_dir, "addressbook", mangled_uri, NULL);
62         e_book_backend_set_cache_dir (backend, filename);
63         g_free (filename);
64
65         g_free (mangled_uri);
66 }
67
68 static void
69 book_backend_get_backend_property (EBookBackend *backend,
70                                    EDataBook *book,
71                                    guint32 opid,
72                                    GCancellable *cancellable,
73                                    const gchar *prop_name)
74 {
75         g_return_if_fail (backend != NULL);
76         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
77         g_return_if_fail (book != NULL);
78         g_return_if_fail (prop_name != NULL);
79
80         if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
81                 e_data_book_respond_get_backend_property (book, opid, NULL, e_book_backend_is_opened (backend) ? "TRUE" : "FALSE");
82         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
83                 e_data_book_respond_get_backend_property (book, opid, NULL, e_book_backend_is_opening (backend) ? "TRUE" : "FALSE");
84         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
85                 e_data_book_respond_get_backend_property (book, opid, NULL, e_backend_get_online (E_BACKEND (backend)) ? "TRUE" : "FALSE");
86         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
87                 e_data_book_respond_get_backend_property (book, opid, NULL, e_book_backend_is_readonly (backend) ? "TRUE" : "FALSE");
88         } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
89                 e_data_book_respond_get_backend_property (book, opid, NULL, e_book_backend_get_cache_dir (backend));
90         } else {
91                 e_data_book_respond_get_backend_property (book, opid, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_NOT_SUPPORTED, _("Unknown book property '%s'"), prop_name), NULL);
92         }
93 }
94
95 static void
96 book_backend_set_backend_property (EBookBackend *backend,
97                                    EDataBook *book,
98                                    guint32 opid,
99                                    GCancellable *cancellable,
100                                    const gchar *prop_name,
101                                    const gchar *prop_value)
102 {
103         g_return_if_fail (backend != NULL);
104         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
105         g_return_if_fail (book != NULL);
106         g_return_if_fail (prop_name != NULL);
107
108         e_data_book_respond_set_backend_property (book, opid, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_NOT_SUPPORTED, _("Cannot change value of book property '%s'"), prop_name));
109 }
110
111 static void
112 book_backend_set_property (GObject *object,
113                            guint property_id,
114                            const GValue *value,
115                            GParamSpec *pspec)
116 {
117         switch (property_id) {
118                 case PROP_CACHE_DIR:
119                         e_book_backend_set_cache_dir (
120                                 E_BOOK_BACKEND (object),
121                                 g_value_get_string (value));
122                         return;
123         }
124
125         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
126 }
127
128 static void
129 book_backend_get_property (GObject *object,
130                            guint property_id,
131                            GValue *value,
132                            GParamSpec *pspec)
133 {
134         switch (property_id) {
135                 case PROP_CACHE_DIR:
136                         g_value_set_string (
137                                 value, e_book_backend_get_cache_dir (
138                                 E_BOOK_BACKEND (object)));
139                         return;
140         }
141
142         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
143 }
144
145 static void
146 book_backend_dispose (GObject *object)
147 {
148         EBookBackendPrivate *priv;
149
150         priv = E_BOOK_BACKEND_GET_PRIVATE (object);
151
152         if (priv->views != NULL) {
153                 g_slist_free (priv->views);
154                 priv->views = NULL;
155         }
156
157         /* Chain up to parent's dispose() method. */
158         G_OBJECT_CLASS (e_book_backend_parent_class)->dispose (object);
159 }
160
161 static void
162 book_backend_finalize (GObject *object)
163 {
164         EBookBackendPrivate *priv;
165
166         priv = E_BOOK_BACKEND_GET_PRIVATE (object);
167
168         g_slist_free (priv->clients);
169
170         g_mutex_free (priv->clients_mutex);
171         g_mutex_free (priv->views_mutex);
172
173         g_free (priv->cache_dir);
174
175         /* Chain up to parent's finalize() method. */
176         G_OBJECT_CLASS (e_book_backend_parent_class)->finalize (object);
177 }
178
179 static gboolean
180 view_notify_update (EDataBookView *view,
181                     gpointer contact)
182 {
183         e_data_book_view_notify_update (view, contact);
184
185         return TRUE;
186 }
187
188 static void
189 book_backend_notify_update (EBookBackend *backend,
190                             const EContact *contact)
191 {
192         e_book_backend_foreach_view (backend, view_notify_update, (gpointer) contact);
193 }
194
195 static void
196 e_book_backend_class_init (EBookBackendClass *class)
197 {
198         GObjectClass *object_class;
199
200         g_type_class_add_private (class, sizeof (EBookBackendPrivate));
201
202         object_class = G_OBJECT_CLASS (class);
203         object_class->set_property = book_backend_set_property;
204         object_class->get_property = book_backend_get_property;
205         object_class->dispose = book_backend_dispose;
206         object_class->finalize = book_backend_finalize;
207
208         class->get_backend_property = book_backend_get_backend_property;
209         class->set_backend_property = book_backend_set_backend_property;
210         class->notify_update        = book_backend_notify_update;
211
212         g_object_class_install_property (
213                 object_class,
214                 PROP_CACHE_DIR,
215                 g_param_spec_string (
216                         "cache-dir",
217                         "Cache Dir",
218                         "The backend's cache directory",
219                         NULL,
220                         G_PARAM_READWRITE |
221                         G_PARAM_STATIC_STRINGS));
222 }
223
224 static void
225 e_book_backend_init (EBookBackend *backend)
226 {
227         backend->priv = E_BOOK_BACKEND_GET_PRIVATE (backend);
228
229         backend->priv->clients = NULL;
230         backend->priv->clients_mutex = g_mutex_new ();
231
232         backend->priv->views = NULL;
233         backend->priv->views_mutex = g_mutex_new ();
234 }
235
236 /**
237  * e_book_backend_get_cache_dir:
238  * @backend: an #EBookBackend
239  *
240  * Returns the cache directory for the given backend.
241  *
242  * Returns: the cache directory for the backend
243  *
244  * Since: 2.32
245  **/
246 const gchar *
247 e_book_backend_get_cache_dir (EBookBackend *backend)
248 {
249         g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
250
251         return backend->priv->cache_dir;
252 }
253
254 /**
255  * e_book_backend_set_cache_dir:
256  * @backend: an #EBookBackend
257  * @cache_dir: a local cache directory
258  *
259  * Sets the cache directory for the given backend.
260  *
261  * Note that #EBookBackend is initialized with a usable default based on
262  * the #ESource given to e_book_backend_open().  Backends should
263  * not override the default without good reason.
264  *
265  * Since: 2.32
266  **/
267 void
268 e_book_backend_set_cache_dir (EBookBackend *backend,
269                               const gchar *cache_dir)
270 {
271         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
272         g_return_if_fail (cache_dir != NULL);
273
274         g_free (backend->priv->cache_dir);
275         backend->priv->cache_dir = g_strdup (cache_dir);
276
277         g_object_notify (G_OBJECT (backend), "cache-dir");
278 }
279
280 /**
281  * e_book_backend_open:
282  * @backend: an #EBookBackend
283  * @book: an #EDataBook
284  * @opid: the ID to use for this operation
285  * @cancellable: a #GCancellable for the operation
286  * @only_if_exists: %TRUE to prevent the creation of a new book
287  *
288  * Executes an 'open' request specified by @opid on @book
289  * using @backend. This call might be finished
290  * with e_data_book_respond_open() or e_book_backend_respond_opened(),
291  * though the overall opening phase finishes only after call
292  * of e_book_backend_notify_opened() after which call the backend
293  * is either fully opened (including authentication against (remote)
294  * server/storage) or an error was encountered during this opening phase.
295  * 'opened' and 'opening' properties are updated automatically.
296  * The backend refuses all other operations until the opening phase is finished.
297  *
298  * The e_book_backend_notify_opened() is called either from this function
299  * or from e_book_backend_authenticate_user(), or after necessary steps
300  * initiated by these two functions.
301  *
302  * The opening phase usually works like this:
303  * 1) client requests open for the backend
304  * 2) server receives this request and calls e_book_backend_open() - the opening phase begun
305  * 3) either the backend is opened during this call, and notifies client
306  *    with e_book_backend_notify_opened() about that. This is usually
307  *    for local backends; their opening phase is finished
308  * 4) or the backend requires authentication, thus it notifies client
309  *    about that with e_book_backend_notify_auth_required() and is
310  *    waiting for credentials, which will be received from client
311  *    by e_book_backend_authenticate_user() call. Backend's opening
312  *    phase is still running in this case, thus it doesn't call
313  *    e_book_backend_notify_opened() within e_book_backend_open() call.
314  * 5) when backend receives credentials in e_book_backend_authenticate_user()
315  *    then it tries to authenticate against a server/storage with them
316  *    and only after it knows result of the authentication, whether user
317  *    was or wasn't authenticated, it notifies client with the result
318  *    by e_book_backend_notify_opened() and it's opening phase is
319  *    finished now. If there was no error returned then the backend is
320  *    considered opened, otherwise it's considered closed. Use AuthenticationFailed
321  *    error when the given credentials were rejected by the server/store, which
322  *    will result in a re-prompt on the client side, otherwise use AuthenticationRequired
323  *    if there was anything wrong with the given credentials. Set error's
324  *    message to a reason for a re-prompt, it'll be shown to a user.
325  * 6) client checks error returned from e_book_backend_notify_opened() and
326  *    reprompts for a password if it was AuthenticationFailed. Otherwise
327  *    considers backend opened based on the error presence (no error means success).
328  *
329  * In any case, the call of e_book_backend_open() should be always finished
330  * with e_data_book_respond_open(), which has no influence on the opening phase,
331  * or alternatively with e_book_backend_respond_opened(). Never use authentication
332  * errors in e_data_book_respond_open() to notify the client the authentication is
333  * required, there is e_book_backend_notify_auth_required() for this.
334  **/
335 void
336 e_book_backend_open (EBookBackend *backend,
337                      EDataBook *book,
338                      guint32 opid,
339                      GCancellable *cancellable,
340                      gboolean only_if_exists)
341 {
342         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
343         g_return_if_fail (E_IS_DATA_BOOK (book));
344
345         g_mutex_lock (backend->priv->clients_mutex);
346
347         if (e_book_backend_is_opened (backend)) {
348                 g_mutex_unlock (backend->priv->clients_mutex);
349
350                 e_data_book_report_readonly (book, backend->priv->readonly);
351                 e_data_book_report_online (book, backend->priv->online);
352
353                 e_book_backend_respond_opened (backend, book, opid, NULL);
354         } else if (e_book_backend_is_opening (backend)) {
355                 g_mutex_unlock (backend->priv->clients_mutex);
356
357                 e_data_book_respond_open (book, opid, EDB_OPENING_ERROR);
358         } else {
359                 backend->priv->opening = TRUE;
360                 g_mutex_unlock (backend->priv->clients_mutex);
361
362                 /* Subclasses may need to call e_book_backend_get_cache_dir() in
363                  * their open() methods, so get the "cache-dir" property
364                  * initialized before we call the method. */
365                 book_backend_set_default_cache_dir (backend);
366
367                 g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->open != NULL);
368
369                 (* E_BOOK_BACKEND_GET_CLASS (backend)->open) (backend, book, opid, cancellable, only_if_exists);
370         }
371 }
372
373 /**
374  * e_book_backend_remove:
375  * @backend: an #EBookBackend
376  * @book: an #EDataBook
377  * @cancellable: a #GCancellable for the operation
378  * @opid: the ID to use for this operation
379  *
380  * Executes a 'remove' request to remove all of @backend's data,
381  * specified by @opid on @book.
382  * This might be finished with e_data_book_respond_remove().
383  **/
384 void
385 e_book_backend_remove (EBookBackend *backend,
386                        EDataBook *book,
387                        guint32 opid,
388                        GCancellable *cancellable)
389 {
390         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
391         g_return_if_fail (E_IS_DATA_BOOK (book));
392         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->remove);
393
394         if (e_book_backend_is_opening (backend))
395                 e_data_book_respond_remove (book, opid, EDB_OPENING_ERROR);
396         else
397                 (* E_BOOK_BACKEND_GET_CLASS (backend)->remove) (backend, book, opid, cancellable);
398 }
399
400 /**
401  * e_book_backend_refresh:
402  * @backend: an #EBookBackend
403  * @book: an #EDataBook
404  * @opid: the ID to use for this operation
405  * @cancellable: a #GCancellable for the operation
406  *
407  * Refreshes the address book being accessed by the given backend.
408  * This might be finished with e_data_book_respond_refresh(),
409  * and it might be called as soon as possible; it doesn't mean
410  * that the refreshing is done after calling that, the backend
411  * is only notifying client whether it started the refresh process
412  * or not.
413  *
414  * Since: 3.2
415  **/
416 void
417 e_book_backend_refresh (EBookBackend *backend,
418                         EDataBook *book,
419                         guint32 opid,
420                         GCancellable *cancellable)
421 {
422         g_return_if_fail (backend != NULL);
423         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
424
425         if (e_book_backend_is_opening (backend))
426                 e_data_book_respond_refresh (book, opid, EDB_OPENING_ERROR);
427         else if (!E_BOOK_BACKEND_GET_CLASS (backend)->refresh)
428                 e_data_book_respond_refresh (book, opid, e_data_book_create_error (E_DATA_BOOK_STATUS_NOT_SUPPORTED, NULL));
429         else if (!e_book_backend_is_opened (backend))
430                 e_data_book_respond_refresh (book, opid, EDB_NOT_OPENED_ERROR);
431         else
432                 (* E_BOOK_BACKEND_GET_CLASS (backend)->refresh) (backend, book, opid, cancellable);
433 }
434
435 /**
436  * e_book_backend_create_contacts
437  * @backend: an #EBookBackend
438  * @book: an #EDataBook
439  * @opid: the ID to use for this operation
440  * @cancellable: a #GCancellable for the operation
441  * @vcards: a #GSList of vCards to add
442  *
443  * Executes a 'create contacts' request specified by @opid on @book
444  * using @backend.
445  * This might be finished with e_data_book_respond_create_contacts().
446  *
447  * Since: 3.4
448  **/
449 void
450 e_book_backend_create_contacts (EBookBackend *backend,
451                                EDataBook *book,
452                                guint32 opid,
453                                GCancellable *cancellable,
454                                const GSList *vcards)
455 {
456         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
457         g_return_if_fail (E_IS_DATA_BOOK (book));
458         g_return_if_fail (vcards);
459         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->create_contacts);
460
461         if (e_book_backend_is_opening (backend))
462                 e_data_book_respond_create_contacts (book, opid, EDB_OPENING_ERROR, NULL);
463         else if (!e_book_backend_is_opened (backend))
464                 e_data_book_respond_create_contacts (book, opid, EDB_NOT_OPENED_ERROR, NULL);
465         else
466                 (* E_BOOK_BACKEND_GET_CLASS (backend)->create_contacts) (backend, book, opid, cancellable, vcards);
467 }
468
469 /**
470  * e_book_backend_remove_contacts:
471  * @backend: an #EBookBackend
472  * @book: an #EDataBook
473  * @opid: the ID to use for this operation
474  * @cancellable: a #GCancellable for the operation
475  * @id_list: list of string IDs to remove
476  *
477  * Executes a 'remove contacts' request specified by @opid on @book
478  * using @backend.
479  * This might be finished with e_data_book_respond_remove_contacts().
480  **/
481 void
482 e_book_backend_remove_contacts (EBookBackend *backend,
483                                 EDataBook *book,
484                                 guint32 opid,
485                                 GCancellable *cancellable,
486                                 const GSList *id_list)
487 {
488         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
489         g_return_if_fail (E_IS_DATA_BOOK (book));
490         g_return_if_fail (id_list);
491         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->remove_contacts);
492
493         if (e_book_backend_is_opening (backend))
494                 e_data_book_respond_remove_contacts (book, opid, EDB_OPENING_ERROR, NULL);
495         else if (!e_book_backend_is_opened (backend))
496                 e_data_book_respond_remove_contacts (book, opid, EDB_NOT_OPENED_ERROR, NULL);
497         else
498                 (* E_BOOK_BACKEND_GET_CLASS (backend)->remove_contacts) (backend, book, opid, cancellable, id_list);
499 }
500
501 /**
502  * e_book_backend_modify_contacts:
503  * @backend: an #EBookBackend
504  * @book: an #EDataBook
505  * @opid: the ID to use for this operation
506  * @cancellable: a #GCancellable for the operation
507  * @vcards: the VCards to update
508  *
509  * Executes a 'modify contacts' request specified by @opid on @book
510  * using @backend.
511  * This might be finished with e_data_book_respond_modify_contacts().
512  *
513  * Since: 3.4
514  **/
515 void
516 e_book_backend_modify_contacts (EBookBackend *backend,
517                                EDataBook *book,
518                                guint32 opid,
519                                GCancellable *cancellable,
520                                const GSList *vcards)
521 {
522         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
523         g_return_if_fail (E_IS_DATA_BOOK (book));
524         g_return_if_fail (vcards);
525         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->modify_contacts);
526
527         if (e_book_backend_is_opening (backend))
528                 e_data_book_respond_modify_contacts (book, opid, EDB_OPENING_ERROR, NULL);
529         else if (!e_book_backend_is_opened (backend))
530                 e_data_book_respond_modify_contacts (book, opid, EDB_NOT_OPENED_ERROR, NULL);
531         else
532                 (* E_BOOK_BACKEND_GET_CLASS (backend)->modify_contacts) (backend, book, opid, cancellable, vcards);
533 }
534
535 /**
536  * e_book_backend_get_contact:
537  * @backend: an #EBookBackend
538  * @book: an #EDataBook
539  * @opid: the ID to use for this operation
540  * @cancellable: a #GCancellable for the operation
541  * @id: the ID of the contact to get
542  *
543  * Executes a 'get contact' request specified by @opid on @book
544  * using @backend.
545  * This might be finished with e_data_book_respond_get_contact().
546  **/
547 void
548 e_book_backend_get_contact (EBookBackend *backend,
549                             EDataBook *book,
550                             guint32 opid,
551                             GCancellable *cancellable,
552                             const gchar *id)
553 {
554         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
555         g_return_if_fail (E_IS_DATA_BOOK (book));
556         g_return_if_fail (id);
557         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->get_contact);
558
559         if (e_book_backend_is_opening (backend))
560                 e_data_book_respond_get_contact (book, opid, EDB_OPENING_ERROR, NULL);
561         else if (!e_book_backend_is_opened (backend))
562                 e_data_book_respond_get_contact (book, opid, EDB_NOT_OPENED_ERROR, NULL);
563         else
564                 (* E_BOOK_BACKEND_GET_CLASS (backend)->get_contact) (backend, book, opid, cancellable, id);
565 }
566
567 /**
568  * e_book_backend_get_contact_list:
569  * @backend: an #EBookBackend
570  * @book: an #EDataBook
571  * @opid: the ID to use for this operation
572  * @cancellable: a #GCancellable for the operation
573  * @query: the s-expression to match
574  *
575  * Executes a 'get contact list' request specified by @opid on @book
576  * using @backend.
577  * This might be finished with e_data_book_respond_get_contact_list().
578  **/
579 void
580 e_book_backend_get_contact_list (EBookBackend *backend,
581                                  EDataBook *book,
582                                  guint32 opid,
583                                  GCancellable *cancellable,
584                                  const gchar *query)
585 {
586         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
587         g_return_if_fail (E_IS_DATA_BOOK (book));
588         g_return_if_fail (query);
589         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list);
590
591         if (e_book_backend_is_opening (backend))
592                 e_data_book_respond_get_contact_list (book, opid, EDB_OPENING_ERROR, NULL);
593         else if (!e_book_backend_is_opened (backend))
594                 e_data_book_respond_get_contact_list (book, opid, EDB_NOT_OPENED_ERROR, NULL);
595         else
596                 (* E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list) (backend, book, opid, cancellable, query);
597 }
598
599 /**
600  * e_book_backend_get_contact_list_uids:
601  * @backend: an #EBookBackend
602  * @book: an #EDataBook
603  * @opid: the ID to use for this operation
604  * @cancellable: a #GCancellable for the operation
605  * @query: the s-expression to match
606  *
607  * Executes a 'get contact list uids' request specified by @opid on @book
608  * using @backend.
609  * This might be finished with e_data_book_respond_get_contact_list_uids().
610  *
611  * Since: 3.2
612  **/
613 void
614 e_book_backend_get_contact_list_uids (EBookBackend *backend,
615                                       EDataBook *book,
616                                       guint32 opid,
617                                       GCancellable *cancellable,
618                                       const gchar *query)
619 {
620         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
621         g_return_if_fail (E_IS_DATA_BOOK (book));
622         g_return_if_fail (query);
623         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list_uids);
624
625         if (e_book_backend_is_opening (backend))
626                 e_data_book_respond_get_contact_list_uids (book, opid, EDB_OPENING_ERROR, NULL);
627         else if (!e_book_backend_is_opened (backend))
628                 e_data_book_respond_get_contact_list_uids (book, opid, EDB_NOT_OPENED_ERROR, NULL);
629         else
630                 (* E_BOOK_BACKEND_GET_CLASS (backend)->get_contact_list_uids) (backend, book, opid, cancellable, query);
631 }
632
633 /**
634  * e_book_backend_start_book_view:
635  * @backend: an #EBookBackend
636  * @view: the #EDataBookView to start
637  *
638  * Starts running the query specified by @view, emitting signals for
639  * matching contacts.
640  **/
641 void
642 e_book_backend_start_book_view (EBookBackend *backend,
643                                 EDataBookView *view)
644 {
645         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
646         g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
647         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->start_book_view);
648
649         (* E_BOOK_BACKEND_GET_CLASS (backend)->start_book_view) (backend, view);
650 }
651
652 /**
653  * e_book_backend_stop_book_view:
654  * @backend: an #EBookBackend
655  * @view: the #EDataBookView to stop
656  *
657  * Stops running the query specified by @view, emitting no more signals.
658  **/
659 void
660 e_book_backend_stop_book_view (EBookBackend *backend,
661                                EDataBookView *view)
662 {
663         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
664         g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
665         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->stop_book_view);
666
667         (* E_BOOK_BACKEND_GET_CLASS (backend)->stop_book_view) (backend, view);
668 }
669
670 /**
671  * e_book_backend_authenticate_user:
672  * @backend: an #EBookBackend
673  * @cancellable: a #GCancellable for the operation
674  * @credentials: #ECredentials to use for authentication
675  *
676  * Notifies @backend about @credentials provided by user to use
677  * for authentication. This notification is usually called during
678  * opening phase as a response to e_book_backend_notify_auth_required()
679  * on the client side and it results in setting property 'opening' to %TRUE
680  * unless the backend is already opened. This function finishes opening
681  * phase, thus it should be finished with e_book_backend_notify_opened().
682  *
683  * See information at e_book_backend_open() for more details
684  * how the opening phase works.
685  **/
686 void
687 e_book_backend_authenticate_user (EBookBackend *backend,
688                                   GCancellable *cancellable,
689                                   ECredentials *credentials)
690 {
691         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
692         g_return_if_fail (credentials != NULL);
693         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->authenticate_user);
694
695         if (backend->priv->opened)
696                 backend->priv->opening = TRUE;
697
698         (* E_BOOK_BACKEND_GET_CLASS (backend)->authenticate_user) (backend, cancellable, credentials);
699 }
700
701 /**
702  * e_book_backend_add_book_view:
703  * @backend: an #EBookBackend
704  * @view: an #EDataBookView
705  *
706  * Adds @view to @backend for querying.
707  **/
708 void
709 e_book_backend_add_book_view (EBookBackend *backend,
710                               EDataBookView *view)
711 {
712         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
713
714         g_mutex_lock (backend->priv->views_mutex);
715
716         e_data_book_view_ref (view);
717         backend->priv->views = g_slist_append (backend->priv->views, view);
718
719         g_mutex_unlock (backend->priv->views_mutex);
720 }
721
722 /**
723  * e_book_backend_remove_book_view:
724  * @backend: an #EBookBackend
725  * @view: an #EDataBookView
726  *
727  * Removes @view from @backend.
728  **/
729 void
730 e_book_backend_remove_book_view (EBookBackend *backend,
731                                  EDataBookView *view)
732 {
733         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
734
735         g_mutex_lock (backend->priv->views_mutex);
736
737         backend->priv->views = g_slist_remove (backend->priv->views, view);
738         e_data_book_view_unref (view);
739
740         g_mutex_unlock (backend->priv->views_mutex);
741 }
742
743 /**
744  * e_book_backend_add_client:
745  * @backend: An addressbook backend.
746  * @book: the corba object representing the client connection.
747  *
748  * Adds a client to an addressbook backend.
749  *
750  * Returns: TRUE on success, FALSE on failure to add the client.
751  */
752 gboolean
753 e_book_backend_add_client (EBookBackend *backend,
754                            EDataBook *book)
755 {
756         g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
757         g_return_val_if_fail (E_IS_DATA_BOOK (book), FALSE);
758
759         g_mutex_lock (backend->priv->clients_mutex);
760         backend->priv->clients = g_slist_prepend (backend->priv->clients, book);
761         g_mutex_unlock (backend->priv->clients_mutex);
762
763         return TRUE;
764 }
765
766 /**
767  * e_book_backend_remove_client:
768  * @backend: an #EBookBackend
769  * @book: an #EDataBook to remove
770  *
771  * Removes @book from the list of @backend's clients.
772  **/
773 void
774 e_book_backend_remove_client (EBookBackend *backend,
775                               EDataBook *book)
776 {
777         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
778         g_return_if_fail (E_IS_DATA_BOOK (book));
779
780         /* Make sure the backend stays alive while holding the mutex. */
781         g_object_ref (backend);
782
783         /* Disconnect */
784         g_mutex_lock (backend->priv->clients_mutex);
785         backend->priv->clients = g_slist_remove (backend->priv->clients, book);
786
787         if (backend->priv->clients == NULL)
788                 backend->priv->opening = FALSE;
789
790         g_mutex_unlock (backend->priv->clients_mutex);
791
792         g_object_unref (backend);
793 }
794
795 /**
796  * e_book_backend_foreach_view:
797  * @backend: an #EBookBackend
798  * @callback: callback to call
799  * @user_data: user_data passed into the @callback
800  *
801  * Calls @callback for each known book view of this @backend.
802  * @callback returns %FALSE to stop further processing.
803  *
804  * Since: 3.2
805  **/
806 void
807 e_book_backend_foreach_view (EBookBackend *backend,
808                              gboolean (*callback) (EDataBookView *view,
809                                                    gpointer user_data),
810                              gpointer user_data)
811 {
812         const GSList *views;
813         EDataBookView *view;
814         gboolean stop = FALSE;
815
816         g_return_if_fail (backend != NULL);
817         g_return_if_fail (callback != NULL);
818
819         g_mutex_lock (backend->priv->views_mutex);
820
821         for (views = backend->priv->views; views && !stop; views = views->next) {
822                 view = E_DATA_BOOK_VIEW (views->data);
823                 stop = !callback (view, user_data);
824         }
825
826         g_mutex_unlock (backend->priv->views_mutex);
827 }
828
829 /**
830  * e_book_backend_get_book_backend_property:
831  * @backend: an #EBookBackend
832  * @book: an #EDataBook
833  * @opid: the ID to use for this operation
834  * @cancellable: a #GCancellable for the operation
835  * @prop_name: property name to get value of; cannot be NULL
836  *
837  * Calls the get_backend_property method on the given backend.
838  * This might be finished with e_data_book_respond_get_backend_property().
839  * Default implementation takes care of common properties and returns
840  * an 'unsupported' error for any unknown properties. The subclass may
841  * always call this default implementation for properties which fetching
842  * it doesn't overwrite.
843  *
844  * Since: 3.2
845  **/
846 void
847 e_book_backend_get_backend_property (EBookBackend *backend,
848                                      EDataBook *book,
849                                      guint32 opid,
850                                      GCancellable *cancellable,
851                                      const gchar *prop_name)
852 {
853         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
854         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->get_backend_property);
855
856         E_BOOK_BACKEND_GET_CLASS (backend)->get_backend_property (backend, book, opid, cancellable, prop_name);
857 }
858
859 /**
860  * e_book_backend_set_backend_property:
861  * @backend: an #EBookBackend
862  * @book: an #EDataBook
863  * @opid: the ID to use for this operation
864  * @cancellable: a #GCancellable for the operation
865  * @prop_name: property name to change; cannot be NULL
866  * @prop_value: value to set to @prop_name; cannot be NULL
867  *
868  * Calls the set_backend_property method on the given backend.
869  * This might be finished with e_data_book_respond_set_backend_property().
870  * Default implementation simply returns an 'unsupported' error.
871  * The subclass may always call this default implementation for properties
872  * which fetching it doesn't overwrite.
873  *
874  * Since: 3.2
875  **/
876 void
877 e_book_backend_set_backend_property (EBookBackend *backend,
878                                      EDataBook *book,
879                                      guint32 opid,
880                                      GCancellable *cancellable,
881                                      const gchar *prop_name,
882                                      const gchar *prop_value)
883 {
884         g_return_if_fail (backend != NULL);
885         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
886         g_return_if_fail (prop_name != NULL);
887         g_return_if_fail (prop_value != NULL);
888         g_return_if_fail (E_BOOK_BACKEND_GET_CLASS (backend)->set_backend_property != NULL);
889
890         E_BOOK_BACKEND_GET_CLASS (backend)->set_backend_property (backend, book, opid, cancellable, prop_name, prop_value);
891 }
892
893 /**
894  * e_book_backend_is_opened:
895  * @backend: an #EBookBackend
896  *
897  * Checks if @backend's storage has been opened (and
898  * authenticated, if necessary) and the backend itself
899  * is ready for accessing. This property is changed automatically
900  * within call of e_book_backend_notify_opened().
901  *
902  * Returns: %TRUE if fully opened, %FALSE otherwise.
903  *
904  * Since: 3.2
905  **/
906 gboolean
907 e_book_backend_is_opened (EBookBackend *backend)
908 {
909         g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
910
911         return backend->priv->opened;
912 }
913
914 /**
915  * e_book_backend_is_opening:
916  * @backend: an #EBookBackend
917  *
918  * Checks if @backend is processing its opening phase, which
919  * includes everything since the e_book_backend_open() call,
920  * through authentication, up to e_book_backend_notify_opened().
921  * This property is managed automatically and the backend deny
922  * every operation except of cancel and authenticate_user while
923  * it is being opening.
924  *
925  * Returns: %TRUE if opening phase is in the effect, %FALSE otherwise.
926  *
927  * Since: 3.2
928  **/
929 gboolean
930 e_book_backend_is_opening (EBookBackend *backend)
931 {
932         g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
933
934         return backend->priv->opening;
935 }
936
937 /**
938  * e_book_backend_is_readonly:
939  * @backend: an #EBookBackend
940  *
941  * Checks if we can write to @backend.
942  *
943  * Returns: %TRUE if writeable, %FALSE if not.
944  *
945  * Since: 3.2
946  **/
947 gboolean
948 e_book_backend_is_readonly (EBookBackend *backend)
949 {
950         g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
951
952         return backend->priv->readonly;
953 }
954
955 /**
956  * e_book_backend_is_removed:
957  * @backend: an #EBookBackend
958  *
959  * Checks if @backend has been removed from its physical storage.
960  *
961  * Returns: %TRUE if @backend has been removed, %FALSE otherwise.
962  **/
963 gboolean
964 e_book_backend_is_removed (EBookBackend *backend)
965 {
966         g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), FALSE);
967
968         return backend->priv->removed;
969 }
970
971 /**
972  * e_book_backend_set_is_removed:
973  * @backend: an #EBookBackend
974  * @is_removed: A flag indicating whether the backend's storage was removed
975  *
976  * Sets the flag indicating whether @backend was removed to @is_removed.
977  * Meant to be used by backend implementations.
978  **/
979 void
980 e_book_backend_set_is_removed (EBookBackend *backend,
981                                gboolean is_removed)
982 {
983         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
984
985         backend->priv->removed = is_removed;
986 }
987
988 /**
989  * e_book_backend_sync:
990  * @backend: an #EBookbackend
991  *
992  * Write all pending data to disk.  This is only required under special
993  * circumstances (for example before a live backup) and should not be used in
994  * normal use.
995  *
996  * Since: 1.12
997  */
998 void
999 e_book_backend_sync (EBookBackend *backend)
1000 {
1001         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1002
1003         g_object_ref (backend);
1004
1005         if (E_BOOK_BACKEND_GET_CLASS (backend)->sync)
1006                 (* E_BOOK_BACKEND_GET_CLASS (backend)->sync) (backend);
1007
1008         g_object_unref (backend);
1009 }
1010
1011 /**
1012  * e_book_backend_notify_update:
1013  * @backend: an #EBookBackend
1014  * @contact: a new or modified contact
1015  *
1016  * Notifies all of @backend's book views about the new or modified
1017  * contacts @contact.
1018  *
1019  * e_data_book_respond_create_contacts() and e_data_book_respond_modify_contacts() call this
1020  * function for you. You only need to call this from your backend if
1021  * contacts are created or modified by another (non-PAS-using) client.
1022  **/
1023 void
1024 e_book_backend_notify_update (EBookBackend *backend,
1025                               const EContact *contact)
1026 {
1027         E_BOOK_BACKEND_GET_CLASS (backend)->notify_update (backend, contact);
1028 }
1029
1030 static gboolean
1031 view_notify_remove (EDataBookView *view,
1032                     gpointer id)
1033 {
1034         e_data_book_view_notify_remove (view, id);
1035
1036         return TRUE;
1037 }
1038
1039 /**
1040  * e_book_backend_notify_remove:
1041  * @backend: an #EBookBackend
1042  * @id: a contact id
1043  *
1044  * Notifies all of @backend's book views that the contact with UID
1045  * @id has been removed.
1046  *
1047  * e_data_book_respond_remove_contacts() calls this function for you. You
1048  * only need to call this from your backend if contacts are removed by
1049  * another (non-PAS-using) client.
1050  **/
1051 void
1052 e_book_backend_notify_remove (EBookBackend *backend,
1053                               const gchar *id)
1054 {
1055         e_book_backend_foreach_view (backend, view_notify_remove, (gpointer) id);
1056 }
1057
1058 static gboolean
1059 view_notify_complete (EDataBookView *view,
1060                       gpointer unused)
1061 {
1062         e_data_book_view_notify_complete (view, NULL /* SUCCESS */);
1063
1064         return TRUE;
1065 }
1066
1067 /**
1068  * e_book_backend_notify_complete:
1069  * @backend: an #EBookbackend
1070  *
1071  * Notifies all of @backend's book views that the current set of
1072  * notifications is complete; use this after a series of
1073  * e_book_backend_notify_update() and e_book_backend_notify_remove() calls.
1074  **/
1075 void
1076 e_book_backend_notify_complete (EBookBackend *backend)
1077 {
1078         e_book_backend_foreach_view (backend, view_notify_complete, NULL);
1079 }
1080
1081 \f
1082 /**
1083  * e_book_backend_notify_error:
1084  * @backend: an #EBookBackend
1085  * @message: an error message
1086  *
1087  * Notifies each backend listener about an error. This is meant to be used
1088  * for cases where is no GError return possibility, to notify user about
1089  * an issue.
1090  *
1091  * Since: 3.2
1092  **/
1093 void
1094 e_book_backend_notify_error (EBookBackend *backend,
1095                              const gchar *message)
1096 {
1097         EBookBackendPrivate *priv;
1098         GSList *clients;
1099
1100         priv = backend->priv;
1101
1102         g_mutex_lock (priv->clients_mutex);
1103
1104         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1105                 e_data_book_report_error (E_DATA_BOOK (clients->data), message);
1106
1107         g_mutex_unlock (priv->clients_mutex);
1108 }
1109
1110 /**
1111  * e_book_backend_notify_readonly:
1112  * @backend: an #EBookBackend
1113  * @is_readonly: flag indicating readonly status
1114  *
1115  * Notifies all backend's clients about the current readonly state.
1116  *
1117  * Since: 3.2
1118  **/
1119 void
1120 e_book_backend_notify_readonly (EBookBackend *backend,
1121                                 gboolean is_readonly)
1122 {
1123         EBookBackendPrivate *priv;
1124         GSList *clients;
1125
1126         priv = backend->priv;
1127         priv->readonly = is_readonly;
1128         g_mutex_lock (priv->clients_mutex);
1129
1130         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1131                 e_data_book_report_readonly (E_DATA_BOOK (clients->data), is_readonly);
1132
1133         g_mutex_unlock (priv->clients_mutex);
1134
1135 }
1136
1137 /**
1138  * e_book_backend_notify_online:
1139  * @backend: an #EBookBackend
1140  * @is_online: flag indicating whether @backend is connected and online
1141  *
1142  * Notifies clients of @backend's connection status indicated by @is_online.
1143  * Meant to be used by backend implementations.
1144  *
1145  * Since: 3.2
1146  **/
1147 void
1148 e_book_backend_notify_online (EBookBackend *backend,
1149                               gboolean is_online)
1150 {
1151         EBookBackendPrivate *priv;
1152         GSList *clients;
1153
1154         priv = backend->priv;
1155         priv->online = is_online;
1156         g_mutex_lock (priv->clients_mutex);
1157
1158         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1159                 e_data_book_report_online (E_DATA_BOOK (clients->data), is_online);
1160
1161         g_mutex_unlock (priv->clients_mutex);
1162 }
1163
1164 /**
1165  * e_book_backend_notify_auth_required:
1166  * @backend: an #EBookBackend
1167  * @is_self: Use %TRUE to indicate the authentication is required
1168  *    for the @backend, otheriwse the authentication is for any
1169  *    other source. Having @credentials %NULL means @is_self
1170  *    automatically.
1171  * @credentials: an #ECredentials that contains extra information for
1172  *    a source for which authentication is requested.
1173  *    This parameter can be %NULL to indicate "for this book".
1174  *
1175  * Notifies clients that @backend requires authentication in order to
1176  * connect. This function call does not influence 'opening', but 
1177  * influences 'opened' property, which is set to %FALSE when @is_self
1178  * is %TRUE or @credentials is %NULL. Opening phase is finished
1179  * by e_book_backend_notify_opened() if this is requested for @backend.
1180  *
1181  * See e_book_backend_open() for a description how the whole opening
1182  * phase works.
1183  *
1184  * Meant to be used by backend implementations.
1185  **/
1186 void
1187 e_book_backend_notify_auth_required (EBookBackend *backend,
1188                                      gboolean is_self,
1189                                      const ECredentials *credentials)
1190 {
1191         EBookBackendPrivate *priv;
1192         GSList *clients;
1193
1194         priv = backend->priv;
1195         g_mutex_lock (priv->clients_mutex);
1196
1197         if (is_self || !credentials)
1198                 priv->opened = FALSE;
1199
1200         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1201                 e_data_book_report_auth_required (E_DATA_BOOK (clients->data), credentials);
1202
1203         g_mutex_unlock (priv->clients_mutex);
1204 }
1205
1206 /**
1207  * e_book_backend_notify_opened:
1208  * @backend: an #EBookBackend
1209  * @error: a #GError corresponding to the error encountered during
1210  *    the opening phase. Use %NULL for success. The @error is freed
1211  *    automatically if not %NULL.
1212  *
1213  * Notifies clients that @backend finished its opening phase.
1214  * See e_book_backend_open() for more information how the opening
1215  * phase works. Calling this function changes 'opening' property,
1216  * same as 'opened'. 'opening' is set to %FALSE and the backend
1217  * is considered 'opened' only if the @error is %NULL.
1218  *
1219  * See also: e_book_backend_respond_opened()
1220  *
1221  * Note: The @error is freed automatically if not %NULL.
1222  *
1223  * Meant to be used by backend implementations.
1224  *
1225  * Since: 3.2
1226  **/
1227 void
1228 e_book_backend_notify_opened (EBookBackend *backend,
1229                               GError *error)
1230 {
1231         EBookBackendPrivate *priv;
1232         GSList *clients;
1233
1234         priv = backend->priv;
1235         g_mutex_lock (priv->clients_mutex);
1236
1237         priv->opening = FALSE;
1238         priv->opened = error == NULL;
1239
1240         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1241                 e_data_book_report_opened (E_DATA_BOOK (clients->data), error);
1242
1243         g_mutex_unlock (priv->clients_mutex);
1244
1245         if (error)
1246                 g_error_free (error);
1247 }
1248
1249 /**
1250  * e_book_backend_notify_property_changed:
1251  * @backend: an #EBookBackend
1252  * @prop_name: property name, which changed
1253  * @prop_value: new property value
1254  *
1255  * Notifies clients about property value change.
1256  *
1257  * Since: 3.2
1258  **/
1259 void
1260 e_book_backend_notify_property_changed (EBookBackend *backend,
1261                                         const gchar *prop_name,
1262                                         const gchar *prop_value)
1263 {
1264         EBookBackendPrivate *priv;
1265         GSList *clients;
1266
1267         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1268         g_return_if_fail (prop_name != NULL);
1269         g_return_if_fail (*prop_name != '\0');
1270         g_return_if_fail (prop_value != NULL);
1271
1272         priv = backend->priv;
1273         g_mutex_lock (priv->clients_mutex);
1274
1275         for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
1276                 e_data_book_report_backend_property_changed (E_DATA_BOOK (clients->data), prop_name, prop_value);
1277
1278         g_mutex_unlock (priv->clients_mutex);
1279 }
1280
1281 /**
1282  * e_book_backend_respond_opened:
1283  * @backend: an #EBookBackend
1284  * @book: an #EDataBook
1285  * @opid: an operation ID
1286  * @error: result error; can be %NULL, if it isn't then it's automatically freed
1287  *
1288  * This is a replacement for e_data_book_respond_open() for cases where
1289  * the finish of 'open' method call also finishes backend opening phase.
1290  * This function covers calling of both e_book_backend_notify_opened()
1291  * and e_data_book_respond_open() with the same @error.
1292  *
1293  * See e_book_backend_open() for more details how the opening phase works.
1294  *
1295  * Since: 3.2
1296  **/
1297 void
1298 e_book_backend_respond_opened (EBookBackend *backend,
1299                                EDataBook *book,
1300                                guint32 opid,
1301                                GError *error)
1302 {
1303         GError *copy = NULL;
1304
1305         g_return_if_fail (backend != NULL);
1306         g_return_if_fail (E_IS_BOOK_BACKEND (backend));
1307         g_return_if_fail (book != NULL);
1308         g_return_if_fail (opid != 0);
1309
1310         if (error)
1311                 copy = g_error_copy (error);
1312
1313         e_book_backend_notify_opened (backend, copy);
1314         e_data_book_respond_open (book, opid, error);
1315 }