1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /**
19 * SECTION: e-book-meta-backend
20 * @include: libedata-book/libedata-book.h
21 * @short_description: An #EBookBackend descendant for book backends
22 *
23 * The #EBookMetaBackend is an abstract #EBookBackend descendant which
24 * aims to implement all evolution-data-server internals for the backend
25 * itself and lefts the backend do as minimum work as possible, like
26 * loading and saving contacts, listing available contacts and so on,
27 * thus the backend implementation can focus on things like converting
28 * (possibly) remote data into vCard objects and back.
29 *
30 * As the #EBookMetaBackend uses an #EBookCache, the offline support
31 * is provided by default.
32 *
33 * The structure is thread safe.
34 **/
35
36 #include "evolution-data-server-config.h"
37
38 #include <glib.h>
39 #include <glib/gi18n-lib.h>
40 #include <glib/gstdio.h>
41
42 #include "e-book-backend-sexp.h"
43 #include "e-book-backend.h"
44 #include "e-data-book-cursor-cache.h"
45 #include "e-data-book-factory.h"
46
47 #include "e-book-meta-backend.h"
48
49 #define EBMB_KEY_SYNC_TAG "ebmb::sync-tag"
50 #define EBMB_KEY_EVER_CONNECTED "ebmb::ever-connected"
51 #define EBMB_KEY_CONNECTED_WRITABLE "ebmb::connected-writable"
52
53 #define LOCAL_PREFIX "file://"
54
55 /* How many times can repeat an operation when credentials fail. */
56 #define MAX_REPEAT_COUNT 3
57
58 /* How long to wait for credentials, in seconds, during the operation repeat cycle */
59 #define MAX_WAIT_FOR_CREDENTIALS_SECS 60
60
61 struct _EBookMetaBackendPrivate {
62 GMutex connect_lock;
63 GMutex property_lock;
64 GMutex wait_credentials_lock;
65 GCond wait_credentials_cond;
66 guint wait_credentials_stamp;
67 GError *create_cache_error;
68 EBookCache *cache;
69 ENamedParameters *last_credentials;
70 GHashTable *view_cancellables;
71 GCancellable *refresh_cancellable; /* Set when refreshing the content */
72 GCancellable *source_changed_cancellable; /* Set when processing source changed signal */
73 GCancellable *go_offline_cancellable; /* Set when going offline */
74 gboolean current_online_state; /* The only state of the internal structures;
75 used to detect false notifications on EBackend::online */
76 gulong source_changed_id;
77 gulong notify_online_id;
78 gulong revision_changed_id;
79 guint refresh_timeout_id;
80
81 gboolean refresh_after_authenticate;
82 gint ever_connected;
83 gint connected_writable;
84 gint64 last_refresh_time; /* The time when the last refresh was called */
85
86 /* Last successful connect data, for some extensions */
87 guint16 authentication_port;
88 gchar *authentication_host;
89 gchar *authentication_user;
90 gchar *authentication_method;
91 gchar *authentication_proxy_uid;
92 gchar *authentication_credential_name;
93 SoupURI *webdav_soup_uri;
94
95 GSList *cursors;
96 };
97
98 enum {
99 PROP_0,
100 PROP_CACHE
101 };
102
103 enum {
104 REFRESH_COMPLETED,
105 SOURCE_CHANGED,
106 LAST_SIGNAL
107 };
108
109 static guint signals[LAST_SIGNAL];
110
111 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (EBookMetaBackend, e_book_meta_backend, E_TYPE_BOOK_BACKEND_SYNC)
112
113 G_DEFINE_BOXED_TYPE (EBookMetaBackendInfo, e_book_meta_backend_info, e_book_meta_backend_info_copy, e_book_meta_backend_info_free)
114
115 static void ebmb_schedule_source_changed (EBookMetaBackend *meta_backend);
116 static void ebmb_schedule_go_offline (EBookMetaBackend *meta_backend);
117 static gboolean ebmb_save_contact_wrapper_sync (EBookMetaBackend *meta_backend,
118 EBookCache *book_cache,
119 gboolean overwrite_existing,
120 EConflictResolution conflict_resolution,
121 /* const */ EContact *in_contact,
122 const gchar *extra,
123 guint32 opflags,
124 const gchar *orig_uid,
125 gboolean *out_requires_put,
126 gchar **out_new_uid,
127 gchar **out_new_extra,
128 GCancellable *cancellable,
129 GError **error);
130
131 /**
132 * e_book_meta_backend_info_new:
133 * @uid: a contact UID; cannot be %NULL
134 * @revision: (nullable): the contact revision; can be %NULL
135 * @object: (nullable): the contact object as a vCard string; can be %NULL
136 * @extra: (nullable): extra backend-specific data; can be %NULL
137 *
138 * Creates a new #EBookMetaBackendInfo prefilled with the given values.
139 *
140 * Returns: (transfer full): A new #EBookMetaBackendInfo. Free it with
141 * e_book_meta_backend_info_free(), when no longer needed.
142 *
143 * Since: 3.26
144 **/
145 EBookMetaBackendInfo *
e_book_meta_backend_info_new(const gchar * uid,const gchar * revision,const gchar * object,const gchar * extra)146 e_book_meta_backend_info_new (const gchar *uid,
147 const gchar *revision,
148 const gchar *object,
149 const gchar *extra)
150 {
151 EBookMetaBackendInfo *info;
152
153 g_return_val_if_fail (uid != NULL, NULL);
154
155 info = g_slice_new0 (EBookMetaBackendInfo);
156 info->uid = g_strdup (uid);
157 info->revision = g_strdup (revision);
158 info->object = g_strdup (object);
159 info->extra = g_strdup (extra);
160
161 return info;
162 }
163
164 /**
165 * e_book_meta_backend_info_copy:
166 * @src: (nullable): a source EBookMetaBackendInfo to copy, or %NULL
167 *
168 * Returns: (transfer full): Copy of the given @src. Free it with
169 * e_book_meta_backend_info_free() when no longer needed.
170 * If the @src is %NULL, then returns %NULL as well.
171 *
172 * Since: 3.26
173 **/
174 EBookMetaBackendInfo *
e_book_meta_backend_info_copy(const EBookMetaBackendInfo * src)175 e_book_meta_backend_info_copy (const EBookMetaBackendInfo *src)
176 {
177 if (!src)
178 return NULL;
179
180 return e_book_meta_backend_info_new (src->uid, src->revision, src->object, src->extra);
181 }
182
183 /**
184 * e_book_meta_backend_info_free:
185 * @ptr: (nullable): an #EBookMetaBackendInfo
186 *
187 * Frees the @ptr structure, previously allocated with e_book_meta_backend_info_new()
188 * or e_book_meta_backend_info_copy().
189 *
190 * Since: 3.26
191 **/
192 void
e_book_meta_backend_info_free(gpointer ptr)193 e_book_meta_backend_info_free (gpointer ptr)
194 {
195 EBookMetaBackendInfo *info = ptr;
196
197 if (info) {
198 g_free (info->uid);
199 g_free (info->revision);
200 g_free (info->object);
201 g_free (info->extra);
202 g_slice_free (EBookMetaBackendInfo, info);
203 }
204 }
205
206 /* Unref returned cancellable with g_object_unref(), when done with it */
207 static GCancellable *
ebmb_create_view_cancellable(EBookMetaBackend * meta_backend,EDataBookView * view)208 ebmb_create_view_cancellable (EBookMetaBackend *meta_backend,
209 EDataBookView *view)
210 {
211 GCancellable *cancellable;
212
213 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), NULL);
214 g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (view), NULL);
215
216 g_mutex_lock (&meta_backend->priv->property_lock);
217
218 cancellable = g_cancellable_new ();
219 g_hash_table_insert (meta_backend->priv->view_cancellables, view, g_object_ref (cancellable));
220
221 g_mutex_unlock (&meta_backend->priv->property_lock);
222
223 return cancellable;
224 }
225
226 static GCancellable *
ebmb_steal_view_cancellable(EBookMetaBackend * meta_backend,EDataBookView * view)227 ebmb_steal_view_cancellable (EBookMetaBackend *meta_backend,
228 EDataBookView *view)
229 {
230 GCancellable *cancellable;
231
232 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), NULL);
233 g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (view), NULL);
234
235 g_mutex_lock (&meta_backend->priv->property_lock);
236
237 cancellable = g_hash_table_lookup (meta_backend->priv->view_cancellables, view);
238 if (cancellable) {
239 g_object_ref (cancellable);
240 g_hash_table_remove (meta_backend->priv->view_cancellables, view);
241 }
242
243 g_mutex_unlock (&meta_backend->priv->property_lock);
244
245 return cancellable;
246 }
247
248 static void
ebmb_update_connection_values(EBookMetaBackend * meta_backend)249 ebmb_update_connection_values (EBookMetaBackend *meta_backend)
250 {
251 ESource *source;
252
253 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
254
255 source = e_backend_get_source (E_BACKEND (meta_backend));
256
257 g_mutex_lock (&meta_backend->priv->property_lock);
258
259 meta_backend->priv->authentication_port = 0;
260 g_clear_pointer (&meta_backend->priv->authentication_host, g_free);
261 g_clear_pointer (&meta_backend->priv->authentication_user, g_free);
262 g_clear_pointer (&meta_backend->priv->authentication_method, g_free);
263 g_clear_pointer (&meta_backend->priv->authentication_proxy_uid, g_free);
264 g_clear_pointer (&meta_backend->priv->authentication_credential_name, g_free);
265 g_clear_pointer (&meta_backend->priv->webdav_soup_uri, (GDestroyNotify) soup_uri_free);
266
267 if (source && e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
268 ESourceAuthentication *auth_extension;
269
270 auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
271
272 meta_backend->priv->authentication_port = e_source_authentication_get_port (auth_extension);
273 meta_backend->priv->authentication_host = e_source_authentication_dup_host (auth_extension);
274 meta_backend->priv->authentication_user = e_source_authentication_dup_user (auth_extension);
275 meta_backend->priv->authentication_method = e_source_authentication_dup_method (auth_extension);
276 meta_backend->priv->authentication_proxy_uid = e_source_authentication_dup_proxy_uid (auth_extension);
277 meta_backend->priv->authentication_credential_name = e_source_authentication_dup_credential_name (auth_extension);
278 }
279
280 if (source && e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) {
281 ESourceWebdav *webdav_extension;
282
283 webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
284
285 meta_backend->priv->webdav_soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
286 }
287
288 g_mutex_unlock (&meta_backend->priv->property_lock);
289
290 e_book_meta_backend_set_ever_connected (meta_backend, TRUE);
291 e_book_meta_backend_set_connected_writable (meta_backend, e_book_backend_get_writable (E_BOOK_BACKEND (meta_backend)));
292 }
293
294 static gboolean
ebmb_gather_locally_cached_objects_cb(EBookCache * book_cache,const gchar * uid,const gchar * revision,const gchar * object,const gchar * extra,guint32 custom_flags,EOfflineState offline_state,gpointer user_data)295 ebmb_gather_locally_cached_objects_cb (EBookCache *book_cache,
296 const gchar *uid,
297 const gchar *revision,
298 const gchar *object,
299 const gchar *extra,
300 guint32 custom_flags,
301 EOfflineState offline_state,
302 gpointer user_data)
303 {
304 GHashTable *locally_cached = user_data;
305
306 g_return_val_if_fail (uid != NULL, FALSE);
307 g_return_val_if_fail (locally_cached != NULL, FALSE);
308
309 if (offline_state == E_OFFLINE_STATE_SYNCED) {
310 g_hash_table_insert (locally_cached,
311 g_strdup (uid),
312 g_strdup (revision));
313 }
314
315 return TRUE;
316 }
317
318 static gboolean
ebmb_get_changes_sync(EBookMetaBackend * meta_backend,const gchar * last_sync_tag,gboolean is_repeat,gchar ** out_new_sync_tag,gboolean * out_repeat,GSList ** out_created_objects,GSList ** out_modified_objects,GSList ** out_removed_objects,GCancellable * cancellable,GError ** error)319 ebmb_get_changes_sync (EBookMetaBackend *meta_backend,
320 const gchar *last_sync_tag,
321 gboolean is_repeat,
322 gchar **out_new_sync_tag,
323 gboolean *out_repeat,
324 GSList **out_created_objects,
325 GSList **out_modified_objects,
326 GSList **out_removed_objects,
327 GCancellable *cancellable,
328 GError **error)
329 {
330 EBackend *backend;
331 GSList *existing_objects = NULL;
332 gboolean success;
333
334 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
335 g_return_val_if_fail (out_created_objects, FALSE);
336 g_return_val_if_fail (out_modified_objects, FALSE);
337 g_return_val_if_fail (out_removed_objects, FALSE);
338
339 *out_created_objects = NULL;
340 *out_modified_objects = NULL;
341 *out_removed_objects = NULL;
342
343 backend = E_BACKEND (meta_backend);
344
345 if (!e_backend_get_online (backend) &&
346 !e_backend_is_destination_reachable (backend, cancellable, NULL))
347 return TRUE;
348 else
349 e_backend_set_online (backend, TRUE);
350
351 if (!e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error) ||
352 !e_book_meta_backend_list_existing_sync (meta_backend, out_new_sync_tag, &existing_objects, cancellable, error)) {
353 return FALSE;
354 }
355
356 success = e_book_meta_backend_split_changes_sync (meta_backend, existing_objects, out_created_objects,
357 out_modified_objects, out_removed_objects, cancellable, error);
358
359 g_slist_free_full (existing_objects, e_book_meta_backend_info_free);
360
361 return success;
362 }
363
364 static gboolean
ebmb_search_sync(EBookMetaBackend * meta_backend,const gchar * expr,gboolean meta_contact,GSList ** out_contacts,GCancellable * cancellable,GError ** error)365 ebmb_search_sync (EBookMetaBackend *meta_backend,
366 const gchar *expr,
367 gboolean meta_contact,
368 GSList **out_contacts,
369 GCancellable *cancellable,
370 GError **error)
371 {
372 EBookCache *book_cache;
373 gboolean success;
374
375 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
376 g_return_val_if_fail (out_contacts != NULL, FALSE);
377
378 *out_contacts = NULL;
379 book_cache = e_book_meta_backend_ref_cache (meta_backend);
380
381 g_return_val_if_fail (book_cache != NULL, FALSE);
382
383 success = e_book_cache_search (book_cache, expr, meta_contact, out_contacts, cancellable, error);
384
385 if (success) {
386 GSList *link;
387
388 for (link = *out_contacts; link; link = g_slist_next (link)) {
389 EBookCacheSearchData *search_data = link->data;
390 EContact *contact = NULL;
391
392 if (search_data) {
393 contact = e_contact_new_from_vcard_with_uid (search_data->vcard, search_data->uid);
394 e_book_cache_search_data_free (search_data);
395 }
396
397 link->data = contact;
398 }
399 }
400
401 g_object_unref (book_cache);
402
403 return success;
404 }
405
406 static gboolean
ebmb_search_uids_sync(EBookMetaBackend * meta_backend,const gchar * expr,GSList ** out_uids,GCancellable * cancellable,GError ** error)407 ebmb_search_uids_sync (EBookMetaBackend *meta_backend,
408 const gchar *expr,
409 GSList **out_uids,
410 GCancellable *cancellable,
411 GError **error)
412 {
413 EBookCache *book_cache;
414 gboolean success;
415
416 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
417 g_return_val_if_fail (out_uids != NULL, FALSE);
418
419 *out_uids = NULL;
420
421 book_cache = e_book_meta_backend_ref_cache (meta_backend);
422 g_return_val_if_fail (book_cache != NULL, FALSE);
423
424 success = e_book_cache_search_uids (book_cache, expr, out_uids, cancellable, error);
425
426 g_object_unref (book_cache);
427
428 return success;
429 }
430
431 static gboolean
ebmb_requires_reconnect(EBookMetaBackend * meta_backend)432 ebmb_requires_reconnect (EBookMetaBackend *meta_backend)
433 {
434 ESource *source;
435 gboolean requires = FALSE;
436
437 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
438
439 source = e_backend_get_source (E_BACKEND (meta_backend));
440 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
441
442 g_mutex_lock (&meta_backend->priv->property_lock);
443
444 if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
445 ESourceAuthentication *auth_extension;
446
447 auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
448
449 e_source_extension_property_lock (E_SOURCE_EXTENSION (auth_extension));
450
451 requires = meta_backend->priv->authentication_port != e_source_authentication_get_port (auth_extension) ||
452 g_strcmp0 (meta_backend->priv->authentication_host, e_source_authentication_get_host (auth_extension)) != 0 ||
453 g_strcmp0 (meta_backend->priv->authentication_user, e_source_authentication_get_user (auth_extension)) != 0 ||
454 g_strcmp0 (meta_backend->priv->authentication_method, e_source_authentication_get_method (auth_extension)) != 0 ||
455 g_strcmp0 (meta_backend->priv->authentication_proxy_uid, e_source_authentication_get_proxy_uid (auth_extension)) != 0 ||
456 g_strcmp0 (meta_backend->priv->authentication_credential_name, e_source_authentication_get_credential_name (auth_extension)) != 0;
457
458 e_source_extension_property_unlock (E_SOURCE_EXTENSION (auth_extension));
459 }
460
461 if (!requires && e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) {
462 ESourceWebdav *webdav_extension;
463 SoupURI *soup_uri;
464
465 webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
466 soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
467
468 requires = (!meta_backend->priv->webdav_soup_uri && soup_uri) ||
469 (soup_uri && meta_backend->priv->webdav_soup_uri &&
470 !soup_uri_equal (meta_backend->priv->webdav_soup_uri, soup_uri));
471
472 if (soup_uri)
473 soup_uri_free (soup_uri);
474 }
475
476 g_mutex_unlock (&meta_backend->priv->property_lock);
477
478 return requires;
479 }
480
481 static gboolean
ebmb_get_ssl_error_details(EBookMetaBackend * meta_backend,gchar ** out_certificate_pem,GTlsCertificateFlags * out_certificate_errors)482 ebmb_get_ssl_error_details (EBookMetaBackend *meta_backend,
483 gchar **out_certificate_pem,
484 GTlsCertificateFlags *out_certificate_errors)
485 {
486 return FALSE;
487 }
488
489 static GSList * /* gchar * */
ebmb_gather_photos_local_filenames(EBookMetaBackend * meta_backend,EContact * contact)490 ebmb_gather_photos_local_filenames (EBookMetaBackend *meta_backend,
491 EContact *contact)
492 {
493 EBookCache *book_cache;
494 GList *attributes, *link;
495 GSList *filenames = NULL;
496 gchar *cache_path;
497
498 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), NULL);
499 g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
500
501 book_cache = e_book_meta_backend_ref_cache (meta_backend);
502 g_return_val_if_fail (book_cache != NULL, NULL);
503
504 cache_path = g_path_get_dirname (e_cache_get_filename (E_CACHE (book_cache)));
505
506 g_object_unref (book_cache);
507
508 attributes = e_vcard_get_attributes (E_VCARD (contact));
509
510 for (link = attributes; link; link = g_list_next (link)) {
511 EVCardAttribute *attr = link->data;
512 const gchar *attr_name;
513 GList *values;
514
515 attr_name = e_vcard_attribute_get_name (attr);
516 if (!attr_name || (
517 g_ascii_strcasecmp (attr_name, EVC_PHOTO) != 0 &&
518 g_ascii_strcasecmp (attr_name, EVC_LOGO) != 0)) {
519 continue;
520 }
521
522 values = e_vcard_attribute_get_param (attr, EVC_VALUE);
523 if (values && g_ascii_strcasecmp (values->data, "uri") == 0) {
524 gchar *url;
525
526 url = e_vcard_attribute_get_value (attr);
527 if (url && g_str_has_prefix (url, LOCAL_PREFIX)) {
528 gchar *filename;
529
530 filename = g_filename_from_uri (url, NULL, NULL);
531 if (filename && g_str_has_prefix (filename, cache_path))
532 filenames = g_slist_prepend (filenames, filename);
533 else
534 g_free (filename);
535 }
536
537 g_free (url);
538 }
539 }
540
541 g_free (cache_path);
542
543 return filenames;
544 }
545
546 static void
ebmb_start_view_thread_func(EBookBackend * book_backend,gpointer user_data,GCancellable * cancellable,GError ** error)547 ebmb_start_view_thread_func (EBookBackend *book_backend,
548 gpointer user_data,
549 GCancellable *cancellable,
550 GError **error)
551 {
552 EDataBookView *view = user_data;
553 EBookBackendSExp *sexp;
554 GSList *contacts = NULL;
555 const gchar *expr = NULL;
556 gboolean meta_contact = FALSE;
557 GHashTable *fields_of_interest;
558 GError *local_error = NULL;
559
560 g_return_if_fail (E_IS_BOOK_META_BACKEND (book_backend));
561 g_return_if_fail (E_IS_DATA_BOOK_VIEW (view));
562
563 if (g_cancellable_set_error_if_cancelled (cancellable, error))
564 return;
565
566 /* Fill the view with known (locally stored) contacts satisfying the expression */
567 sexp = e_data_book_view_get_sexp (view);
568 if (sexp)
569 expr = e_book_backend_sexp_text (sexp);
570
571 fields_of_interest = e_data_book_view_get_fields_of_interest (view);
572 if (fields_of_interest && g_hash_table_size (fields_of_interest) == 2) {
573 GHashTableIter iter;
574 gpointer key, value;
575
576 meta_contact = TRUE;
577
578 g_hash_table_iter_init (&iter, fields_of_interest);
579 while (g_hash_table_iter_next (&iter, &key, &value)) {
580 const gchar *field_name = key;
581 EContactField field = e_contact_field_id (field_name);
582
583 if (field != E_CONTACT_UID &&
584 field != E_CONTACT_REV) {
585 meta_contact = FALSE;
586 break;
587 }
588 }
589 }
590
591 if (e_book_meta_backend_search_sync (E_BOOK_META_BACKEND (book_backend), expr, meta_contact, &contacts, cancellable, &local_error) && contacts) {
592 if (!g_cancellable_is_cancelled (cancellable)) {
593 GSList *link;
594
595 for (link = contacts; link; link = g_slist_next (link)) {
596 EContact *contact = link->data;
597 gchar *vcard;
598
599 if (!contact)
600 continue;
601
602 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
603 e_data_book_view_notify_update_prefiltered_vcard (view,
604 e_contact_get_const (contact, E_CONTACT_UID),
605 vcard);
606
607 g_free (vcard);
608 }
609 }
610
611 g_slist_free_full (contacts, g_object_unref);
612 }
613
614 e_data_book_view_notify_complete (view, local_error);
615
616 g_clear_error (&local_error);
617 }
618
619 static gboolean
ebmb_upload_local_changes_sync(EBookMetaBackend * meta_backend,EBookCache * book_cache,EConflictResolution conflict_resolution,GCancellable * cancellable,GError ** error)620 ebmb_upload_local_changes_sync (EBookMetaBackend *meta_backend,
621 EBookCache *book_cache,
622 EConflictResolution conflict_resolution,
623 GCancellable *cancellable,
624 GError **error)
625 {
626 GSList *offline_changes, *link;
627 GHashTable *covered_uids;
628 ECache *cache;
629 gboolean success = TRUE;
630
631 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
632 g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE);
633
634 cache = E_CACHE (book_cache);
635 covered_uids = g_hash_table_new (g_str_hash, g_str_equal);
636
637 offline_changes = e_cache_get_offline_changes (cache, cancellable, error);
638 for (link = offline_changes; link && success; link = g_slist_next (link)) {
639 ECacheOfflineChange *change = link->data;
640 gchar *extra = NULL;
641 guint32 opflags = 0;
642
643 success = !g_cancellable_set_error_if_cancelled (cancellable, error);
644 if (!success)
645 break;
646
647 if (!change || g_hash_table_contains (covered_uids, change->uid))
648 continue;
649
650 g_hash_table_insert (covered_uids, change->uid, NULL);
651
652 if (!e_book_cache_get_contact_extra (book_cache, change->uid, &extra, cancellable, NULL))
653 extra = NULL;
654
655 if (!e_book_cache_get_contact_custom_flags (book_cache, change->uid, &opflags, cancellable, NULL))
656 opflags = 0;
657
658 if (change->state == E_OFFLINE_STATE_LOCALLY_CREATED ||
659 change->state == E_OFFLINE_STATE_LOCALLY_MODIFIED) {
660 EContact *contact = NULL;
661
662 success = e_book_cache_get_contact (book_cache, change->uid, FALSE, &contact, cancellable, error);
663 if (success) {
664 success = ebmb_save_contact_wrapper_sync (meta_backend, book_cache,
665 change->state == E_OFFLINE_STATE_LOCALLY_MODIFIED,
666 conflict_resolution, contact, extra, opflags, change->uid, NULL, NULL, NULL, cancellable, error);
667 }
668
669 g_clear_object (&contact);
670 } else if (change->state == E_OFFLINE_STATE_LOCALLY_DELETED) {
671 GError *local_error = NULL;
672
673 success = e_book_meta_backend_remove_contact_sync (meta_backend, conflict_resolution,
674 change->uid, extra, change->object, opflags, cancellable, &local_error);
675
676 if (!success) {
677 if (g_error_matches (local_error, E_BOOK_CLIENT_ERROR, E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND)) {
678 g_clear_error (&local_error);
679 success = TRUE;
680 } else if (local_error) {
681 g_propagate_error (error, local_error);
682 }
683 }
684 } else {
685 g_warn_if_reached ();
686 }
687
688 g_free (extra);
689 }
690
691 g_slist_free_full (offline_changes, e_cache_offline_change_free);
692 g_hash_table_destroy (covered_uids);
693
694 if (success)
695 success = e_cache_clear_offline_changes (cache, cancellable, error);
696
697 return success;
698 }
699
700 static void
ebmb_foreach_cursor(EBookMetaBackend * meta_backend,EContact * contact,void (* func)(EDataBookCursor * cursor,EContact * contact))701 ebmb_foreach_cursor (EBookMetaBackend *meta_backend,
702 EContact *contact,
703 void (* func) (EDataBookCursor *cursor, EContact *contact))
704 {
705 GSList *link;
706
707 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
708 g_return_if_fail (func != NULL);
709
710 g_mutex_lock (&meta_backend->priv->property_lock);
711
712 for (link = meta_backend->priv->cursors; link; link = g_slist_next (link)) {
713 EDataBookCursor *cursor = link->data;
714
715 func (cursor, contact);
716 }
717
718 g_mutex_unlock (&meta_backend->priv->property_lock);
719 }
720
721 static gboolean
ebmb_maybe_remove_from_cache(EBookMetaBackend * meta_backend,EBookCache * book_cache,ECacheOfflineFlag offline_flag,const gchar * uid,guint32 opflags,GCancellable * cancellable,GError ** error)722 ebmb_maybe_remove_from_cache (EBookMetaBackend *meta_backend,
723 EBookCache *book_cache,
724 ECacheOfflineFlag offline_flag,
725 const gchar *uid,
726 guint32 opflags,
727 GCancellable *cancellable,
728 GError **error)
729 {
730 EBookBackend *book_backend;
731 EContact *contact = NULL;
732 GSList *local_photos, *link;
733 GError *local_error = NULL;
734
735 g_return_val_if_fail (uid != NULL, FALSE);
736
737 if (!e_book_cache_get_contact (book_cache, uid, FALSE, &contact, cancellable, &local_error)) {
738 if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
739 g_clear_error (&local_error);
740 return TRUE;
741 }
742
743 g_propagate_error (error, local_error);
744 return FALSE;
745 }
746
747 book_backend = E_BOOK_BACKEND (meta_backend);
748
749 if (!e_book_cache_remove_contact (book_cache, uid, opflags, offline_flag, cancellable, error)) {
750 g_object_unref (contact);
751 return FALSE;
752 }
753
754 local_photos = ebmb_gather_photos_local_filenames (meta_backend, contact);
755 for (link = local_photos; link; link = g_slist_next (link)) {
756 const gchar *filename = link->data;
757
758 if (filename && g_unlink (filename) == -1) {
759 /* Ignore these errors */
760 }
761 }
762
763 g_slist_free_full (local_photos, g_free);
764
765 e_book_backend_notify_remove (book_backend, uid);
766
767 ebmb_foreach_cursor (meta_backend, contact, e_data_book_cursor_contact_removed);
768
769 g_object_unref (contact);
770
771 return TRUE;
772 }
773
774 static void ebmb_ensure_refresh_timeout_set_locked (EBookMetaBackend *meta_backend);
775
776 static gboolean
ebmb_refresh_internal_sync(EBookMetaBackend * meta_backend,gboolean with_connection_error,GCancellable * cancellable,GError ** error)777 ebmb_refresh_internal_sync (EBookMetaBackend *meta_backend,
778 gboolean with_connection_error,
779 GCancellable *cancellable,
780 GError **error)
781 {
782 #ifdef HAVE_GPOWERPROFILEMONITOR
783 GPowerProfileMonitor *power_monitor;
784 #endif
785 EBookCache *book_cache;
786 gboolean success = FALSE, repeat = TRUE, is_repeat = FALSE;
787
788 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
789
790 if (g_cancellable_set_error_if_cancelled (cancellable, error))
791 goto done;
792
793 #ifdef HAVE_GPOWERPROFILEMONITOR
794 /* Silently ignore the refresh request when in the power-saver mode */
795 power_monitor = g_power_profile_monitor_dup_default ();
796 if (power_monitor && g_power_profile_monitor_get_power_saver_enabled (power_monitor)) {
797 g_clear_object (&power_monitor);
798 success = TRUE;
799 goto done;
800 }
801
802 g_clear_object (&power_monitor);
803 #endif
804
805 e_book_backend_foreach_view_notify_progress (E_BOOK_BACKEND (meta_backend), TRUE, 0, _("Refreshing…"));
806
807 if (!e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, with_connection_error ? error : NULL) ||
808 !e_backend_get_online (E_BACKEND (meta_backend))) { /* Failed connecting moves backend to offline */
809 g_mutex_lock (&meta_backend->priv->property_lock);
810 meta_backend->priv->refresh_after_authenticate = TRUE;
811 g_mutex_unlock (&meta_backend->priv->property_lock);
812 goto done;
813 }
814
815 g_mutex_lock (&meta_backend->priv->property_lock);
816 meta_backend->priv->refresh_after_authenticate = FALSE;
817 g_mutex_unlock (&meta_backend->priv->property_lock);
818
819 book_cache = e_book_meta_backend_ref_cache (meta_backend);
820 if (!book_cache) {
821 g_warn_if_reached ();
822 goto done;
823 }
824
825 if (with_connection_error) {
826 /* Skip upload when not initiated by the user (as part of the Refresh operation) */
827 success = TRUE;
828 } else {
829 GError *local_error = NULL;
830
831 success = ebmb_upload_local_changes_sync (meta_backend, book_cache, E_CONFLICT_RESOLUTION_KEEP_SERVER, cancellable, &local_error);
832
833 if (local_error) {
834 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND))
835 e_backend_set_online (E_BACKEND (meta_backend), FALSE);
836
837 g_propagate_error (error, local_error);
838 success = FALSE;
839 }
840 }
841
842 e_cache_lock (E_CACHE (book_cache), E_CACHE_LOCK_WRITE);
843
844 while (repeat && success &&
845 !g_cancellable_set_error_if_cancelled (cancellable, error)) {
846 GSList *created_objects = NULL, *modified_objects = NULL, *removed_objects = NULL;
847 gchar *last_sync_tag, *new_sync_tag = NULL;
848 GError *local_error = NULL;
849
850 repeat = FALSE;
851
852 last_sync_tag = e_cache_dup_key (E_CACHE (book_cache), EBMB_KEY_SYNC_TAG, NULL);
853 if (last_sync_tag && !*last_sync_tag) {
854 g_free (last_sync_tag);
855 last_sync_tag = NULL;
856 }
857
858 success = e_book_meta_backend_get_changes_sync (meta_backend, last_sync_tag, is_repeat, &new_sync_tag, &repeat,
859 &created_objects, &modified_objects, &removed_objects, cancellable, &local_error);
860
861 if (local_error) {
862 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND))
863 e_backend_set_online (E_BACKEND (meta_backend), FALSE);
864
865 g_propagate_error (error, local_error);
866 local_error = NULL;
867 success = FALSE;
868 }
869
870 if (success) {
871 success = e_book_meta_backend_process_changes_sync (meta_backend, created_objects, modified_objects,
872 removed_objects, cancellable, error);
873 }
874
875 if (success && new_sync_tag)
876 e_cache_set_key (E_CACHE (book_cache), EBMB_KEY_SYNC_TAG, new_sync_tag, NULL);
877
878 g_slist_free_full (created_objects, e_book_meta_backend_info_free);
879 g_slist_free_full (modified_objects, e_book_meta_backend_info_free);
880 g_slist_free_full (removed_objects, e_book_meta_backend_info_free);
881 g_free (last_sync_tag);
882 g_free (new_sync_tag);
883
884 is_repeat = TRUE;
885 }
886
887 /* Always commit, to store at least what could be processed */
888 e_cache_unlock (E_CACHE (book_cache), E_CACHE_UNLOCK_COMMIT);
889
890 g_object_unref (book_cache);
891
892 done:
893 g_mutex_lock (&meta_backend->priv->property_lock);
894
895 if (meta_backend->priv->refresh_cancellable == cancellable)
896 g_clear_object (&meta_backend->priv->refresh_cancellable);
897
898 ebmb_ensure_refresh_timeout_set_locked (meta_backend);
899
900 meta_backend->priv->last_refresh_time = g_get_real_time ();
901
902 g_mutex_unlock (&meta_backend->priv->property_lock);
903
904 e_book_backend_foreach_view_notify_progress (E_BOOK_BACKEND (meta_backend), TRUE, 0, NULL);
905
906 g_signal_emit (meta_backend, signals[REFRESH_COMPLETED], 0, NULL);
907
908 return success;
909 }
910
911 static void
ebmb_refresh_thread_func(EBookBackend * book_backend,gpointer user_data,GCancellable * cancellable,GError ** error)912 ebmb_refresh_thread_func (EBookBackend *book_backend,
913 gpointer user_data,
914 GCancellable *cancellable,
915 GError **error)
916 {
917 EBookMetaBackend *meta_backend;
918
919 g_return_if_fail (E_IS_BOOK_META_BACKEND (book_backend));
920
921 meta_backend = E_BOOK_META_BACKEND (book_backend);
922
923 ebmb_refresh_internal_sync (meta_backend, FALSE, cancellable, error);
924 }
925
926 static void
ebmb_source_refresh_timeout_cb(ESource * source,gpointer user_data)927 ebmb_source_refresh_timeout_cb (ESource *source,
928 gpointer user_data)
929 {
930 GWeakRef *weak_ref = user_data;
931 EBookMetaBackend *meta_backend;
932
933 g_return_if_fail (weak_ref != NULL);
934
935 meta_backend = g_weak_ref_get (weak_ref);
936 if (meta_backend) {
937 e_book_meta_backend_schedule_refresh (meta_backend);
938 g_object_unref (meta_backend);
939 }
940 }
941
942 /* Should hold the property_lock when calling this */
943 static void
ebmb_ensure_refresh_timeout_set_locked(EBookMetaBackend * meta_backend)944 ebmb_ensure_refresh_timeout_set_locked (EBookMetaBackend *meta_backend)
945 {
946 if (!meta_backend->priv->refresh_timeout_id) {
947 ESource *source = e_backend_get_source (E_BACKEND (meta_backend));
948
949 meta_backend->priv->refresh_timeout_id = e_source_refresh_add_timeout (source, NULL,
950 ebmb_source_refresh_timeout_cb, e_weak_ref_new (meta_backend), (GDestroyNotify) e_weak_ref_free);
951 }
952 }
953
954 static void
ebmb_source_changed_thread_func(EBookBackend * book_backend,gpointer user_data,GCancellable * cancellable,GError ** error)955 ebmb_source_changed_thread_func (EBookBackend *book_backend,
956 gpointer user_data,
957 GCancellable *cancellable,
958 GError **error)
959 {
960 EBookMetaBackend *meta_backend;
961
962 g_return_if_fail (E_IS_BOOK_META_BACKEND (book_backend));
963
964 if (g_cancellable_set_error_if_cancelled (cancellable, error))
965 return;
966
967 meta_backend = E_BOOK_META_BACKEND (book_backend);
968
969 g_mutex_lock (&meta_backend->priv->property_lock);
970 ebmb_ensure_refresh_timeout_set_locked (meta_backend);
971 g_mutex_unlock (&meta_backend->priv->property_lock);
972
973 g_signal_emit (meta_backend, signals[SOURCE_CHANGED], 0, NULL);
974
975 if (e_book_meta_backend_requires_reconnect (meta_backend) &&
976 (e_backend_get_online (E_BACKEND (meta_backend)) || e_backend_is_destination_reachable (E_BACKEND (meta_backend), cancellable, NULL))) {
977 gboolean can_refresh;
978
979 g_mutex_lock (&meta_backend->priv->connect_lock);
980 can_refresh = e_book_meta_backend_disconnect_sync (meta_backend, cancellable, error);
981 g_mutex_unlock (&meta_backend->priv->connect_lock);
982
983 if (can_refresh)
984 e_book_meta_backend_schedule_refresh (meta_backend);
985 }
986
987 g_mutex_lock (&meta_backend->priv->property_lock);
988
989 if (meta_backend->priv->source_changed_cancellable == cancellable)
990 g_clear_object (&meta_backend->priv->source_changed_cancellable);
991
992 g_mutex_unlock (&meta_backend->priv->property_lock);
993 }
994
995 static void
ebmb_go_offline_thread_func(EBookBackend * book_backend,gpointer user_data,GCancellable * cancellable,GError ** error)996 ebmb_go_offline_thread_func (EBookBackend *book_backend,
997 gpointer user_data,
998 GCancellable *cancellable,
999 GError **error)
1000 {
1001 EBookMetaBackend *meta_backend;
1002
1003 g_return_if_fail (E_IS_BOOK_META_BACKEND (book_backend));
1004
1005 if (g_cancellable_set_error_if_cancelled (cancellable, error))
1006 return;
1007
1008 meta_backend = E_BOOK_META_BACKEND (book_backend);
1009
1010 g_mutex_lock (&meta_backend->priv->connect_lock);
1011 e_book_meta_backend_disconnect_sync (meta_backend, cancellable, error);
1012 g_mutex_unlock (&meta_backend->priv->connect_lock);
1013
1014 g_mutex_lock (&meta_backend->priv->property_lock);
1015
1016 if (meta_backend->priv->go_offline_cancellable == cancellable)
1017 g_clear_object (&meta_backend->priv->go_offline_cancellable);
1018
1019 g_mutex_unlock (&meta_backend->priv->property_lock);
1020 }
1021
1022 static gboolean
ebmb_put_contact(EBookMetaBackend * meta_backend,EBookCache * book_cache,ECacheOfflineFlag offline_flag,EContact * contact,const gchar * extra,guint32 opflags,GCancellable * cancellable,GError ** error)1023 ebmb_put_contact (EBookMetaBackend *meta_backend,
1024 EBookCache *book_cache,
1025 ECacheOfflineFlag offline_flag,
1026 EContact *contact,
1027 const gchar *extra,
1028 guint32 opflags,
1029 GCancellable *cancellable,
1030 GError **error)
1031 {
1032 EContact *existing_contact = NULL;
1033 gboolean success = TRUE;
1034
1035 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
1036
1037 success = e_book_meta_backend_store_inline_photos_sync (meta_backend, contact, cancellable, error);
1038
1039 if (success && e_book_cache_get_contact (book_cache,
1040 e_contact_get_const (contact, E_CONTACT_UID), FALSE, &existing_contact, cancellable, NULL)) {
1041 GSList *old_photos, *new_photos, *link;
1042
1043 old_photos = ebmb_gather_photos_local_filenames (meta_backend, existing_contact);
1044 if (old_photos) {
1045 GHashTable *photos_hash;
1046
1047 photos_hash = g_hash_table_new (g_str_hash, g_str_equal);
1048
1049 new_photos = ebmb_gather_photos_local_filenames (meta_backend, contact);
1050
1051 for (link = new_photos; link; link = g_slist_next (link)) {
1052 const gchar *filename = link->data;
1053
1054 if (filename)
1055 g_hash_table_insert (photos_hash, (gpointer) filename, NULL);
1056 }
1057
1058 for (link = old_photos; link; link = g_slist_next (link)) {
1059 const gchar *filename = link->data;
1060
1061 if (filename && !g_hash_table_contains (photos_hash, filename)) {
1062 if (g_unlink (filename) == -1) {
1063 /* Ignore these errors */
1064 }
1065 }
1066 }
1067
1068 g_slist_free_full (old_photos, g_free);
1069 g_slist_free_full (new_photos, g_free);
1070 g_hash_table_destroy (photos_hash);
1071 }
1072 }
1073
1074 success = success && e_book_cache_put_contact (book_cache, contact, extra, opflags, offline_flag, cancellable, error);
1075
1076 if (success)
1077 e_book_backend_notify_update (E_BOOK_BACKEND (meta_backend), contact);
1078
1079 g_clear_object (&existing_contact);
1080
1081 return success;
1082 }
1083
1084 static gboolean
ebmb_load_contact_wrapper_sync(EBookMetaBackend * meta_backend,EBookCache * book_cache,const gchar * uid,const gchar * preloaded_object,const gchar * preloaded_extra,gchar ** out_new_uid,GCancellable * cancellable,GError ** error)1085 ebmb_load_contact_wrapper_sync (EBookMetaBackend *meta_backend,
1086 EBookCache *book_cache,
1087 const gchar *uid,
1088 const gchar *preloaded_object,
1089 const gchar *preloaded_extra,
1090 gchar **out_new_uid,
1091 GCancellable *cancellable,
1092 GError **error)
1093 {
1094 ECacheOfflineFlag offline_flag = E_CACHE_IS_ONLINE;
1095 EContact *contact = NULL;
1096 gchar *extra = NULL;
1097 gboolean success = TRUE;
1098 GError *local_error = NULL;
1099
1100 if (preloaded_object && *preloaded_object) {
1101 contact = e_contact_new_from_vcard_with_uid (preloaded_object, uid);
1102 if (!contact) {
1103 g_propagate_error (error, e_client_error_create_fmt (E_CLIENT_ERROR_INVALID_ARG, _("Preloaded object for UID “%s” is invalid"), uid));
1104 return FALSE;
1105 }
1106 } else if (!e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error) ||
1107 !e_book_meta_backend_load_contact_sync (meta_backend, uid, preloaded_extra, &contact, &extra, cancellable, error)) {
1108 g_free (extra);
1109 return FALSE;
1110 } else if (!contact) {
1111 g_propagate_error (error, e_client_error_create_fmt (E_CLIENT_ERROR_INVALID_ARG, _("Received object for UID “%s” is invalid"), uid));
1112 g_free (extra);
1113 return FALSE;
1114 }
1115
1116 success = ebmb_put_contact (meta_backend, book_cache, offline_flag,
1117 contact, extra ? extra : preloaded_extra, 0, cancellable, &local_error);
1118
1119 if (success && out_new_uid)
1120 *out_new_uid = e_contact_get (contact, E_CONTACT_UID);
1121
1122 g_object_unref (contact);
1123 g_free (extra);
1124
1125 if (local_error) {
1126 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND))
1127 e_backend_set_online (E_BACKEND (meta_backend), FALSE);
1128
1129 g_propagate_error (error, local_error);
1130 success = FALSE;
1131 }
1132
1133 return success;
1134 }
1135
1136 static gboolean
ebmb_save_contact_wrapper_sync(EBookMetaBackend * meta_backend,EBookCache * book_cache,gboolean overwrite_existing,EConflictResolution conflict_resolution,EContact * in_contact,const gchar * extra,guint32 opflags,const gchar * orig_uid,gboolean * out_requires_put,gchar ** out_new_uid,gchar ** out_new_extra,GCancellable * cancellable,GError ** error)1137 ebmb_save_contact_wrapper_sync (EBookMetaBackend *meta_backend,
1138 EBookCache *book_cache,
1139 gboolean overwrite_existing,
1140 EConflictResolution conflict_resolution,
1141 /* const */ EContact *in_contact,
1142 const gchar *extra,
1143 guint32 opflags,
1144 const gchar *orig_uid,
1145 gboolean *out_requires_put,
1146 gchar **out_new_uid,
1147 gchar **out_new_extra,
1148 GCancellable *cancellable,
1149 GError **error)
1150 {
1151 EContact *contact;
1152 gchar *new_uid = NULL, *new_extra = NULL;
1153 gboolean success = TRUE;
1154 GError *local_error = NULL;
1155
1156 if (out_requires_put)
1157 *out_requires_put = TRUE;
1158
1159 if (out_new_uid)
1160 *out_new_uid = NULL;
1161
1162 contact = e_contact_duplicate (in_contact);
1163
1164 success = e_book_meta_backend_inline_local_photos_sync (meta_backend, contact, cancellable, error);
1165
1166 success = success && e_book_meta_backend_save_contact_sync (meta_backend, overwrite_existing, conflict_resolution,
1167 contact, extra, opflags, &new_uid, &new_extra, cancellable, &local_error);
1168
1169 if (success && new_uid && *new_uid) {
1170 gchar *loaded_uid = NULL;
1171
1172 success = ebmb_load_contact_wrapper_sync (meta_backend, book_cache, new_uid, NULL,
1173 new_extra ? new_extra : extra, &loaded_uid, cancellable, error);
1174
1175 if (success && g_strcmp0 (loaded_uid, orig_uid) != 0)
1176 success = ebmb_maybe_remove_from_cache (meta_backend, book_cache, E_CACHE_IS_ONLINE, orig_uid, opflags, cancellable, error);
1177
1178 if (success && out_new_uid)
1179 *out_new_uid = loaded_uid;
1180 else
1181 g_free (loaded_uid);
1182
1183 if (out_requires_put)
1184 *out_requires_put = FALSE;
1185 }
1186
1187 g_free (new_uid);
1188
1189 if (success && out_new_extra)
1190 *out_new_extra = new_extra;
1191 else
1192 g_free (new_extra);
1193 g_object_unref (contact);
1194
1195 if (local_error) {
1196 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND))
1197 e_backend_set_online (E_BACKEND (meta_backend), FALSE);
1198
1199 g_propagate_error (error, local_error);
1200 success = FALSE;
1201 }
1202
1203 return success;
1204 }
1205
1206 static gchar *
ebmb_get_backend_property(EBookBackend * book_backend,const gchar * prop_name)1207 ebmb_get_backend_property (EBookBackend *book_backend,
1208 const gchar *prop_name)
1209 {
1210 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), NULL);
1211 g_return_val_if_fail (prop_name != NULL, NULL);
1212
1213 if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REVISION)) {
1214 EBookCache *book_cache;
1215 gchar *revision = NULL;
1216
1217 book_cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (book_backend));
1218 if (book_cache) {
1219 revision = e_cache_dup_revision (E_CACHE (book_cache));
1220 g_object_unref (book_cache);
1221 } else {
1222 g_warn_if_reached ();
1223 }
1224
1225 return revision;
1226 } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
1227 return g_strdup (e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (book_backend)));
1228 } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
1229 return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
1230 } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
1231 GString *fields;
1232 gint ii;
1233
1234 fields = g_string_sized_new (1024);
1235
1236 /* Claim to support everything by default */
1237 for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
1238 if (fields->len > 0)
1239 g_string_append_c (fields, ',');
1240 g_string_append (fields, e_contact_field_name (ii));
1241 }
1242
1243 return g_string_free (fields, FALSE);
1244 }
1245
1246 /* Chain up to parent's method. */
1247 return E_BOOK_BACKEND_CLASS (e_book_meta_backend_parent_class)->impl_get_backend_property (book_backend, prop_name);
1248 }
1249
1250 static gboolean
ebmb_open_sync(EBookBackendSync * book_backend,GCancellable * cancellable,GError ** error)1251 ebmb_open_sync (EBookBackendSync *book_backend,
1252 GCancellable *cancellable,
1253 GError **error)
1254 {
1255 EBookMetaBackend *meta_backend;
1256 ESource *source;
1257
1258 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
1259
1260 if (e_book_backend_is_opened (E_BOOK_BACKEND (book_backend)))
1261 return TRUE;
1262
1263 meta_backend = E_BOOK_META_BACKEND (book_backend);
1264 if (meta_backend->priv->create_cache_error) {
1265 g_propagate_error (error, meta_backend->priv->create_cache_error);
1266 meta_backend->priv->create_cache_error = NULL;
1267 return FALSE;
1268 }
1269
1270 source = e_backend_get_source (E_BACKEND (book_backend));
1271
1272 if (!meta_backend->priv->source_changed_id) {
1273 meta_backend->priv->source_changed_id = g_signal_connect_swapped (source, "changed",
1274 G_CALLBACK (ebmb_schedule_source_changed), meta_backend);
1275 }
1276
1277 if (e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) {
1278 ESourceWebdav *webdav_extension;
1279
1280 webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
1281 e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
1282 }
1283
1284 if (e_book_meta_backend_get_ever_connected (meta_backend)) {
1285 e_book_backend_set_writable (E_BOOK_BACKEND (meta_backend),
1286 e_book_meta_backend_get_connected_writable (meta_backend));
1287 } else {
1288 if (!e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error)) {
1289 g_mutex_lock (&meta_backend->priv->property_lock);
1290 meta_backend->priv->refresh_after_authenticate = TRUE;
1291 g_mutex_unlock (&meta_backend->priv->property_lock);
1292
1293 return FALSE;
1294 }
1295 }
1296
1297 e_book_meta_backend_schedule_refresh (E_BOOK_META_BACKEND (book_backend));
1298
1299 return TRUE;
1300 }
1301
1302 static gboolean
ebmb_refresh_sync(EBookBackendSync * book_backend,GCancellable * cancellable,GError ** error)1303 ebmb_refresh_sync (EBookBackendSync *book_backend,
1304 GCancellable *cancellable,
1305 GError **error)
1306 {
1307 EBookMetaBackend *meta_backend;
1308 EBackend *backend;
1309 gboolean success;
1310
1311 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
1312
1313 meta_backend = E_BOOK_META_BACKEND (book_backend);
1314 backend = E_BACKEND (meta_backend);
1315
1316 if (!e_backend_get_online (backend) &&
1317 e_backend_is_destination_reachable (backend, cancellable, NULL))
1318 e_backend_set_online (backend, TRUE);
1319
1320 if (!e_backend_get_online (backend))
1321 return TRUE;
1322
1323 success = e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error);
1324
1325 if (success)
1326 e_book_meta_backend_schedule_refresh (meta_backend);
1327
1328 return success;
1329 }
1330
1331 static gboolean
ebmb_create_contact_sync(EBookMetaBackend * meta_backend,EBookCache * book_cache,ECacheOfflineFlag * offline_flag,EConflictResolution conflict_resolution,guint32 opflags,EContact * contact,gchar ** out_new_uid,EContact ** out_new_contact,GCancellable * cancellable,GError ** error)1332 ebmb_create_contact_sync (EBookMetaBackend *meta_backend,
1333 EBookCache *book_cache,
1334 ECacheOfflineFlag *offline_flag,
1335 EConflictResolution conflict_resolution,
1336 guint32 opflags,
1337 EContact *contact,
1338 gchar **out_new_uid,
1339 EContact **out_new_contact,
1340 GCancellable *cancellable,
1341 GError **error)
1342 {
1343 const gchar *uid;
1344 gchar *new_uid = NULL, *new_extra = NULL;
1345 gboolean success, requires_put = TRUE;
1346
1347 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
1348
1349 uid = e_contact_get_const (contact, E_CONTACT_UID);
1350 if (!uid) {
1351 gchar *new_uid;
1352
1353 new_uid = e_util_generate_uid ();
1354 if (!new_uid) {
1355 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_INVALID_ARG, NULL));
1356 return FALSE;
1357 }
1358
1359 e_contact_set (contact, E_CONTACT_UID, new_uid);
1360 uid = e_contact_get_const (contact, E_CONTACT_UID);
1361
1362 g_free (new_uid);
1363 }
1364
1365 if (e_cache_contains (E_CACHE (book_cache), uid, E_CACHE_EXCLUDE_DELETED)) {
1366 g_propagate_error (error, e_book_client_error_create (E_BOOK_CLIENT_ERROR_CONTACT_ID_ALREADY_EXISTS, NULL));
1367 return FALSE;
1368 }
1369
1370 if (*offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
1371 if (e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
1372 *offline_flag = E_CACHE_IS_ONLINE;
1373 } else {
1374 *offline_flag = E_CACHE_IS_OFFLINE;
1375 }
1376 }
1377
1378 if (*offline_flag == E_CACHE_IS_ONLINE) {
1379 if (!ebmb_save_contact_wrapper_sync (meta_backend, book_cache, FALSE, conflict_resolution, contact, NULL, opflags, uid,
1380 &requires_put, &new_uid, &new_extra, cancellable, error)) {
1381 return FALSE;
1382 }
1383 }
1384
1385 if (requires_put) {
1386 success = e_book_cache_put_contact (book_cache, contact, new_extra, opflags, *offline_flag, cancellable, error);
1387 if (success)
1388 e_book_backend_notify_update (E_BOOK_BACKEND (meta_backend), contact);
1389 } else {
1390 success = TRUE;
1391 }
1392
1393 if (success) {
1394 if (out_new_uid)
1395 *out_new_uid = g_strdup (new_uid ? new_uid : uid);
1396 if (out_new_contact) {
1397 if (new_uid) {
1398 if (!e_book_cache_get_contact (book_cache, new_uid, FALSE, out_new_contact, cancellable, NULL))
1399 *out_new_contact = g_object_ref (contact);
1400 } else {
1401 *out_new_contact = g_object_ref (contact);
1402 }
1403 }
1404 }
1405
1406 g_free (new_uid);
1407 g_free (new_extra);
1408
1409 return success;
1410 }
1411
1412 static gboolean
ebmb_create_contacts_sync(EBookBackendSync * book_backend,const gchar * const * vcards,guint32 opflags,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1413 ebmb_create_contacts_sync (EBookBackendSync *book_backend,
1414 const gchar * const *vcards,
1415 guint32 opflags,
1416 GSList **out_contacts,
1417 GCancellable *cancellable,
1418 GError **error)
1419 {
1420 EBookMetaBackend *meta_backend;
1421 EBookCache *book_cache;
1422 ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
1423 EConflictResolution conflict_resolution = e_book_util_operation_flags_to_conflict_resolution (opflags);
1424 gint ii;
1425 gboolean success = TRUE;
1426
1427 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
1428 g_return_val_if_fail (vcards != NULL, FALSE);
1429 g_return_val_if_fail (out_contacts != NULL, FALSE);
1430
1431 *out_contacts = NULL;
1432
1433 if (!e_book_backend_get_writable (E_BOOK_BACKEND (book_backend))) {
1434 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_PERMISSION_DENIED, NULL));
1435 return FALSE;
1436 }
1437
1438 meta_backend = E_BOOK_META_BACKEND (book_backend);
1439 book_cache = e_book_meta_backend_ref_cache (meta_backend);
1440 g_return_val_if_fail (book_cache != NULL, FALSE);
1441
1442 for (ii = 0; vcards[ii] && success; ii++) {
1443 EContact *contact, *new_contact = NULL;
1444
1445 if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
1446 success = FALSE;
1447 break;
1448 }
1449
1450 contact = e_contact_new_from_vcard (vcards[ii]);
1451 if (!contact) {
1452 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_INVALID_ARG, NULL));
1453 success = FALSE;
1454 break;
1455 }
1456
1457 success = ebmb_create_contact_sync (meta_backend, book_cache, &offline_flag, conflict_resolution,
1458 opflags, contact, NULL, &new_contact, cancellable, error);
1459
1460 if (success) {
1461 ebmb_foreach_cursor (meta_backend, new_contact, e_data_book_cursor_contact_added);
1462
1463 *out_contacts = g_slist_prepend (*out_contacts, new_contact);
1464 }
1465
1466 g_object_unref (contact);
1467 }
1468
1469 g_object_unref (book_cache);
1470
1471 if (success) {
1472 *out_contacts = g_slist_reverse (*out_contacts);
1473 } else {
1474 g_slist_free_full (*out_contacts, g_object_unref);
1475 *out_contacts = NULL;
1476 }
1477
1478 return success;
1479 }
1480
1481 static gboolean
ebmb_modify_contact_sync(EBookMetaBackend * meta_backend,EBookCache * book_cache,ECacheOfflineFlag * offline_flag,EConflictResolution conflict_resolution,guint32 opflags,EContact * contact,EContact ** out_new_contact,GCancellable * cancellable,GError ** error)1482 ebmb_modify_contact_sync (EBookMetaBackend *meta_backend,
1483 EBookCache *book_cache,
1484 ECacheOfflineFlag *offline_flag,
1485 EConflictResolution conflict_resolution,
1486 guint32 opflags,
1487 EContact *contact,
1488 EContact **out_new_contact,
1489 GCancellable *cancellable,
1490 GError **error)
1491 {
1492 const gchar *uid;
1493 EContact *existing_contact = NULL;
1494 gchar *extra = NULL, *new_uid = NULL, *new_extra = NULL;
1495 gboolean success = TRUE, requires_put = TRUE;
1496 GError *local_error = NULL;
1497
1498 g_return_val_if_fail (contact != NULL, FALSE);
1499
1500 uid = e_contact_get_const (contact, E_CONTACT_UID);
1501 if (!uid) {
1502 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_INVALID_ARG, NULL));
1503 return FALSE;
1504 }
1505
1506 if (!e_book_cache_get_contact (book_cache, uid, FALSE, &existing_contact, cancellable, &local_error)) {
1507 if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
1508 g_clear_error (&local_error);
1509 local_error = e_book_client_error_create (E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND, NULL);
1510 }
1511
1512 g_propagate_error (error, local_error);
1513
1514 return FALSE;
1515 }
1516
1517 if (!e_book_cache_get_contact_extra (book_cache, uid, &extra, cancellable, NULL))
1518 extra = NULL;
1519
1520 if (success && *offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
1521 if (e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
1522 *offline_flag = E_CACHE_IS_ONLINE;
1523 } else {
1524 *offline_flag = E_CACHE_IS_OFFLINE;
1525 }
1526 }
1527
1528 if (success && *offline_flag == E_CACHE_IS_ONLINE) {
1529 success = ebmb_save_contact_wrapper_sync (meta_backend, book_cache, TRUE, conflict_resolution,
1530 contact, extra, opflags, uid, &requires_put, &new_uid, &new_extra, cancellable, error);
1531 }
1532
1533 if (success && requires_put)
1534 success = ebmb_put_contact (meta_backend, book_cache, *offline_flag, contact, new_extra ? new_extra : extra, opflags, cancellable, error);
1535
1536 if (success && out_new_contact) {
1537 if (new_uid) {
1538 if (!e_book_cache_get_contact (book_cache, new_uid, FALSE, out_new_contact, cancellable, NULL))
1539 *out_new_contact = NULL;
1540 } else {
1541 *out_new_contact = g_object_ref (contact);
1542 }
1543 }
1544
1545 g_clear_object (&existing_contact);
1546 g_free (new_extra);
1547 g_free (new_uid);
1548 g_free (extra);
1549
1550 return success;
1551 }
1552
1553 static gboolean
ebmb_modify_contacts_sync(EBookBackendSync * book_backend,const gchar * const * vcards,guint32 opflags,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1554 ebmb_modify_contacts_sync (EBookBackendSync *book_backend,
1555 const gchar * const *vcards,
1556 guint32 opflags,
1557 GSList **out_contacts,
1558 GCancellable *cancellable,
1559 GError **error)
1560 {
1561 EBookMetaBackend *meta_backend;
1562 EBookCache *book_cache;
1563 ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
1564 EConflictResolution conflict_resolution = e_book_util_operation_flags_to_conflict_resolution (opflags);
1565 gint ii;
1566 gboolean success = TRUE;
1567
1568 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
1569 g_return_val_if_fail (vcards != NULL, FALSE);
1570 g_return_val_if_fail (out_contacts != NULL, FALSE);
1571
1572 *out_contacts = NULL;
1573
1574 if (!e_book_backend_get_writable (E_BOOK_BACKEND (book_backend))) {
1575 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_PERMISSION_DENIED, NULL));
1576 return FALSE;
1577 }
1578
1579 meta_backend = E_BOOK_META_BACKEND (book_backend);
1580 book_cache = e_book_meta_backend_ref_cache (meta_backend);
1581 g_return_val_if_fail (book_cache != NULL, FALSE);
1582
1583 for (ii = 0; vcards[ii] && success; ii++) {
1584 EContact *contact, *new_contact = NULL;
1585
1586 if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
1587 success = FALSE;
1588 break;
1589 }
1590
1591 contact = e_contact_new_from_vcard (vcards[ii]);
1592 if (!contact) {
1593 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_INVALID_ARG, NULL));
1594 success = FALSE;
1595 break;
1596 }
1597
1598 success = ebmb_modify_contact_sync (meta_backend, book_cache, &offline_flag, conflict_resolution, opflags,
1599 contact, &new_contact, cancellable, error);
1600
1601 if (success && new_contact) {
1602 ebmb_foreach_cursor (meta_backend, contact, e_data_book_cursor_contact_removed);
1603 ebmb_foreach_cursor (meta_backend, new_contact, e_data_book_cursor_contact_added);
1604
1605 *out_contacts = g_slist_prepend (*out_contacts, g_object_ref (new_contact));
1606 }
1607
1608 g_clear_object (&new_contact);
1609 g_object_unref (contact);
1610 }
1611
1612 g_object_unref (book_cache);
1613
1614 if (success) {
1615 *out_contacts = g_slist_reverse (*out_contacts);
1616 } else {
1617 g_slist_free_full (*out_contacts, g_object_unref);
1618 *out_contacts = NULL;
1619 }
1620
1621 return success;
1622 }
1623
1624 static gboolean
ebmb_remove_contact_sync(EBookMetaBackend * meta_backend,EBookCache * book_cache,ECacheOfflineFlag * offline_flag,EConflictResolution conflict_resolution,guint32 opflags,const gchar * uid,GCancellable * cancellable,GError ** error)1625 ebmb_remove_contact_sync (EBookMetaBackend *meta_backend,
1626 EBookCache *book_cache,
1627 ECacheOfflineFlag *offline_flag,
1628 EConflictResolution conflict_resolution,
1629 guint32 opflags,
1630 const gchar *uid,
1631 GCancellable *cancellable,
1632 GError **error)
1633 {
1634 EContact *existing_contact = NULL;
1635 gchar *extra = NULL;
1636 gboolean success = TRUE;
1637 GError *local_error = NULL;
1638
1639 g_return_val_if_fail (uid != NULL, FALSE);
1640
1641 if (!e_book_cache_get_contact (book_cache, uid, FALSE, &existing_contact, cancellable, &local_error)) {
1642 if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
1643 g_clear_error (&local_error);
1644 local_error = e_book_client_error_create (E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND, NULL);
1645 }
1646
1647 g_propagate_error (error, local_error);
1648
1649 return FALSE;
1650 }
1651
1652 if (*offline_flag == E_CACHE_OFFLINE_UNKNOWN) {
1653 if (e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL)) {
1654 *offline_flag = E_CACHE_IS_ONLINE;
1655 } else {
1656 *offline_flag = E_CACHE_IS_OFFLINE;
1657 }
1658 }
1659
1660 if (!e_book_cache_get_contact_extra (book_cache, uid, &extra, cancellable, NULL))
1661 extra = NULL;
1662
1663 if (*offline_flag == E_CACHE_IS_ONLINE &&
1664 e_cache_get_offline_state (E_CACHE (book_cache), uid, cancellable, NULL) != E_OFFLINE_STATE_LOCALLY_CREATED) {
1665 gchar *vcard_string = NULL;
1666
1667 g_warn_if_fail (e_book_cache_get_vcard (book_cache, uid, FALSE, &vcard_string, cancellable, NULL));
1668
1669 success = e_book_meta_backend_remove_contact_sync (meta_backend, conflict_resolution, uid, extra, vcard_string, opflags, cancellable, &local_error);
1670
1671 g_free (vcard_string);
1672
1673 if (local_error) {
1674 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND))
1675 e_backend_set_online (E_BACKEND (meta_backend), FALSE);
1676
1677 g_propagate_error (error, local_error);
1678 success = FALSE;
1679 }
1680 }
1681
1682 success = success && ebmb_maybe_remove_from_cache (meta_backend, book_cache, *offline_flag, uid, opflags, cancellable, error);
1683
1684 g_clear_object (&existing_contact);
1685 g_free (extra);
1686
1687 return success;
1688 }
1689
1690 static gboolean
ebmb_remove_contacts_sync(EBookBackendSync * book_backend,const gchar * const * uids,guint32 opflags,GSList ** out_removed_uids,GCancellable * cancellable,GError ** error)1691 ebmb_remove_contacts_sync (EBookBackendSync *book_backend,
1692 const gchar * const *uids,
1693 guint32 opflags,
1694 GSList **out_removed_uids,
1695 GCancellable *cancellable,
1696 GError **error)
1697 {
1698 EBookMetaBackend *meta_backend;
1699 EBookCache *book_cache;
1700 ECacheOfflineFlag offline_flag = E_CACHE_OFFLINE_UNKNOWN;
1701 EConflictResolution conflict_resolution = e_book_util_operation_flags_to_conflict_resolution (opflags);
1702 gint ii;
1703 gboolean success = TRUE;
1704
1705 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
1706 g_return_val_if_fail (uids != NULL, FALSE);
1707 g_return_val_if_fail (out_removed_uids != NULL, FALSE);
1708
1709 *out_removed_uids = NULL;
1710
1711 if (!e_book_backend_get_writable (E_BOOK_BACKEND (book_backend))) {
1712 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_PERMISSION_DENIED, NULL));
1713 return FALSE;
1714 }
1715
1716 meta_backend = E_BOOK_META_BACKEND (book_backend);
1717 book_cache = e_book_meta_backend_ref_cache (meta_backend);
1718 g_return_val_if_fail (book_cache != NULL, FALSE);
1719
1720 for (ii = 0; uids[ii] && success; ii++) {
1721 const gchar *uid = uids[ii];
1722
1723 if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
1724 success = FALSE;
1725 break;
1726 }
1727
1728 if (!uid) {
1729 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_INVALID_ARG, NULL));
1730 success = FALSE;
1731 break;
1732 }
1733
1734 success = ebmb_remove_contact_sync (meta_backend, book_cache, &offline_flag, conflict_resolution, opflags, uid, cancellable, error);
1735 if (success)
1736 *out_removed_uids = g_slist_prepend (*out_removed_uids, g_strdup (uid));
1737 }
1738
1739 g_object_unref (book_cache);
1740
1741 *out_removed_uids = g_slist_reverse (*out_removed_uids);
1742
1743 return success;
1744 }
1745
1746 static EContact *
ebmb_get_contact_sync(EBookBackendSync * book_backend,const gchar * uid,GCancellable * cancellable,GError ** error)1747 ebmb_get_contact_sync (EBookBackendSync *book_backend,
1748 const gchar *uid,
1749 GCancellable *cancellable,
1750 GError **error)
1751 {
1752 EBookMetaBackend *meta_backend;
1753 EBookCache *book_cache;
1754 EContact *contact = NULL;
1755 GError *local_error = NULL;
1756
1757 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), NULL);
1758 g_return_val_if_fail (uid && *uid, NULL);
1759
1760 meta_backend = E_BOOK_META_BACKEND (book_backend);
1761 book_cache = e_book_meta_backend_ref_cache (meta_backend);
1762
1763 g_return_val_if_fail (book_cache != NULL, NULL);
1764
1765 if (!e_book_cache_get_contact (book_cache, uid, FALSE, &contact, cancellable, &local_error) &&
1766 g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
1767 gchar *loaded_uid = NULL;
1768 gboolean found = FALSE;
1769
1770 g_clear_error (&local_error);
1771
1772 /* Ignore errors here, just try whether it's on the remote side, but not in the local cache */
1773 if (e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, NULL) &&
1774 ebmb_load_contact_wrapper_sync (meta_backend, book_cache, uid, NULL, NULL, &loaded_uid, cancellable, NULL)) {
1775 found = e_book_cache_get_contact (book_cache, loaded_uid, FALSE, &contact, cancellable, NULL);
1776 }
1777
1778 if (!found)
1779 g_propagate_error (error, e_book_client_error_create (E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND, NULL));
1780
1781 g_free (loaded_uid);
1782 } else if (local_error) {
1783 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_OTHER_ERROR, local_error->message));
1784 g_clear_error (&local_error);
1785 }
1786
1787 g_object_unref (book_cache);
1788
1789 return contact;
1790 }
1791
1792 static gboolean
ebmb_get_contact_list_sync(EBookBackendSync * book_backend,const gchar * query,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1793 ebmb_get_contact_list_sync (EBookBackendSync *book_backend,
1794 const gchar *query,
1795 GSList **out_contacts,
1796 GCancellable *cancellable,
1797 GError **error)
1798 {
1799 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
1800 g_return_val_if_fail (out_contacts != NULL, FALSE);
1801
1802 *out_contacts = NULL;
1803
1804 return e_book_meta_backend_search_sync (E_BOOK_META_BACKEND (book_backend), query, FALSE, out_contacts, cancellable, error);
1805 }
1806
1807 static gboolean
ebmb_get_contact_list_uids_sync(EBookBackendSync * book_backend,const gchar * query,GSList ** out_uids,GCancellable * cancellable,GError ** error)1808 ebmb_get_contact_list_uids_sync (EBookBackendSync *book_backend,
1809 const gchar *query,
1810 GSList **out_uids,
1811 GCancellable *cancellable,
1812 GError **error)
1813 {
1814 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
1815 g_return_val_if_fail (out_uids != NULL, FALSE);
1816
1817 *out_uids = NULL;
1818
1819 return e_book_meta_backend_search_uids_sync (E_BOOK_META_BACKEND (book_backend), query, out_uids, cancellable, error);
1820 }
1821
1822 static void
ebmb_start_view(EBookBackend * book_backend,EDataBookView * view)1823 ebmb_start_view (EBookBackend *book_backend,
1824 EDataBookView *view)
1825 {
1826 GCancellable *cancellable;
1827
1828 g_return_if_fail (E_IS_BOOK_META_BACKEND (book_backend));
1829
1830 cancellable = ebmb_create_view_cancellable (E_BOOK_META_BACKEND (book_backend), view);
1831
1832 e_book_backend_schedule_custom_operation (book_backend, cancellable,
1833 ebmb_start_view_thread_func, g_object_ref (view), g_object_unref);
1834
1835 g_object_unref (cancellable);
1836 }
1837
1838 static void
ebmb_stop_view(EBookBackend * book_backend,EDataBookView * view)1839 ebmb_stop_view (EBookBackend *book_backend,
1840 EDataBookView *view)
1841 {
1842 GCancellable *cancellable;
1843
1844 g_return_if_fail (E_IS_BOOK_META_BACKEND (book_backend));
1845
1846 cancellable = ebmb_steal_view_cancellable (E_BOOK_META_BACKEND (book_backend), view);
1847 if (cancellable) {
1848 g_cancellable_cancel (cancellable);
1849 g_object_unref (cancellable);
1850 }
1851 }
1852
1853 static EDataBookDirect *
ebmb_get_direct_book(EBookBackend * book_backend)1854 ebmb_get_direct_book (EBookBackend *book_backend)
1855 {
1856 EBookMetaBackendClass *klass;
1857 EBookCache *book_cache;
1858 EDataBookDirect *direct_book;
1859 const gchar *cache_filename;
1860 gchar *backend_path;
1861 gchar *dirname;
1862 const gchar *modules_env;
1863
1864 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), NULL);
1865
1866 klass = E_BOOK_META_BACKEND_GET_CLASS (book_backend);
1867 g_return_val_if_fail (klass != NULL, NULL);
1868
1869 if (!klass->backend_module_filename ||
1870 !klass->backend_factory_type_name)
1871 return NULL;
1872
1873 book_cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (book_backend));
1874 g_return_val_if_fail (book_cache != NULL, NULL);
1875
1876 cache_filename = e_cache_get_filename (E_CACHE (book_cache));
1877 dirname = g_path_get_dirname (cache_filename);
1878
1879 modules_env = g_getenv (EDS_ADDRESS_BOOK_MODULES);
1880
1881 /* Support in-tree testing / relocated modules */
1882 if (modules_env) {
1883 backend_path = g_build_filename (modules_env, klass->backend_module_filename, NULL);
1884 } else if (klass->backend_module_directory) {
1885 backend_path = g_build_filename (klass->backend_module_directory, klass->backend_module_filename, NULL);
1886 } else {
1887 backend_path = g_build_filename (BACKENDDIR, klass->backend_module_filename, NULL);
1888 }
1889
1890 direct_book = e_data_book_direct_new (backend_path, klass->backend_factory_type_name, dirname);
1891
1892 g_object_unref (book_cache);
1893 g_free (backend_path);
1894 g_free (dirname);
1895
1896 return direct_book;
1897 }
1898
1899 static void
ebmb_configure_direct(EBookBackend * book_backend,const gchar * base_directory)1900 ebmb_configure_direct (EBookBackend *book_backend,
1901 const gchar *base_directory)
1902 {
1903 EBookMetaBackend *meta_backend;
1904 EBookCache *book_cache;
1905 const gchar *cache_filename;
1906 gchar *dirname;
1907
1908 g_return_if_fail (E_IS_BOOK_META_BACKEND (book_backend));
1909
1910 if (!base_directory)
1911 return;
1912
1913 meta_backend = E_BOOK_META_BACKEND (book_backend);
1914
1915 book_cache = e_book_meta_backend_ref_cache (meta_backend);
1916 g_return_if_fail (book_cache != NULL);
1917
1918 cache_filename = e_cache_get_filename (E_CACHE (book_cache));
1919 dirname = g_path_get_dirname (cache_filename);
1920
1921 /* Did path for the cache change? Change the cache as well */
1922 if (dirname && !g_str_equal (base_directory, dirname) &&
1923 !g_str_has_prefix (dirname, base_directory)) {
1924 gchar *filename = g_path_get_basename (cache_filename);
1925 gchar *new_cache_filename;
1926 EBookCache *new_cache;
1927 ESource *source;
1928
1929 new_cache_filename = g_build_filename (base_directory, filename, NULL);
1930 source = e_backend_get_source (E_BACKEND (book_backend));
1931
1932 g_clear_error (&meta_backend->priv->create_cache_error);
1933
1934 new_cache = e_book_cache_new (new_cache_filename, source, NULL, &meta_backend->priv->create_cache_error);
1935 g_prefix_error (&meta_backend->priv->create_cache_error, _("Failed to create cache “%s”:"), new_cache_filename);
1936
1937 if (new_cache) {
1938 e_book_meta_backend_set_cache (meta_backend, new_cache);
1939 g_clear_object (&new_cache);
1940 }
1941
1942 g_free (new_cache_filename);
1943 g_free (filename);
1944 }
1945
1946 g_free (dirname);
1947 g_object_unref (book_cache);
1948 }
1949
1950 static gboolean
ebmb_set_locale(EBookBackend * book_backend,const gchar * locale,GCancellable * cancellable,GError ** error)1951 ebmb_set_locale (EBookBackend *book_backend,
1952 const gchar *locale,
1953 GCancellable *cancellable,
1954 GError **error)
1955 {
1956 EBookMetaBackend *meta_backend;
1957 EBookCache *book_cache;
1958 gboolean success;
1959
1960 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
1961
1962 meta_backend = E_BOOK_META_BACKEND (book_backend);
1963
1964 book_cache = e_book_meta_backend_ref_cache (meta_backend);
1965 g_return_val_if_fail (book_cache != NULL, FALSE);
1966
1967 success = e_book_cache_set_locale (book_cache, locale, cancellable, error);
1968 if (success) {
1969 GSList *link;
1970
1971 g_mutex_lock (&meta_backend->priv->property_lock);
1972
1973 for (link = meta_backend->priv->cursors; success && link; link = g_slist_next (link)) {
1974 EDataBookCursor *cursor = link->data;
1975
1976 success = e_data_book_cursor_load_locale (cursor, NULL, cancellable, error);
1977 }
1978
1979 g_mutex_unlock (&meta_backend->priv->property_lock);
1980 }
1981
1982 g_object_unref (book_cache);
1983
1984 return success;
1985 }
1986
1987 static gchar *
ebmb_dup_locale(EBookBackend * book_backend)1988 ebmb_dup_locale (EBookBackend *book_backend)
1989 {
1990 EBookMetaBackend *meta_backend;
1991 EBookCache *book_cache;
1992 gchar *locale;
1993
1994 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), NULL);
1995
1996 meta_backend = E_BOOK_META_BACKEND (book_backend);
1997
1998 book_cache = e_book_meta_backend_ref_cache (meta_backend);
1999 g_return_val_if_fail (book_cache != NULL, NULL);
2000
2001 locale = e_book_cache_dup_locale (book_cache);
2002
2003 g_object_unref (book_cache);
2004
2005 return locale;
2006 }
2007
2008 static EDataBookCursor *
ebmb_create_cursor(EBookBackend * book_backend,EContactField * sort_fields,EBookCursorSortType * sort_types,guint n_fields,GError ** error)2009 ebmb_create_cursor (EBookBackend *book_backend,
2010 EContactField *sort_fields,
2011 EBookCursorSortType *sort_types,
2012 guint n_fields,
2013 GError **error)
2014 {
2015 EBookMetaBackend *meta_backend;
2016 EBookCache *book_cache;
2017 EDataBookCursor *cursor;
2018
2019 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), NULL);
2020
2021 meta_backend = E_BOOK_META_BACKEND (book_backend);
2022
2023 book_cache = e_book_meta_backend_ref_cache (meta_backend);
2024 g_return_val_if_fail (book_cache != NULL, NULL);
2025
2026 cursor = e_data_book_cursor_cache_new (book_backend, book_cache, sort_fields, sort_types, n_fields, error);
2027
2028 if (cursor) {
2029 g_mutex_lock (&meta_backend->priv->property_lock);
2030
2031 meta_backend->priv->cursors = g_slist_prepend (meta_backend->priv->cursors, cursor);
2032
2033 g_mutex_unlock (&meta_backend->priv->property_lock);
2034 }
2035
2036 g_object_unref (book_cache);
2037
2038 return cursor;
2039 }
2040
2041 static gboolean
ebmb_delete_cursor(EBookBackend * book_backend,EDataBookCursor * cursor,GError ** error)2042 ebmb_delete_cursor (EBookBackend *book_backend,
2043 EDataBookCursor *cursor,
2044 GError **error)
2045 {
2046 EBookMetaBackend *meta_backend;
2047 GSList *link;
2048
2049 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (book_backend), FALSE);
2050
2051 meta_backend = E_BOOK_META_BACKEND (book_backend);
2052
2053 g_mutex_lock (&meta_backend->priv->property_lock);
2054
2055 link = g_slist_find (meta_backend->priv->cursors, cursor);
2056
2057 if (link) {
2058 meta_backend->priv->cursors = g_slist_remove (meta_backend->priv->cursors, cursor);
2059 g_object_unref (cursor);
2060 } else {
2061 g_set_error_literal (
2062 error,
2063 E_CLIENT_ERROR,
2064 E_CLIENT_ERROR_INVALID_ARG,
2065 _("Requested to delete an unrelated cursor"));
2066 }
2067
2068 g_mutex_unlock (&meta_backend->priv->property_lock);
2069
2070 return link != NULL;
2071 }
2072
2073 static ESourceAuthenticationResult
ebmb_authenticate_sync(EBackend * backend,const ENamedParameters * credentials,gchar ** out_certificate_pem,GTlsCertificateFlags * out_certificate_errors,GCancellable * cancellable,GError ** error)2074 ebmb_authenticate_sync (EBackend *backend,
2075 const ENamedParameters *credentials,
2076 gchar **out_certificate_pem,
2077 GTlsCertificateFlags *out_certificate_errors,
2078 GCancellable *cancellable,
2079 GError **error)
2080 {
2081 EBookMetaBackend *meta_backend;
2082 ESourceAuthenticationResult auth_result = E_SOURCE_AUTHENTICATION_UNKNOWN;
2083 gboolean success, refresh_after_authenticate = FALSE;
2084
2085 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR);
2086
2087 meta_backend = E_BOOK_META_BACKEND (backend);
2088
2089 if (!e_backend_get_online (backend) &&
2090 !e_backend_is_destination_reachable (backend, cancellable, NULL)) {
2091 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_REPOSITORY_OFFLINE, NULL));
2092
2093 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
2094 meta_backend->priv->wait_credentials_stamp++;
2095 g_cond_broadcast (&meta_backend->priv->wait_credentials_cond);
2096 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
2097
2098 return E_SOURCE_AUTHENTICATION_ERROR;
2099 }
2100
2101 g_mutex_lock (&meta_backend->priv->connect_lock);
2102
2103 /* Always disconnect first, then provide new credentials. */
2104 e_book_meta_backend_disconnect_sync (meta_backend, cancellable, NULL);
2105
2106 e_source_set_connection_status (e_backend_get_source (backend), E_SOURCE_CONNECTION_STATUS_CONNECTING);
2107
2108 success = e_book_meta_backend_connect_sync (meta_backend, credentials, &auth_result,
2109 out_certificate_pem, out_certificate_errors, cancellable, error);
2110
2111 if (success) {
2112 ebmb_update_connection_values (meta_backend);
2113 auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
2114
2115 e_source_set_connection_status (e_backend_get_source (backend), E_SOURCE_CONNECTION_STATUS_CONNECTED);
2116 } else {
2117 if (auth_result == E_SOURCE_AUTHENTICATION_UNKNOWN)
2118 auth_result = E_SOURCE_AUTHENTICATION_ERROR;
2119
2120 e_source_set_connection_status (e_backend_get_source (backend), E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
2121 }
2122 g_mutex_unlock (&meta_backend->priv->connect_lock);
2123
2124 g_mutex_lock (&meta_backend->priv->property_lock);
2125
2126 e_named_parameters_free (meta_backend->priv->last_credentials);
2127 if (success) {
2128 meta_backend->priv->last_credentials = e_named_parameters_new_clone (credentials);
2129
2130 refresh_after_authenticate = meta_backend->priv->refresh_after_authenticate;
2131 meta_backend->priv->refresh_after_authenticate = FALSE;
2132 } else {
2133 meta_backend->priv->last_credentials = NULL;
2134 }
2135
2136 g_mutex_unlock (&meta_backend->priv->property_lock);
2137
2138 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
2139 meta_backend->priv->wait_credentials_stamp++;
2140 g_cond_broadcast (&meta_backend->priv->wait_credentials_cond);
2141 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
2142
2143 if (refresh_after_authenticate)
2144 e_book_meta_backend_schedule_refresh (meta_backend);
2145
2146 return auth_result;
2147 }
2148
2149 static void
ebmb_schedule_source_changed(EBookMetaBackend * meta_backend)2150 ebmb_schedule_source_changed (EBookMetaBackend *meta_backend)
2151 {
2152 GCancellable *cancellable;
2153
2154 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
2155
2156 g_mutex_lock (&meta_backend->priv->property_lock);
2157
2158 if (meta_backend->priv->source_changed_cancellable) {
2159 /* Already updating */
2160 g_mutex_unlock (&meta_backend->priv->property_lock);
2161 return;
2162 }
2163
2164 cancellable = g_cancellable_new ();
2165 meta_backend->priv->source_changed_cancellable = g_object_ref (cancellable);
2166
2167 g_mutex_unlock (&meta_backend->priv->property_lock);
2168
2169 e_book_backend_schedule_custom_operation (E_BOOK_BACKEND (meta_backend), cancellable,
2170 ebmb_source_changed_thread_func, NULL, NULL);
2171
2172 g_object_unref (cancellable);
2173 }
2174
2175 static void
ebmb_schedule_go_offline(EBookMetaBackend * meta_backend)2176 ebmb_schedule_go_offline (EBookMetaBackend *meta_backend)
2177 {
2178 GCancellable *cancellable;
2179
2180 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
2181
2182 g_mutex_lock (&meta_backend->priv->property_lock);
2183
2184 /* Cancel anything ongoing now, but disconnect in a dedicated thread */
2185 if (meta_backend->priv->refresh_cancellable) {
2186 g_cancellable_cancel (meta_backend->priv->refresh_cancellable);
2187 g_clear_object (&meta_backend->priv->refresh_cancellable);
2188 }
2189
2190 if (meta_backend->priv->source_changed_cancellable) {
2191 g_cancellable_cancel (meta_backend->priv->source_changed_cancellable);
2192 g_clear_object (&meta_backend->priv->source_changed_cancellable);
2193 }
2194
2195 if (meta_backend->priv->go_offline_cancellable) {
2196 /* Already going offline */
2197 g_mutex_unlock (&meta_backend->priv->property_lock);
2198 return;
2199 }
2200
2201 cancellable = g_cancellable_new ();
2202 meta_backend->priv->go_offline_cancellable = g_object_ref (cancellable);
2203
2204 g_mutex_unlock (&meta_backend->priv->property_lock);
2205
2206 e_book_backend_schedule_custom_operation (E_BOOK_BACKEND (meta_backend), cancellable,
2207 ebmb_go_offline_thread_func, NULL, NULL);
2208
2209 g_object_unref (cancellable);
2210 }
2211
2212 static void
ebmb_notify_online_cb(GObject * object,GParamSpec * param,gpointer user_data)2213 ebmb_notify_online_cb (GObject *object,
2214 GParamSpec *param,
2215 gpointer user_data)
2216 {
2217 EBookMetaBackend *meta_backend = user_data;
2218 gboolean new_value;
2219
2220 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
2221
2222 new_value = e_backend_get_online (E_BACKEND (meta_backend));
2223 if (!new_value == !meta_backend->priv->current_online_state)
2224 return;
2225
2226 meta_backend->priv->current_online_state = new_value;
2227
2228 if (new_value) {
2229 gint64 now = g_get_real_time ();
2230
2231 /* Do not auto-run refresh (due to getting online) more than once per hour */
2232 if (now - meta_backend->priv->last_refresh_time >= G_USEC_PER_SEC * ((gint64) 60) * 60) {
2233 meta_backend->priv->last_refresh_time = now;
2234
2235 e_book_meta_backend_schedule_refresh (meta_backend);
2236 }
2237 } else {
2238 ebmb_schedule_go_offline (meta_backend);
2239 }
2240 }
2241
2242 static void
ebmb_cancel_view_cb(gpointer key,gpointer value,gpointer user_data)2243 ebmb_cancel_view_cb (gpointer key,
2244 gpointer value,
2245 gpointer user_data)
2246 {
2247 GCancellable *cancellable = value;
2248
2249 g_return_if_fail (G_IS_CANCELLABLE (cancellable));
2250
2251 g_cancellable_cancel (cancellable);
2252 }
2253
2254 static void
ebmb_wait_for_credentials_cancelled_cb(GCancellable * cancellable,gpointer user_data)2255 ebmb_wait_for_credentials_cancelled_cb (GCancellable *cancellable,
2256 gpointer user_data)
2257 {
2258 EBookMetaBackend *meta_backend = user_data;
2259
2260 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
2261
2262 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
2263 g_cond_broadcast (&meta_backend->priv->wait_credentials_cond);
2264 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
2265 }
2266
2267 static gboolean
ebmb_maybe_wait_for_credentials(EBookMetaBackend * meta_backend,guint wait_credentials_stamp,const GError * op_error,GCancellable * cancellable)2268 ebmb_maybe_wait_for_credentials (EBookMetaBackend *meta_backend,
2269 guint wait_credentials_stamp,
2270 const GError *op_error,
2271 GCancellable *cancellable)
2272 {
2273 EBackend *backend;
2274 ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN;
2275 gchar *certificate_pem = NULL;
2276 GTlsCertificateFlags certificate_errors = 0;
2277 gboolean got_credentials = FALSE;
2278 GError *local_error = NULL;
2279
2280 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
2281
2282 if (!op_error || g_cancellable_is_cancelled (cancellable))
2283 return FALSE;
2284
2285 if (g_error_matches (op_error, E_CLIENT_ERROR, E_CLIENT_ERROR_TLS_NOT_AVAILABLE) &&
2286 e_book_meta_backend_get_ssl_error_details (meta_backend, &certificate_pem, &certificate_errors)) {
2287 reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
2288 } else if (g_error_matches (op_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_REQUIRED)) {
2289 reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
2290 } else if (g_error_matches (op_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_FAILED)) {
2291 reason = E_SOURCE_CREDENTIALS_REASON_REJECTED;
2292 }
2293
2294 if (reason == E_SOURCE_CREDENTIALS_REASON_UNKNOWN)
2295 return FALSE;
2296
2297 backend = E_BACKEND (meta_backend);
2298
2299 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
2300
2301 if (wait_credentials_stamp != meta_backend->priv->wait_credentials_stamp ||
2302 e_backend_credentials_required_sync (backend, reason, certificate_pem, certificate_errors,
2303 op_error, cancellable, &local_error)) {
2304 gint64 wait_end_time;
2305 gulong handler_id;
2306
2307 wait_end_time = g_get_monotonic_time () + MAX_WAIT_FOR_CREDENTIALS_SECS * G_TIME_SPAN_SECOND;
2308
2309 handler_id = cancellable ? g_signal_connect (cancellable, "cancelled",
2310 G_CALLBACK (ebmb_wait_for_credentials_cancelled_cb), meta_backend) : 0;
2311
2312 while (wait_credentials_stamp == meta_backend->priv->wait_credentials_stamp &&
2313 !g_cancellable_is_cancelled (cancellable)) {
2314 if (!g_cond_wait_until (&meta_backend->priv->wait_credentials_cond, &meta_backend->priv->wait_credentials_lock, wait_end_time))
2315 break;
2316 }
2317
2318 if (handler_id)
2319 g_signal_handler_disconnect (cancellable, handler_id);
2320
2321 if (wait_credentials_stamp != meta_backend->priv->wait_credentials_stamp)
2322 got_credentials = e_source_get_connection_status (e_backend_get_source (backend)) == E_SOURCE_CONNECTION_STATUS_CONNECTED;
2323 } else {
2324 g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");
2325 }
2326
2327 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
2328
2329 g_clear_error (&local_error);
2330 g_free (certificate_pem);
2331
2332 return got_credentials;
2333 }
2334
2335 static void
e_book_meta_backend_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)2336 e_book_meta_backend_set_property (GObject *object,
2337 guint property_id,
2338 const GValue *value,
2339 GParamSpec *pspec)
2340 {
2341 switch (property_id) {
2342 case PROP_CACHE:
2343 e_book_meta_backend_set_cache (
2344 E_BOOK_META_BACKEND (object),
2345 g_value_get_object (value));
2346 return;
2347 }
2348
2349 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2350 }
2351
2352 static void
e_book_meta_backend_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)2353 e_book_meta_backend_get_property (GObject *object,
2354 guint property_id,
2355 GValue *value,
2356 GParamSpec *pspec)
2357 {
2358 switch (property_id) {
2359 case PROP_CACHE:
2360 g_value_take_object (
2361 value,
2362 e_book_meta_backend_ref_cache (
2363 E_BOOK_META_BACKEND (object)));
2364 return;
2365 }
2366
2367 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2368 }
2369
2370 static void
e_book_meta_backend_constructed(GObject * object)2371 e_book_meta_backend_constructed (GObject *object)
2372 {
2373 EBookMetaBackend *meta_backend = E_BOOK_META_BACKEND (object);
2374
2375 /* Chain up to parent's method. */
2376 G_OBJECT_CLASS (e_book_meta_backend_parent_class)->constructed (object);
2377
2378 meta_backend->priv->current_online_state = e_backend_get_online (E_BACKEND (meta_backend));
2379
2380 meta_backend->priv->notify_online_id = g_signal_connect (meta_backend, "notify::online",
2381 G_CALLBACK (ebmb_notify_online_cb), meta_backend);
2382
2383 if (!meta_backend->priv->cache) {
2384 EBookCache *cache;
2385 ESource *source;
2386 gchar *filename;
2387
2388 source = e_backend_get_source (E_BACKEND (meta_backend));
2389 filename = g_build_filename (e_book_backend_get_cache_dir (E_BOOK_BACKEND (meta_backend)), "cache.db", NULL);
2390 cache = e_book_cache_new (filename, source, NULL, &meta_backend->priv->create_cache_error);
2391 g_prefix_error (&meta_backend->priv->create_cache_error, _("Failed to create cache “%s”:"), filename);
2392
2393 g_free (filename);
2394
2395 if (cache) {
2396 e_book_meta_backend_set_cache (meta_backend, cache);
2397 g_clear_object (&cache);
2398 }
2399 }
2400 }
2401
2402 static void
e_book_meta_backend_dispose(GObject * object)2403 e_book_meta_backend_dispose (GObject *object)
2404 {
2405 EBookMetaBackend *meta_backend = E_BOOK_META_BACKEND (object);
2406 ESource *source = e_backend_get_source (E_BACKEND (meta_backend));
2407
2408 g_mutex_lock (&meta_backend->priv->property_lock);
2409
2410 if (meta_backend->priv->cursors) {
2411 g_slist_free_full (meta_backend->priv->cursors, g_object_unref);
2412 meta_backend->priv->cursors = NULL;
2413 }
2414
2415 if (meta_backend->priv->refresh_timeout_id) {
2416 if (source)
2417 e_source_refresh_remove_timeout (source, meta_backend->priv->refresh_timeout_id);
2418 meta_backend->priv->refresh_timeout_id = 0;
2419 }
2420
2421 if (meta_backend->priv->source_changed_id) {
2422 if (source)
2423 g_signal_handler_disconnect (source, meta_backend->priv->source_changed_id);
2424 meta_backend->priv->source_changed_id = 0;
2425 }
2426
2427 if (meta_backend->priv->notify_online_id) {
2428 g_signal_handler_disconnect (meta_backend, meta_backend->priv->notify_online_id);
2429 meta_backend->priv->notify_online_id = 0;
2430 }
2431
2432 if (meta_backend->priv->revision_changed_id) {
2433 if (meta_backend->priv->cache)
2434 g_signal_handler_disconnect (meta_backend->priv->cache, meta_backend->priv->revision_changed_id);
2435 meta_backend->priv->revision_changed_id = 0;
2436 }
2437
2438 g_hash_table_foreach (meta_backend->priv->view_cancellables, ebmb_cancel_view_cb, NULL);
2439
2440 if (meta_backend->priv->refresh_cancellable) {
2441 g_cancellable_cancel (meta_backend->priv->refresh_cancellable);
2442 g_clear_object (&meta_backend->priv->refresh_cancellable);
2443 }
2444
2445 if (meta_backend->priv->source_changed_cancellable) {
2446 g_cancellable_cancel (meta_backend->priv->source_changed_cancellable);
2447 g_clear_object (&meta_backend->priv->source_changed_cancellable);
2448 }
2449
2450 if (meta_backend->priv->go_offline_cancellable) {
2451 g_cancellable_cancel (meta_backend->priv->go_offline_cancellable);
2452 g_clear_object (&meta_backend->priv->go_offline_cancellable);
2453 }
2454
2455 e_named_parameters_free (meta_backend->priv->last_credentials);
2456 meta_backend->priv->last_credentials = NULL;
2457
2458 g_mutex_unlock (&meta_backend->priv->property_lock);
2459
2460 /* Chain up to parent's method. */
2461 G_OBJECT_CLASS (e_book_meta_backend_parent_class)->dispose (object);
2462 }
2463
2464 static void
e_book_meta_backend_finalize(GObject * object)2465 e_book_meta_backend_finalize (GObject *object)
2466 {
2467 EBookMetaBackend *meta_backend = E_BOOK_META_BACKEND (object);
2468
2469 g_clear_object (&meta_backend->priv->cache);
2470 g_clear_object (&meta_backend->priv->refresh_cancellable);
2471 g_clear_object (&meta_backend->priv->source_changed_cancellable);
2472 g_clear_object (&meta_backend->priv->go_offline_cancellable);
2473 g_clear_error (&meta_backend->priv->create_cache_error);
2474 g_clear_pointer (&meta_backend->priv->authentication_host, g_free);
2475 g_clear_pointer (&meta_backend->priv->authentication_user, g_free);
2476 g_clear_pointer (&meta_backend->priv->authentication_method, g_free);
2477 g_clear_pointer (&meta_backend->priv->authentication_proxy_uid, g_free);
2478 g_clear_pointer (&meta_backend->priv->authentication_credential_name, g_free);
2479 g_clear_pointer (&meta_backend->priv->webdav_soup_uri, (GDestroyNotify) soup_uri_free);
2480
2481 g_mutex_clear (&meta_backend->priv->connect_lock);
2482 g_mutex_clear (&meta_backend->priv->property_lock);
2483 g_mutex_clear (&meta_backend->priv->wait_credentials_lock);
2484 g_cond_clear (&meta_backend->priv->wait_credentials_cond);
2485 g_hash_table_destroy (meta_backend->priv->view_cancellables);
2486
2487 /* Chain up to parent's method. */
2488 G_OBJECT_CLASS (e_book_meta_backend_parent_class)->finalize (object);
2489 }
2490
2491 static void
e_book_meta_backend_class_init(EBookMetaBackendClass * klass)2492 e_book_meta_backend_class_init (EBookMetaBackendClass *klass)
2493 {
2494 GObjectClass *object_class;
2495 EBackendClass *backend_class;
2496 EBookBackendClass *book_backend_class;
2497 EBookBackendSyncClass *book_backend_sync_class;
2498
2499 klass->backend_factory_type_name = NULL;
2500 klass->backend_module_filename = NULL;
2501 klass->get_changes_sync = ebmb_get_changes_sync;
2502 klass->search_sync = ebmb_search_sync;
2503 klass->search_uids_sync = ebmb_search_uids_sync;
2504 klass->requires_reconnect = ebmb_requires_reconnect;
2505 klass->get_ssl_error_details = ebmb_get_ssl_error_details;
2506
2507 book_backend_sync_class = E_BOOK_BACKEND_SYNC_CLASS (klass);
2508 book_backend_sync_class->open_sync = ebmb_open_sync;
2509 book_backend_sync_class->refresh_sync = ebmb_refresh_sync;
2510 book_backend_sync_class->create_contacts_sync = ebmb_create_contacts_sync;
2511 book_backend_sync_class->modify_contacts_sync = ebmb_modify_contacts_sync;
2512 book_backend_sync_class->remove_contacts_sync = ebmb_remove_contacts_sync;
2513 book_backend_sync_class->get_contact_sync = ebmb_get_contact_sync;
2514 book_backend_sync_class->get_contact_list_sync = ebmb_get_contact_list_sync;
2515 book_backend_sync_class->get_contact_list_uids_sync = ebmb_get_contact_list_uids_sync;
2516
2517 book_backend_class = E_BOOK_BACKEND_CLASS (klass);
2518 book_backend_class->impl_get_backend_property = ebmb_get_backend_property;
2519 book_backend_class->impl_start_view = ebmb_start_view;
2520 book_backend_class->impl_stop_view = ebmb_stop_view;
2521 book_backend_class->impl_get_direct_book = ebmb_get_direct_book;
2522 book_backend_class->impl_configure_direct = ebmb_configure_direct;
2523 book_backend_class->impl_set_locale = ebmb_set_locale;
2524 book_backend_class->impl_dup_locale = ebmb_dup_locale;
2525 book_backend_class->impl_create_cursor = ebmb_create_cursor;
2526 book_backend_class->impl_delete_cursor = ebmb_delete_cursor;
2527
2528 backend_class = E_BACKEND_CLASS (klass);
2529 backend_class->authenticate_sync = ebmb_authenticate_sync;
2530
2531 object_class = G_OBJECT_CLASS (klass);
2532 object_class->set_property = e_book_meta_backend_set_property;
2533 object_class->get_property = e_book_meta_backend_get_property;
2534 object_class->constructed = e_book_meta_backend_constructed;
2535 object_class->dispose = e_book_meta_backend_dispose;
2536 object_class->finalize = e_book_meta_backend_finalize;
2537
2538 /**
2539 * EBookMetaBackend:cache:
2540 *
2541 * The #EBookCache being used for this meta backend.
2542 **/
2543 g_object_class_install_property (
2544 object_class,
2545 PROP_CACHE,
2546 g_param_spec_object (
2547 "cache",
2548 "Cache",
2549 "Book Cache",
2550 E_TYPE_BOOK_CACHE,
2551 G_PARAM_READWRITE |
2552 G_PARAM_EXPLICIT_NOTIFY |
2553 G_PARAM_STATIC_STRINGS));
2554
2555 /* This signal is meant for testing purposes mainly */
2556 signals[REFRESH_COMPLETED] = g_signal_new (
2557 "refresh-completed",
2558 G_OBJECT_CLASS_TYPE (klass),
2559 G_SIGNAL_RUN_LAST,
2560 0,
2561 NULL, NULL, NULL,
2562 G_TYPE_NONE, 0, G_TYPE_NONE);
2563
2564 /**
2565 * EBookMetaBackend::source-changed
2566 *
2567 * This signal is emitted whenever the underlying backend #ESource
2568 * changes. Unlike the #ESource's 'changed' signal this one is
2569 * tight to the #EBookMetaBackend itself and is emitted from
2570 * a dedicated thread, thus it doesn't block the main thread.
2571 *
2572 * Since: 3.26
2573 **/
2574 signals[SOURCE_CHANGED] = g_signal_new (
2575 "source-changed",
2576 G_OBJECT_CLASS_TYPE (klass),
2577 G_SIGNAL_RUN_LAST,
2578 G_STRUCT_OFFSET (EBookMetaBackendClass, source_changed),
2579 NULL, NULL, NULL,
2580 G_TYPE_NONE, 0, G_TYPE_NONE);
2581 }
2582
2583 static void
e_book_meta_backend_init(EBookMetaBackend * meta_backend)2584 e_book_meta_backend_init (EBookMetaBackend *meta_backend)
2585 {
2586 meta_backend->priv = e_book_meta_backend_get_instance_private (meta_backend);
2587
2588 g_mutex_init (&meta_backend->priv->connect_lock);
2589 g_mutex_init (&meta_backend->priv->property_lock);
2590 g_mutex_init (&meta_backend->priv->wait_credentials_lock);
2591 g_cond_init (&meta_backend->priv->wait_credentials_cond);
2592
2593 meta_backend->priv->view_cancellables = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
2594 meta_backend->priv->current_online_state = FALSE;
2595 meta_backend->priv->refresh_after_authenticate = FALSE;
2596 meta_backend->priv->ever_connected = -1;
2597 meta_backend->priv->connected_writable = -1;
2598 }
2599
2600 /**
2601 * e_book_meta_backend_get_capabilities:
2602 * @meta_backend: an #EBookMetaBackend
2603 *
2604 * Returns: an #EBookBackend::capabilities property to be used by
2605 * the descendant in conjunction to the descendant's capabilities
2606 * in the result of e_book_backend_get_backend_property() with
2607 * #CLIENT_BACKEND_PROPERTY_CAPABILITIES.
2608 *
2609 * Since: 3.26
2610 **/
2611 const gchar *
e_book_meta_backend_get_capabilities(EBookMetaBackend * meta_backend)2612 e_book_meta_backend_get_capabilities (EBookMetaBackend *meta_backend)
2613 {
2614 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), NULL);
2615
2616 return "refresh-supported" ","
2617 "bulk-adds" ","
2618 "bulk-modifies" ","
2619 "bulk-removes";
2620 }
2621
2622 /**
2623 * e_book_meta_backend_set_ever_connected:
2624 * @meta_backend: an #EBookMetaBackend
2625 * @value: value to set
2626 *
2627 * Sets whether the @meta_backend ever made a successful connection
2628 * to its destination.
2629 *
2630 * This is used by the @meta_backend itself, during the opening phase,
2631 * when it had not been connected yet, then it does so immediately, to
2632 * eventually report settings error easily.
2633 *
2634 * Since: 3.26
2635 **/
2636 void
e_book_meta_backend_set_ever_connected(EBookMetaBackend * meta_backend,gboolean value)2637 e_book_meta_backend_set_ever_connected (EBookMetaBackend *meta_backend,
2638 gboolean value)
2639 {
2640 EBookCache *book_cache;
2641
2642 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
2643
2644 if ((value ? 1 : 0) == meta_backend->priv->ever_connected)
2645 return;
2646
2647 book_cache = e_book_meta_backend_ref_cache (meta_backend);
2648 meta_backend->priv->ever_connected = value ? 1 : 0;
2649 e_cache_set_key_int (E_CACHE (book_cache), EBMB_KEY_EVER_CONNECTED, meta_backend->priv->ever_connected, NULL);
2650 g_clear_object (&book_cache);
2651 }
2652
2653 /**
2654 * e_book_meta_backend_get_ever_connected:
2655 * @meta_backend: an #EBookMetaBackend
2656 *
2657 * Returns: Whether the @meta_backend ever made a successful connection
2658 * to its destination.
2659 *
2660 * Since: 3.26
2661 **/
2662 gboolean
e_book_meta_backend_get_ever_connected(EBookMetaBackend * meta_backend)2663 e_book_meta_backend_get_ever_connected (EBookMetaBackend *meta_backend)
2664 {
2665 gboolean result;
2666
2667 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
2668
2669 if (meta_backend->priv->ever_connected == -1) {
2670 EBookCache *book_cache;
2671
2672 book_cache = e_book_meta_backend_ref_cache (meta_backend);
2673 result = e_cache_get_key_int (E_CACHE (book_cache), EBMB_KEY_EVER_CONNECTED, NULL) == 1;
2674 g_clear_object (&book_cache);
2675
2676 meta_backend->priv->ever_connected = result ? 1 : 0;
2677 } else {
2678 result = meta_backend->priv->ever_connected == 1;
2679 }
2680
2681 return result;
2682 }
2683
2684 /**
2685 * e_book_meta_backend_set_connected_writable:
2686 * @meta_backend: an #EBookMetaBackend
2687 * @value: value to set
2688 *
2689 * Sets whether the @meta_backend connected to a writable destination.
2690 * This value has meaning only if e_book_meta_backend_get_ever_connected()
2691 * is %TRUE.
2692 *
2693 * This is used by the @meta_backend itself, during the opening phase,
2694 * to set the backend writable or not also in the offline mode.
2695 *
2696 * Since: 3.26
2697 **/
2698 void
e_book_meta_backend_set_connected_writable(EBookMetaBackend * meta_backend,gboolean value)2699 e_book_meta_backend_set_connected_writable (EBookMetaBackend *meta_backend,
2700 gboolean value)
2701 {
2702 EBookCache *book_cache;
2703
2704 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
2705
2706 if ((value ? 1 : 0) == meta_backend->priv->connected_writable)
2707 return;
2708
2709 book_cache = e_book_meta_backend_ref_cache (meta_backend);
2710 meta_backend->priv->connected_writable = value ? 1 : 0;
2711 e_cache_set_key_int (E_CACHE (book_cache), EBMB_KEY_CONNECTED_WRITABLE, meta_backend->priv->connected_writable, NULL);
2712 g_clear_object (&book_cache);
2713 }
2714
2715 /**
2716 * e_book_meta_backend_get_connected_writable:
2717 * @meta_backend: an #EBookMetaBackend
2718 *
2719 * This value has meaning only if e_book_meta_backend_get_ever_connected()
2720 * is %TRUE.
2721 *
2722 * Returns: Whether the @meta_backend connected to a writable destination.
2723 *
2724 * Since: 3.26
2725 **/
2726 gboolean
e_book_meta_backend_get_connected_writable(EBookMetaBackend * meta_backend)2727 e_book_meta_backend_get_connected_writable (EBookMetaBackend *meta_backend)
2728 {
2729 gboolean result;
2730
2731 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
2732
2733 if (meta_backend->priv->connected_writable == -1) {
2734 EBookCache *book_cache;
2735
2736 book_cache = e_book_meta_backend_ref_cache (meta_backend);
2737 result = e_cache_get_key_int (E_CACHE (book_cache), EBMB_KEY_CONNECTED_WRITABLE, NULL) == 1;
2738 g_clear_object (&book_cache);
2739
2740 meta_backend->priv->connected_writable = result ? 1 : 0;
2741 } else {
2742 result = meta_backend->priv->connected_writable == 1;
2743 }
2744
2745 return result;
2746 }
2747
2748 /**
2749 * e_book_meta_backend_dup_sync_tag:
2750 * @meta_backend: an #EBookMetaBackend
2751 *
2752 * Returns the last known synchronization tag, the same as used to
2753 * call e_book_meta_backend_get_changes_sync().
2754 *
2755 * Free the returned string with g_free(), when no longer needed.
2756 *
2757 * Returns: (transfer full) (nullable): The last known synchronization tag,
2758 * or %NULL, when none is stored.
2759 *
2760 * Since: 3.28
2761 **/
2762 gchar *
e_book_meta_backend_dup_sync_tag(EBookMetaBackend * meta_backend)2763 e_book_meta_backend_dup_sync_tag (EBookMetaBackend *meta_backend)
2764 {
2765 EBookCache *book_cache;
2766 gchar *sync_tag;
2767
2768 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), NULL);
2769
2770 book_cache = e_book_meta_backend_ref_cache (meta_backend);
2771 if (!book_cache)
2772 return NULL;
2773
2774 sync_tag = e_cache_dup_key (E_CACHE (book_cache), EBMB_KEY_SYNC_TAG, NULL);
2775 if (sync_tag && !*sync_tag) {
2776 g_free (sync_tag);
2777 sync_tag = NULL;
2778 }
2779
2780 g_clear_object (&book_cache);
2781
2782 return sync_tag;
2783 }
2784
2785 static void
ebmb_cache_revision_changed_cb(ECache * cache,gpointer user_data)2786 ebmb_cache_revision_changed_cb (ECache *cache,
2787 gpointer user_data)
2788 {
2789 EBookMetaBackend *meta_backend = user_data;
2790 gchar *revision;
2791
2792 g_return_if_fail (E_IS_CACHE (cache));
2793 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
2794
2795 revision = e_cache_dup_revision (cache);
2796 if (revision) {
2797 e_book_backend_notify_property_changed (E_BOOK_BACKEND (meta_backend),
2798 E_BOOK_BACKEND_PROPERTY_REVISION, revision);
2799 g_free (revision);
2800 }
2801 }
2802
2803 /**
2804 * e_book_meta_backend_set_cache:
2805 * @meta_backend: an #EBookMetaBackend
2806 * @cache: an #EBookCache to use
2807 *
2808 * Sets the @cache as the cache to be used by the @meta_backend.
2809 * By default, a cache.db in EBookBackend::cache-dir is created
2810 * in the constructed method. This function can be used to override
2811 * the default.
2812 *
2813 * Note the @meta_backend adds its own reference to the @cache.
2814 *
2815 * Since: 3.26
2816 **/
2817 void
e_book_meta_backend_set_cache(EBookMetaBackend * meta_backend,EBookCache * cache)2818 e_book_meta_backend_set_cache (EBookMetaBackend *meta_backend,
2819 EBookCache *cache)
2820 {
2821 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
2822 g_return_if_fail (E_IS_BOOK_CACHE (cache));
2823
2824 g_mutex_lock (&meta_backend->priv->property_lock);
2825
2826 if (meta_backend->priv->cache == cache) {
2827 g_mutex_unlock (&meta_backend->priv->property_lock);
2828 return;
2829 }
2830
2831 g_clear_error (&meta_backend->priv->create_cache_error);
2832
2833 if (meta_backend->priv->cache) {
2834 g_signal_handler_disconnect (meta_backend->priv->cache,
2835 meta_backend->priv->revision_changed_id);
2836 }
2837
2838 g_clear_object (&meta_backend->priv->cache);
2839 meta_backend->priv->cache = g_object_ref (cache);
2840
2841 meta_backend->priv->revision_changed_id = g_signal_connect_object (meta_backend->priv->cache,
2842 "revision-changed", G_CALLBACK (ebmb_cache_revision_changed_cb), meta_backend, 0);
2843
2844 g_mutex_unlock (&meta_backend->priv->property_lock);
2845
2846 g_object_notify (G_OBJECT (meta_backend), "cache");
2847 }
2848
2849 /**
2850 * e_book_meta_backend_ref_cache:
2851 * @meta_backend: an #EBookMetaBackend
2852 *
2853 * Returns: (transfer full): Referenced #EBookCache, which is used by @meta_backend.
2854 * Unref it with g_object_unref(), when no longer needed.
2855 *
2856 * Since: 3.26
2857 **/
2858 EBookCache *
e_book_meta_backend_ref_cache(EBookMetaBackend * meta_backend)2859 e_book_meta_backend_ref_cache (EBookMetaBackend *meta_backend)
2860 {
2861 EBookCache *cache;
2862
2863 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), NULL);
2864
2865 g_mutex_lock (&meta_backend->priv->property_lock);
2866
2867 if (meta_backend->priv->cache)
2868 cache = g_object_ref (meta_backend->priv->cache);
2869 else
2870 cache = NULL;
2871
2872 g_mutex_unlock (&meta_backend->priv->property_lock);
2873
2874 return cache;
2875 }
2876
2877 static gchar *
ebmb_get_mime_type(const gchar * url,const gchar * content,gsize content_len)2878 ebmb_get_mime_type (const gchar *url,
2879 const gchar *content,
2880 gsize content_len)
2881 {
2882 gchar *content_type, *filename = NULL, *mime_type = NULL;
2883
2884 if (url) {
2885 filename = g_filename_from_uri (url, NULL, NULL);
2886 if (filename) {
2887 gchar *extension;
2888
2889 /* When storing inline attachments to the local file,
2890 the file extension is the mime type as stored in the attribute */
2891 extension = strrchr (filename, '.');
2892 if (extension)
2893 extension++;
2894
2895 if (extension) {
2896 mime_type = g_uri_unescape_string (extension, NULL);
2897 if (mime_type && !strchr (mime_type, '/')) {
2898 gchar *tmp;
2899
2900 tmp = g_strconcat ("image/", mime_type, NULL);
2901
2902 g_free (mime_type);
2903 mime_type = tmp;
2904 }
2905
2906 content_type = g_content_type_from_mime_type (mime_type);
2907
2908 if (!content_type) {
2909 g_free (mime_type);
2910 mime_type = NULL;
2911 }
2912
2913 g_free (content_type);
2914 }
2915 }
2916 }
2917
2918 if (!mime_type) {
2919 content_type = g_content_type_guess (filename, (const guchar *) content, content_len, NULL);
2920
2921 if (content_type)
2922 mime_type = g_content_type_get_mime_type (content_type);
2923
2924 g_free (content_type);
2925 }
2926
2927 g_free (filename);
2928
2929 return mime_type;
2930 }
2931
2932 /**
2933 * e_book_meta_backend_inline_local_photos_sync:
2934 * @meta_backend: an #EBookMetaBackend
2935 * @contact: an #EContact to work with
2936 * @cancellable: optional #GCancellable object, or %NULL
2937 * @error: return location for a #GError, or %NULL
2938 *
2939 * Changes all URL photos and logos which point to a local file in @contact
2940 * to inline type, aka adds the file content into the @contact.
2941 * This is called automatically before e_book_meta_backend_save_contact_sync().
2942 *
2943 * The reverse operation is e_book_meta_backend_store_inline_photos_sync().
2944 *
2945 * Returns: Whether succeeded.
2946 *
2947 * Since: 3.26
2948 **/
2949 gboolean
e_book_meta_backend_inline_local_photos_sync(EBookMetaBackend * meta_backend,EContact * contact,GCancellable * cancellable,GError ** error)2950 e_book_meta_backend_inline_local_photos_sync (EBookMetaBackend *meta_backend,
2951 EContact *contact,
2952 GCancellable *cancellable,
2953 GError **error)
2954 {
2955 GList *attributes, *link;
2956 gboolean success = TRUE;
2957
2958 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
2959 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
2960
2961 attributes = e_vcard_get_attributes (E_VCARD (contact));
2962
2963 for (link = attributes; link; link = g_list_next (link)) {
2964 EVCardAttribute *attr = link->data;
2965 const gchar *attr_name;
2966 GList *values;
2967
2968 attr_name = e_vcard_attribute_get_name (attr);
2969 if (!attr_name || (
2970 g_ascii_strcasecmp (attr_name, EVC_PHOTO) != 0 &&
2971 g_ascii_strcasecmp (attr_name, EVC_LOGO) != 0)) {
2972 continue;
2973 }
2974
2975 values = e_vcard_attribute_get_param (attr, EVC_VALUE);
2976 if (values && g_ascii_strcasecmp (values->data, "uri") == 0) {
2977 gchar *url;
2978
2979 url = e_vcard_attribute_get_value (attr);
2980 if (url && g_str_has_prefix (url, LOCAL_PREFIX)) {
2981 GFile *file;
2982 gchar *basename;
2983 gchar *content;
2984 gsize len;
2985
2986 file = g_file_new_for_uri (url);
2987 basename = g_file_get_basename (file);
2988 if (g_file_load_contents (file, cancellable, &content, &len, NULL, error)) {
2989 gchar *mime_type;
2990 const gchar *image_type, *pp;
2991
2992 mime_type = ebmb_get_mime_type (url, content, len);
2993 if (mime_type && (pp = strchr (mime_type, '/'))) {
2994 image_type = pp + 1;
2995 } else {
2996 image_type = "X-EVOLUTION-UNKNOWN";
2997 }
2998
2999 e_vcard_attribute_remove_param (attr, EVC_TYPE);
3000 e_vcard_attribute_remove_param (attr, EVC_ENCODING);
3001 e_vcard_attribute_remove_param (attr, EVC_VALUE);
3002 e_vcard_attribute_remove_values (attr);
3003
3004 e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_TYPE), image_type);
3005 e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_ENCODING), "b");
3006 e_vcard_attribute_add_value_decoded (attr, content, len);
3007
3008 g_free (mime_type);
3009 g_free (content);
3010 } else {
3011 success = FALSE;
3012 }
3013
3014 g_object_unref (file);
3015 g_free (basename);
3016 }
3017
3018 g_free (url);
3019 }
3020 }
3021
3022 return success;
3023 }
3024
3025 static gchar *
ebmb_create_photo_local_filename(EBookMetaBackend * meta_backend,const gchar * uid,const gchar * attr_name,gint fileindex,const gchar * type)3026 ebmb_create_photo_local_filename (EBookMetaBackend *meta_backend,
3027 const gchar *uid,
3028 const gchar *attr_name,
3029 gint fileindex,
3030 const gchar *type)
3031 {
3032 EBookCache *book_cache;
3033 gchar *local_filename, *cache_path, *checksum, *prefix, *extension, *filename;
3034
3035 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), NULL);
3036 g_return_val_if_fail (uid != NULL, NULL);
3037 g_return_val_if_fail (attr_name != NULL, NULL);
3038
3039 book_cache = e_book_meta_backend_ref_cache (meta_backend);
3040 g_return_val_if_fail (book_cache != NULL, NULL);
3041
3042 cache_path = g_path_get_dirname (e_cache_get_filename (E_CACHE (book_cache)));
3043 checksum = g_compute_checksum_for_string (G_CHECKSUM_SHA1, uid, -1);
3044 prefix = g_strdup_printf ("%s-%s-%d", attr_name, checksum, fileindex);
3045
3046 if (type && *type)
3047 extension = g_uri_escape_string (type, NULL, TRUE);
3048 else
3049 extension = NULL;
3050
3051 filename = g_strconcat (prefix, extension ? "." : NULL, extension, NULL);
3052
3053 local_filename = g_build_filename (cache_path, filename, NULL);
3054
3055 g_object_unref (book_cache);
3056 g_free (cache_path);
3057 g_free (checksum);
3058 g_free (prefix);
3059 g_free (extension);
3060 g_free (filename);
3061
3062 return local_filename;
3063 }
3064
3065 /**
3066 * e_book_meta_backend_store_inline_photos_sync:
3067 * @meta_backend: an #EBookMetaBackend
3068 * @contact: an #EContact to work with
3069 * @cancellable: optional #GCancellable object, or %NULL
3070 * @error: return location for a #GError, or %NULL
3071 *
3072 * Changes all inline photos and logos to URL type in @contact, which
3073 * will point to a local file instead, beside the cache file.
3074 * This is called automatically after e_book_meta_backend_load_contact_sync().
3075 *
3076 * The reverse operation is e_book_meta_backend_inline_local_photos_sync().
3077 *
3078 * Returns: Whether succeeded.
3079 *
3080 * Since: 3.26
3081 **/
3082 gboolean
e_book_meta_backend_store_inline_photos_sync(EBookMetaBackend * meta_backend,EContact * contact,GCancellable * cancellable,GError ** error)3083 e_book_meta_backend_store_inline_photos_sync (EBookMetaBackend *meta_backend,
3084 EContact *contact,
3085 GCancellable *cancellable,
3086 GError **error)
3087 {
3088 gint fileindex;
3089 GList *attributes, *link;
3090 gboolean success = TRUE;
3091
3092 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3093 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
3094
3095 attributes = e_vcard_get_attributes (E_VCARD (contact));
3096
3097 for (link = attributes, fileindex = 0; link; link = g_list_next (link), fileindex++) {
3098 EVCardAttribute *attr = link->data;
3099 const gchar *attr_name;
3100 GList *values;
3101
3102 attr_name = e_vcard_attribute_get_name (attr);
3103 if (!attr_name || (
3104 g_ascii_strcasecmp (attr_name, EVC_PHOTO) != 0 &&
3105 g_ascii_strcasecmp (attr_name, EVC_LOGO) != 0)) {
3106 continue;
3107 }
3108
3109 values = e_vcard_attribute_get_param (attr, EVC_ENCODING);
3110 if (values && (g_ascii_strcasecmp (values->data, "b") == 0 || g_ascii_strcasecmp (values->data, "base64") == 0)) {
3111 values = e_vcard_attribute_get_values_decoded (attr);
3112 if (values && values->data) {
3113 const GString *decoded = values->data;
3114 gchar *local_filename;
3115
3116 if (!decoded->len)
3117 continue;
3118
3119 values = e_vcard_attribute_get_param (attr, EVC_TYPE);
3120
3121 local_filename = ebmb_create_photo_local_filename (meta_backend, e_contact_get_const (contact, E_CONTACT_UID),
3122 attr_name, fileindex, values ? values->data : NULL);
3123 if (local_filename &&
3124 g_file_set_contents (local_filename, decoded->str, decoded->len, error)) {
3125 gchar *url;
3126
3127 e_vcard_attribute_remove_param (attr, EVC_TYPE);
3128 e_vcard_attribute_remove_param (attr, EVC_ENCODING);
3129 e_vcard_attribute_remove_param (attr, EVC_VALUE);
3130 e_vcard_attribute_remove_values (attr);
3131
3132 url = g_filename_to_uri (local_filename, NULL, NULL);
3133
3134 e_vcard_attribute_add_param_with_value (attr, e_vcard_attribute_param_new (EVC_VALUE), "uri");
3135 e_vcard_attribute_add_value (attr, url);
3136
3137 g_free (url);
3138 } else {
3139 success = FALSE;
3140 }
3141
3142 g_free (local_filename);
3143 }
3144 }
3145 }
3146
3147 return success;
3148 }
3149
3150 /**
3151 * e_book_meta_backend_empty_cache_sync:
3152 * @meta_backend: an #EBookMetaBackend
3153 * @cancellable: optional #GCancellable object, or %NULL
3154 * @error: return location for a #GError, or %NULL
3155 *
3156 * Empties the local cache by removing all known contacts from it
3157 * and notifies about such removal any opened views.
3158 *
3159 * Returns: Whether succeeded.
3160 *
3161 * Since: 3.26
3162 **/
3163 gboolean
e_book_meta_backend_empty_cache_sync(EBookMetaBackend * meta_backend,GCancellable * cancellable,GError ** error)3164 e_book_meta_backend_empty_cache_sync (EBookMetaBackend *meta_backend,
3165 GCancellable *cancellable,
3166 GError **error)
3167 {
3168 EBookBackend *book_backend;
3169 EBookCache *book_cache;
3170 GSList *uids = NULL, *link;
3171 gchar *cache_path, *cache_filename;
3172 gboolean success;
3173
3174 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3175
3176 book_cache = e_book_meta_backend_ref_cache (meta_backend);
3177 g_return_val_if_fail (book_cache != NULL, FALSE);
3178
3179 e_cache_lock (E_CACHE (book_cache), E_CACHE_LOCK_WRITE);
3180
3181 book_backend = E_BOOK_BACKEND (meta_backend);
3182
3183 success = e_book_cache_search_uids (book_cache, NULL, &uids, cancellable, error);
3184 if (success)
3185 success = e_cache_remove_all (E_CACHE (book_cache), cancellable, error);
3186
3187 e_cache_unlock (E_CACHE (book_cache), success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
3188
3189 cache_path = g_path_get_dirname (e_cache_get_filename (E_CACHE (book_cache)));
3190 cache_filename = g_path_get_basename (e_cache_get_filename (E_CACHE (book_cache)));
3191
3192 g_object_unref (book_cache);
3193
3194 if (success) {
3195 GDir *dir;
3196
3197 for (link = uids; link; link = g_slist_next (link)) {
3198 const gchar *uid = link->data;
3199
3200 if (!uid)
3201 continue;
3202
3203 e_book_backend_notify_remove (book_backend, uid);
3204 }
3205
3206 g_mutex_lock (&meta_backend->priv->property_lock);
3207
3208 for (link = meta_backend->priv->cursors; link; link = g_slist_next (link)) {
3209 EDataBookCursor *cursor = link->data;
3210
3211 e_data_book_cursor_recalculate (cursor, cancellable, NULL);
3212 }
3213
3214 g_mutex_unlock (&meta_backend->priv->property_lock);
3215
3216 /* Remove also all photos and logos stored beside the cache */
3217 dir = g_dir_open (cache_path, 0, NULL);
3218 if (dir) {
3219 const gchar *filename;
3220
3221 while (filename = g_dir_read_name (dir), filename) {
3222 if ((g_str_has_prefix (filename, EVC_PHOTO) ||
3223 g_str_has_prefix (filename, EVC_LOGO)) &&
3224 g_strcmp0 (cache_filename, filename) != 0) {
3225 if (g_unlink (filename) == -1) {
3226 /* Something failed, ignore the error */
3227 }
3228 }
3229 }
3230
3231 g_dir_close (dir);
3232 }
3233 }
3234
3235 g_slist_free_full (uids, g_free);
3236 g_free (cache_filename);
3237 g_free (cache_path);
3238
3239 return success;
3240 }
3241
3242 /**
3243 * e_book_meta_backend_schedule_refresh:
3244 * @meta_backend: an #EBookMetaBackend
3245 *
3246 * Schedules refresh of the content of the @meta_backend. If there's any
3247 * already scheduled, then the function does nothing.
3248 *
3249 * Use e_book_meta_backend_refresh_sync() to refresh the @meta_backend
3250 * immediately.
3251 *
3252 * Since: 3.26
3253 **/
3254 void
e_book_meta_backend_schedule_refresh(EBookMetaBackend * meta_backend)3255 e_book_meta_backend_schedule_refresh (EBookMetaBackend *meta_backend)
3256 {
3257 GCancellable *cancellable;
3258
3259 g_return_if_fail (E_IS_BOOK_META_BACKEND (meta_backend));
3260
3261 g_mutex_lock (&meta_backend->priv->property_lock);
3262
3263 if (meta_backend->priv->refresh_cancellable) {
3264 /* Already refreshing the content */
3265 g_mutex_unlock (&meta_backend->priv->property_lock);
3266 return;
3267 }
3268
3269 cancellable = g_cancellable_new ();
3270 meta_backend->priv->refresh_cancellable = g_object_ref (cancellable);
3271
3272 g_mutex_unlock (&meta_backend->priv->property_lock);
3273
3274 e_book_backend_schedule_custom_operation (E_BOOK_BACKEND (meta_backend), cancellable,
3275 ebmb_refresh_thread_func, NULL, NULL);
3276
3277 g_object_unref (cancellable);
3278 }
3279
3280 /**
3281 * e_book_meta_backend_refresh_sync:
3282 * @meta_backend: an #EBookMetaBackend
3283 * @cancellable: optional #GCancellable object, or %NULL
3284 * @error: return location for a #GError, or %NULL
3285 *
3286 * Refreshes the @meta_backend immediately. To just schedule refresh
3287 * operation call e_book_meta_backend_schedule_refresh().
3288 *
3289 * Returns: Whether succeeded.
3290 *
3291 * Since: 3.26
3292 **/
3293 gboolean
e_book_meta_backend_refresh_sync(EBookMetaBackend * meta_backend,GCancellable * cancellable,GError ** error)3294 e_book_meta_backend_refresh_sync (EBookMetaBackend *meta_backend,
3295 GCancellable *cancellable,
3296 GError **error)
3297 {
3298 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3299
3300 return ebmb_refresh_internal_sync (meta_backend, TRUE, cancellable, error);
3301 }
3302
3303 /**
3304 * e_book_meta_backend_ensure_connected_sync:
3305 * @meta_backend: an #EBookMetaBackend
3306 * @cancellable: optional #GCancellable object, or %NULL
3307 * @error: return location for a #GError, or %NULL
3308 *
3309 * Ensures that the @meta_backend is connected to its destination.
3310 *
3311 * Returns: Whether succeeded.
3312 *
3313 * Since: 3.26
3314 **/
3315 gboolean
e_book_meta_backend_ensure_connected_sync(EBookMetaBackend * meta_backend,GCancellable * cancellable,GError ** error)3316 e_book_meta_backend_ensure_connected_sync (EBookMetaBackend *meta_backend,
3317 GCancellable *cancellable,
3318 GError **error)
3319 {
3320 EBackend *backend;
3321 ENamedParameters *credentials;
3322 ESource *source;
3323 ESourceAuthenticationResult auth_result = E_SOURCE_AUTHENTICATION_UNKNOWN;
3324 ESourceCredentialsReason creds_reason = E_SOURCE_CREDENTIALS_REASON_ERROR;
3325 gchar *certificate_pem = NULL;
3326 GTlsCertificateFlags certificate_errors = 0;
3327 GError *local_error = NULL;
3328
3329 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3330
3331 backend = E_BACKEND (meta_backend);
3332
3333 if (!e_backend_get_online (backend) &&
3334 e_backend_is_destination_reachable (backend, cancellable, NULL))
3335 e_backend_set_online (backend, TRUE);
3336
3337 if (!e_backend_get_online (backend)) {
3338 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_REPOSITORY_OFFLINE, NULL));
3339
3340 return FALSE;
3341 }
3342
3343 g_mutex_lock (&meta_backend->priv->property_lock);
3344 credentials = e_named_parameters_new_clone (meta_backend->priv->last_credentials);
3345 g_mutex_unlock (&meta_backend->priv->property_lock);
3346
3347 g_mutex_lock (&meta_backend->priv->connect_lock);
3348
3349 source = e_backend_get_source (backend);
3350
3351 if (e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_CONNECTED)
3352 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
3353
3354 if (e_book_meta_backend_connect_sync (meta_backend, credentials, &auth_result, &certificate_pem, &certificate_errors,
3355 cancellable, &local_error)) {
3356 ebmb_update_connection_values (meta_backend);
3357 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
3358 g_mutex_unlock (&meta_backend->priv->connect_lock);
3359 e_named_parameters_free (credentials);
3360
3361 return TRUE;
3362 }
3363
3364 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
3365
3366 g_mutex_unlock (&meta_backend->priv->connect_lock);
3367
3368 e_named_parameters_free (credentials);
3369
3370 g_warn_if_fail (auth_result != E_SOURCE_AUTHENTICATION_ACCEPTED);
3371
3372 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND)) {
3373 e_backend_set_online (backend, FALSE);
3374 g_propagate_error (error, local_error);
3375 g_free (certificate_pem);
3376
3377 return FALSE;
3378 }
3379
3380 switch (auth_result) {
3381 case E_SOURCE_AUTHENTICATION_UNKNOWN:
3382 if (local_error)
3383 g_propagate_error (error, local_error);
3384 g_free (certificate_pem);
3385 return FALSE;
3386 case E_SOURCE_AUTHENTICATION_ERROR:
3387 creds_reason = E_SOURCE_CREDENTIALS_REASON_ERROR;
3388 break;
3389 case E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED:
3390 creds_reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
3391 break;
3392 case E_SOURCE_AUTHENTICATION_ACCEPTED:
3393 g_warn_if_reached ();
3394 break;
3395 case E_SOURCE_AUTHENTICATION_REJECTED:
3396 creds_reason = E_SOURCE_CREDENTIALS_REASON_REJECTED;
3397 break;
3398 case E_SOURCE_AUTHENTICATION_REQUIRED:
3399 creds_reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
3400 break;
3401 }
3402
3403 e_backend_schedule_credentials_required (backend, creds_reason, certificate_pem, certificate_errors,
3404 local_error, cancellable, G_STRFUNC);
3405
3406 g_clear_error (&local_error);
3407 g_free (certificate_pem);
3408
3409 return FALSE;
3410 }
3411
3412 /**
3413 * e_book_meta_backend_split_changes_sync:
3414 * @meta_backend: an #EBookMetaBackend
3415 * @objects: (inout) (element-type EBookMetaBackendInfo):
3416 * a #GSList of #EBookMetaBackendInfo object infos to split
3417 * @out_created_objects: (out) (element-type EBookMetaBackendInfo) (transfer full):
3418 * a #GSList of #EBookMetaBackendInfo object infos which had been created
3419 * @out_modified_objects: (out) (element-type EBookMetaBackendInfo) (transfer full):
3420 * a #GSList of #EBookMetaBackendInfo object infos which had been modified
3421 * @out_removed_objects: (out) (element-type EBookMetaBackendInfo) (transfer full) (nullable):
3422 * a #GSList of #EBookMetaBackendInfo object infos which had been removed;
3423 * it can be %NULL, to not gather list of removed object infos
3424 * @cancellable: optional #GCancellable object, or %NULL
3425 * @error: return location for a #GError, or %NULL
3426 *
3427 * Splits @objects into created/modified/removed lists according to current local
3428 * cache content. Only the @out_removed_objects can be %NULL, others cannot.
3429 * The function modifies @objects by moving its 'data' to corresponding out
3430 * lists and sets the @objects 'data' to %NULL.
3431 *
3432 * Each output #GSList should be freed with
3433 * g_slist_free_full (objects, e_book_meta_backend_info_free);
3434 * when no longer needed.
3435 *
3436 * The caller is still responsible to free @objects as well.
3437 *
3438 * Returns: Whether succeeded.
3439 *
3440 * Since: 3.26
3441 **/
3442 gboolean
e_book_meta_backend_split_changes_sync(EBookMetaBackend * meta_backend,GSList * objects,GSList ** out_created_objects,GSList ** out_modified_objects,GSList ** out_removed_objects,GCancellable * cancellable,GError ** error)3443 e_book_meta_backend_split_changes_sync (EBookMetaBackend *meta_backend,
3444 GSList *objects,
3445 GSList **out_created_objects,
3446 GSList **out_modified_objects,
3447 GSList **out_removed_objects,
3448 GCancellable *cancellable,
3449 GError **error)
3450 {
3451 GHashTable *locally_cached; /* EContactId * ~> gchar *revision */
3452 GHashTableIter iter;
3453 GSList *link;
3454 EBookCache *book_cache;
3455 gpointer key, value;
3456
3457 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3458 g_return_val_if_fail (out_created_objects, FALSE);
3459 g_return_val_if_fail (out_modified_objects, FALSE);
3460
3461 *out_created_objects = NULL;
3462 *out_modified_objects = NULL;
3463
3464 if (out_removed_objects)
3465 *out_removed_objects = NULL;
3466
3467 book_cache = e_book_meta_backend_ref_cache (meta_backend);
3468 g_return_val_if_fail (book_cache != NULL, FALSE);
3469
3470 locally_cached = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
3471
3472 if (!e_book_cache_search_with_callback (book_cache, NULL,
3473 ebmb_gather_locally_cached_objects_cb, locally_cached, cancellable, error)) {
3474 g_hash_table_destroy (locally_cached);
3475 g_object_unref (book_cache);
3476 return FALSE;
3477 }
3478
3479 for (link = objects; link; link = g_slist_next (link)) {
3480 EBookMetaBackendInfo *nfo = link->data;
3481
3482 if (!nfo)
3483 continue;
3484
3485 if (!g_hash_table_contains (locally_cached, nfo->uid)) {
3486 link->data = NULL;
3487
3488 *out_created_objects = g_slist_prepend (*out_created_objects, nfo);
3489 } else {
3490 const gchar *local_revision = g_hash_table_lookup (locally_cached, nfo->uid);
3491
3492 if (g_strcmp0 (local_revision, nfo->revision) != 0) {
3493 link->data = NULL;
3494
3495 *out_modified_objects = g_slist_prepend (*out_modified_objects, nfo);
3496 }
3497
3498 g_hash_table_remove (locally_cached, nfo->uid);
3499 }
3500 }
3501
3502 if (out_removed_objects) {
3503 /* What left in the hash table is removed from the remote side */
3504 g_hash_table_iter_init (&iter, locally_cached);
3505 while (g_hash_table_iter_next (&iter, &key, &value)) {
3506 const gchar *uid = key;
3507 const gchar *revision = value;
3508 EBookMetaBackendInfo *nfo;
3509
3510 if (!uid) {
3511 g_warn_if_reached ();
3512 continue;
3513 }
3514
3515 nfo = e_book_meta_backend_info_new (uid, revision, NULL, NULL);
3516 *out_removed_objects = g_slist_prepend (*out_removed_objects, nfo);
3517 }
3518
3519 *out_removed_objects = g_slist_reverse (*out_removed_objects);
3520 }
3521
3522 g_hash_table_destroy (locally_cached);
3523 g_object_unref (book_cache);
3524
3525 *out_created_objects = g_slist_reverse (*out_created_objects);
3526 *out_modified_objects = g_slist_reverse (*out_modified_objects);
3527
3528 return TRUE;
3529 }
3530
3531 /**
3532 * e_book_meta_backend_process_changes_sync:
3533 * @meta_backend: an #EBookMetaBackend
3534 * @created_objects: (element-type EBookMetaBackendInfo) (nullable):
3535 * a #GSList of #EBookMetaBackendInfo object infos which had been created
3536 * @modified_objects: (element-type EBookMetaBackendInfo) (nullable):
3537 * a #GSList of #EBookMetaBackendInfo object infos which had been modified
3538 * @removed_objects: (element-type EBookMetaBackendInfo) (nullable):
3539 * a #GSList of #EBookMetaBackendInfo object infos which had been removed
3540 * @cancellable: optional #GCancellable object, or %NULL
3541 * @error: return location for a #GError, or %NULL
3542 *
3543 * Processes given changes by updating local cache content accordingly.
3544 * The @meta_backend processes the changes like being online and particularly
3545 * requires to be online to load created and modified objects when needed.
3546 *
3547 * Returns: Whether succeeded.
3548 *
3549 * Since: 3.26
3550 **/
3551 gboolean
e_book_meta_backend_process_changes_sync(EBookMetaBackend * meta_backend,const GSList * created_objects,const GSList * modified_objects,const GSList * removed_objects,GCancellable * cancellable,GError ** error)3552 e_book_meta_backend_process_changes_sync (EBookMetaBackend *meta_backend,
3553 const GSList *created_objects,
3554 const GSList *modified_objects,
3555 const GSList *removed_objects,
3556 GCancellable *cancellable,
3557 GError **error)
3558 {
3559 EBookCache *book_cache;
3560 GHashTable *covered_uids;
3561 GString *invalid_objects = NULL;
3562 GSList *link;
3563 gboolean success = TRUE;
3564
3565 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3566
3567 book_cache = e_book_meta_backend_ref_cache (meta_backend);
3568 g_return_val_if_fail (book_cache != NULL, FALSE);
3569
3570 covered_uids = g_hash_table_new (g_str_hash, g_str_equal);
3571
3572 /* Removed objects first */
3573 for (link = (GSList *) removed_objects; link && success; link = g_slist_next (link)) {
3574 EBookMetaBackendInfo *nfo = link->data;
3575
3576 if (!nfo) {
3577 g_warn_if_reached ();
3578 continue;
3579 }
3580
3581 success = ebmb_maybe_remove_from_cache (meta_backend, book_cache, E_CACHE_IS_ONLINE, nfo->uid, 0, cancellable, error);
3582 }
3583
3584 /* Then modified objects */
3585 for (link = (GSList *) modified_objects; link && success; link = g_slist_next (link)) {
3586 EBookMetaBackendInfo *nfo = link->data;
3587 GError *local_error = NULL;
3588
3589 if (!nfo || !nfo->uid || !*nfo->uid ||
3590 g_hash_table_contains (covered_uids, nfo->uid))
3591 continue;
3592
3593 g_hash_table_insert (covered_uids, nfo->uid, NULL);
3594
3595 success = ebmb_load_contact_wrapper_sync (meta_backend, book_cache, nfo->uid, nfo->object, nfo->extra, NULL, cancellable, &local_error);
3596
3597 /* Do not stop on invalid objects, just notify about them later, and load as many as possible */
3598 if (!success && g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_INVALID_ARG)) {
3599 if (!invalid_objects) {
3600 invalid_objects = g_string_new (local_error->message);
3601 } else {
3602 g_string_append_c (invalid_objects, '\n');
3603 g_string_append (invalid_objects, local_error->message);
3604 }
3605 g_clear_error (&local_error);
3606 success = TRUE;
3607 } else if (local_error) {
3608 g_propagate_error (error, local_error);
3609 }
3610 }
3611
3612 g_hash_table_remove_all (covered_uids);
3613
3614 /* Finally created objects */
3615 for (link = (GSList *) created_objects; link && success; link = g_slist_next (link)) {
3616 EBookMetaBackendInfo *nfo = link->data;
3617 GError *local_error = NULL;
3618
3619 if (!nfo || !nfo->uid || !*nfo->uid)
3620 continue;
3621
3622 success = ebmb_load_contact_wrapper_sync (meta_backend, book_cache, nfo->uid, nfo->object, nfo->extra, NULL, cancellable, &local_error);
3623
3624 /* Do not stop on invalid objects, just notify about them later, and load as many as possible */
3625 if (!success && g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_INVALID_ARG)) {
3626 if (!invalid_objects) {
3627 invalid_objects = g_string_new (local_error->message);
3628 } else {
3629 g_string_append_c (invalid_objects, '\n');
3630 g_string_append (invalid_objects, local_error->message);
3631 }
3632 g_clear_error (&local_error);
3633 success = TRUE;
3634 } else if (local_error) {
3635 g_propagate_error (error, local_error);
3636 }
3637 }
3638
3639 g_hash_table_destroy (covered_uids);
3640
3641 if (invalid_objects) {
3642 e_book_backend_notify_error (E_BOOK_BACKEND (meta_backend), invalid_objects->str);
3643
3644 g_string_free (invalid_objects, TRUE);
3645 }
3646
3647 g_clear_object (&book_cache);
3648
3649 return success;
3650 }
3651
3652 /**
3653 * e_book_meta_backend_connect_sync:
3654 * @meta_backend: an #EBookMetaBackend
3655 * @credentials: (nullable): an #ENamedParameters with previously used credentials, or %NULL
3656 * @out_auth_result: (out): an #ESourceAuthenticationResult with an authentication result
3657 * @out_certificate_pem: (out) (transfer full): a PEM encoded certificate on failure, or %NULL
3658 * @out_certificate_errors: (out): a #GTlsCertificateFlags on failure, or 0
3659 * @cancellable: optional #GCancellable object, or %NULL
3660 * @error: return location for a #GError, or %NULL
3661 *
3662 * This is called always before any operation which requires a connection
3663 * to the remote side. It can fail with an #E_CLIENT_ERROR_REPOSITORY_OFFLINE
3664 * error to indicate that the remote side cannot be currently reached. Other
3665 * errors are propagated to the caller/client side. This method is not called
3666 * when the backend is offline.
3667 *
3668 * The descendant should also call e_book_backend_set_writable() after successful
3669 * connect to the remote side. This value is stored for later use, when being
3670 * opened offline.
3671 *
3672 * The @credentials parameter consists of the previously used credentials.
3673 * It's always %NULL with the first connection attempt. To get the credentials,
3674 * just set the @out_auth_result to %E_SOURCE_AUTHENTICATION_REQUIRED for
3675 * the first time and the function will be called again once the credentials
3676 * are available. See the documentation of #ESourceAuthenticationResult for
3677 * other available results.
3678 *
3679 * The out parameters are passed to e_backend_schedule_credentials_required()
3680 * and are ignored when the descendant returns %TRUE, aka they are used
3681 * only if the connection fails. The @out_certificate_pem and @out_certificate_errors
3682 * should be used together and they can be left untouched if the failure reason was
3683 * not related to certificate. Use @out_auth_result %E_SOURCE_AUTHENTICATION_UNKNOWN
3684 * to indicate other error than @credentials error, otherwise the @error is used
3685 * according to @out_auth_result value.
3686 *
3687 * It is mandatory to implement this virtual method by the descendant.
3688 *
3689 * Returns: Whether succeeded.
3690 *
3691 * Since: 3.26
3692 **/
3693 gboolean
e_book_meta_backend_connect_sync(EBookMetaBackend * meta_backend,const ENamedParameters * credentials,ESourceAuthenticationResult * out_auth_result,gchar ** out_certificate_pem,GTlsCertificateFlags * out_certificate_errors,GCancellable * cancellable,GError ** error)3694 e_book_meta_backend_connect_sync (EBookMetaBackend *meta_backend,
3695 const ENamedParameters *credentials,
3696 ESourceAuthenticationResult *out_auth_result,
3697 gchar **out_certificate_pem,
3698 GTlsCertificateFlags *out_certificate_errors,
3699 GCancellable *cancellable,
3700 GError **error)
3701 {
3702 EBookMetaBackendClass *klass;
3703
3704 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3705
3706 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
3707 g_return_val_if_fail (klass != NULL, FALSE);
3708 g_return_val_if_fail (klass->connect_sync != NULL, FALSE);
3709
3710 return klass->connect_sync (meta_backend, credentials, out_auth_result, out_certificate_pem, out_certificate_errors, cancellable, error);
3711 }
3712
3713 /**
3714 * e_book_meta_backend_disconnect_sync:
3715 * @meta_backend: an #EBookMetaBackend
3716 * @cancellable: optional #GCancellable object, or %NULL
3717 * @error: return location for a #GError, or %NULL
3718 *
3719 * This is called when the backend goes into offline mode or
3720 * when the disconnect is required. The implementation should
3721 * not report any error when it is called and the @meta_backend
3722 * is not connected.
3723 *
3724 * It is mandatory to implement this virtual method by the descendant.
3725 *
3726 * Returns: Whether succeeded.
3727 *
3728 * Since: 3.26
3729 **/
3730 gboolean
e_book_meta_backend_disconnect_sync(EBookMetaBackend * meta_backend,GCancellable * cancellable,GError ** error)3731 e_book_meta_backend_disconnect_sync (EBookMetaBackend *meta_backend,
3732 GCancellable *cancellable,
3733 GError **error)
3734 {
3735 EBookMetaBackendClass *klass;
3736
3737 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3738
3739 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
3740 g_return_val_if_fail (klass != NULL, FALSE);
3741 g_return_val_if_fail (klass->disconnect_sync != NULL, FALSE);
3742
3743 return klass->disconnect_sync (meta_backend, cancellable, error);
3744 }
3745
3746 /**
3747 * e_book_meta_backend_get_changes_sync:
3748 * @meta_backend: an #EBookMetaBackend
3749 * @last_sync_tag: (nullable): optional sync tag from the last check
3750 * @is_repeat: set to %TRUE when this is the repeated call
3751 * @out_new_sync_tag: (out) (transfer full): new sync tag to store on success
3752 * @out_repeat: (out): whether to repeat this call again; default is %FALSE
3753 * @out_created_objects: (out) (element-type EBookMetaBackendInfo) (transfer full):
3754 * a #GSList of #EBookMetaBackendInfo object infos which had been created since
3755 * the last check
3756 * @out_modified_objects: (out) (element-type EBookMetaBackendInfo) (transfer full):
3757 * a #GSList of #EBookMetaBackendInfo object infos which had been modified since
3758 * the last check
3759 * @out_removed_objects: (out) (element-type EBookMetaBackendInfo) (transfer full):
3760 * a #GSList of #EBookMetaBackendInfo object infos which had been removed since
3761 * the last check
3762 * @cancellable: optional #GCancellable object, or %NULL
3763 * @error: return location for a #GError, or %NULL
3764 *
3765 * Gathers the changes since the last check which had been done
3766 * on the remote side.
3767 *
3768 * The @last_sync_tag can be used as a tag of the last check. This can be %NULL,
3769 * when there was no previous call or when the descendant doesn't store any
3770 * such tags. The @out_new_sync_tag can be populated with a value to be stored
3771 * and used the next time.
3772 *
3773 * The @out_repeat can be set to %TRUE when the descendant didn't finish
3774 * read of all the changes. In that case the @meta_backend calls this
3775 * function again with the @out_new_sync_tag as the @last_sync_tag, but also
3776 * notifies about the found changes immediately. The @is_repeat is set
3777 * to %TRUE as well in this case, otherwise it's %FALSE.
3778 *
3779 * The descendant can populate also EBookMetaBackendInfo::object of
3780 * the @out_created_objects and @out_modified_objects, if known, in which
3781 * case this will be used instead of loading it with e_book_meta_backend_load_contact_sync().
3782 *
3783 * It is optional to implement this virtual method by the descendant.
3784 * The default implementation calls e_book_meta_backend_list_existing_sync()
3785 * and then compares the list with the current content of the local cache
3786 * and populates the respective lists appropriately.
3787 *
3788 * Each output #GSList should be freed with
3789 * g_slist_free_full (objects, e_book_meta_backend_info_free);
3790 * when no longer needed.
3791 *
3792 * Returns: Whether succeeded.
3793 *
3794 * Since: 3.26
3795 **/
3796 gboolean
e_book_meta_backend_get_changes_sync(EBookMetaBackend * meta_backend,const gchar * last_sync_tag,gboolean is_repeat,gchar ** out_new_sync_tag,gboolean * out_repeat,GSList ** out_created_objects,GSList ** out_modified_objects,GSList ** out_removed_objects,GCancellable * cancellable,GError ** error)3797 e_book_meta_backend_get_changes_sync (EBookMetaBackend *meta_backend,
3798 const gchar *last_sync_tag,
3799 gboolean is_repeat,
3800 gchar **out_new_sync_tag,
3801 gboolean *out_repeat,
3802 GSList **out_created_objects,
3803 GSList **out_modified_objects,
3804 GSList **out_removed_objects,
3805 GCancellable *cancellable,
3806 GError **error)
3807 {
3808 EBookMetaBackendClass *klass;
3809 gint repeat_count = 0;
3810 gboolean success = FALSE;
3811 GError *local_error = NULL;
3812
3813 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3814 g_return_val_if_fail (out_new_sync_tag != NULL, FALSE);
3815 g_return_val_if_fail (out_repeat != NULL, FALSE);
3816 g_return_val_if_fail (out_created_objects != NULL, FALSE);
3817 g_return_val_if_fail (out_created_objects != NULL, FALSE);
3818 g_return_val_if_fail (out_modified_objects != NULL, FALSE);
3819 g_return_val_if_fail (out_removed_objects != NULL, FALSE);
3820
3821 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
3822 g_return_val_if_fail (klass != NULL, FALSE);
3823 g_return_val_if_fail (klass->get_changes_sync != NULL, FALSE);
3824
3825 while (!success && repeat_count <= MAX_REPEAT_COUNT) {
3826 guint wait_credentials_stamp;
3827
3828 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
3829 wait_credentials_stamp = meta_backend->priv->wait_credentials_stamp;
3830 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
3831
3832 g_clear_error (&local_error);
3833 repeat_count++;
3834
3835 success = klass->get_changes_sync (meta_backend,
3836 last_sync_tag,
3837 is_repeat,
3838 out_new_sync_tag,
3839 out_repeat,
3840 out_created_objects,
3841 out_modified_objects,
3842 out_removed_objects,
3843 cancellable,
3844 &local_error);
3845
3846 if (!success && repeat_count <= MAX_REPEAT_COUNT && !ebmb_maybe_wait_for_credentials (meta_backend, wait_credentials_stamp, local_error, cancellable))
3847 break;
3848 }
3849
3850 if (local_error)
3851 g_propagate_error (error, local_error);
3852
3853 return success;
3854 }
3855
3856 /**
3857 * e_book_meta_backend_list_existing_sync:
3858 * @meta_backend: an #EBookMetaBackend
3859 * @out_new_sync_tag: (out) (transfer full): optional return location for a new sync tag
3860 * @out_existing_objects: (out) (element-type EBookMetaBackendInfo) (transfer full):
3861 * a #GSList of #EBookMetaBackendInfo object infos which are stored on the remote side
3862 * @cancellable: optional #GCancellable object, or %NULL
3863 * @error: return location for a #GError, or %NULL
3864 *
3865 * Used to get list of all existing objects on the remote side. The descendant
3866 * can optionally provide @out_new_sync_tag, which will be stored on success, if
3867 * not %NULL. The descendant can populate also EBookMetaBackendInfo::object of
3868 * the @out_existing_objects, if known, in which case this will be used instead
3869 * of loading it with e_book_meta_backend_load_contact_sync().
3870 *
3871 * It is mandatory to implement this virtual method by the descendant, unless
3872 * it implements its own #EBookMetaBackendClass.get_changes_sync().
3873 *
3874 * The @out_existing_objects #GSList should be freed with
3875 * g_slist_free_full (objects, e_book_meta_backend_info_free);
3876 * when no longer needed.
3877 *
3878 * Returns: Whether succeeded.
3879 *
3880 * Since: 3.26
3881 **/
3882 gboolean
e_book_meta_backend_list_existing_sync(EBookMetaBackend * meta_backend,gchar ** out_new_sync_tag,GSList ** out_existing_objects,GCancellable * cancellable,GError ** error)3883 e_book_meta_backend_list_existing_sync (EBookMetaBackend *meta_backend,
3884 gchar **out_new_sync_tag,
3885 GSList **out_existing_objects,
3886 GCancellable *cancellable,
3887 GError **error)
3888 {
3889 EBookMetaBackendClass *klass;
3890 gint repeat_count = 0;
3891 gboolean success = FALSE;
3892 GError *local_error = NULL;
3893
3894 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3895 g_return_val_if_fail (out_existing_objects != NULL, FALSE);
3896
3897 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
3898 g_return_val_if_fail (klass != NULL, FALSE);
3899 g_return_val_if_fail (klass->list_existing_sync != NULL, FALSE);
3900
3901
3902 while (!success && repeat_count <= MAX_REPEAT_COUNT) {
3903 guint wait_credentials_stamp;
3904
3905 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
3906 wait_credentials_stamp = meta_backend->priv->wait_credentials_stamp;
3907 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
3908
3909 g_clear_error (&local_error);
3910 repeat_count++;
3911
3912 success = klass->list_existing_sync (meta_backend, out_new_sync_tag, out_existing_objects, cancellable, &local_error);
3913
3914 if (!success && repeat_count <= MAX_REPEAT_COUNT && !ebmb_maybe_wait_for_credentials (meta_backend, wait_credentials_stamp, local_error, cancellable))
3915 break;
3916 }
3917
3918 if (local_error)
3919 g_propagate_error (error, local_error);
3920
3921 return success;
3922 }
3923
3924 /**
3925 * e_book_meta_backend_load_contact_sync:
3926 * @meta_backend: an #EBookMetaBackend
3927 * @uid: a contact UID
3928 * @extra: (nullable): optional extra data stored with the contact, or %NULL
3929 * @out_contact: (out) (transfer full): a loaded contact, as an #EContact
3930 * @out_extra: (out) (transfer full): an extra data to store to #EBookCache with this contact
3931 * @cancellable: optional #GCancellable object, or %NULL
3932 * @error: return location for a #GError, or %NULL
3933 *
3934 * Loads a contact from the remote side.
3935 *
3936 * It is mandatory to implement this virtual method by the descendant.
3937 *
3938 * The returned @out_contact should be freed with g_object_unref(),
3939 * when no longer needed.
3940 *
3941 * The returned @out_extra should be freed with g_free(), when no longer
3942 * needed.
3943 *
3944 * Returns: Whether succeeded.
3945 *
3946 * Since: 3.26
3947 **/
3948 gboolean
e_book_meta_backend_load_contact_sync(EBookMetaBackend * meta_backend,const gchar * uid,const gchar * extra,EContact ** out_contact,gchar ** out_extra,GCancellable * cancellable,GError ** error)3949 e_book_meta_backend_load_contact_sync (EBookMetaBackend *meta_backend,
3950 const gchar *uid,
3951 const gchar *extra,
3952 EContact **out_contact,
3953 gchar **out_extra,
3954 GCancellable *cancellable,
3955 GError **error)
3956 {
3957 EBookMetaBackendClass *klass;
3958 gint repeat_count = 0;
3959 gboolean success = FALSE;
3960 GError *local_error = NULL;
3961
3962 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
3963 g_return_val_if_fail (uid != NULL, FALSE);
3964 g_return_val_if_fail (out_contact != NULL, FALSE);
3965 g_return_val_if_fail (out_extra != NULL, FALSE);
3966
3967 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
3968 g_return_val_if_fail (klass != NULL, FALSE);
3969 g_return_val_if_fail (klass->load_contact_sync != NULL, FALSE);
3970
3971
3972 while (!success && repeat_count <= MAX_REPEAT_COUNT) {
3973 guint wait_credentials_stamp;
3974
3975 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
3976 wait_credentials_stamp = meta_backend->priv->wait_credentials_stamp;
3977 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
3978
3979 g_clear_error (&local_error);
3980 repeat_count++;
3981
3982 success = klass->load_contact_sync (meta_backend, uid, extra, out_contact, out_extra, cancellable, &local_error);
3983
3984 if (!success && repeat_count <= MAX_REPEAT_COUNT && !ebmb_maybe_wait_for_credentials (meta_backend, wait_credentials_stamp, local_error, cancellable))
3985 break;
3986 }
3987
3988 if (local_error)
3989 g_propagate_error (error, local_error);
3990
3991 return success;
3992 }
3993
3994 /**
3995 * e_book_meta_backend_save_contact_sync:
3996 * @meta_backend: an #EBookMetaBackend
3997 * @overwrite_existing: %TRUE when can overwrite existing contacts, %FALSE otherwise
3998 * @conflict_resolution: one of #EConflictResolution, what to do on conflicts
3999 * @contact: an #EContact to save
4000 * @extra: (nullable): extra data saved with the contacts in an #EBookCache
4001 * @opflags: bit-or of EBookOperationFlags
4002 * @out_new_uid: (out) (transfer full): return location for the UID of the saved contact
4003 * @out_new_extra: (out) (transfer full): return location for the extra data to store with the contact
4004 * @cancellable: optional #GCancellable object, or %NULL
4005 * @error: return location for a #GError, or %NULL
4006 *
4007 * Saves one contact into the remote side. When the @overwrite_existing is %TRUE, then
4008 * the descendant can overwrite an object with the same UID on the remote side
4009 * (usually used for modify). The @conflict_resolution defines what to do when
4010 * the remote side had made any changes to the object since the last update.
4011 *
4012 * The @contact has already converted locally stored photos and logos
4013 * into inline variants, thus it's not needed to call
4014 * e_book_meta_backend_inline_local_photos_sync() by the descendant.
4015 *
4016 * The @out_new_uid can be populated with a UID of the saved contact as the server
4017 * assigned it to it. This UID, if set, is loaded from the remote side afterwards,
4018 * also to see whether any changes had been made to the contact by the remote side.
4019 *
4020 * The @out_new_extra can be populated with a new extra data to save with the contact.
4021 * Left it %NULL, to keep the same value as the @extra.
4022 *
4023 * The descendant can use an #E_CLIENT_ERROR_OUT_OF_SYNC error to indicate that
4024 * the save failed due to made changes on the remote side, and let the @meta_backend
4025 * resolve this conflict based on the @conflict_resolution on its own.
4026 * The #E_CLIENT_ERROR_OUT_OF_SYNC error should not be used when the descendant
4027 * is able to resolve the conflicts itself.
4028 *
4029 * It is mandatory to implement this virtual method by the writable descendant.
4030 *
4031 * Returns: Whether succeeded.
4032 *
4033 * Since: 3.26
4034 **/
4035 gboolean
e_book_meta_backend_save_contact_sync(EBookMetaBackend * meta_backend,gboolean overwrite_existing,EConflictResolution conflict_resolution,EContact * contact,const gchar * extra,guint32 opflags,gchar ** out_new_uid,gchar ** out_new_extra,GCancellable * cancellable,GError ** error)4036 e_book_meta_backend_save_contact_sync (EBookMetaBackend *meta_backend,
4037 gboolean overwrite_existing,
4038 EConflictResolution conflict_resolution,
4039 /* const */ EContact *contact,
4040 const gchar *extra,
4041 guint32 opflags,
4042 gchar **out_new_uid,
4043 gchar **out_new_extra,
4044 GCancellable *cancellable,
4045 GError **error)
4046 {
4047 EBookMetaBackendClass *klass;
4048 gint repeat_count = 0;
4049 gboolean success = FALSE;
4050 GError *local_error = NULL;
4051
4052 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
4053 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
4054 g_return_val_if_fail (out_new_uid != NULL, FALSE);
4055 g_return_val_if_fail (out_new_extra != NULL, FALSE);
4056
4057 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
4058 g_return_val_if_fail (klass != NULL, FALSE);
4059
4060 if (!klass->save_contact_sync) {
4061 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
4062 return FALSE;
4063 }
4064
4065
4066 while (!success && repeat_count <= MAX_REPEAT_COUNT) {
4067 guint wait_credentials_stamp;
4068
4069 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
4070 wait_credentials_stamp = meta_backend->priv->wait_credentials_stamp;
4071 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
4072
4073 g_clear_error (&local_error);
4074 repeat_count++;
4075
4076 success = klass->save_contact_sync (meta_backend,
4077 overwrite_existing,
4078 conflict_resolution,
4079 contact,
4080 extra,
4081 opflags,
4082 out_new_uid,
4083 out_new_extra,
4084 cancellable,
4085 &local_error);
4086
4087 if (!success && repeat_count <= MAX_REPEAT_COUNT && !ebmb_maybe_wait_for_credentials (meta_backend, wait_credentials_stamp, local_error, cancellable))
4088 break;
4089 }
4090
4091 if (local_error)
4092 g_propagate_error (error, local_error);
4093
4094 return success;
4095 }
4096
4097 /**
4098 * e_book_meta_backend_remove_contact_sync:
4099 * @meta_backend: an #EBookMetaBackend
4100 * @conflict_resolution: an #EConflictResolution to use
4101 * @uid: a contact UID
4102 * @extra: (nullable): extra data being saved with the contact in the local cache, or %NULL
4103 * @object: (nullable): corresponding vCard object, as stored in the local cache, or %NULL
4104 * @opflags: bit-or of #EBookOperationFlags
4105 * @cancellable: optional #GCancellable object, or %NULL
4106 * @error: return location for a #GError, or %NULL
4107 *
4108 * Removes a contact from the remote side. The @object is not %NULL when
4109 * it's removing locally deleted object in offline mode. Being it %NULL,
4110 * the descendant can obtain the object from the #EBookCache.
4111 *
4112 * It is mandatory to implement this virtual method by the writable descendant.
4113 *
4114 * Returns: Whether succeeded.
4115 *
4116 * Since: 3.26
4117 **/
4118 gboolean
e_book_meta_backend_remove_contact_sync(EBookMetaBackend * meta_backend,EConflictResolution conflict_resolution,const gchar * uid,const gchar * extra,const gchar * object,guint32 opflags,GCancellable * cancellable,GError ** error)4119 e_book_meta_backend_remove_contact_sync (EBookMetaBackend *meta_backend,
4120 EConflictResolution conflict_resolution,
4121 const gchar *uid,
4122 const gchar *extra,
4123 const gchar *object,
4124 guint32 opflags,
4125 GCancellable *cancellable,
4126 GError **error)
4127 {
4128 EBookMetaBackendClass *klass;
4129 gint repeat_count = 0;
4130 gboolean success = FALSE;
4131 GError *local_error = NULL;
4132
4133 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
4134 g_return_val_if_fail (uid != NULL, FALSE);
4135
4136 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
4137 g_return_val_if_fail (klass != NULL, FALSE);
4138
4139 if (!klass->remove_contact_sync) {
4140 g_propagate_error (error, e_client_error_create (E_CLIENT_ERROR_NOT_SUPPORTED, NULL));
4141 return FALSE;
4142 }
4143
4144
4145 while (!success && repeat_count <= MAX_REPEAT_COUNT) {
4146 guint wait_credentials_stamp;
4147
4148 g_mutex_lock (&meta_backend->priv->wait_credentials_lock);
4149 wait_credentials_stamp = meta_backend->priv->wait_credentials_stamp;
4150 g_mutex_unlock (&meta_backend->priv->wait_credentials_lock);
4151
4152 g_clear_error (&local_error);
4153 repeat_count++;
4154
4155 success = klass->remove_contact_sync (meta_backend, conflict_resolution, uid, extra, object, opflags, cancellable, &local_error);
4156
4157 if (!success && repeat_count <= MAX_REPEAT_COUNT && !ebmb_maybe_wait_for_credentials (meta_backend, wait_credentials_stamp, local_error, cancellable))
4158 break;
4159 }
4160
4161 if (local_error)
4162 g_propagate_error (error, local_error);
4163
4164 return success;
4165 }
4166
4167 /**
4168 * e_book_meta_backend_search_sync:
4169 * @meta_backend: an #EBookMetaBackend
4170 * @expr: (nullable): a search expression, or %NULL
4171 * @meta_contact: %TRUE, when return #EContact filled with UID and REV only, %FALSE to return full contacts
4172 * @out_contacts: (out) (transfer full) (element-type EContact): return location for the found contacts as #EContact
4173 * @cancellable: optional #GCancellable object, or %NULL
4174 * @error: return location for a #GError, or %NULL
4175 *
4176 * Searches @meta_backend with given expression @expr and returns
4177 * found contacts as a #GSList of #EContact @out_contacts.
4178 * Free the returned @out_contacts with g_slist_free_full (contacts, g_object_unref);
4179 * when no longer needed.
4180 * When the @expr is %NULL, all objects are returned. To get
4181 * UID-s instead, call e_book_meta_backend_search_uids_sync().
4182 *
4183 * It is optional to implement this virtual method by the descendant.
4184 * The default implementation searches @meta_backend's cache. It's also
4185 * not required to be online for searching, thus @meta_backend doesn't
4186 * ensure it.
4187 *
4188 * Returns: Whether succeeded.
4189 *
4190 * Since: 3.26
4191 **/
4192 gboolean
e_book_meta_backend_search_sync(EBookMetaBackend * meta_backend,const gchar * expr,gboolean meta_contact,GSList ** out_contacts,GCancellable * cancellable,GError ** error)4193 e_book_meta_backend_search_sync (EBookMetaBackend *meta_backend,
4194 const gchar *expr,
4195 gboolean meta_contact,
4196 GSList **out_contacts,
4197 GCancellable *cancellable,
4198 GError **error)
4199 {
4200 EBookMetaBackendClass *klass;
4201
4202 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
4203 g_return_val_if_fail (out_contacts != NULL, FALSE);
4204
4205 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
4206 g_return_val_if_fail (klass != NULL, FALSE);
4207 g_return_val_if_fail (klass->search_sync != NULL, FALSE);
4208
4209 return klass->search_sync (meta_backend, expr, meta_contact, out_contacts, cancellable, error);
4210 }
4211
4212 /**
4213 * e_book_meta_backend_search_uids_sync:
4214 * @meta_backend: an #EBookMetaBackend
4215 * @expr: (nullable): a search expression, or %NULL
4216 * @out_uids: (out) (transfer full) (element-type utf8): return location for the found contact UID-s
4217 * @cancellable: optional #GCancellable object, or %NULL
4218 * @error: return location for a #GError, or %NULL
4219 *
4220 * Searches @meta_backend with given expression @expr and returns
4221 * found contact UID-s as a #GSList @out_contacts.
4222 * Free the returned @out_uids with g_slist_free_full (uids, g_free);
4223 * when no longer needed.
4224 * When the @expr is %NULL, all UID-s are returned. To get #EContact(s)
4225 * instead, call e_book_meta_backend_search_sync().
4226 *
4227 * It is optional to implement this virtual method by the descendant.
4228 * The default implementation searches @meta_backend's cache. It's also
4229 * not required to be online for searching, thus @meta_backend doesn't
4230 * ensure it.
4231 *
4232 * Returns: Whether succeeded.
4233 *
4234 * Since: 3.26
4235 **/
4236 gboolean
e_book_meta_backend_search_uids_sync(EBookMetaBackend * meta_backend,const gchar * expr,GSList ** out_uids,GCancellable * cancellable,GError ** error)4237 e_book_meta_backend_search_uids_sync (EBookMetaBackend *meta_backend,
4238 const gchar *expr,
4239 GSList **out_uids,
4240 GCancellable *cancellable,
4241 GError **error)
4242 {
4243 EBookMetaBackendClass *klass;
4244
4245 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
4246 g_return_val_if_fail (out_uids != NULL, FALSE);
4247
4248 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
4249 g_return_val_if_fail (klass != NULL, FALSE);
4250 g_return_val_if_fail (klass->search_uids_sync != NULL, FALSE);
4251
4252 return klass->search_uids_sync (meta_backend, expr, out_uids, cancellable, error);
4253 }
4254
4255 /**
4256 * e_book_meta_backend_requires_reconnect:
4257 * @meta_backend: an #EBookMetaBackend
4258 *
4259 * Determines, whether current source content requires reconnect of the backend.
4260 *
4261 * It is optional to implement this virtual method by the descendant. The default
4262 * implementation compares %E_SOURCE_EXTENSION_AUTHENTICATION and
4263 * %E_SOURCE_EXTENSION_WEBDAV_BACKEND, if existing in the source,
4264 * with the values after the last successful connect and returns
4265 * %TRUE when they changed. It always return %TRUE when there was
4266 * no successful connect done yet.
4267 *
4268 * Returns: %TRUE, when reconnect is required, %FALSE otherwise.
4269 *
4270 * Since: 3.26
4271 **/
4272 gboolean
e_book_meta_backend_requires_reconnect(EBookMetaBackend * meta_backend)4273 e_book_meta_backend_requires_reconnect (EBookMetaBackend *meta_backend)
4274 {
4275 EBookMetaBackendClass *klass;
4276
4277 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
4278
4279 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
4280 g_return_val_if_fail (klass != NULL, FALSE);
4281 g_return_val_if_fail (klass->requires_reconnect != NULL, FALSE);
4282
4283 return klass->requires_reconnect (meta_backend);
4284 }
4285
4286 /**
4287 * e_book_meta_backend_get_ssl_error_details:
4288 * @meta_backend: an #EBookMetaBackend
4289 * @out_certificate_pem: (out): SSL certificate encoded in PEM format
4290 * @out_certificate_errors: (out): bit-or of #GTlsCertificateFlags claiming the certificate errors
4291 *
4292 * It is optional to implement this virtual method by the descendants.
4293 * It is used to receive SSL error details when any online operation
4294 * returns E_CLIENT_ERROR, E_CLIENT_ERROR_TLS_NOT_AVAILABLE error.
4295 *
4296 * Returns: %TRUE, when the SSL error details had been available and
4297 * the out parameters populated, %FALSE otherwise.
4298 *
4299 * Since: 3.28
4300 **/
4301 gboolean
e_book_meta_backend_get_ssl_error_details(EBookMetaBackend * meta_backend,gchar ** out_certificate_pem,GTlsCertificateFlags * out_certificate_errors)4302 e_book_meta_backend_get_ssl_error_details (EBookMetaBackend *meta_backend,
4303 gchar **out_certificate_pem,
4304 GTlsCertificateFlags *out_certificate_errors)
4305 {
4306 EBookMetaBackendClass *klass;
4307
4308 g_return_val_if_fail (E_IS_BOOK_META_BACKEND (meta_backend), FALSE);
4309
4310 klass = E_BOOK_META_BACKEND_GET_CLASS (meta_backend);
4311 g_return_val_if_fail (klass != NULL, FALSE);
4312 g_return_val_if_fail (klass->get_ssl_error_details != NULL, FALSE);
4313
4314 return klass->get_ssl_error_details (meta_backend, out_certificate_pem, out_certificate_errors);
4315 }
4316