1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* e-book-backend-file.c - File contact backend.
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 * Authors: Nat Friedman <nat@novell.com>
22 * Chris Toshok <toshok@ximian.com>
23 * Hans Petter Jansson <hpj@novell.com>
39 #include <glib/gstdio.h>
40 #include <glib/gi18n-lib.h>
42 #include "libebackend/e-dbhash.h"
43 #include "libebackend/e-db3-utils.h"
45 #include "libedataserver/e-data-server-util.h"
46 #include "libedataserver/e-flag.h"
48 #include "libebook/e-contact.h"
50 #include "libedata-book/e-book-backend-sexp.h"
51 #include "libedata-book/e-book-backend-sqlitedb.h"
52 #include "libedata-book/e-data-book.h"
53 #include "libedata-book/e-data-book-view.h"
55 #include "e-book-backend-file.h"
57 #define E_BOOK_BACKEND_FILE_GET_PRIVATE(obj) \
58 (G_TYPE_INSTANCE_GET_PRIVATE \
59 ((obj), E_TYPE_BOOK_BACKEND_FILE, EBookBackendFilePrivate))
63 #define CHANGES_DB_SUFFIX ".changes.db"
65 #define E_BOOK_BACKEND_FILE_VERSION_NAME "PAS-DB-VERSION"
66 #define E_BOOK_BACKEND_FILE_VERSION "0.2"
68 #define E_BOOK_BACKEND_FILE_REVISION_NAME "PAS-DB-REVISION"
70 #define PAS_ID_PREFIX "pas-id-"
72 #define SQLITEDB_EMAIL_ID "addressbook@localbackend.com"
73 #define SQLITEDB_FOLDER_ID "folder_id"
74 #define SQLITEDB_FOLDER_NAME "folder"
76 #define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL)
77 #define EDB_ERROR_EX(_code, _msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg)
78 #define EDB_NOT_OPENED_ERROR EDB_ERROR(NOT_OPENED)
80 G_DEFINE_TYPE (EBookBackendFile, e_book_backend_file, E_TYPE_BOOK_BACKEND_SYNC)
82 struct _EBookBackendFilePrivate {
91 EBookBackendSqliteDB *sqlitedb;
103 } PhotoModifiedStatus;
105 G_LOCK_DEFINE_STATIC (db_environments);
106 static GHashTable *db_environments = NULL;
113 db_error_to_gerror (const gint db_error,
116 if (db_error && perror && *perror)
117 g_clear_error (perror);
123 g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND));
126 g_propagate_error (perror, EDB_ERROR (PERMISSION_DENIED));
129 g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "db error 0x%x (%s)", db_error, db_strerror (db_error) ? db_strerror (db_error) : _("Unknown error")));
135 string_to_dbt (const gchar *str,
138 memset (dbt, 0, sizeof (*dbt));
139 dbt->data = (gpointer) str;
140 dbt->size = strlen (str) + 1;
141 dbt->flags = DB_DBT_USERMEM;
145 remove_file (const gchar *filename,
148 if (-1 == g_unlink (filename)) {
149 if (errno == EACCES || errno == EPERM) {
150 g_propagate_error (error, EDB_ERROR (PERMISSION_DENIED));
152 g_propagate_error (error, e_data_book_create_error_fmt
153 (E_DATA_BOOK_STATUS_OTHER_ERROR,
154 _("Failed to remove file '%s': %s"),
155 filename, g_strerror (errno)));
164 create_directory (const gchar *dirname,
169 rv = g_mkdir_with_parents (dirname, 0700);
170 if (rv == -1 && errno != EEXIST) {
171 g_warning ("failed to make directory %s: %s", dirname, g_strerror (errno));
172 if (errno == EACCES || errno == EPERM)
173 g_propagate_error (error, EDB_ERROR (PERMISSION_DENIED));
175 g_propagate_error (error,
176 e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
177 _("Failed to make directory %s: %s"),
178 dirname, g_strerror (errno)));
185 create_contact (const gchar *uid,
188 return e_contact_new_from_vcard_with_uid (vcard, uid);
192 load_vcard (EBookBackendFile *bf,
197 DB *db = bf->priv->file_db;
198 DBT id_dbt, vcard_dbt;
202 /* Get the old contact from the db and compare the photo fields */
203 string_to_dbt (uid, &id_dbt);
204 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
205 vcard_dbt.flags = DB_DBT_MALLOC;
207 db_error = db->get (db, txn, &id_dbt, &vcard_dbt, 0);
210 vcard = vcard_dbt.data;
212 g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
213 g_propagate_error (error, EDB_ERROR (CONTACT_NOT_FOUND));
221 load_contact (EBookBackendFile *bf,
226 EContact *contact = NULL;
229 if ((vcard = load_vcard (bf, txn, uid, error)) != NULL) {
230 contact = create_contact (uid, vcard);
238 check_remove_uri_for_field (EContact *old_contact,
239 EContact *new_contact,
242 EContactPhoto *old_photo = NULL, *new_photo = NULL;
245 old_photo = e_contact_get (old_contact, field);
251 new_photo = e_contact_get (new_contact, field);
253 if (new_photo == NULL ||
254 g_ascii_strcasecmp (old_photo->data.uri, new_photo->data.uri))
255 uri = g_strdup (old_photo->data.uri);
257 uri = g_strdup (old_photo->data.uri);
260 e_contact_photo_free (old_photo);
261 e_contact_photo_free (new_photo);
267 maybe_delete_uri (EBookBackendFile *bf,
270 GError *error = NULL;
273 /* A uri that does not give us a filename is certainly not
274 * a uri that we created for a local file, just skip it */
275 if ((filename = g_filename_from_uri (uri, NULL, NULL)) == NULL)
278 /* If the file is in our path it belongs to us and we need to delete it.
280 if (!strncmp (bf->priv->photo_dirname, filename, strlen (bf->priv->photo_dirname))) {
282 d(g_print ("Deleting uri file: %s\n", filename));
284 /* Deleting uris should not cause the backend to fail to update
285 * a contact so the best we can do from here is log warnings
286 * when we fail to unlink a file from the disk.
288 if (!remove_file (filename, &error)) {
289 g_warning ("Unable to cleanup photo uri: %s", error->message);
290 g_error_free (error);
298 maybe_delete_unused_uris (EBookBackendFile *bf,
299 EContact *old_contact,
300 EContact *new_contact)
302 gchar *uri_photo, *uri_logo;
304 g_return_if_fail (old_contact != NULL);
306 /* If there is no new contact, collect all the uris to delete from old_contact
308 * Otherwise, if any of the photo uri fields have changed in new_contact, then collect the
309 * old uris for those fields from old_contact to delete
311 uri_photo = check_remove_uri_for_field (old_contact, new_contact, E_CONTACT_PHOTO);
312 uri_logo = check_remove_uri_for_field (old_contact, new_contact, E_CONTACT_LOGO);
315 maybe_delete_uri (bf, uri_photo);
320 maybe_delete_uri (bf, uri_logo);
326 e_book_backend_file_extract_path_from_source (ESource *source,
327 GetPathType path_type)
329 const gchar *user_data_dir;
330 const gchar *source_dir;
331 gchar *mangled_source_dir;
332 gchar *filename = NULL;
334 user_data_dir = e_get_user_data_dir ();
335 source_dir = e_source_peek_relative_uri (source);
337 if (!source_dir || !g_str_equal (source_dir, "system"))
338 source_dir = e_source_get_uid (source);
340 /* Mangle the URI to not contain invalid characters. */
341 mangled_source_dir = g_strdelimit (g_strdup (source_dir), ":/", '_');
344 case GET_PATH_DB_DIR:
345 filename = g_build_filename
346 (user_data_dir, "addressbook", mangled_source_dir, NULL);
348 case GET_PATH_PHOTO_DIR:
349 filename = g_build_filename
350 (user_data_dir, "addressbook", mangled_source_dir, "photos", NULL);
355 g_free (mangled_source_dir);
361 safe_name_for_photo (EBookBackendFile *bf,
363 EContactPhoto *photo,
366 gchar *fullname = NULL, *name, *str;
367 gchar *suffix = NULL;
370 g_assert (photo->type == E_CONTACT_PHOTO_TYPE_INLINED);
372 /* Get a suitable filename extension */
373 if (photo->data.inlined.mime_type != NULL &&
374 photo->data.inlined.mime_type[0] != '\0') {
375 suffix = g_uri_escape_string (photo->data.inlined.mime_type,
378 gchar *mime_type = NULL;
379 gchar *content_type = NULL;
381 content_type = g_content_type_guess (NULL,
382 photo->data.inlined.data,
383 photo->data.inlined.length,
387 mime_type = g_content_type_get_mime_type (content_type);
390 suffix = g_uri_escape_string (mime_type, NULL, TRUE);
392 suffix = g_strdup ("data");
395 g_free (content_type);
398 /* Create a filename based on the uid/field */
399 name = g_strconcat (e_contact_get_const (contact, E_CONTACT_UID), "_",
400 e_contact_field_name (field), NULL);
401 name = g_strdelimit (name, NULL, '_');
406 str = e_filename_mkdir_encoded (bf->priv->photo_dirname, name, NULL, i);
407 fullname = g_strdup_printf ("%s.%s", str, suffix);
411 } while (g_file_test (fullname, G_FILE_TEST_EXISTS));
420 hard_link_photo (EBookBackendFile *bf,
423 const gchar *src_filename,
426 gchar *fullname = NULL, *name, *str;
430 /* Copy over the file suffix */
431 suffix = strrchr (src_filename, '.');
438 /* Create a filename based on uid/field */
439 name = g_strconcat (e_contact_get_const (contact, E_CONTACT_UID), "_",
440 e_contact_field_name (field), NULL);
441 name = g_strdelimit (name, NULL, '_');
446 str = e_filename_mkdir_encoded (bf->priv->photo_dirname, name, NULL, i);
447 fullname = g_strdup_printf ("%s.%s", str, suffix);
452 ret = link (src_filename, fullname);
454 } while (ret < 0 && errno == EEXIST);
457 if (errno == EACCES || errno == EPERM) {
458 g_propagate_error (error, EDB_ERROR (PERMISSION_DENIED));
460 g_propagate_error (error, e_data_book_create_error_fmt
461 (E_DATA_BOOK_STATUS_OTHER_ERROR,
462 _("Failed to create hardlink for resource '%s': %s"),
463 src_filename, g_strerror (errno)));
475 is_backend_owned_uri (EBookBackendFile *bf,
482 /* Errors converting from uri definitily indicate it was
483 * not our uri to begin with, so just disregard this error. */
484 filename = g_filename_from_uri (uri, NULL, NULL);
488 dirname = g_path_get_dirname (filename);
490 owned_uri = (strcmp (dirname, bf->priv->photo_dirname) == 0);
498 static PhotoModifiedStatus
499 maybe_transform_vcard_field_for_photo (EBookBackendFile *bf,
500 EContact *old_contact,
505 PhotoModifiedStatus status = STATUS_NORMAL;
506 EContactPhoto *photo;
508 if (field != E_CONTACT_PHOTO && field != E_CONTACT_LOGO)
511 photo = e_contact_get (contact, field);
515 if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
516 EContactPhoto *new_photo;
517 gchar *new_photo_path;
520 /* Create a unique filename with an extension (hopefully) based on the mime type */
521 new_photo_path = safe_name_for_photo (bf, contact, photo, field);
524 g_filename_to_uri (new_photo_path, NULL, error)) == NULL) {
526 status = STATUS_ERROR;
527 } else if (!g_file_set_contents (new_photo_path,
528 (const gchar *) photo->data.inlined.data,
529 photo->data.inlined.length,
532 status = STATUS_ERROR;
534 new_photo = e_contact_photo_new ();
535 new_photo->type = E_CONTACT_PHOTO_TYPE_URI;
536 new_photo->data.uri = g_strdup (uri);
538 e_contact_set (contact, field, new_photo);
540 d(g_print ("Backend modified incomming binary blob to be %s:\n", uri));
542 status = STATUS_MODIFIED;
544 e_contact_photo_free (new_photo);
548 g_free (new_photo_path);
550 } else { /* E_CONTACT_PHOTO_TYPE_URI */
552 EContactPhoto *old_photo = NULL, *new_photo;
554 /* First determine that the new contact uri points to our 'photos' directory,
555 * if not then we do nothing
557 if (!is_backend_owned_uri (bf, photo->data.uri))
560 /* Now check if the uri is changed from the BDB copy
562 uid = e_contact_get_const (contact, E_CONTACT_UID);
564 g_propagate_error (error, EDB_ERROR_EX (OTHER_ERROR, _("No UID in the contact")));
565 status = STATUS_ERROR;
570 old_photo = e_contact_get (old_contact, field);
572 /* Unless we are receiving the same uri that we already have
573 * stored in the BDB... */
574 if (!old_photo || old_photo->type == E_CONTACT_PHOTO_TYPE_INLINED ||
575 g_ascii_strcasecmp (old_photo->data.uri, photo->data.uri) != 0) {
578 gchar *new_uri = NULL;
580 /* ... Assume that the incomming uri belongs to another contact
581 * still in the BDB. Lets go ahead and create a hard link to the
582 * photo file and create a new name for the incomming uri, and
583 * use that in the incomming contact to save in place.
585 * This piece of code is here to ensure there are no problems if
586 * the libebook user decides to cross-reference and start "sharing"
587 * uris that we've previously stored in the photo directory.
589 * We use the hard-link here to off-load the necessary ref-counting
590 * logic to the file-system.
592 filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
593 g_assert (filename); /* we already checked this with 'is_backend_owned_uri ()' */
595 new_filename = hard_link_photo (bf, contact, field, filename, error);
598 status = STATUS_ERROR;
599 else if ((new_uri = g_filename_to_uri (new_filename, NULL, error)) == NULL) {
600 /* If we fail here... we need to clean up the hardlink we just created */
601 GError *local_err = NULL;
602 if (!remove_file (new_filename, &local_err)) {
603 g_warning ("Unable to cleanup photo uri: %s", local_err->message);
604 g_error_free (local_err);
606 status = STATUS_ERROR;
609 new_photo = e_contact_photo_new ();
610 new_photo->type = E_CONTACT_PHOTO_TYPE_URI;
611 new_photo->data.uri = new_uri;
613 e_contact_set (contact, field, new_photo);
615 d(g_print ("Backend modified incomming shared uri to be %s:\n", new_uri));
617 e_contact_photo_free (new_photo);
618 status = STATUS_MODIFIED;
620 g_free (new_filename);
625 e_contact_photo_free (old_photo);
630 e_contact_photo_free (photo);
636 * When a contact is added or modified we receive a vCard,
637 * this function checks if we've received inline data
638 * and replaces it with a uri notation.
640 * If this function modifies 'contact' then it will
641 * return the 'modified' status and 'vcard_ret' (if specified)
642 * will be set to a newly allocated vcard string.
644 static PhotoModifiedStatus
645 maybe_transform_vcard_for_photo (EBookBackendFile *bf,
646 EContact *old_contact,
651 PhotoModifiedStatus status;
652 gboolean modified = FALSE;
654 status = maybe_transform_vcard_field_for_photo (bf, old_contact, contact,
655 E_CONTACT_PHOTO, error);
656 modified = (status == STATUS_MODIFIED);
658 if (status != STATUS_ERROR) {
659 status = maybe_transform_vcard_field_for_photo (bf, old_contact, contact,
660 E_CONTACT_LOGO, error);
661 modified = modified || (status == STATUS_MODIFIED);
664 if (status != STATUS_ERROR) {
667 *vcard_ret = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
668 status = STATUS_MODIFIED;
676 build_sqlitedb (EBookBackendFilePrivate *bfpriv)
678 DB *db = bfpriv->file_db;
681 DBT id_dbt, vcard_dbt;
682 GSList *contacts = NULL;
683 GError *error = NULL;
684 gboolean skipped_version = FALSE;
685 gboolean skipped_revision = FALSE;
688 g_warning (G_STRLOC ": Not opened yet");
692 db_error = db->cursor (db, NULL, &dbc, 0);
695 g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
699 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
700 memset (&id_dbt, 0, sizeof (id_dbt));
701 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST);
703 while (db_error == 0) {
704 gboolean skip = FALSE;
706 /* don't include the version and revision in the list of cards */
707 if (!skipped_version && !strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
708 skipped_version = TRUE;
710 } else if (!skipped_revision && !strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME)) {
711 skipped_revision = TRUE;
716 EContact *contact = create_contact (id_dbt.data, vcard_dbt.data);
718 contacts = g_slist_prepend (contacts, contact);
721 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
727 /* Detect error case */
728 if (db_error != DB_NOTFOUND) {
729 g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error));
730 e_util_free_object_slist (contacts);
734 if (!e_book_backend_sqlitedb_add_contacts (bfpriv->sqlitedb,
736 contacts, FALSE, &error)) {
737 g_warning ("Failed to build contact summary: %s", error->message);
738 g_error_free (error);
739 e_util_free_object_slist (contacts);
743 e_util_free_object_slist (contacts);
745 if (!e_book_backend_sqlitedb_set_is_populated (bfpriv->sqlitedb, SQLITEDB_FOLDER_ID, TRUE, &error)) {
746 g_warning ("Failed to set the sqlitedb populated flag: %s", error->message);
747 g_error_free (error);
755 e_book_backend_file_create_unique_id (void)
757 /* use a 32 counter and the 32 bit timestamp to make an id.
758 * it's doubtful 2^32 id's will be created in a second, so we
761 return g_strdup_printf (PAS_ID_PREFIX "%08lX%08X", time(NULL), c++);
765 e_book_backend_file_new_revision (EBookBackendFile *bf)
767 gchar time_string[100] = {0};
768 const struct tm *tm = NULL;
774 strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
776 return g_strdup_printf ("%s(%d)", time_string, bf->priv->rev_counter++);
779 /* For now just bump the revision and set it in the DB every
780 * time the revision bumps, this is the safest approach and
781 * its unclear so far if bumping the revision string for
782 * every DB modification is going to really be an overhead.
785 e_book_backend_file_bump_revision (EBookBackendFile *bf)
787 DB *db = bf->priv->file_db;
788 DBT revision_name_dbt, revision_dbt;
791 g_free (bf->priv->revision);
792 bf->priv->revision = e_book_backend_file_new_revision (bf);
794 string_to_dbt (E_BOOK_BACKEND_FILE_REVISION_NAME, &revision_name_dbt);
795 string_to_dbt (bf->priv->revision, &revision_dbt);
796 db_error = db->put (db, NULL, &revision_name_dbt, &revision_dbt, 0);
799 g_warning (G_STRLOC ": db->put failed while bumping the revision string: %s",
800 db_strerror (db_error));
802 e_book_backend_notify_property_changed (E_BOOK_BACKEND (bf),
803 BOOK_BACKEND_PROPERTY_REVISION,
808 e_book_backend_file_load_revision (EBookBackendFile *bf)
810 DB *db = bf->priv->file_db;
811 DBT version_name_dbt, version_dbt;
814 string_to_dbt (E_BOOK_BACKEND_FILE_REVISION_NAME, &version_name_dbt);
815 memset (&version_dbt, 0, sizeof (version_dbt));
816 version_dbt.flags = DB_DBT_MALLOC;
818 db_error = db->get (db, NULL, &version_name_dbt, &version_dbt, 0);
821 bf->priv->revision = version_dbt.data;
824 /* key was not in file */
825 bf->priv->revision = e_book_backend_file_new_revision (bf);
830 set_revision (EContact *contact)
832 gchar time_string[100] = {0};
833 const struct tm *tm = NULL;
839 strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
840 e_contact_set (contact, E_CONTACT_REV, time_string);
845 * This method will return TRUE if all the contacts were properly created.
846 * If at least one contact fails, the method will return FALSE, all
847 * changes will be reverted (the @contacts list will stay empty) and
848 * @perror will be set.
851 do_create (EBookBackendFile *bf,
852 const GSList *vcards_req,
856 DB *db = bf->priv->file_db;
857 DB_ENV *env = bf->priv->env;
859 GSList *slist = NULL;
862 PhotoModifiedStatus status = STATUS_NORMAL;
865 g_assert (vcards_req);
868 g_propagate_error (perror, EDB_NOT_OPENED_ERROR);
872 /* Begin transaction */
873 db_error = env->txn_begin (env, NULL, &txn, 0);
875 g_warning (G_STRLOC ": env->txn_begin failed with %s", db_strerror (db_error));
876 db_error_to_gerror (db_error, perror);
880 for (l = vcards_req; l != NULL; l = l->next) {
881 DBT id_dbt, vcard_dbt;
885 const gchar *vcard_req;
888 vcard_req = (const gchar *) l->data;
890 id = e_book_backend_file_create_unique_id ();
891 contact = e_contact_new_from_vcard_with_uid (vcard_req, id);
893 rev = e_contact_get_const (contact, E_CONTACT_REV);
895 set_revision (contact);
897 status = maybe_transform_vcard_for_photo (bf, NULL, contact, NULL, perror);
899 if (status != STATUS_ERROR) {
900 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
902 string_to_dbt (id, &id_dbt);
903 string_to_dbt (vcard, &vcard_dbt);
905 db_error = db->put (db, txn, &id_dbt, &vcard_dbt, 0);
912 if (db_error == 0 && status != STATUS_ERROR) {
913 /* Contact was added successfully, add it to the return list */
914 if (contacts != NULL)
915 slist = g_slist_prepend (slist, contact);
916 } else if (db_error != 0) {
917 /* Contact could not be added */
918 g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error));
919 g_object_unref (contact);
920 db_error_to_gerror (db_error, perror);
922 /* Abort as soon as an error occurs */
924 } else if (status == STATUS_ERROR) {
925 /* Contact could not be added */
926 g_warning (G_STRLOC ": db->put failed with %s",
927 (perror && *perror) ? (*perror)->message :
928 "Unknown error transforming vcard");
929 g_object_unref (contact);
931 /* Abort as soon as an error occurs */
936 if (db_error == 0 && status != STATUS_ERROR) {
937 /* Commit transaction */
938 db_error = txn->commit (txn, 0);
940 /* Flush cache information to disk */
941 if (db->sync (db, 0) != 0) {
942 g_warning ("db->sync failed with %s", db_strerror (db_error));
945 g_warning (G_STRLOC ": txn->commit failed with %s", db_strerror (db_error));
946 db_error_to_gerror (db_error, perror);
950 /* Rollback transaction */
954 if (db_error == 0 && status != STATUS_ERROR) {
955 if (contacts != NULL)
956 *contacts = g_slist_reverse (slist);
960 if (contacts != NULL)
963 e_util_free_object_slist (slist);
969 e_book_backend_file_create_contacts (EBookBackendSync *backend,
971 GCancellable *cancellable,
972 const GSList *vcards,
973 GSList **added_contacts,
976 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
978 if (do_create (bf, vcards, added_contacts, perror)) {
979 GError *error = NULL;
981 if (!e_book_backend_sqlitedb_add_contacts (bf->priv->sqlitedb,
983 *added_contacts, FALSE, &error)) {
984 g_warning ("Failed to add contacts to summary: %s", error->message);
985 g_error_free (error);
988 e_book_backend_file_bump_revision (bf);
993 e_book_backend_file_remove_contacts (EBookBackendSync *backend,
995 GCancellable *cancellable,
996 const GSList *id_list,
1000 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1001 DB *db = bf->priv->file_db;
1002 DB_ENV *env = bf->priv->env;
1005 GSList *removed_ids = NULL, *removed_contacts = NULL;
1009 g_propagate_error (perror, EDB_NOT_OPENED_ERROR);
1013 /* Begin transaction */
1014 db_error = env->txn_begin (env, NULL, &txn, 0);
1015 if (db_error != 0) {
1016 g_warning (G_STRLOC ": env->txn_begin failed with %s", db_strerror (db_error));
1017 db_error_to_gerror (db_error, perror);
1021 for (l = id_list; l != NULL; l = l->next) {
1028 contact = load_contact (bf, txn, id, NULL);
1030 removed_contacts = g_slist_prepend (removed_contacts, contact);
1032 /* Then go on to delete from the db */
1033 string_to_dbt (id, &id_dbt);
1035 db_error = db->del (db, txn, &id_dbt, 0);
1036 if (db_error != 0) {
1037 g_warning (G_STRLOC ": db->del failed with %s", db_strerror (db_error));
1038 db_error_to_gerror (db_error, perror);
1039 /* Abort as soon as a removal fails */
1043 removed_ids = g_slist_prepend (removed_ids, g_strdup (id));
1046 if (db_error == 0) {
1047 /* Commit transaction */
1048 db_error = txn->commit (txn, 0);
1049 if (db_error == 0) {
1050 /* Flush cache information to disk */
1051 if (db->sync (db, 0) != 0) {
1052 g_warning ("db->sync failed with %s", db_strerror (db_error));
1055 g_warning (G_STRLOC ": txn->commit failed with %s", db_strerror (db_error));
1056 db_error_to_gerror (db_error, perror);
1059 /* Rollback transaction */
1063 if (db_error == 0) {
1064 GError *error = NULL;
1066 /* Delete URI associated to those contacts */
1067 for (l = removed_contacts; l; l = l->next) {
1068 maybe_delete_unused_uris (bf, E_CONTACT (l->data), NULL);
1071 /* Remove from summary as well */
1072 if (!e_book_backend_sqlitedb_remove_contacts (bf->priv->sqlitedb,
1074 removed_ids, &error)) {
1075 g_warning ("Failed to remove contacts from the summary: %s", error->message);
1076 g_error_free (error);
1082 e_util_free_string_slist (removed_ids);
1085 e_book_backend_file_bump_revision (bf);
1086 g_slist_free_full (removed_contacts, g_object_unref);
1090 e_book_backend_file_modify_contacts (EBookBackendSync *backend,
1092 GCancellable *cancellable,
1093 const GSList *vcards,
1097 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1098 DB *db = bf->priv->file_db;
1099 DB_ENV *env = bf->priv->env;
1102 const GSList *lold, *l;
1103 GSList *old_contacts = NULL, *modified_contacts = NULL;
1105 PhotoModifiedStatus status = STATUS_NORMAL;
1108 g_propagate_error (perror, EDB_NOT_OPENED_ERROR);
1112 /* Begin transaction */
1113 db_error = env->txn_begin (env, NULL, &txn, 0);
1114 if (db_error != 0) {
1115 g_warning (G_STRLOC ": env->txn_begin failed with %s", db_strerror (db_error));
1116 db_error_to_gerror (db_error, perror);
1120 for (l = vcards; l != NULL; l = l->next) {
1121 gchar *id, *lookup_id;
1122 gchar *vcard_with_rev;
1123 DBT id_dbt, vcard_dbt;
1124 EContact *contact, *old_contact;
1126 contact = e_contact_new_from_vcard (l->data);
1127 id = e_contact_get (contact, E_CONTACT_UID);
1130 g_propagate_error (perror, EDB_ERROR_EX (OTHER_ERROR, _("No UID in the contact")));
1131 g_object_unref (contact);
1135 old_contact = load_contact (bf, txn, id, perror);
1137 g_warning (G_STRLOC ": Failed to load contact %s", id);
1138 status = STATUS_ERROR;
1141 g_object_unref (contact);
1144 old_contacts = g_slist_prepend (old_contacts, old_contact);
1146 /* Transform incomming photo blobs to uris before storing this to the DB */
1147 status = maybe_transform_vcard_for_photo (bf, old_contact, contact, NULL, perror);
1148 if (status == STATUS_ERROR) {
1149 g_warning (G_STRLOC ": Error transforming contact %s: %s",
1150 id, (perror && *perror) ? (*perror)->message : "Unknown Error");
1153 g_object_unref (old_contact);
1154 g_object_unref (contact);
1158 /* update the revision (modified time of contact) */
1159 set_revision (contact);
1160 vcard_with_rev = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
1162 /* This is disgusting, but for a time cards were added with
1163 * ID's that are no longer used (they contained both the uri
1164 * and the id.) If we recognize it as a uri (file:///...) trim
1165 * off everything before the last '/', and use that as the
1167 if (!strncmp (id, "file:///", strlen ("file:///"))) {
1168 lookup_id = strrchr (id, '/') + 1;
1173 string_to_dbt (lookup_id, &id_dbt);
1174 string_to_dbt (vcard_with_rev, &vcard_dbt);
1176 db_error = db->put (db, txn, &id_dbt, &vcard_dbt, 0);
1177 g_free (vcard_with_rev);
1179 if (db_error != 0) {
1180 g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error));
1181 db_error_to_gerror (db_error, perror);
1183 /* Abort as soon as a modification fails */
1185 g_object_unref (contact);
1189 modified_contacts = g_slist_prepend (modified_contacts, contact);
1190 ids = g_slist_prepend (ids, id);
1193 if (db_error == 0 && status != STATUS_ERROR) {
1194 /* Commit transaction */
1195 db_error = txn->commit (txn, 0);
1196 if (db_error == 0) {
1197 /* Flush cache information to disk */
1198 if (db->sync (db, 0) != 0) {
1199 g_warning ("db->sync failed with %s", db_strerror (db_error));
1202 g_warning (G_STRLOC ": txn->commit failed with %s", db_strerror (db_error));
1203 db_error_to_gerror (db_error, perror);
1206 /* Rollback transaction */
1210 if (db_error == 0 && status != STATUS_ERROR) {
1211 GError *error = NULL;
1213 /* Delete old photo file uris if need be (this will compare the new contact
1214 * with the current copy in the BDB to extract the uris to delete) */
1215 lold = old_contacts;
1216 l = modified_contacts;
1218 maybe_delete_unused_uris (bf, E_CONTACT (lold->data), E_CONTACT (l->data));
1223 /* Update summary as well */
1224 if (!e_book_backend_sqlitedb_remove_contacts (bf->priv->sqlitedb,
1227 g_warning ("Failed to remove contacts from the summary: %s", error->message);
1228 g_error_free (error);
1229 } else if (!e_book_backend_sqlitedb_add_contacts (bf->priv->sqlitedb,
1231 modified_contacts, FALSE, &error)) {
1232 g_warning ("Failed to add contacts to summary: %s", error->message);
1233 g_error_free (error);
1236 *contacts = g_slist_reverse (modified_contacts);
1239 e_util_free_object_slist (modified_contacts);
1242 e_util_free_string_slist (ids);
1243 g_slist_free_full (old_contacts, g_object_unref);
1245 e_book_backend_file_bump_revision (bf);
1249 e_book_backend_file_get_contact (EBookBackendSync *backend,
1251 GCancellable *cancellable,
1256 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1258 if (!bf || !bf->priv || !bf->priv->file_db) {
1259 g_propagate_error (perror, EDB_NOT_OPENED_ERROR);
1263 *vcard = load_vcard (bf, NULL, id, perror);
1267 e_book_backend_file_get_contact_list (EBookBackendSync *backend,
1269 GCancellable *cancellable,
1274 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1275 DB *db = bf->priv->file_db;
1278 DBT id_dbt, vcard_dbt;
1279 EBookBackendSExp *card_sexp = NULL;
1280 gboolean search_needed;
1281 const gchar *search = query;
1282 GSList *contact_list = NULL, *l;
1283 GSList *summary_list = NULL;
1284 gboolean searched_summary = FALSE;
1285 gboolean with_all_required_fields = FALSE;
1287 d(printf ("e_book_backend_file_get_contact_list (%s)\n", search));
1290 g_propagate_error (perror, EDB_NOT_OPENED_ERROR);
1294 summary_list = e_book_backend_sqlitedb_search (bf->priv->sqlitedb,
1298 &with_all_required_fields, NULL);
1302 for (l = summary_list; l; l = l->next) {
1303 EbSdbSearchData *data = l->data;
1305 if (with_all_required_fields) {
1306 contact_list = g_slist_prepend (contact_list, data->vcard);
1309 /* In this case the sqlitedb helped us with the query, but
1310 * the return information is incomplete so we need to load it up.
1314 vcard = load_vcard (bf, NULL, data->uid, perror);
1316 /* Break out on the first BDB error */
1320 contact_list = g_slist_prepend (contact_list, vcard);
1324 g_slist_foreach (summary_list, (GFunc) e_book_backend_sqlitedb_search_data_free, NULL);
1325 g_slist_free (summary_list);
1328 search_needed = TRUE;
1329 if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")"))
1330 search_needed = FALSE;
1332 card_sexp = e_book_backend_sexp_new (search);
1334 g_propagate_error (perror, EDB_ERROR (INVALID_QUERY));
1338 db_error = db->cursor (db, NULL, &dbc, 0);
1340 if (db_error != 0) {
1341 g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
1342 /* XXX this needs to be some CouldNotOpen error */
1343 db_error_to_gerror (db_error, perror);
1347 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
1348 vcard_dbt.flags = DB_DBT_MALLOC;
1349 memset (&id_dbt, 0, sizeof (id_dbt));
1350 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST);
1352 while (db_error == 0) {
1354 /* don't include the version or revision in the list of cards */
1355 if ((id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
1356 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) &&
1357 (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1
1358 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) {
1360 if ((!search_needed) || (card_sexp != NULL && e_book_backend_sexp_match_vcard (card_sexp, vcard_dbt.data))) {
1361 contact_list = g_slist_prepend (contact_list, vcard_dbt.data);
1363 free (vcard_dbt.data);
1366 free (vcard_dbt.data);
1369 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
1372 g_object_unref (card_sexp);
1374 if (db_error == DB_NOTFOUND) {
1377 g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error));
1378 db_error_to_gerror (db_error, perror);
1381 db_error = dbc->c_close (dbc);
1382 if (db_error != 0) {
1383 g_warning (G_STRLOC ": dbc->c_close failed with %s", db_strerror (db_error));
1387 *contacts = contact_list;
1391 e_book_backend_file_get_contact_list_uids (EBookBackendSync *backend,
1393 GCancellable *cancellable,
1395 GSList **contacts_uids,
1398 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1399 DB *db = bf->priv->file_db;
1402 DBT id_dbt, vcard_dbt;
1403 EBookBackendSExp *card_sexp = NULL;
1404 gboolean search_needed;
1405 const gchar *search = query;
1406 GSList *uids = NULL;
1407 gboolean searched = FALSE;
1409 d(printf ("e_book_backend_file_get_contact_list (%s)\n", search));
1412 g_propagate_error (perror, EDB_NOT_OPENED_ERROR);
1416 uids = e_book_backend_sqlitedb_search_uids (bf->priv->sqlitedb,
1418 search, &searched, NULL);
1421 search_needed = TRUE;
1422 if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")"))
1423 search_needed = FALSE;
1425 card_sexp = e_book_backend_sexp_new (search);
1427 g_propagate_error (perror, EDB_ERROR (INVALID_QUERY));
1431 db_error = db->cursor (db, NULL, &dbc, 0);
1433 if (db_error != 0) {
1434 g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
1435 /* XXX this needs to be some CouldNotOpen error */
1436 db_error_to_gerror (db_error, perror);
1440 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
1441 vcard_dbt.flags = DB_DBT_MALLOC;
1442 memset (&id_dbt, 0, sizeof (id_dbt));
1443 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST);
1445 while (db_error == 0) {
1447 /* don't include the version or revision in the list of cards */
1448 if ((id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
1449 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) &&
1450 (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1
1451 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) {
1453 if ((!search_needed) || (card_sexp != NULL && e_book_backend_sexp_match_vcard (card_sexp, vcard_dbt.data))) {
1454 uids = g_slist_prepend (uids, g_strdup (id_dbt.data));
1458 g_free (vcard_dbt.data);
1460 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
1463 g_object_unref (card_sexp);
1465 if (db_error == DB_NOTFOUND) {
1468 g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error));
1469 db_error_to_gerror (db_error, perror);
1472 db_error = dbc->c_close (dbc);
1473 if (db_error != 0) {
1474 g_warning (G_STRLOC ": dbc->c_close failed with %s", db_strerror (db_error));
1478 *contacts_uids = g_slist_reverse (uids);
1482 EBookBackendFile *bf;
1485 } FileBackendSearchClosure;
1488 closure_destroy (FileBackendSearchClosure *closure)
1490 d(printf ("destroying search closure\n"));
1491 e_flag_free (closure->running);
1495 static FileBackendSearchClosure *
1496 init_closure (EDataBookView *book_view,
1497 EBookBackendFile *bf)
1499 FileBackendSearchClosure *closure = g_new (FileBackendSearchClosure, 1);
1502 closure->thread = NULL;
1503 closure->running = e_flag_new ();
1505 g_object_set_data_full (G_OBJECT (book_view), "EBookBackendFile.BookView::closure",
1506 closure, (GDestroyNotify) closure_destroy);
1511 static FileBackendSearchClosure *
1512 get_closure (EDataBookView *book_view)
1514 return g_object_get_data (G_OBJECT (book_view), "EBookBackendFile.BookView::closure");
1518 notify_update_vcard (EDataBookView *book_view,
1519 gboolean prefiltered,
1524 e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard);
1526 e_data_book_view_notify_update_vcard (book_view, id, vcard);
1530 book_view_thread (gpointer data)
1532 EDataBookView *book_view;
1533 FileBackendSearchClosure *closure;
1534 EBookBackendFile *bf;
1537 DBT id_dbt, vcard_dbt;
1539 gboolean allcontacts;
1540 GSList *summary_list, *l;
1541 GHashTable *fields_of_interest;
1542 gboolean searched = FALSE;
1543 gboolean with_all_required_fields = FALSE;
1545 g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (data), NULL);
1548 closure = get_closure (book_view);
1550 g_warning (G_STRLOC ": NULL closure in book view thread");
1555 d(printf ("starting initial population of book view\n"));
1557 /* ref the book view because it'll be removed and unrefed
1558 * when/if it's stopped */
1559 e_data_book_view_ref (book_view);
1561 db = bf->priv->file_db;
1562 query = e_data_book_view_get_card_query (book_view);
1563 fields_of_interest = e_data_book_view_get_fields_of_interest (book_view);
1566 e_data_book_view_notify_complete (book_view, EDB_NOT_OPENED_ERROR);
1567 e_data_book_view_unref (book_view);
1571 if ( !strcmp (query, "(contains \"x-evolution-any-field\" \"\")")) {
1572 e_data_book_view_notify_progress (book_view, -1, _("Loading..."));
1575 e_data_book_view_notify_progress (book_view, -1, _("Searching..."));
1576 allcontacts = FALSE;
1579 d(printf ("signalling parent thread\n"));
1580 e_flag_set (closure->running);
1582 summary_list = e_book_backend_sqlitedb_search (bf->priv->sqlitedb,
1584 query, fields_of_interest,
1585 &searched, &with_all_required_fields, NULL);
1589 for (l = summary_list; l; l = l->next) {
1590 EbSdbSearchData *data = l->data;
1591 gchar *vcard = NULL;
1593 if (with_all_required_fields) {
1594 vcard = data->vcard;
1597 GError *error = NULL;
1599 /* The sqlitedb summary did not satisfy 'fields-of-interest',
1600 * load the complete vcard here. */
1601 vcard = load_vcard (bf, NULL, data->uid, &error);
1604 g_warning ("Error loading contact %s: %s",
1605 data->uid, error->message);
1606 g_error_free (error);
1614 notify_update_vcard (book_view, TRUE, data->uid, vcard);
1618 g_slist_foreach (summary_list, (GFunc) e_book_backend_sqlitedb_search_data_free, NULL);
1619 g_slist_free (summary_list);
1621 /* iterate over the db and do the query there */
1624 memset (&id_dbt, 0, sizeof (id_dbt));
1625 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
1626 vcard_dbt.flags = DB_DBT_MALLOC;
1628 db_error = db->cursor (db, NULL, &dbc, 0);
1629 if (db_error == 0) {
1631 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST);
1632 while (db_error == 0) {
1634 if (!e_flag_is_set (closure->running))
1637 /* don't include the version in the list of cards */
1638 if (strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME) &&
1639 strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME)) {
1640 notify_update_vcard (book_view, allcontacts,
1641 id_dbt.data, vcard_dbt.data);
1644 g_free (vcard_dbt.data);
1645 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
1649 if (db_error && db_error != DB_NOTFOUND)
1650 g_warning ("e_book_backend_file_search: error building list: %s",
1651 db_strerror (db_error));
1653 else if (db_error == DB_RUNRECOVERY) {
1654 g_warning ("e_book_backend_file_search: error getting the cursor for %s",
1655 bf->priv->filename);
1661 if (e_flag_is_set (closure->running))
1662 e_data_book_view_notify_complete (book_view, NULL /* Success */);
1664 e_data_book_view_unref (book_view);
1666 d(printf ("finished population of book view\n"));
1672 e_book_backend_file_start_book_view (EBookBackend *backend,
1673 EDataBookView *book_view)
1675 FileBackendSearchClosure *closure = init_closure (book_view, E_BOOK_BACKEND_FILE (backend));
1677 d(printf ("starting book view thread\n"));
1678 closure->thread = g_thread_create (book_view_thread, book_view, TRUE, NULL);
1680 e_flag_wait (closure->running);
1682 /* at this point we know the book view thread is actually running */
1683 d(printf ("returning from start_book_view\n"));
1687 e_book_backend_file_stop_book_view (EBookBackend *backend,
1688 EDataBookView *book_view)
1690 FileBackendSearchClosure *closure = get_closure (book_view);
1696 d(printf ("stopping query\n"));
1697 need_join = e_flag_is_set (closure->running);
1698 e_flag_clear (closure->running);
1701 g_thread_join (closure->thread);
1705 e_book_backend_file_authenticate_user (EBookBackendSync *backend,
1706 GCancellable *cancellable,
1707 ECredentials *credentials,
1716 ** 0.0 just a list of cards
1718 ** 0.1 same as 0.0, but with the version tag
1720 ** 0.2 not a real format upgrade, just a hack to fix broken ids caused
1721 ** by a bug in early betas, but we only need to convert them if
1722 ** the previous version is 0.1, since the bug existed after 0.1
1726 e_book_backend_file_upgrade_db (EBookBackendFile *bf,
1729 DB *db = bf->priv->file_db;
1731 DBT version_name_dbt, version_dbt;
1734 g_warning (G_STRLOC ": No DB opened");
1738 if (strcmp (old_version, "0.0")
1739 && strcmp (old_version, "0.1")) {
1740 g_warning ("unsupported version '%s' found in PAS backend file\n",
1745 if (!strcmp (old_version, "0.1")) {
1746 /* we just loop through all the cards in the db,
1747 * giving them valid ids if they don't have them */
1748 DBT id_dbt, vcard_dbt;
1750 gint card_failed = 0;
1752 db_error = db->cursor (db, NULL, &dbc, 0);
1753 if (db_error != 0) {
1754 g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error));
1758 memset (&id_dbt, 0, sizeof (id_dbt));
1759 memset (&vcard_dbt, 0, sizeof (vcard_dbt));
1761 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST);
1763 while (db_error == 0) {
1764 if ((id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
1765 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) &&
1766 (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1
1767 || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) {
1770 contact = create_contact (id_dbt.data, vcard_dbt.data);
1772 /* the cards we're looking for are
1773 * created with a normal id dbt, but
1774 * with the id field in the vcard set
1775 * to something that doesn't match.
1776 * so, we need to modify the card to
1777 * have the same id as the the dbt. */
1778 if (strcmp (id_dbt.data, e_contact_get_const (contact, E_CONTACT_UID))) {
1781 e_contact_set (contact, E_CONTACT_UID, id_dbt.data);
1783 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
1784 string_to_dbt (vcard, &vcard_dbt);
1786 db_error = db->put (db, NULL,
1787 &id_dbt, &vcard_dbt, 0);
1795 g_object_unref (contact);
1798 db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
1804 g_warning ("failed to update %d cards", card_failed);
1809 string_to_dbt (E_BOOK_BACKEND_FILE_VERSION_NAME, &version_name_dbt);
1810 string_to_dbt (E_BOOK_BACKEND_FILE_VERSION, &version_dbt);
1812 db_error = db->put (db, NULL, &version_name_dbt, &version_dbt, 0);
1820 e_book_backend_file_maybe_upgrade_db (EBookBackendFile *bf)
1822 DB *db = bf->priv->file_db;
1823 DBT version_name_dbt, version_dbt;
1826 gboolean ret_val = TRUE;
1829 g_warning (G_STRLOC ": No DB opened");
1833 string_to_dbt (E_BOOK_BACKEND_FILE_VERSION_NAME, &version_name_dbt);
1834 memset (&version_dbt, 0, sizeof (version_dbt));
1835 version_dbt.flags = DB_DBT_MALLOC;
1837 db_error = db->get (db, NULL, &version_name_dbt, &version_dbt, 0);
1838 if (db_error == 0) {
1840 version = version_dbt.data;
1843 /* key was not in file */
1844 version = g_strdup ("0.0");
1847 if (strcmp (version, E_BOOK_BACKEND_FILE_VERSION))
1848 ret_val = e_book_backend_file_upgrade_db (bf, version);
1855 #ifdef CREATE_DEFAULT_VCARD
1856 # include <libedata-book/ximian-vcard.h>
1860 #if (DB_VERSION_MAJOR > 4) || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
1861 file_errcall (const DB_ENV *env,
1865 file_errcall (const gchar *buf1,
1869 g_warning ("libdb error: %s", buf2);
1873 e_book_backend_file_open (EBookBackendSync *backend,
1875 GCancellable *cancellable,
1876 gboolean only_if_exists,
1879 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1880 gchar *dirname, *filename;
1881 gboolean readonly = TRUE;
1886 GError *local_error = NULL;
1887 global_env *genv = NULL;
1889 #ifdef CREATE_DEFAULT_VCARD
1890 gboolean create_default_vcard = FALSE;
1893 source = e_backend_get_source (E_BACKEND (backend));
1894 dirname = e_book_backend_file_extract_path_from_source (
1895 source, GET_PATH_DB_DIR);
1896 filename = g_build_filename (dirname, "addressbook.db", NULL);
1898 db_error = e_db3_utils_maybe_recover (filename);
1899 if (db_error != 0) {
1900 g_warning ("db recovery failed with %s", db_strerror (db_error));
1903 db_error_to_gerror (db_error, perror);
1907 G_LOCK (db_environments);
1908 if (db_environments) {
1909 genv = g_hash_table_lookup (db_environments, dirname);
1911 if (genv && genv->ref_count > 0) {
1915 db_error = db_env_create (&env, 0);
1916 if (db_error != 0) {
1917 g_warning ("db_env_create failed with %s", db_strerror (db_error));
1918 G_UNLOCK (db_environments);
1921 db_error_to_gerror (db_error, perror);
1925 env->set_errcall (env, file_errcall);
1927 /* Set the allocation routines to the non-aborting GLib functions */
1928 env->set_alloc (env, (gpointer (*)(gsize)) g_try_malloc,
1929 (gpointer (*)(gpointer , gsize)) g_try_realloc,
1932 /* Make sure the database directory is created
1933 * or env->open will fail */
1934 if (!only_if_exists) {
1935 if (!create_directory (dirname, perror)) {
1936 g_warning ("failed to create directory at %s", dirname);
1937 G_UNLOCK (db_environments);
1945 * DB_INIT_TXN enables transaction support. It requires DB_INIT_LOCK to
1946 * initialize the locking subsystem and DB_INIT_LOG for the logging
1949 * DB_INIT_MPOOL enables the in-memory cache.
1951 * Note that we need either DB_INIT_CDB or DB_INIT_LOCK, because we will
1952 * have multiple threads reading and writing concurrently without
1953 * any locking above libdb. Right now DB_INIT_LOCK is used because
1954 * DB_INIT_TXN conflicts with DB_INIT_CDB.
1956 db_error = (*env->open) (env, dirname, DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG | DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_THREAD, 0);
1957 if (db_error != 0) {
1958 env->close (env, 0);
1959 g_warning ("db_env_open failed with %s", db_strerror (db_error));
1960 G_UNLOCK (db_environments);
1963 db_error_to_gerror (db_error, perror);
1967 /* Insert in the db_environments hash table */
1968 if (!db_environments) {
1969 db_environments = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1971 genv = g_malloc0 (sizeof (global_env));
1972 genv->ref_count = 1;
1974 g_hash_table_insert (db_environments, g_strdup (dirname), genv);
1976 G_UNLOCK (db_environments);
1978 bf->priv->env = env;
1980 db_error = db_create (&db, env, 0);
1981 if (db_error != 0) {
1982 g_warning ("db_create failed with %s", db_strerror (db_error));
1985 db_error_to_gerror (db_error, perror);
1989 db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD | DB_AUTO_COMMIT, 0666);
1991 if (db_error == DB_OLD_VERSION) {
1992 db_error = e_db3_utils_upgrade_format (filename);
1994 if (db_error != 0) {
1995 g_warning ("db format upgrade failed with %s", db_strerror (db_error));
1998 db_error_to_gerror (db_error, perror);
2003 db_error = db_create (&db, env, 0);
2004 if (db_error != 0) {
2005 g_warning ("db_create failed with %s", db_strerror (db_error));
2008 db_error_to_gerror (db_error, perror);
2012 db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD | DB_AUTO_COMMIT, 0666);
2015 if (db_error == 0) {
2019 db_error = db_create (&db, env, 0);
2020 if (db_error != 0) {
2021 g_warning ("db_create failed with %s", db_strerror (db_error));
2024 db_error_to_gerror (db_error, perror);
2028 db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_RDONLY | DB_THREAD | DB_AUTO_COMMIT, 0666);
2030 if (db_error != 0 && !only_if_exists) {
2032 /* the database didn't exist, so we create the
2033 * directory then the .db */
2036 if (!create_directory (dirname, perror)) {
2042 db_error = db_create (&db, env, 0);
2043 if (db_error != 0) {
2044 g_warning ("db_create failed with %s", db_strerror (db_error));
2047 db_error_to_gerror (db_error, perror);
2051 db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_CREATE | DB_THREAD | DB_AUTO_COMMIT, 0666);
2052 if (db_error != 0) {
2054 g_warning ("db->open (... %s ... DB_CREATE ...) failed with %s", filename, db_strerror (db_error));
2057 #ifdef CREATE_DEFAULT_VCARD
2058 create_default_vcard = TRUE;
2066 bf->priv->file_db = db;
2068 if (db_error != 0) {
2069 bf->priv->file_db = NULL;
2072 db_error_to_gerror (db_error, perror);
2076 #ifdef CREATE_DEFAULT_VCARD
2077 if (create_default_vcard) {
2079 l.data = XIMIAN_VCARD;
2082 if (!do_create (bf, &l, NULL, NULL))
2083 g_warning ("Cannot create default contact");
2087 if (!e_book_backend_file_maybe_upgrade_db (bf)) {
2089 bf->priv->file_db = NULL;
2092 g_propagate_error (perror, EDB_ERROR_EX (OTHER_ERROR, "e_book_backend_file_maybe_upgrade_db failed"));
2096 g_free (bf->priv->dirname);
2097 g_free (bf->priv->filename);
2098 bf->priv->dirname = dirname;
2099 bf->priv->filename = filename;
2101 bf->priv->sqlitedb = e_book_backend_sqlitedb_new (bf->priv->dirname,
2104 SQLITEDB_FOLDER_NAME,
2107 if (!bf->priv->sqlitedb)
2110 if (!e_book_backend_sqlitedb_get_is_populated (bf->priv->sqlitedb,
2114 g_propagate_error (perror, local_error);
2116 } else if (!build_sqlitedb (bf->priv)) {
2117 g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
2118 _("Failed to build summary for an address book %s"),
2119 bf->priv->filename));
2123 /* Resolve the photo directory here */
2124 dirname = e_book_backend_file_extract_path_from_source (
2125 source, GET_PATH_PHOTO_DIR);
2126 if (!only_if_exists && !create_directory (dirname, perror))
2128 bf->priv->photo_dirname = dirname;
2130 e_book_backend_file_load_revision (bf);
2132 e_book_backend_notify_online (E_BOOK_BACKEND (backend), TRUE);
2133 e_book_backend_notify_readonly (E_BOOK_BACKEND (backend), readonly);
2134 e_book_backend_notify_opened (E_BOOK_BACKEND (backend), NULL /* Success */);
2136 e_book_backend_notify_property_changed (E_BOOK_BACKEND (backend),
2137 BOOK_BACKEND_PROPERTY_REVISION,
2138 bf->priv->revision);
2142 select_changes (const gchar *name)
2146 if (strlen (name) < strlen (CHANGES_DB_SUFFIX))
2149 p = strstr (name, CHANGES_DB_SUFFIX);
2153 if (strlen (p) != strlen (CHANGES_DB_SUFFIX))
2160 remove_photos (const gchar *dirname)
2164 dir = g_dir_open (dirname, 0, NULL);
2168 while ((name = g_dir_read_name (dir))) {
2169 gchar *full_path = g_build_filename (dirname, name, NULL);
2170 if (-1 == g_unlink (full_path)) {
2171 g_warning ("failed to remove photo file %s: %s",
2172 full_path, g_strerror (errno));
2182 e_book_backend_file_remove (EBookBackendSync *backend,
2184 GCancellable *cancellable,
2187 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
2190 if (!remove_file (bf->priv->filename, perror))
2193 if (!e_book_backend_sqlitedb_remove (bf->priv->sqlitedb, perror))
2196 /* unref the sqlitedb before we remove the file so it's not written out again */
2197 g_object_unref (bf->priv->sqlitedb);
2198 bf->priv->sqlitedb = NULL;
2200 dir = g_dir_open (bf->priv->dirname, 0, NULL);
2204 while ((name = g_dir_read_name (dir))) {
2205 if (select_changes (name)) {
2206 gchar *full_path = g_build_filename (bf->priv->dirname, name, NULL);
2207 if (g_unlink (full_path) == -1) {
2208 g_warning ("failed to remove change db `%s': %s", full_path, g_strerror (errno));
2217 /* Remove photos and the photo directory */
2218 remove_photos (bf->priv->photo_dirname);
2219 if (-1 == g_rmdir (bf->priv->photo_dirname))
2220 g_warning ("failed to remove directory `%s`: %s", bf->priv->photo_dirname, g_strerror (errno));
2222 /* Try removing the base directory now */
2223 if (-1 == g_rmdir (bf->priv->dirname))
2224 g_warning ("failed to remove directory `%s`: %s", bf->priv->dirname, g_strerror (errno));
2226 /* we may not have actually succeeded in removing the
2227 * backend's files/dirs, but there's nothing we can do about
2228 * it here.. the only time we should return failure is if we
2229 * failed to remove the actual data. a failure should mean
2230 * that the addressbook is still valid */
2234 e_book_backend_file_get_backend_property (EBookBackendSync *backend,
2236 GCancellable *cancellable,
2237 const gchar *prop_name,
2241 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
2242 gboolean processed = TRUE;
2244 g_return_val_if_fail (prop_name != NULL, FALSE);
2245 g_return_val_if_fail (prop_value != NULL, FALSE);
2247 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
2248 *prop_value = g_strdup ("local,do-initial-query,bulk-adds,bulk-modifies,bulk-removes,contact-lists");
2249 } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
2250 *prop_value = g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
2251 } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
2252 GSList *fields = NULL;
2255 /* XXX we need a way to say "we support everything", since the
2256 * file backend does */
2257 for (i = 1; i < E_CONTACT_FIELD_LAST; i++)
2258 fields = g_slist_append (fields, (gpointer) e_contact_field_name (i));
2260 *prop_value = e_data_book_string_slist_to_comma_string (fields);
2261 g_slist_free (fields);
2262 } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_AUTH_METHODS)) {
2264 } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REVISION)) {
2265 *prop_value = g_strdup (bf->priv->revision);
2274 e_book_backend_file_notify_online_cb (EBookBackend *backend,
2277 if (e_book_backend_is_opened (backend))
2278 e_book_backend_notify_online (backend, TRUE);
2282 e_book_backend_file_sync (EBookBackend *backend)
2284 EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
2287 g_return_if_fail (bf != NULL);
2289 if (bf->priv->file_db) {
2290 db_error = bf->priv->file_db->sync (bf->priv->file_db, 0);
2292 g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
2298 EBookBackendFile *bf;
2302 view_notify_update (EDataBookView *view,
2305 NotifyData *ndata = data;
2306 GHashTable *fields = e_data_book_view_get_fields_of_interest (view);
2307 gboolean notified = FALSE;
2308 gboolean with_all_required_fields = FALSE;
2310 if (e_book_backend_sqlitedb_is_summary_query (e_data_book_view_get_card_query (view)) &&
2311 e_book_backend_sqlitedb_is_summary_fields (fields)) {
2313 const gchar *uid = e_contact_get_const (ndata->contact, E_CONTACT_UID);
2316 vcard = e_book_backend_sqlitedb_get_vcard_string (ndata->bf->priv->sqlitedb,
2317 SQLITEDB_FOLDER_ID, uid,
2318 fields, &with_all_required_fields, NULL);
2321 if (with_all_required_fields) {
2322 e_data_book_view_notify_update_prefiltered_vcard (view, uid, vcard);
2330 e_data_book_view_notify_update (view, ndata->contact);
2336 e_book_backend_file_notify_update (EBookBackend *backend,
2337 const EContact *contact)
2339 NotifyData data = { (EContact *) contact, E_BOOK_BACKEND_FILE (backend) };
2341 e_book_backend_foreach_view (backend, view_notify_update, &data);
2345 e_book_backend_file_dispose (GObject *object)
2347 EBookBackendFile *bf;
2350 bf = E_BOOK_BACKEND_FILE (object);
2352 if (bf->priv->file_db) {
2353 bf->priv->file_db->close (bf->priv->file_db, 0);
2354 bf->priv->file_db = NULL;
2357 G_LOCK (db_environments);
2358 genv = g_hash_table_lookup (db_environments, bf->priv->dirname);
2361 if (genv->ref_count == 0) {
2362 genv->env->close (genv->env, 0);
2364 g_hash_table_remove (db_environments, bf->priv->dirname);
2366 if (g_hash_table_size (db_environments) == 0) {
2367 g_hash_table_destroy (db_environments);
2368 db_environments = NULL;
2371 G_UNLOCK (db_environments);
2373 if (bf->priv->sqlitedb) {
2374 g_object_unref (bf->priv->sqlitedb);
2375 bf->priv->sqlitedb = NULL;
2378 G_OBJECT_CLASS (e_book_backend_file_parent_class)->dispose (object);
2382 e_book_backend_file_finalize (GObject *object)
2384 EBookBackendFilePrivate *priv;
2386 priv = E_BOOK_BACKEND_FILE_GET_PRIVATE (object);
2388 g_free (priv->filename);
2389 g_free (priv->dirname);
2390 g_free (priv->photo_dirname);
2391 g_free (priv->revision);
2393 /* Chain up to parent's finalize() method. */
2394 G_OBJECT_CLASS (e_book_backend_file_parent_class)->finalize (object);
2398 /* Avoid compiler warning by providing a function with exactly the
2399 * prototype that db_env_set_func_open() wants for the open method.
2403 my_open (const gchar *name,
2409 if (oflag & O_CREAT) {
2411 va_start (arg, oflag);
2412 mode = va_arg (arg, gint);
2416 return g_open (name, oflag, mode);
2420 my_rename (const gchar *oldname,
2421 const gchar *newname)
2423 return g_rename (oldname, newname);
2427 my_exists (const gchar *name,
2430 if (!g_file_test (name, G_FILE_TEST_EXISTS))
2433 *isdirp = g_file_test (name, G_FILE_TEST_IS_DIR);
2438 my_unlink (const gchar *name)
2440 return g_unlink (name);
2446 e_book_backend_file_class_init (EBookBackendFileClass *class)
2448 GObjectClass *object_class = G_OBJECT_CLASS (class);
2449 EBookBackendSyncClass *sync_class;
2450 EBookBackendClass *backend_class;
2452 g_type_class_add_private (class, sizeof (EBookBackendFilePrivate));
2454 sync_class = E_BOOK_BACKEND_SYNC_CLASS (class);
2455 backend_class = E_BOOK_BACKEND_CLASS (class);
2457 /* Set the virtual methods. */
2458 backend_class->start_book_view = e_book_backend_file_start_book_view;
2459 backend_class->stop_book_view = e_book_backend_file_stop_book_view;
2460 backend_class->sync = e_book_backend_file_sync;
2461 backend_class->notify_update = e_book_backend_file_notify_update;
2463 sync_class->open_sync = e_book_backend_file_open;
2464 sync_class->remove_sync = e_book_backend_file_remove;
2465 sync_class->get_backend_property_sync = e_book_backend_file_get_backend_property;
2466 sync_class->create_contacts_sync = e_book_backend_file_create_contacts;
2467 sync_class->remove_contacts_sync = e_book_backend_file_remove_contacts;
2468 sync_class->modify_contacts_sync = e_book_backend_file_modify_contacts;
2469 sync_class->get_contact_sync = e_book_backend_file_get_contact;
2470 sync_class->get_contact_list_sync = e_book_backend_file_get_contact_list;
2471 sync_class->get_contact_list_uids_sync = e_book_backend_file_get_contact_list_uids;
2472 sync_class->authenticate_user_sync = e_book_backend_file_authenticate_user;
2474 object_class->dispose = e_book_backend_file_dispose;
2475 object_class->finalize = e_book_backend_file_finalize;
2478 /* Use the gstdio wrappers to open, check, rename and unlink
2481 db_env_set_func_open (my_open);
2482 db_env_set_func_close (close);
2483 db_env_set_func_exists (my_exists);
2484 db_env_set_func_rename (my_rename);
2485 db_env_set_func_unlink (my_unlink);
2490 e_book_backend_file_init (EBookBackendFile *backend)
2492 backend->priv = E_BOOK_BACKEND_FILE_GET_PRIVATE (backend);
2495 backend, "notify::online",
2496 G_CALLBACK (e_book_backend_file_notify_online_cb), NULL);