1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-book-backend-file.c - File contact backend.
4 *
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6 * Copyright (C) 2012 Intel Corporation
7 *
8 * This library is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15 * for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors: Nat Friedman <nat@novell.com>
21 * Chris Toshok <toshok@ximian.com>
22 * Hans Petter Jansson <hpj@novell.com>
23 * Tristan Van Berkom <tristanvb@openismus.com>
24 */
25
26 #include "evolution-data-server-config.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37
38 #include <glib/gstdio.h>
39 #include <glib/gi18n-lib.h>
40
41 #include "e-book-backend-file.h"
42
43 #ifdef HAVE_LIBDB
44 #include "e-book-backend-file-migrate-bdb.h"
45 #endif
46
47 #define d(x)
48
49 #define E_BOOK_BACKEND_FILE_VERSION_NAME "PAS-DB-VERSION"
50 #define E_BOOK_BACKEND_FILE_VERSION "0.2"
51
52 #define E_BOOK_BACKEND_FILE_REVISION_NAME "PAS-DB-REVISION"
53
54 #define PAS_ID_PREFIX "pas-id-"
55
56 /* We need to specify the folderid in order to properly
57 * migrate data from the old EBookSqliteDB API.
58 */
59 #define SQLITEDB_FOLDER_ID "folder_id"
60 #define SQLITE_REVISION_KEY "revision"
61
62 /* Forward Declarations */
63 static void e_book_backend_file_initable_init
64 (GInitableIface *iface);
65
66 struct _EBookBackendFilePrivate {
67 gchar *base_directory;
68 gchar *photo_dirname;
69 gchar *revision;
70 gchar *locale;
71 volatile gint rev_counter;
72 gboolean revision_guards;
73 GRWLock lock;
74 GList *cursors;
75
76 EBookSqlite *sqlitedb;
77 };
78
79 G_DEFINE_TYPE_WITH_CODE (
80 EBookBackendFile,
81 e_book_backend_file,
82 E_TYPE_BOOK_BACKEND_SYNC,
83 G_ADD_PRIVATE (EBookBackendFile)
84 G_IMPLEMENT_INTERFACE (
85 G_TYPE_INITABLE,
86 e_book_backend_file_initable_init))
87
88 /****************************************************************
89 * File Management helper APIs *
90 ****************************************************************/
91 typedef enum {
92 GET_PATH_DB_DIR,
93 GET_PATH_PHOTO_DIR
94 } GetPathType;
95
96 typedef enum {
97 STATUS_NORMAL = 0,
98 STATUS_MODIFIED,
99 STATUS_ERROR
100 } PhotoModifiedStatus;
101
102 static gboolean
remove_file(const gchar * filename,GError ** error)103 remove_file (const gchar *filename,
104 GError **error)
105 {
106 if (-1 == g_unlink (filename)) {
107 if (errno == EACCES || errno == EPERM) {
108 g_set_error_literal (
109 error, E_CLIENT_ERROR,
110 E_CLIENT_ERROR_PERMISSION_DENIED,
111 e_client_error_to_string (
112 E_CLIENT_ERROR_PERMISSION_DENIED));
113 } else {
114 g_set_error (
115 error, E_CLIENT_ERROR,
116 E_CLIENT_ERROR_OTHER_ERROR,
117 _("Failed to remove file “%s”: %s"),
118 filename, g_strerror (errno));
119 }
120 return FALSE;
121 }
122
123 return TRUE;
124 }
125
126 static gboolean
create_directory(const gchar * dirname,GError ** error)127 create_directory (const gchar *dirname,
128 GError **error)
129 {
130 gint rv;
131
132 rv = g_mkdir_with_parents (dirname, 0700);
133 if (rv == -1 && errno != EEXIST) {
134 g_warning ("failed to make directory %s: %s", dirname, g_strerror (errno));
135 if (errno == EACCES || errno == EPERM)
136 g_set_error_literal (
137 error, E_CLIENT_ERROR,
138 E_CLIENT_ERROR_PERMISSION_DENIED,
139 e_client_error_to_string (
140 E_CLIENT_ERROR_PERMISSION_DENIED));
141 else
142 g_set_error (
143 error, E_CLIENT_ERROR,
144 E_CLIENT_ERROR_OTHER_ERROR,
145 _("Failed to make directory %s: %s"),
146 dirname, g_strerror (errno));
147 return FALSE;
148 }
149 return TRUE;
150 }
151
152 static gchar *
check_remove_uri_for_field(EContact * old_contact,EContact * new_contact,EContactField field)153 check_remove_uri_for_field (EContact *old_contact,
154 EContact *new_contact,
155 EContactField field)
156 {
157 EContactPhoto *old_photo = NULL, *new_photo = NULL;
158 gchar *uri = NULL;
159
160 old_photo = e_contact_get (old_contact, field);
161 if (!old_photo)
162 return NULL;
163
164 if (new_contact) {
165
166 new_photo = e_contact_get (new_contact, field);
167
168 if (new_photo == NULL ||
169 g_ascii_strcasecmp (old_photo->data.uri, new_photo->data.uri))
170 uri = g_strdup (old_photo->data.uri);
171 } else {
172 uri = g_strdup (old_photo->data.uri);
173 }
174
175 e_contact_photo_free (old_photo);
176 e_contact_photo_free (new_photo);
177
178 return uri;
179 }
180
181 static void
maybe_delete_uri(EBookBackendFile * bf,const gchar * uri)182 maybe_delete_uri (EBookBackendFile *bf,
183 const gchar *uri)
184 {
185 GError *error = NULL;
186 gchar *filename;
187
188 /* A uri that does not give us a filename is certainly not
189 * a uri that we created for a local file, just skip it */
190 if ((filename = g_filename_from_uri (uri, NULL, NULL)) == NULL)
191 return;
192
193 /* If the file is in our path it belongs to us and we need to delete it.
194 */
195 if (bf->priv->photo_dirname &&
196 !strncmp (bf->priv->photo_dirname, filename, strlen (bf->priv->photo_dirname))) {
197
198 d (g_print ("Deleting uri file: %s\n", filename));
199
200 /* Deleting uris should not cause the backend to fail to update
201 * a contact so the best we can do from here is log warnings
202 * when we fail to unlink a file from the disk.
203 */
204 if (!remove_file (filename, &error)) {
205 g_warning ("Unable to cleanup photo uri: %s", error->message);
206 g_error_free (error);
207 }
208 }
209
210 g_free (filename);
211 }
212
213 static void
maybe_delete_unused_uris(EBookBackendFile * bf,EContact * old_contact,EContact * new_contact)214 maybe_delete_unused_uris (EBookBackendFile *bf,
215 EContact *old_contact,
216 EContact *new_contact)
217 {
218 gchar *uri_photo, *uri_logo;
219
220 g_return_if_fail (old_contact != NULL);
221
222 /* If there is no new contact, collect all the uris to delete from old_contact
223 *
224 * Otherwise, if any of the photo uri fields have changed in new_contact, then collect the
225 * old uris for those fields from old_contact to delete
226 */
227 uri_photo = check_remove_uri_for_field (old_contact, new_contact, E_CONTACT_PHOTO);
228 uri_logo = check_remove_uri_for_field (old_contact, new_contact, E_CONTACT_LOGO);
229
230 if (uri_photo) {
231 maybe_delete_uri (bf, uri_photo);
232 g_free (uri_photo);
233 }
234
235 if (uri_logo) {
236 maybe_delete_uri (bf, uri_logo);
237 g_free (uri_logo);
238 }
239 }
240
241 static gchar *
e_book_backend_file_extract_path_from_source(ESourceRegistry * registry,ESource * source,GetPathType path_type)242 e_book_backend_file_extract_path_from_source (ESourceRegistry *registry,
243 ESource *source,
244 GetPathType path_type)
245 {
246 ESource *builtin_source;
247 const gchar *user_data_dir;
248 const gchar *uid;
249 gchar *filename = NULL;
250
251 uid = e_source_get_uid (source);
252 g_return_val_if_fail (uid != NULL, NULL);
253
254 user_data_dir = e_get_user_data_dir ();
255
256 builtin_source = e_source_registry_ref_builtin_address_book (registry);
257
258 /* XXX Backward-compatibility hack:
259 *
260 * The special built-in "Personal" data source UIDs are now named
261 * "system-$COMPONENT" but since the data directories are already
262 * split out by component, we'll continue to use the old "system"
263 * directories for these particular data sources. */
264 if (e_source_equal (source, builtin_source))
265 uid = "system";
266
267 switch (path_type) {
268 case GET_PATH_DB_DIR:
269 filename = g_build_filename (
270 user_data_dir, "addressbook", uid, NULL);
271 break;
272 case GET_PATH_PHOTO_DIR:
273 filename = g_build_filename (
274 user_data_dir, "addressbook", uid, "photos", NULL);
275 break;
276 default:
277 g_warn_if_reached ();
278 }
279
280 g_object_unref (builtin_source);
281
282 return filename;
283 }
284
285 static gchar *
safe_name_for_photo(EBookBackendFile * bf,EContact * contact,EContactPhoto * photo,EContactField field)286 safe_name_for_photo (EBookBackendFile *bf,
287 EContact *contact,
288 EContactPhoto *photo,
289 EContactField field)
290 {
291 gchar *fullname = NULL, *name, *str;
292 gchar *suffix = NULL;
293 gint i = 0;
294
295 g_return_val_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED, NULL);
296
297 /* Get a suitable filename extension */
298 if (photo->data.inlined.mime_type != NULL &&
299 photo->data.inlined.mime_type[0] != '\0' &&
300 g_ascii_strcasecmp (photo->data.inlined.mime_type, "image/X-EVOLUTION-UNKNOWN") != 0) {
301 suffix = g_uri_escape_string (
302 photo->data.inlined.mime_type,
303 NULL, TRUE);
304 } else {
305 gchar *mime_type = NULL;
306 gchar *content_type = NULL;
307
308 content_type = g_content_type_guess (
309 NULL,
310 photo->data.inlined.data,
311 photo->data.inlined.length,
312 NULL);
313
314 if (content_type)
315 mime_type = g_content_type_get_mime_type (content_type);
316
317 if (mime_type)
318 suffix = g_uri_escape_string (mime_type, NULL, TRUE);
319 else
320 suffix = g_strdup ("data");
321
322 g_free (mime_type);
323 g_free (content_type);
324 }
325
326 /* Replace any percent in the text with a dash, to avoid escaping issues in URI-s */
327 str = suffix;
328 while (str = strchr (str, '%'), str) {
329 *str = '-';
330 }
331
332 /* Create a filename based on the uid/field */
333 name = g_strconcat (
334 e_contact_get_const (contact, E_CONTACT_UID), "_",
335 e_contact_field_name (field), NULL);
336 name = g_strdelimit (name, NULL, '_');
337
338 /* Replace any percent in the text with a dash, to avoid escaping issues in URI-s */
339 str = name;
340 while (str = strchr (str, '%'), str) {
341 *str = '-';
342 }
343
344 do {
345 g_free (fullname);
346
347 str = e_filename_mkdir_encoded (bf->priv->photo_dirname, name, NULL, i);
348 fullname = g_strdup_printf ("%s.%s", str, suffix);
349 g_free (str);
350
351 i++;
352 } while (g_file_test (fullname, G_FILE_TEST_EXISTS));
353
354 g_free (name);
355 g_free (suffix);
356
357 return fullname;
358 }
359
360 static gchar *
hard_link_photo(EBookBackendFile * bf,EContact * contact,EContactField field,const gchar * src_filename,GError ** error)361 hard_link_photo (EBookBackendFile *bf,
362 EContact *contact,
363 EContactField field,
364 const gchar *src_filename,
365 GError **error)
366 {
367 gchar *fullname = NULL, *name, *str;
368 gint i = 0, ret;
369 const gchar *suffix;
370
371 /* Copy over the file suffix */
372 suffix = strrchr (src_filename, '.');
373 if (suffix)
374 suffix++;
375
376 if (!suffix)
377 suffix = "data";
378
379 /* Create a filename based on uid/field */
380 name = g_strconcat (
381 e_contact_get_const (contact, E_CONTACT_UID), "_",
382 e_contact_field_name (field), NULL);
383 name = g_strdelimit (name, NULL, '_');
384
385 do {
386 g_free (fullname);
387
388 str = e_filename_mkdir_encoded (bf->priv->photo_dirname, name, NULL, i);
389 fullname = g_strdup_printf ("%s.%s", str, suffix);
390 g_free (str);
391
392 i++;
393
394 #ifdef G_OS_WIN32
395 {
396 GFile *src_file = g_file_new_for_path (src_filename);
397 GFile *dst_file = g_file_new_for_path (fullname);
398 GError *copy_error = NULL;
399
400 g_file_copy (src_file, dst_file, G_FILE_COPY_NONE,
401 NULL, NULL, NULL, ©_error);
402
403 if (copy_error != NULL) {
404 g_error_free (copy_error);
405 ret = -1;
406 } else
407 ret = 0;
408
409 g_object_unref (src_file);
410 g_object_unref (dst_file);
411 }
412 #else
413 ret = link (src_filename, fullname);
414 #endif
415
416 } while (ret < 0 && errno == EEXIST);
417
418 if (ret < 0) {
419 if (errno == EACCES || errno == EPERM) {
420 g_set_error_literal (
421 error, E_CLIENT_ERROR,
422 E_CLIENT_ERROR_PERMISSION_DENIED,
423 e_client_error_to_string (
424 E_CLIENT_ERROR_PERMISSION_DENIED));
425 } else {
426 g_set_error (
427 error, E_CLIENT_ERROR,
428 E_CLIENT_ERROR_OTHER_ERROR,
429 _("Failed to create hardlink for resource “%s”: %s"),
430 src_filename, g_strerror (errno));
431 }
432 g_free (fullname);
433 fullname = NULL;
434 }
435
436 g_free (name);
437
438 return fullname;
439 }
440
441 static gboolean
is_backend_owned_uri(EBookBackendFile * bf,const gchar * uri)442 is_backend_owned_uri (EBookBackendFile *bf,
443 const gchar *uri)
444 {
445 gchar *filename;
446 gchar *dirname;
447 gboolean owned_uri;
448
449 /* Errors converting from uri definitily indicate it was
450 * not our uri to begin with, so just disregard this error. */
451 filename = g_filename_from_uri (uri, NULL, NULL);
452 if (!filename)
453 return FALSE;
454
455 dirname = g_path_get_dirname (filename);
456
457 owned_uri = bf->priv->photo_dirname && (strcmp (dirname, bf->priv->photo_dirname) == 0);
458
459 g_free (filename);
460 g_free (dirname);
461
462 return owned_uri;
463 }
464
465 static PhotoModifiedStatus
maybe_transform_vcard_field_for_photo(EBookBackendFile * bf,EContact * old_contact,EContact * contact,EContactField field,GError ** error)466 maybe_transform_vcard_field_for_photo (EBookBackendFile *bf,
467 EContact *old_contact,
468 EContact *contact,
469 EContactField field,
470 GError **error)
471 {
472 PhotoModifiedStatus status = STATUS_NORMAL;
473 EContactPhoto *photo;
474
475 if (field != E_CONTACT_PHOTO && field != E_CONTACT_LOGO)
476 return status;
477
478 photo = e_contact_get (contact, field);
479 if (!photo)
480 return status;
481
482 if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
483 EContactPhoto *new_photo;
484 gchar *new_photo_path;
485 gchar *uri;
486
487 /* Create a unique filename with an extension (hopefully) based on the mime type */
488 new_photo_path = safe_name_for_photo (bf, contact, photo, field);
489
490 if ((uri =
491 g_filename_to_uri (new_photo_path, NULL, error)) == NULL) {
492
493 status = STATUS_ERROR;
494 } else if (!g_file_set_contents (new_photo_path,
495 (const gchar *) photo->data.inlined.data,
496 photo->data.inlined.length,
497 error)) {
498
499 status = STATUS_ERROR;
500 } else {
501 new_photo = e_contact_photo_new ();
502 new_photo->type = E_CONTACT_PHOTO_TYPE_URI;
503 new_photo->data.uri = g_strdup (uri);
504
505 e_contact_set (contact, field, new_photo);
506
507 d (g_print ("Backend modified incomming binary blob to be %s:\n", uri));
508
509 status = STATUS_MODIFIED;
510
511 e_contact_photo_free (new_photo);
512 }
513
514 g_free (uri);
515 g_free (new_photo_path);
516
517 } else { /* E_CONTACT_PHOTO_TYPE_URI */
518 const gchar *uid;
519 EContactPhoto *old_photo = NULL, *new_photo;
520
521 /* First determine that the new contact uri points to our 'photos' directory,
522 * if not then we do nothing
523 */
524 if (!is_backend_owned_uri (bf, photo->data.uri))
525 goto done;
526
527 /* Now check if the uri is changed from the BDB copy
528 */
529 uid = e_contact_get_const (contact, E_CONTACT_UID);
530 if (uid == NULL) {
531 g_set_error_literal (
532 error, E_CLIENT_ERROR,
533 E_CLIENT_ERROR_OTHER_ERROR,
534 _("No UID in the contact"));
535 status = STATUS_ERROR;
536 goto done;
537 }
538
539 if (old_contact)
540 old_photo = e_contact_get (old_contact, field);
541
542 /* Unless we are receiving the same uri that we already have
543 * stored in the BDB... */
544 if (!old_photo || old_photo->type == E_CONTACT_PHOTO_TYPE_INLINED ||
545 g_ascii_strcasecmp (old_photo->data.uri, photo->data.uri) != 0) {
546 gchar *filename;
547 gchar *new_filename;
548 gchar *new_uri = NULL;
549
550 /* ... Assume that the incomming uri belongs to another contact
551 * still in the BDB. Lets go ahead and create a hard link to the
552 * photo file and create a new name for the incomming uri, and
553 * use that in the incomming contact to save in place.
554 *
555 * This piece of code is here to ensure there are no problems if
556 * the libebook user decides to cross-reference and start "sharing"
557 * uris that we've previously stored in the photo directory.
558 *
559 * We use the hard-link here to off-load the necessary ref-counting
560 * logic to the file-system.
561 */
562 filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
563 g_return_val_if_fail (filename, STATUS_NORMAL); /* we already checked this with 'is_backend_owned_uri ()' */
564
565 new_filename = hard_link_photo (bf, contact, field, filename, error);
566
567 if (!new_filename)
568 status = STATUS_ERROR;
569 else if ((new_uri = g_filename_to_uri (new_filename, NULL, error)) == NULL) {
570 /* If we fail here... we need to clean up the hardlink we just created */
571 GError *local_err = NULL;
572 if (!remove_file (new_filename, &local_err)) {
573 g_warning ("Unable to cleanup photo uri: %s", local_err->message);
574 g_error_free (local_err);
575 }
576 status = STATUS_ERROR;
577 } else {
578
579 new_photo = e_contact_photo_new ();
580 new_photo->type = E_CONTACT_PHOTO_TYPE_URI;
581 new_photo->data.uri = new_uri;
582
583 e_contact_set (contact, field, new_photo);
584
585 d (g_print ("Backend modified incomming shared uri to be %s:\n", new_uri));
586
587 e_contact_photo_free (new_photo);
588 status = STATUS_MODIFIED;
589 }
590 g_free (new_filename);
591 g_free (filename);
592 }
593
594 if (old_photo)
595 e_contact_photo_free (old_photo);
596
597 }
598
599 done:
600 e_contact_photo_free (photo);
601
602 return status;
603 }
604
605 /*
606 * When a contact is added or modified we receive a vCard,
607 * this function checks if we've received inline data
608 * and replaces it with a uri notation.
609 *
610 * If this function modifies 'contact' then it will
611 * return the 'modified' status and 'vcard_ret' (if specified)
612 * will be set to a newly allocated vcard string.
613 */
614 static PhotoModifiedStatus
maybe_transform_vcard_for_photo(EBookBackendFile * bf,EContact * old_contact,EContact * contact,GError ** error)615 maybe_transform_vcard_for_photo (EBookBackendFile *bf,
616 EContact *old_contact,
617 EContact *contact,
618 GError **error)
619 {
620 PhotoModifiedStatus status;
621 gboolean modified = FALSE;
622
623 status = maybe_transform_vcard_field_for_photo (
624 bf, old_contact, contact,
625 E_CONTACT_PHOTO, error);
626 modified = (status == STATUS_MODIFIED);
627
628 if (status != STATUS_ERROR) {
629 status = maybe_transform_vcard_field_for_photo (
630 bf, old_contact, contact,
631 E_CONTACT_LOGO, error);
632 modified = modified || (status == STATUS_MODIFIED);
633 }
634
635 if (status != STATUS_ERROR && modified)
636 status = STATUS_MODIFIED;
637
638 return status;
639 }
640
641 /****************************************************************
642 * Global Revisioning Tools *
643 ****************************************************************/
644 static gchar *
e_book_backend_file_create_unique_id(void)645 e_book_backend_file_create_unique_id (void)
646 {
647 gchar *uid = e_util_generate_uid (), *prefixed;
648
649 prefixed = g_strconcat (PAS_ID_PREFIX, uid, NULL);
650 g_free (uid);
651
652 return prefixed;
653 }
654
655 static gchar *
e_book_backend_file_new_revision(EBookBackendFile * bf,gboolean with_counter)656 e_book_backend_file_new_revision (EBookBackendFile *bf,
657 gboolean with_counter)
658 {
659 gchar time_string[100] = {0};
660 const struct tm *tm = NULL;
661 time_t t;
662
663 t = time (NULL);
664 tm = gmtime (&t);
665 if (tm) {
666 struct tm ttm = *tm;
667
668 if (!with_counter && bf->priv->revision_guards) {
669 gint value = g_atomic_int_add (&bf->priv->rev_counter, 1);
670
671 /* Artificial time, which encodes the revision counter */
672 ttm.tm_sec = value % 60;
673 ttm.tm_min = (value / 60 ) % 60;
674 ttm.tm_hour = (value / 3600) % 24;
675 }
676
677 strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", &ttm);
678 }
679
680 if (with_counter)
681 return g_strdup_printf ("%s(%d)", time_string, g_atomic_int_add (&bf->priv->rev_counter, 1));
682
683 return g_strdup (time_string);
684 }
685
686 /* For now just bump the revision and set it in the DB every
687 * time the revision bumps, this is the safest approach and
688 * its unclear so far if bumping the revision string for
689 * every DB modification is going to really be an overhead.
690 */
691 static gboolean
e_book_backend_file_bump_revision(EBookBackendFile * bf,GError ** error)692 e_book_backend_file_bump_revision (EBookBackendFile *bf,
693 GError **error)
694 {
695 GError *local_error = NULL;
696 gchar *new_revision;
697 gboolean success;
698
699 new_revision = e_book_backend_file_new_revision (bf, TRUE);
700 success = e_book_sqlite_set_key_value (
701 bf->priv->sqlitedb,
702 SQLITE_REVISION_KEY,
703 new_revision,
704 &local_error);
705
706 if (success) {
707 g_free (bf->priv->revision);
708 bf->priv->revision = new_revision;
709
710 e_book_backend_notify_property_changed (E_BOOK_BACKEND (bf),
711 E_BOOK_BACKEND_PROPERTY_REVISION,
712 bf->priv->revision);
713 } else {
714 g_free (new_revision);
715 g_warning (
716 G_STRLOC ": Error setting database revision: %s",
717 local_error->message);
718 g_propagate_error (error, local_error);
719 }
720
721 return success;
722 }
723
724 static void
e_book_backend_file_load_revision(EBookBackendFile * bf)725 e_book_backend_file_load_revision (EBookBackendFile *bf)
726 {
727 GError *error = NULL;
728
729 if (!e_book_sqlite_get_key_value (bf->priv->sqlitedb,
730 SQLITE_REVISION_KEY,
731 &bf->priv->revision,
732 &error)) {
733 g_warning (
734 G_STRLOC ": Error loading database revision: %s",
735 error ? error->message : "Unknown error");
736 g_clear_error (&error);
737 } else if (bf->priv->revision == NULL) {
738 e_book_backend_file_bump_revision (bf, NULL);
739 }
740 }
741
742 static void
e_book_backend_file_load_locale(EBookBackendFile * bf)743 e_book_backend_file_load_locale (EBookBackendFile *bf)
744 {
745 GError *error = NULL;
746
747 if (!e_book_sqlite_get_locale (bf->priv->sqlitedb,
748 &bf->priv->locale,
749 &error)) {
750 g_warning (
751 G_STRLOC ": Error loading database locale setting: %s",
752 error ? error->message : "Unknown error");
753 g_clear_error (&error);
754 }
755 }
756
757 static void
set_revision(EBookBackendFile * bf,EContact * contact)758 set_revision (EBookBackendFile *bf,
759 EContact *contact)
760 {
761 gchar *rev;
762
763 rev = e_book_backend_file_new_revision (bf, FALSE);
764 e_contact_set (contact, E_CONTACT_REV, rev);
765 g_free (rev);
766 }
767
768 /****************************************************************
769 * Dealing with cursor updates *
770 ****************************************************************/
771 static void
cursors_contact_added(EBookBackendFile * bf,EContact * contact)772 cursors_contact_added (EBookBackendFile *bf,
773 EContact *contact)
774 {
775 GList *l;
776
777 for (l = bf->priv->cursors; l; l = l->next) {
778 EDataBookCursor *cursor = l->data;
779
780 e_data_book_cursor_contact_added (cursor, contact);
781 }
782 }
783
784 static void
cursors_contact_removed(EBookBackendFile * bf,EContact * contact)785 cursors_contact_removed (EBookBackendFile *bf,
786 EContact *contact)
787 {
788 GList *l;
789
790 for (l = bf->priv->cursors; l; l = l->next) {
791 EDataBookCursor *cursor = l->data;
792
793 e_data_book_cursor_contact_removed (cursor, contact);
794 }
795 }
796
797 /****************************************************************
798 * Main Backend Implementation *
799 ****************************************************************/
800
801 /**
802 * This method will return TRUE if all the contacts were properly created.
803 * If at least one contact fails, the method will return FALSE, all
804 * changes will be reverted (the @contacts list will stay empty) and
805 * @perror will be set.
806 */
807 static gboolean
do_create(EBookBackendFile * bf,const gchar * const * vcards,GSList ** out_contacts,GCancellable * cancellable,GError ** error)808 do_create (EBookBackendFile *bf,
809 const gchar * const *vcards,
810 GSList **out_contacts,
811 GCancellable *cancellable,
812 GError **error)
813 {
814 PhotoModifiedStatus status = STATUS_NORMAL;
815 guint ii, length;
816 GError *local_error = NULL;
817
818 length = g_strv_length ((gchar **) vcards);
819
820 for (ii = 0; ii < length; ii++) {
821 gchar *id;
822 const gchar *rev;
823 EContact *contact;
824
825 contact = e_contact_new_from_vcard (vcards[ii]);
826
827 /* Preserve original UID, create a unique UID if needed */
828 if (e_contact_get_const (contact, E_CONTACT_UID) == NULL) {
829 id = e_book_backend_file_create_unique_id ();
830 e_contact_set (contact, E_CONTACT_UID, id);
831 g_free (id);
832 }
833
834 rev = e_contact_get_const (contact, E_CONTACT_REV);
835 if (!(rev && *rev))
836 set_revision (bf, contact);
837
838 status = maybe_transform_vcard_for_photo (bf, NULL, contact, error);
839
840 if (status != STATUS_ERROR) {
841
842 /* Contact was added successfully. */
843 *out_contacts = g_slist_prepend (*out_contacts, contact);
844 } else {
845 /* Contact could not be transformed */
846 g_warning (
847 G_STRLOC ": Error transforming vcard with image data %s",
848 (error && *error) ? (*error)->message :
849 "Unknown error transforming vcard");
850 g_object_unref (contact);
851
852 /* Abort as soon as an error occurs */
853 break;
854 }
855 }
856
857 if (status != STATUS_ERROR) {
858 GSList *link;
859
860 if (!e_book_sqlite_add_contacts (bf->priv->sqlitedb,
861 *out_contacts, NULL, FALSE,
862 cancellable,
863 &local_error)) {
864
865 if (g_error_matches (local_error,
866 E_BOOK_SQLITE_ERROR,
867 E_BOOK_SQLITE_ERROR_CONSTRAINT)) {
868 g_set_error (
869 error, E_BOOK_CLIENT_ERROR,
870 E_BOOK_CLIENT_ERROR_CONTACT_ID_ALREADY_EXISTS,
871 _("Conflicting UIDs found in added contacts"));
872 g_clear_error (&local_error);
873 } else {
874 g_warning ("Failed to add contacts: %s", local_error->message);
875 g_propagate_error (error, local_error);
876 }
877
878 status = STATUS_ERROR;
879 }
880
881 /* After adding any contacts, notify any cursors that the new contacts are added */
882 for (link = *out_contacts; link; link = g_slist_next (link)) {
883 cursors_contact_added (bf, link->data);
884 }
885 }
886
887 return (status != STATUS_ERROR);
888 }
889
890 typedef struct {
891 EBookBackendFile *bf;
892 GThread *thread;
893 EFlag *running;
894 } FileBackendSearchClosure;
895
896 static void
closure_destroy(FileBackendSearchClosure * closure)897 closure_destroy (FileBackendSearchClosure *closure)
898 {
899 d (printf ("destroying search closure\n"));
900 e_flag_free (closure->running);
901 if (closure->thread)
902 g_thread_unref (closure->thread);
903 g_free (closure);
904 }
905
906 static FileBackendSearchClosure *
init_closure(EDataBookView * book_view,EBookBackendFile * bf)907 init_closure (EDataBookView *book_view,
908 EBookBackendFile *bf)
909 {
910 FileBackendSearchClosure *closure = g_new (FileBackendSearchClosure, 1);
911
912 closure->bf = bf;
913 closure->thread = NULL;
914 closure->running = e_flag_new ();
915
916 g_object_set_data_full (
917 G_OBJECT (book_view),
918 "EBookBackendFile.BookView::closure",
919 closure, (GDestroyNotify) closure_destroy);
920
921 return closure;
922 }
923
924 static FileBackendSearchClosure *
get_closure(EDataBookView * book_view)925 get_closure (EDataBookView *book_view)
926 {
927 return g_object_get_data (
928 G_OBJECT (book_view),
929 "EBookBackendFile.BookView::closure");
930 }
931
932 static void
notify_update_vcard(EDataBookView * book_view,gboolean prefiltered,const gchar * id,const gchar * vcard)933 notify_update_vcard (EDataBookView *book_view,
934 gboolean prefiltered,
935 const gchar *id,
936 const gchar *vcard)
937 {
938 if (prefiltered)
939 e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard);
940 else
941 e_data_book_view_notify_update_vcard (book_view, id, vcard);
942 }
943
944 static gboolean
uid_rev_fields(GHashTable * fields_of_interest)945 uid_rev_fields (GHashTable *fields_of_interest)
946 {
947 GHashTableIter iter;
948 gpointer key, value;
949
950 if (!fields_of_interest || g_hash_table_size (fields_of_interest) > 2)
951 return FALSE;
952
953 g_hash_table_iter_init (&iter, fields_of_interest);
954 while (g_hash_table_iter_next (&iter, &key, &value)) {
955 const gchar *field_name = key;
956 EContactField field = e_contact_field_id (field_name);
957
958 if (field != E_CONTACT_UID &&
959 field != E_CONTACT_REV)
960 return FALSE;
961 }
962
963 return TRUE;
964 }
965
966 static gpointer
book_view_thread(gpointer user_data)967 book_view_thread (gpointer user_data)
968 {
969 EDataBookView *book_view = user_data;
970 FileBackendSearchClosure *closure;
971 EBookBackendFile *bf;
972 EBookBackendSExp *sexp;
973 const gchar *query;
974 GSList *summary_list = NULL, *l;
975 GHashTable *fields_of_interest;
976 GError *local_error = NULL;
977 gboolean meta_contact, success;
978
979 g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (book_view), NULL);
980
981 closure = get_closure (book_view);
982 if (!closure) {
983 g_warning (G_STRLOC ": NULL closure in book view thread");
984 return NULL;
985 }
986 bf = closure->bf;
987
988 d (printf ("starting initial population of book view\n"));
989
990 /* ref the book view because it'll be removed and unrefed
991 * when/if it's stopped */
992 g_object_ref (book_view);
993
994 sexp = e_data_book_view_get_sexp (book_view);
995 query = e_book_backend_sexp_text (sexp);
996
997 fields_of_interest = e_data_book_view_get_fields_of_interest (book_view);
998 meta_contact = uid_rev_fields (fields_of_interest);
999
1000 if (query && !strcmp (query, "(contains \"x-evolution-any-field\" \"\")")) {
1001 e_data_book_view_notify_progress (book_view, -1, _("Loading..."));
1002 } else {
1003 e_data_book_view_notify_progress (book_view, -1, _("Searching..."));
1004 }
1005
1006 d (printf ("signalling parent thread\n"));
1007 e_flag_set (closure->running);
1008
1009 g_rw_lock_reader_lock (&(bf->priv->lock));
1010 success = e_book_sqlite_search (
1011 bf->priv->sqlitedb,
1012 query,
1013 meta_contact,
1014 &summary_list,
1015 NULL, /* GCancellable */
1016 &local_error);
1017 g_rw_lock_reader_unlock (&(bf->priv->lock));
1018
1019 if (!success) {
1020 g_warning (G_STRLOC ": Failed to query initial contacts: %s", local_error->message);
1021 g_error_free (local_error);
1022 e_data_book_view_notify_complete (
1023 book_view,
1024 g_error_new_literal (
1025 E_CLIENT_ERROR,
1026 E_CLIENT_ERROR_NOT_OPENED,
1027 e_client_error_to_string (
1028 E_CLIENT_ERROR_NOT_OPENED)));
1029 g_object_unref (book_view);
1030 return NULL;
1031 }
1032
1033 for (l = summary_list; l; l = l->next) {
1034 EbSqlSearchData *data = l->data;
1035 gchar *vcard = NULL;
1036
1037 vcard = data->vcard;
1038 data->vcard = NULL;
1039
1040 notify_update_vcard (book_view, TRUE, data->uid, vcard);
1041 g_free (vcard);
1042 }
1043
1044 g_slist_foreach (summary_list, (GFunc) e_book_sqlite_search_data_free, NULL);
1045 g_slist_free (summary_list);
1046
1047 if (e_flag_is_set (closure->running))
1048 e_data_book_view_notify_complete (book_view, NULL /* Success */);
1049
1050 g_object_unref (book_view);
1051
1052 d (printf ("finished population of book view\n"));
1053
1054 return NULL;
1055 }
1056
1057 static void
book_backend_file_dispose(GObject * object)1058 book_backend_file_dispose (GObject *object)
1059 {
1060 EBookBackendFile *bf;
1061
1062 bf = E_BOOK_BACKEND_FILE (object);
1063
1064 g_rw_lock_writer_lock (&(bf->priv->lock));
1065
1066 if (bf->priv->cursors) {
1067 g_list_free_full (bf->priv->cursors, g_object_unref);
1068 bf->priv->cursors = NULL;
1069 }
1070
1071 g_clear_object (&bf->priv->sqlitedb);
1072
1073 g_rw_lock_writer_unlock (&(bf->priv->lock));
1074
1075 G_OBJECT_CLASS (e_book_backend_file_parent_class)->dispose (object);
1076 }
1077
1078 static void
book_backend_file_finalize(GObject * object)1079 book_backend_file_finalize (GObject *object)
1080 {
1081 EBookBackendFilePrivate *priv;
1082
1083 priv = E_BOOK_BACKEND_FILE (object)->priv;
1084
1085 g_free (priv->photo_dirname);
1086 g_free (priv->revision);
1087 g_free (priv->locale);
1088 g_free (priv->base_directory);
1089 g_rw_lock_clear (&(priv->lock));
1090
1091 /* Chain up to parent's finalize() method. */
1092 G_OBJECT_CLASS (e_book_backend_file_parent_class)->finalize (object);
1093 }
1094
1095 static gchar *
book_backend_file_get_backend_property(EBookBackend * backend,const gchar * prop_name)1096 book_backend_file_get_backend_property (EBookBackend *backend,
1097 const gchar *prop_name)
1098 {
1099 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1100
1101 g_return_val_if_fail (prop_name != NULL, NULL);
1102
1103 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
1104 return g_strdup ("local,do-initial-query,bulk-adds,bulk-modifies,bulk-removes,contact-lists");
1105
1106 } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
1107 return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
1108
1109 } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
1110 GString *fields;
1111 gint ii;
1112
1113 fields = g_string_sized_new (1024);
1114
1115 /* XXX we need a way to say "we support everything", since the
1116 * file backend does */
1117 for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
1118 if (fields->len > 0)
1119 g_string_append_c (fields, ',');
1120 g_string_append (fields, e_contact_field_name (ii));
1121 }
1122
1123 return g_string_free (fields, FALSE);
1124
1125 } else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REVISION)) {
1126 gchar *prop_value;
1127
1128 g_rw_lock_reader_lock (&(bf->priv->lock));
1129 prop_value = g_strdup (bf->priv->revision);
1130 g_rw_lock_reader_unlock (&(bf->priv->lock));
1131
1132 return prop_value;
1133 }
1134
1135 /* Chain up to parent's method. */
1136 return E_BOOK_BACKEND_CLASS (e_book_backend_file_parent_class)->impl_get_backend_property (backend, prop_name);
1137 }
1138
1139 static gboolean
book_backend_file_open_sync(EBookBackendSync * backend,GCancellable * cancellable,GError ** error)1140 book_backend_file_open_sync (EBookBackendSync *backend,
1141 GCancellable *cancellable,
1142 GError **error)
1143 {
1144 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1145 ESource *source;
1146 ESourceRevisionGuards *guards;
1147
1148 source = e_backend_get_source (E_BACKEND (backend));
1149
1150 /* Local source is always connected. */
1151 e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
1152
1153 g_type_ensure (E_TYPE_SOURCE_REVISION_GUARDS);
1154 guards = e_source_get_extension (source, E_SOURCE_EXTENSION_REVISION_GUARDS);
1155
1156 bf->priv->revision_guards = e_source_revision_guards_get_enabled (guards);
1157
1158 g_rw_lock_writer_lock (&(bf->priv->lock));
1159 if (!bf->priv->revision) {
1160 e_book_backend_file_load_revision (bf);
1161 e_book_backend_notify_property_changed (
1162 E_BOOK_BACKEND (backend),
1163 E_BOOK_BACKEND_PROPERTY_REVISION,
1164 bf->priv->revision);
1165 }
1166 g_rw_lock_writer_unlock (&(bf->priv->lock));
1167
1168 e_backend_set_online (E_BACKEND (backend), TRUE);
1169 e_book_backend_set_writable (E_BOOK_BACKEND (backend), TRUE);
1170
1171 return TRUE;
1172 }
1173
1174 static gboolean
book_backend_file_create_contacts_sync(EBookBackendSync * backend,const gchar * const * vcards,guint32 opflags,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1175 book_backend_file_create_contacts_sync (EBookBackendSync *backend,
1176 const gchar * const *vcards,
1177 guint32 opflags,
1178 GSList **out_contacts,
1179 GCancellable *cancellable,
1180 GError **error)
1181 {
1182 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1183 gboolean success = FALSE;
1184
1185 g_return_val_if_fail (out_contacts != NULL, FALSE);
1186
1187 *out_contacts = NULL;
1188
1189 g_rw_lock_writer_lock (&(bf->priv->lock));
1190 if (!e_book_sqlite_lock (bf->priv->sqlitedb,
1191 EBSQL_LOCK_WRITE,
1192 cancellable, error)) {
1193 g_rw_lock_writer_unlock (&(bf->priv->lock));
1194 return FALSE;
1195 }
1196
1197 success = do_create (bf, vcards, out_contacts, cancellable, error);
1198
1199 if (success) {
1200 *out_contacts = g_slist_reverse (*out_contacts);
1201 } else {
1202 g_slist_free_full (*out_contacts, g_object_unref);
1203 *out_contacts = NULL;
1204 }
1205
1206 if (success)
1207 success = e_book_backend_file_bump_revision (bf, error);
1208
1209 if (success)
1210 success = e_book_sqlite_unlock (
1211 bf->priv->sqlitedb,
1212 EBSQL_UNLOCK_COMMIT,
1213 error);
1214 else {
1215 GError *local_error = NULL;
1216
1217 /* Rollback transaction */
1218 e_book_sqlite_unlock (
1219 bf->priv->sqlitedb,
1220 EBSQL_UNLOCK_ROLLBACK,
1221 &local_error);
1222
1223 if (local_error != NULL) {
1224 g_warning (
1225 "Failed to rollback transaction "
1226 "after failing to add contacts: %s",
1227 local_error->message);
1228 g_clear_error (&local_error);
1229 }
1230 }
1231
1232 g_rw_lock_writer_unlock (&(bf->priv->lock));
1233
1234 return success;
1235 }
1236
1237 static gboolean
book_backend_file_modify_contacts_sync(EBookBackendSync * backend,const gchar * const * vcards,guint32 opflags,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1238 book_backend_file_modify_contacts_sync (EBookBackendSync *backend,
1239 const gchar * const *vcards,
1240 guint32 opflags,
1241 GSList **out_contacts,
1242 GCancellable *cancellable,
1243 GError **error)
1244 {
1245 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1246 GSList *ids = NULL;
1247 GError *local_error = NULL;
1248 PhotoModifiedStatus status = STATUS_NORMAL;
1249 GSList *old_contacts = NULL;
1250 guint ii, length;
1251
1252 length = g_strv_length ((gchar **) vcards);
1253
1254 g_rw_lock_writer_lock (&(bf->priv->lock));
1255
1256 if (!e_book_sqlite_lock (bf->priv->sqlitedb, EBSQL_LOCK_WRITE, cancellable, error)) {
1257 g_rw_lock_writer_unlock (&(bf->priv->lock));
1258 return FALSE;
1259 }
1260
1261 for (ii = 0; ii < length && status != STATUS_ERROR; ii++) {
1262 gchar *id;
1263 EContact *mod_contact, *old_contact = NULL;
1264 const gchar *mod_contact_rev, *old_contact_rev;
1265
1266 mod_contact = e_contact_new_from_vcard (vcards[ii]);
1267 id = e_contact_get (mod_contact, E_CONTACT_UID);
1268
1269 if (id == NULL) {
1270 status = STATUS_ERROR;
1271
1272 g_set_error_literal (
1273 error, E_CLIENT_ERROR,
1274 E_CLIENT_ERROR_OTHER_ERROR,
1275 _("No UID in the contact"));
1276 g_object_unref (mod_contact);
1277 break;
1278 }
1279
1280 if (!e_book_sqlite_get_contact (bf->priv->sqlitedb,
1281 id, FALSE, &old_contact,
1282 &local_error)) {
1283 g_warning (G_STRLOC ": Failed to load contact %s: %s", id, local_error->message);
1284 g_propagate_error (error, local_error);
1285 local_error = NULL;
1286
1287 status = STATUS_ERROR;
1288
1289 g_free (id);
1290 g_object_unref (mod_contact);
1291 break;
1292 }
1293
1294 if (bf->priv->revision_guards) {
1295 mod_contact_rev = e_contact_get_const (mod_contact, E_CONTACT_REV);
1296 old_contact_rev = e_contact_get_const (old_contact, E_CONTACT_REV);
1297
1298 if (!mod_contact_rev || !old_contact_rev ||
1299 strcmp (mod_contact_rev, old_contact_rev) != 0) {
1300 g_set_error (
1301 error, E_CLIENT_ERROR,
1302 E_CLIENT_ERROR_OUT_OF_SYNC,
1303 _("Tried to modify contact “%s” with out of sync revision"),
1304 (gchar *) e_contact_get_const (mod_contact, E_CONTACT_UID));
1305
1306 status = STATUS_ERROR;
1307
1308 g_free (id);
1309 g_object_unref (mod_contact);
1310 g_object_unref (old_contact);
1311 break;
1312 }
1313 }
1314
1315 /* Transform incomming photo blobs to uris before storing this to the DB */
1316 status = maybe_transform_vcard_for_photo (bf, old_contact, mod_contact, &local_error);
1317 if (status == STATUS_ERROR) {
1318 g_warning (G_STRLOC ": Error transforming contact %s: %s", id, local_error->message);
1319 g_propagate_error (error, local_error);
1320 local_error = NULL;
1321
1322 g_free (id);
1323 g_object_unref (old_contact);
1324 g_object_unref (mod_contact);
1325 break;
1326 }
1327
1328 /* update the revision (modified time of contact) */
1329 set_revision (bf, mod_contact);
1330
1331 old_contacts = g_slist_prepend (old_contacts, old_contact);
1332 *out_contacts = g_slist_prepend (*out_contacts, mod_contact);
1333
1334 ids = g_slist_prepend (ids, id);
1335 }
1336
1337 if (status != STATUS_ERROR) {
1338 GSList *old_link, *mod_link;
1339
1340 /* Delete old photo file uris if need be (this will compare the new contact
1341 * with the current copy in the BDB to extract the uris to delete) */
1342 old_link = old_contacts;
1343 mod_link = *out_contacts;
1344
1345 while (old_link != NULL && mod_link != NULL) {
1346 maybe_delete_unused_uris (
1347 bf,
1348 E_CONTACT (old_link->data),
1349 E_CONTACT (mod_link->data));
1350 old_link = g_slist_next (old_link);
1351 mod_link = g_slist_next (mod_link);
1352 }
1353
1354 /* Update summary as well */
1355 e_book_sqlite_add_contacts (
1356 bf->priv->sqlitedb,
1357 *out_contacts, NULL, TRUE,
1358 cancellable, &local_error);
1359
1360 if (local_error != NULL) {
1361 g_warning (
1362 "Failed to modify contacts: %s",
1363 local_error->message);
1364 g_propagate_error (error, local_error);
1365 local_error = NULL;
1366
1367 status = STATUS_ERROR;
1368 }
1369 }
1370
1371 /* Bump the revision atomically in the same transaction */
1372 if (status != STATUS_ERROR) {
1373 if (!e_book_backend_file_bump_revision (bf, error))
1374 status = STATUS_ERROR;
1375 }
1376
1377 /* Commit or rollback transaction */
1378 if (status != STATUS_ERROR) {
1379 gboolean success;
1380
1381 success = e_book_sqlite_unlock (
1382 bf->priv->sqlitedb,
1383 EBSQL_UNLOCK_COMMIT,
1384 error);
1385 if (!success)
1386 status = STATUS_ERROR;
1387
1388 } else {
1389 /* Rollback transaction */
1390 e_book_sqlite_unlock (
1391 bf->priv->sqlitedb,
1392 EBSQL_UNLOCK_ROLLBACK,
1393 &local_error);
1394
1395 if (local_error != NULL) {
1396 g_warning (
1397 "Failed to rollback transaction after "
1398 "failing to modify contacts: %s",
1399 local_error->message);
1400 g_clear_error (&local_error);
1401 }
1402 }
1403
1404 if (status != STATUS_ERROR) {
1405 *out_contacts = g_slist_reverse (*out_contacts);
1406 } else {
1407 g_slist_free_full (*out_contacts, g_object_unref);
1408 *out_contacts = NULL;
1409 }
1410
1411 /* Now that we've modified the contact(s),
1412 * notify cursors of the changes. */
1413 if (status != STATUS_ERROR) {
1414 GSList *link;
1415
1416 for (link = old_contacts; link; link = g_slist_next (link)) {
1417 cursors_contact_removed (bf, E_CONTACT (link->data));
1418 }
1419
1420 for (link = *out_contacts; link; link = g_slist_next (link)) {
1421 cursors_contact_added (bf, E_CONTACT (link->data));
1422 }
1423 }
1424
1425 g_rw_lock_writer_unlock (&(bf->priv->lock));
1426
1427 g_slist_free_full (old_contacts, g_object_unref);
1428 g_slist_free_full (ids, g_free);
1429
1430 return (status != STATUS_ERROR);
1431 }
1432
1433 static gboolean
book_backend_file_remove_contacts_sync(EBookBackendSync * backend,const gchar * const * uids,guint32 opflags,GSList ** out_removed_uids,GCancellable * cancellable,GError ** error)1434 book_backend_file_remove_contacts_sync (EBookBackendSync *backend,
1435 const gchar * const *uids,
1436 guint32 opflags,
1437 GSList **out_removed_uids,
1438 GCancellable *cancellable,
1439 GError **error)
1440 {
1441 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1442 GSList *removed_ids = NULL, *removed_contacts = NULL;
1443 GError *local_error = NULL;
1444 const GSList *l;
1445 gboolean success = TRUE;
1446 guint ii, length;
1447
1448 g_return_val_if_fail (out_removed_uids != NULL, FALSE);
1449
1450 length = g_strv_length ((gchar **) uids);
1451
1452 g_rw_lock_writer_lock (&(bf->priv->lock));
1453
1454 if (!e_book_sqlite_lock (bf->priv->sqlitedb,
1455 EBSQL_LOCK_WRITE,
1456 cancellable, error)) {
1457 g_rw_lock_writer_unlock (&(bf->priv->lock));
1458 return FALSE;
1459 }
1460
1461 for (ii = 0; ii < length && success; ii++) {
1462 EContact *contact = NULL;
1463
1464 /* First load the EContacts which need to be removed, we might delete some
1465 * photos from disk because of this...
1466 *
1467 * Note: sqlite backend can probably make this faster by executing a
1468 * single query to fetch a list of contacts for a list of ids, the
1469 * current method makes a query for each UID.
1470 */
1471 if (e_book_sqlite_get_contact (bf->priv->sqlitedb,
1472 uids[ii], FALSE, &contact,
1473 &local_error)) {
1474 removed_ids = g_slist_prepend (removed_ids, g_strdup (uids[ii]));
1475 removed_contacts = g_slist_prepend (removed_contacts, contact);
1476 } else {
1477
1478 if (g_error_matches (local_error,
1479 E_BOOK_SQLITE_ERROR,
1480 E_BOOK_SQLITE_ERROR_CONTACT_NOT_FOUND)) {
1481 g_set_error (
1482 error, E_BOOK_CLIENT_ERROR,
1483 E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND,
1484 _("Contact “%s” not found"), uids[ii]);
1485 g_error_free (local_error);
1486 } else {
1487 g_warning ("Failed to fetch contact to be removed: %s", local_error->message);
1488 g_propagate_error (error, local_error);
1489 local_error = NULL;
1490 }
1491 /* Abort as soon as missing contact is to be deleted */
1492 success = FALSE;
1493 break;
1494 }
1495 }
1496
1497 if (success) {
1498
1499 /* Delete URI associated to those contacts */
1500 for (l = removed_contacts; l; l = l->next) {
1501 maybe_delete_unused_uris (bf, E_CONTACT (l->data), NULL);
1502 }
1503
1504 /* Remove from summary as well */
1505 if (!e_book_sqlite_remove_contacts (bf->priv->sqlitedb, removed_ids,
1506 cancellable, &local_error)) {
1507 if (local_error) {
1508 g_warning ("Failed to remove contacts: %s", local_error->message);
1509 g_propagate_error (error, local_error);
1510 }
1511 }
1512
1513 e_book_backend_file_bump_revision (bf, NULL);
1514 }
1515
1516 /* Commit or rollback transaction */
1517 if (success) {
1518 success = e_book_sqlite_unlock (bf->priv->sqlitedb, EBSQL_UNLOCK_COMMIT, error);
1519 } else {
1520 /* Rollback transaction */
1521 if (!e_book_sqlite_unlock (bf->priv->sqlitedb, EBSQL_UNLOCK_ROLLBACK, &local_error)) {
1522 g_warning (
1523 "Failed to rollback transaction after failing to modify contacts: %s",
1524 local_error->message);
1525 g_clear_error (&local_error);
1526 }
1527 }
1528
1529 /* After removing any contacts, notify any cursors that the new contacts are added */
1530 if (success) {
1531 for (l = removed_contacts; l; l = l->next) {
1532 cursors_contact_removed (bf, E_CONTACT (l->data));
1533 }
1534 }
1535
1536 *out_removed_uids = removed_ids;
1537
1538 g_rw_lock_writer_unlock (&(bf->priv->lock));
1539
1540 g_slist_free_full (removed_contacts, (GDestroyNotify) g_object_unref);
1541
1542 return success;
1543 }
1544
1545 static EContact *
book_backend_file_get_contact_sync(EBookBackendSync * backend,const gchar * uid,GCancellable * cancellable,GError ** error)1546 book_backend_file_get_contact_sync (EBookBackendSync *backend,
1547 const gchar *uid,
1548 GCancellable *cancellable,
1549 GError **error)
1550 {
1551 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1552 EContact *contact = NULL;
1553 gboolean success;
1554 GError *local_error = NULL;
1555
1556 g_rw_lock_reader_lock (&(bf->priv->lock));
1557 success = e_book_sqlite_get_contact (
1558 bf->priv->sqlitedb,
1559 uid, FALSE, &contact,
1560 &local_error);
1561 g_rw_lock_reader_unlock (&(bf->priv->lock));
1562
1563 if (!success) {
1564 if (g_error_matches (local_error,
1565 E_BOOK_SQLITE_ERROR,
1566 E_BOOK_SQLITE_ERROR_CONTACT_NOT_FOUND)) {
1567 g_set_error (
1568 error, E_BOOK_CLIENT_ERROR,
1569 E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND,
1570 _("Contact “%s” not found"), uid);
1571 g_error_free (local_error);
1572 } else
1573 g_propagate_error (error, local_error);
1574 }
1575
1576 return contact;
1577 }
1578
1579 static gboolean
book_backend_file_get_contact_list_sync(EBookBackendSync * backend,const gchar * query,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1580 book_backend_file_get_contact_list_sync (EBookBackendSync *backend,
1581 const gchar *query,
1582 GSList **out_contacts,
1583 GCancellable *cancellable,
1584 GError **error)
1585 {
1586 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1587 GSList *summary_list = NULL;
1588 GSList *link;
1589 gboolean success = TRUE;
1590 GError *local_error = NULL;
1591
1592 g_return_val_if_fail (out_contacts != NULL, FALSE);
1593
1594 *out_contacts = NULL;
1595
1596 d (printf ("book_backend_file_get_contact_list_sync (%s)\n", query));
1597
1598 g_rw_lock_reader_lock (&(bf->priv->lock));
1599
1600 success = e_book_sqlite_lock (
1601 bf->priv->sqlitedb,
1602 EBSQL_LOCK_READ,
1603 cancellable, error);
1604 if (!success) {
1605 g_rw_lock_writer_unlock (&(bf->priv->lock));
1606 return FALSE;
1607 }
1608
1609 success = e_book_sqlite_search (
1610 bf->priv->sqlitedb,
1611 query,
1612 FALSE,
1613 &summary_list,
1614 cancellable,
1615 &local_error);
1616
1617 e_book_sqlite_unlock (
1618 bf->priv->sqlitedb,
1619 EBSQL_UNLOCK_NONE,
1620 success ? &local_error : NULL);
1621
1622 g_rw_lock_reader_unlock (&(bf->priv->lock));
1623
1624 if (!success) {
1625
1626 g_warn_if_fail (summary_list == NULL);
1627
1628 if (g_error_matches (local_error,
1629 E_BOOK_SQLITE_ERROR,
1630 E_BOOK_SQLITE_ERROR_UNSUPPORTED_QUERY)) {
1631 g_set_error (
1632 error, E_CLIENT_ERROR,
1633 E_CLIENT_ERROR_NOT_SUPPORTED,
1634 _("Query “%s” not supported"), query);
1635 g_error_free (local_error);
1636
1637 } else if (g_error_matches (local_error,
1638 E_BOOK_SQLITE_ERROR,
1639 E_BOOK_SQLITE_ERROR_INVALID_QUERY)) {
1640 g_set_error (
1641 error, E_CLIENT_ERROR,
1642 E_CLIENT_ERROR_INVALID_QUERY,
1643 _("Invalid Query “%s”"), query);
1644 g_error_free (local_error);
1645
1646 } else {
1647 g_warning ("Failed to fetch contact ids: %s", local_error->message);
1648 g_propagate_error (error, local_error);
1649 }
1650 }
1651
1652 for (link = summary_list; link != NULL; link = g_slist_next (link)) {
1653 EbSqlSearchData *data = link->data;
1654 EContact *contact;
1655
1656 contact = e_contact_new_from_vcard (data->vcard);
1657 link->data = contact;
1658
1659 e_book_sqlite_search_data_free (data);
1660 }
1661
1662 *out_contacts = summary_list;
1663
1664 return success;
1665 }
1666
1667 static gboolean
book_backend_file_get_contact_list_uids_sync(EBookBackendSync * backend,const gchar * query,GSList ** out_uids,GCancellable * cancellable,GError ** error)1668 book_backend_file_get_contact_list_uids_sync (EBookBackendSync *backend,
1669 const gchar *query,
1670 GSList **out_uids,
1671 GCancellable *cancellable,
1672 GError **error)
1673 {
1674 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1675 gboolean success = TRUE;
1676 GError *local_error = NULL;
1677
1678 g_return_val_if_fail (out_uids != NULL, FALSE);
1679
1680 *out_uids = NULL;
1681
1682 d (printf ("book_backend_file_get_contact_list_sync (%s)\n", query));
1683
1684 g_rw_lock_reader_lock (&(bf->priv->lock));
1685
1686 success = e_book_sqlite_lock (
1687 bf->priv->sqlitedb,
1688 EBSQL_LOCK_READ,
1689 cancellable, error);
1690 if (!success) {
1691 g_rw_lock_writer_unlock (&(bf->priv->lock));
1692 return FALSE;
1693 }
1694
1695 success = e_book_sqlite_search_uids (
1696 bf->priv->sqlitedb,
1697 query,
1698 out_uids,
1699 cancellable,
1700 &local_error);
1701 e_book_sqlite_unlock (
1702 bf->priv->sqlitedb,
1703 EBSQL_UNLOCK_NONE,
1704 success ? &local_error : NULL);
1705
1706 g_rw_lock_reader_unlock (&(bf->priv->lock));
1707
1708 if (!success) {
1709 g_warn_if_fail (*out_uids == NULL);
1710
1711 if (g_error_matches (local_error,
1712 E_BOOK_SQLITE_ERROR,
1713 E_BOOK_SQLITE_ERROR_UNSUPPORTED_QUERY)) {
1714 g_set_error (
1715 error, E_CLIENT_ERROR,
1716 E_CLIENT_ERROR_NOT_SUPPORTED,
1717 _("Query “%s” not supported"), query);
1718 g_error_free (local_error);
1719
1720 } else if (g_error_matches (local_error,
1721 E_BOOK_SQLITE_ERROR,
1722 E_BOOK_SQLITE_ERROR_INVALID_QUERY)) {
1723 g_set_error (
1724 error, E_CLIENT_ERROR,
1725 E_CLIENT_ERROR_INVALID_QUERY,
1726 _("Invalid Query “%s”"), query);
1727 g_error_free (local_error);
1728
1729 } else {
1730 g_warning (
1731 "Failed to fetch contact ids: %s",
1732 local_error->message);
1733 g_propagate_error (error, local_error);
1734 }
1735 }
1736
1737 return success;
1738 }
1739
1740 static void
book_backend_file_start_view(EBookBackend * backend,EDataBookView * book_view)1741 book_backend_file_start_view (EBookBackend *backend,
1742 EDataBookView *book_view)
1743 {
1744 FileBackendSearchClosure *closure;
1745
1746 closure = init_closure (book_view, E_BOOK_BACKEND_FILE (backend));
1747
1748 d (printf ("starting book view thread\n"));
1749 closure->thread = g_thread_new (NULL, book_view_thread, book_view);
1750
1751 e_flag_wait (closure->running);
1752
1753 /* at this point we know the book view thread is actually running */
1754 d (printf ("returning from start_view\n"));
1755 }
1756
1757 static void
book_backend_file_stop_view(EBookBackend * backend,EDataBookView * book_view)1758 book_backend_file_stop_view (EBookBackend *backend,
1759 EDataBookView *book_view)
1760 {
1761 FileBackendSearchClosure *closure = get_closure (book_view);
1762 gboolean need_join;
1763
1764 if (!closure)
1765 return;
1766
1767 d (printf ("stopping query\n"));
1768 need_join = e_flag_is_set (closure->running);
1769 e_flag_clear (closure->running);
1770
1771 if (need_join) {
1772 g_thread_join (closure->thread);
1773 closure->thread = NULL;
1774 }
1775 }
1776
1777 static EDataBookDirect *
book_backend_file_get_direct_book(EBookBackend * backend)1778 book_backend_file_get_direct_book (EBookBackend *backend)
1779 {
1780 EDataBookDirect *direct;
1781 ESourceRegistry *registry;
1782 ESource *source;
1783 gchar *backend_path;
1784 gchar *dirname;
1785 const gchar *modules_env = NULL;
1786
1787 modules_env = g_getenv (EDS_ADDRESS_BOOK_MODULES);
1788
1789 source = e_backend_get_source (E_BACKEND (backend));
1790 registry = e_book_backend_get_registry (backend);
1791 dirname = e_book_backend_file_extract_path_from_source (
1792 registry, source, GET_PATH_DB_DIR);
1793
1794 /* Support in-tree testing / relocated modules */
1795 if (modules_env) {
1796 backend_path = g_build_filename (
1797 modules_env, "libebookbackendfile.so", NULL);
1798 } else {
1799 backend_path = g_build_filename (
1800 BACKENDDIR, "libebookbackendfile.so", NULL);
1801 }
1802
1803 direct = e_data_book_direct_new (
1804 backend_path, "EBookBackendFileFactory", dirname);
1805
1806 g_free (backend_path);
1807 g_free (dirname);
1808
1809 return direct;
1810 }
1811
1812 static void
book_backend_file_configure_direct(EBookBackend * backend,const gchar * config)1813 book_backend_file_configure_direct (EBookBackend *backend,
1814 const gchar *config)
1815 {
1816 EBookBackendFilePrivate *priv;
1817
1818 priv = E_BOOK_BACKEND_FILE (backend)->priv;
1819 priv->base_directory = g_strdup (config);
1820 }
1821
1822 static void
book_backend_file_vcard_changed(EbSqlChangeType change_type,const gchar * uid,const gchar * extra,const gchar * vcard,gpointer user_data)1823 book_backend_file_vcard_changed (EbSqlChangeType change_type,
1824 const gchar *uid,
1825 const gchar *extra,
1826 const gchar *vcard,
1827 gpointer user_data)
1828 {
1829 EBookBackend *backend = E_BOOK_BACKEND (user_data);
1830 EContact *contact;
1831
1832 if (change_type == EBSQL_CHANGE_LOCALE_CHANGED) {
1833 contact = e_contact_new_from_vcard_with_uid (vcard, uid);
1834 e_book_backend_notify_update (backend, contact);
1835 g_object_unref (contact);
1836 }
1837 }
1838
1839 static gboolean
book_backend_file_set_locale(EBookBackend * backend,const gchar * locale,GCancellable * cancellable,GError ** error)1840 book_backend_file_set_locale (EBookBackend *backend,
1841 const gchar *locale,
1842 GCancellable *cancellable,
1843 GError **error)
1844 {
1845 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1846 gboolean success;
1847 GList *l;
1848
1849 g_rw_lock_writer_lock (&(bf->priv->lock));
1850
1851 success = e_book_sqlite_lock (
1852 bf->priv->sqlitedb,
1853 EBSQL_LOCK_WRITE,
1854 cancellable, error);
1855 if (!success) {
1856 g_rw_lock_writer_unlock (&(bf->priv->lock));
1857 return FALSE;
1858 }
1859
1860 success = e_book_sqlite_set_locale (
1861 bf->priv->sqlitedb, locale,
1862 cancellable, error);
1863
1864 if (success)
1865 success = e_book_backend_file_bump_revision (bf, error);
1866
1867 if (success) {
1868 success = e_book_sqlite_unlock (
1869 bf->priv->sqlitedb,
1870 EBSQL_UNLOCK_COMMIT,
1871 error);
1872
1873 } else {
1874 GError *local_error = NULL;
1875
1876 /* Rollback transaction */
1877 e_book_sqlite_unlock (
1878 bf->priv->sqlitedb,
1879 EBSQL_UNLOCK_ROLLBACK,
1880 &local_error);
1881
1882 if (local_error != NULL) {
1883 g_warning (
1884 "Failed to rollback transaction "
1885 "after failing to set locale: %s",
1886 local_error->message);
1887 g_clear_error (&local_error);
1888 }
1889 }
1890
1891 /* This must be done outside the EBookSqlite lock,
1892 * as it may try to acquire the lock as well. */
1893 for (l = bf->priv->cursors; success && l; l = l->next) {
1894 EDataBookCursor *cursor = l->data;
1895
1896 success = e_data_book_cursor_load_locale (
1897 cursor, NULL, cancellable, error);
1898 }
1899
1900 /* We set the new locale, now update our local variable */
1901 if (success) {
1902 g_free (bf->priv->locale);
1903 bf->priv->locale = g_strdup (locale);
1904 }
1905
1906 g_rw_lock_writer_unlock (&(bf->priv->lock));
1907
1908 return success;
1909 }
1910
1911 static gchar *
book_backend_file_dup_locale(EBookBackend * backend)1912 book_backend_file_dup_locale (EBookBackend *backend)
1913 {
1914 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1915 gchar *locale;
1916
1917 g_rw_lock_reader_lock (&(bf->priv->lock));
1918 locale = g_strdup (bf->priv->locale);
1919 g_rw_lock_reader_unlock (&(bf->priv->lock));
1920
1921 return locale;
1922 }
1923
1924 static EDataBookCursor *
book_backend_file_create_cursor(EBookBackend * backend,EContactField * sort_fields,EBookCursorSortType * sort_types,guint n_fields,GError ** error)1925 book_backend_file_create_cursor (EBookBackend *backend,
1926 EContactField *sort_fields,
1927 EBookCursorSortType *sort_types,
1928 guint n_fields,
1929 GError **error)
1930 {
1931 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1932 EDataBookCursor *cursor;
1933
1934 g_rw_lock_writer_lock (&(bf->priv->lock));
1935
1936 cursor = e_data_book_cursor_sqlite_new (
1937 backend,
1938 bf->priv->sqlitedb,
1939 SQLITE_REVISION_KEY,
1940 sort_fields,
1941 sort_types,
1942 n_fields,
1943 error);
1944
1945 if (cursor != NULL) {
1946 bf->priv->cursors =
1947 g_list_prepend (bf->priv->cursors, cursor);
1948 }
1949
1950 g_rw_lock_writer_unlock (&(bf->priv->lock));
1951
1952 return cursor;
1953 }
1954
1955 static gboolean
book_backend_file_delete_cursor(EBookBackend * backend,EDataBookCursor * cursor,GError ** error)1956 book_backend_file_delete_cursor (EBookBackend *backend,
1957 EDataBookCursor *cursor,
1958 GError **error)
1959 {
1960 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1961 GList *link;
1962
1963 g_rw_lock_writer_lock (&(bf->priv->lock));
1964
1965 link = g_list_find (bf->priv->cursors, cursor);
1966
1967 if (link != NULL) {
1968 bf->priv->cursors = g_list_delete_link (bf->priv->cursors, link);
1969 g_object_unref (cursor);
1970 } else {
1971 g_set_error_literal (
1972 error,
1973 E_CLIENT_ERROR,
1974 E_CLIENT_ERROR_INVALID_ARG,
1975 _("Requested to delete an unrelated cursor"));
1976 }
1977
1978 g_rw_lock_writer_unlock (&(bf->priv->lock));
1979
1980 return link != NULL;
1981 }
1982
1983 static gboolean
book_backend_file_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)1984 book_backend_file_initable_init (GInitable *initable,
1985 GCancellable *cancellable,
1986 GError **error)
1987 {
1988 EBookBackendFilePrivate *priv;
1989 ESourceBackendSummarySetup *setup_extension;
1990 ESourceRegistry *registry;
1991 ESource *source;
1992 const gchar *extension_name;
1993 #ifdef HAVE_LIBDB
1994 gchar *backup, *filename;
1995 #endif /* HAVE_LIBDB */
1996 gchar *dirname, *fullpath;
1997 gboolean success = TRUE;
1998
1999 priv = E_BOOK_BACKEND_FILE (initable)->priv;
2000
2001 source = e_backend_get_source (E_BACKEND (initable));
2002 registry = e_book_backend_get_registry (E_BOOK_BACKEND (initable));
2003
2004 g_type_ensure (E_TYPE_SOURCE_BACKEND_SUMMARY_SETUP);
2005 extension_name = E_SOURCE_EXTENSION_BACKEND_SUMMARY_SETUP;
2006 setup_extension = e_source_get_extension (source, extension_name);
2007
2008 if (priv->base_directory)
2009 dirname = g_strdup (priv->base_directory);
2010 else
2011 dirname = e_book_backend_file_extract_path_from_source (
2012 registry, source, GET_PATH_DB_DIR);
2013
2014 fullpath = g_build_filename (dirname, "contacts.db", NULL);
2015
2016 #ifdef HAVE_LIBDB
2017 filename = g_build_filename (dirname, "addressbook.db", NULL);
2018 backup = g_build_filename (dirname, "addressbook.db.old", NULL);
2019
2020 /* The old BDB exists, lets migrate that to sqlite right away. */
2021 if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
2022 priv->sqlitedb = e_book_sqlite_new_full (
2023 fullpath, source, setup_extension,
2024 NULL,
2025 book_backend_file_vcard_changed,
2026 initable, NULL, cancellable, error);
2027
2028 if (priv->sqlitedb == NULL) {
2029 success = FALSE;
2030 goto exit;
2031 }
2032
2033 /* Do the migration from BDB, see e-book-backend-file-migrate-bdb.c */
2034 success = e_book_backend_file_migrate_bdb (
2035 priv->sqlitedb, dirname, filename, cancellable, error);
2036
2037 if (!success)
2038 goto exit;
2039
2040 /* Now that we've migrated the database,
2041 * lets rename it instead of unlinking it. */
2042 if (g_rename (filename, backup) < 0) {
2043 g_set_error (
2044 error, G_FILE_ERROR,
2045 g_file_error_from_errno (errno),
2046 _("Failed to rename old database from "
2047 "“%s” to “%s”: %s"), filename, backup,
2048 g_strerror (errno));
2049 success = FALSE;
2050 goto exit;
2051 }
2052 }
2053 #endif /* HAVE_LIBDB */
2054
2055 /* If we already have a handle on this, it means there
2056 * was an old BDB migrated and no need to reopen it. */
2057 if (priv->sqlitedb == NULL) {
2058 gint populated = 0;
2059 GError *local_error = NULL;
2060
2061 /* Ensure the directory exists first. */
2062 success = create_directory (dirname, error);
2063
2064 if (!success)
2065 goto exit;
2066
2067 /* Create the sqlitedb. */
2068 priv->sqlitedb = e_book_sqlite_new_full (
2069 fullpath, source, setup_extension,
2070 NULL,
2071 book_backend_file_vcard_changed,
2072 initable, NULL, cancellable, error);
2073
2074 if (priv->sqlitedb == NULL) {
2075 success = FALSE;
2076 goto exit;
2077 }
2078
2079 /* An sqlite DB only 'exists' if the populated flag is set. */
2080 e_book_sqlite_get_key_value_int (
2081 priv->sqlitedb,
2082 E_BOOK_SQL_IS_POPULATED_KEY,
2083 &populated,
2084 &local_error);
2085
2086 if (local_error != NULL) {
2087 g_propagate_error (error, local_error);
2088 success = FALSE;
2089 goto exit;
2090 }
2091
2092 if (!populated) {
2093 /* Set the populated flag. */
2094 success = e_book_sqlite_set_key_value_int (
2095 priv->sqlitedb,
2096 E_BOOK_SQL_IS_POPULATED_KEY,
2097 TRUE,
2098 error);
2099
2100 if (!success)
2101 goto exit;
2102 }
2103 }
2104
2105 /* Load the locale */
2106 e_book_backend_file_load_locale (E_BOOK_BACKEND_FILE (initable));
2107
2108 /* Resolve the photo directory here. */
2109 priv->photo_dirname =
2110 e_book_backend_file_extract_path_from_source (
2111 registry, source, GET_PATH_PHOTO_DIR);
2112 success = create_directory (priv->photo_dirname, error);
2113
2114 exit:
2115 g_free (dirname);
2116 g_free (fullpath);
2117 #ifdef HAVE_LIBDB
2118 g_free (filename);
2119 g_free (backup);
2120 #endif /* HAVE_LIBDB */
2121
2122 return success;
2123 }
2124
2125 static void
e_book_backend_file_class_init(EBookBackendFileClass * class)2126 e_book_backend_file_class_init (EBookBackendFileClass *class)
2127 {
2128 GObjectClass *object_class;
2129 EBookBackendClass *backend_class;
2130 EBookBackendSyncClass *backend_sync_class;
2131
2132 object_class = G_OBJECT_CLASS (class);
2133 object_class->dispose = book_backend_file_dispose;
2134 object_class->finalize = book_backend_file_finalize;
2135
2136 backend_sync_class = E_BOOK_BACKEND_SYNC_CLASS (class);
2137 backend_sync_class->open_sync = book_backend_file_open_sync;
2138 backend_sync_class->create_contacts_sync = book_backend_file_create_contacts_sync;
2139 backend_sync_class->modify_contacts_sync = book_backend_file_modify_contacts_sync;
2140 backend_sync_class->remove_contacts_sync = book_backend_file_remove_contacts_sync;
2141 backend_sync_class->get_contact_sync = book_backend_file_get_contact_sync;
2142 backend_sync_class->get_contact_list_sync = book_backend_file_get_contact_list_sync;
2143 backend_sync_class->get_contact_list_uids_sync = book_backend_file_get_contact_list_uids_sync;
2144
2145 backend_class = E_BOOK_BACKEND_CLASS (class);
2146 backend_class->impl_get_backend_property = book_backend_file_get_backend_property;
2147 backend_class->impl_start_view = book_backend_file_start_view;
2148 backend_class->impl_stop_view = book_backend_file_stop_view;
2149 backend_class->impl_get_direct_book = book_backend_file_get_direct_book;
2150 backend_class->impl_configure_direct = book_backend_file_configure_direct;
2151 backend_class->impl_set_locale = book_backend_file_set_locale;
2152 backend_class->impl_dup_locale = book_backend_file_dup_locale;
2153 backend_class->impl_create_cursor = book_backend_file_create_cursor;
2154 backend_class->impl_delete_cursor = book_backend_file_delete_cursor;
2155 }
2156
2157 static void
e_book_backend_file_initable_init(GInitableIface * iface)2158 e_book_backend_file_initable_init (GInitableIface *iface)
2159 {
2160 iface->init = book_backend_file_initable_init;
2161 }
2162
2163 static void
e_book_backend_file_init(EBookBackendFile * backend)2164 e_book_backend_file_init (EBookBackendFile *backend)
2165 {
2166 backend->priv = e_book_backend_file_get_instance_private (backend);
2167
2168 g_rw_lock_init (&(backend->priv->lock));
2169 }
2170
2171