diff options
author | Tristan Van Berkom <tristanvb@openismus.com> | 2012-10-29 15:33:09 +0900 |
---|---|---|
committer | Tristan Van Berkom <tristanvb@openismus.com> | 2013-01-27 16:32:50 +0900 |
commit | 7ff1bb6702183a35215e630855cb2c85e95735a4 (patch) | |
tree | b501846357a5dde1aade081bbb7cb78c6a8de40d | |
parent | cf928bc74248e893fdcf3a82ebeed9a531f75416 (diff) | |
download | evolution-data-server-gnome-3-6-plus-bdb-removal.tar.gz |
Local file backend modified to use sqlite db exclusivelygnome-3-6-plus-bdb-removal
Patch includes a migration routine to migrate data from a previously
installed BDB.
Conflicts:
addressbook/backends/file/e-book-backend-file.c
-rw-r--r-- | addressbook/backends/file/Makefile.am | 2 | ||||
-rw-r--r-- | addressbook/backends/file/e-book-backend-file-migrate-bdb.c | 528 | ||||
-rw-r--r-- | addressbook/backends/file/e-book-backend-file-migrate-bdb.h | 43 | ||||
-rw-r--r-- | addressbook/backends/file/e-book-backend-file.c | 1427 |
4 files changed, 865 insertions, 1135 deletions
diff --git a/addressbook/backends/file/Makefile.am b/addressbook/backends/file/Makefile.am index c52c7e31b..92ca13d4d 100644 --- a/addressbook/backends/file/Makefile.am +++ b/addressbook/backends/file/Makefile.am @@ -16,6 +16,8 @@ libebookbackendfile_la_CPPFLAGS = \ libebookbackendfile_la_SOURCES = \ e-book-backend-file.c \ e-book-backend-file.h \ + e-book-backend-file-migrate-bdb.c \ + e-book-backend-file-migrate-bdb.h \ e-book-backend-file-factory.c libebookbackendfile_la_LIBADD = \ diff --git a/addressbook/backends/file/e-book-backend-file-migrate-bdb.c b/addressbook/backends/file/e-book-backend-file-migrate-bdb.c new file mode 100644 index 000000000..1e971cdf3 --- /dev/null +++ b/addressbook/backends/file/e-book-backend-file-migrate-bdb.c @@ -0,0 +1,528 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* e-book-backend-file-migrate-bdb.c - Migration of old BDB database to the new sqlite DB. + * + * Copyright (C) 2012 Openismus GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Authors: Tristan Van Berkom <tristanvb@openismus.com> + * + * Based on work by Nat Friedman, Chris Toshok and Hans Petter Jansson. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> +#include "db.h" +#include <sys/stat.h> +#include <sys/time.h> + +#include <glib/gstdio.h> +#include <glib/gi18n-lib.h> + +#include <libedata-book/libedata-book.h> + +#include "e-book-backend-file-migrate-bdb.h" + + +#define E_BOOK_BACKEND_FILE_REVISION_NAME "PAS-DB-REVISION" +#define E_BOOK_BACKEND_FILE_VERSION_NAME "PAS-DB-VERSION" +#define E_BOOK_BACKEND_FILE_LAST_BDB_VERSION "0.2" + +#define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL) +#define EDB_ERROR_EX(_code, _msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg) + + +G_LOCK_DEFINE_STATIC (db_env); +static DB_ENV *db_env = NULL; + +#ifdef G_OS_WIN32 +static gboolean db_env_initialized = FALSE; +#endif + +/************************************************************** + * Low level BDB interfacing tools * + **************************************************************/ + +#ifdef G_OS_WIN32 +/* Avoid compiler warning by providing a function with exactly the + * prototype that db_env_set_func_open() wants for the open method. + */ + +static gint +my_open (const gchar *name, + gint oflag, + ...) +{ + gint mode = 0; + + if (oflag & O_CREAT) { + va_list arg; + va_start (arg, oflag); + mode = va_arg (arg, gint); + va_end (arg); + } + + return g_open (name, oflag, mode); +} + +gint +my_rename (const gchar *oldname, + const gchar *newname) +{ + return g_rename (oldname, newname); +} + +gint +my_exists (const gchar *name, + gint *isdirp) +{ + if (!g_file_test (name, G_FILE_TEST_EXISTS)) + return ENOENT; + if (isdirp != NULL) + *isdirp = g_file_test (name, G_FILE_TEST_IS_DIR); + return 0; +} + +gint +my_unlink (const gchar *name) +{ + return g_unlink (name); +} + +#endif + +static void +#if (DB_VERSION_MAJOR > 4) || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) +file_errcall (const DB_ENV *env, + const gchar *buf1, + const gchar *buf2) +#else +file_errcall (const gchar *buf1, + gchar *buf2) +#endif +{ + g_warning ("libdb error: %s", buf2); +} + +static void +db_error_to_gerror (const gint db_error, + GError **perror) +{ + if (db_error && perror && *perror) + g_clear_error (perror); + + switch (db_error) { + case 0: + return; + case DB_NOTFOUND: + g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND)); + return; + case EACCES: + g_propagate_error (perror, EDB_ERROR (PERMISSION_DENIED)); + return; + default: + 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"))); + return; + } +} + +static void +string_to_dbt (const gchar *str, + DBT *dbt) +{ + memset (dbt, 0, sizeof (*dbt)); + dbt->data = (gpointer) str; + dbt->size = strlen (str) + 1; + dbt->flags = DB_DBT_USERMEM; +} + +/************************************************************** + * Include the previous migration routines first * + **************************************************************/ +/* +** versions: +** +** 0.0 just a list of cards +** +** 0.1 same as 0.0, but with the version tag +** +** 0.2 not a real format upgrade, just a hack to fix broken ids caused +** by a bug in early betas, but we only need to convert them if +** the previous version is 0.1, since the bug existed after 0.1 +** came about. +*/ +static gboolean +e_book_backend_file_upgrade_db (DB *db, + gchar *old_version) +{ + gint db_error; + DBT version_name_dbt, version_dbt; + + if (!db) { + g_warning (G_STRLOC ": No DB opened"); + return FALSE; + } + + if (strcmp (old_version, "0.0") + && strcmp (old_version, "0.1")) { + g_warning ("unsupported version '%s' found in PAS backend file\n", + old_version); + return FALSE; + } + + if (!strcmp (old_version, "0.1")) { + /* we just loop through all the cards in the db, + * giving them valid ids if they don't have them */ + DBT id_dbt, vcard_dbt; + DBC *dbc; + gint card_failed = 0; + + db_error = db->cursor (db, NULL, &dbc, 0); + if (db_error != 0) { + g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error)); + return FALSE; + } + + memset (&id_dbt, 0, sizeof (id_dbt)); + memset (&vcard_dbt, 0, sizeof (vcard_dbt)); + + db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST); + + while (db_error == 0) { + if ((id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1 + || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) && + (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1 + || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) { + EContact *contact; + + contact = e_contact_new_from_vcard_with_uid (vcard_dbt.data, id_dbt.data); + + /* the cards we're looking for are + * created with a normal id dbt, but + * with the id field in the vcard set + * to something that doesn't match. + * so, we need to modify the card to + * have the same id as the the dbt. */ + if (strcmp (id_dbt.data, e_contact_get_const (contact, E_CONTACT_UID))) { + gchar *vcard; + + e_contact_set (contact, E_CONTACT_UID, id_dbt.data); + + vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); + string_to_dbt (vcard, &vcard_dbt); + + db_error = db->put (db, NULL, + &id_dbt, &vcard_dbt, 0); + + g_free (vcard); + + if (db_error != 0) + card_failed++; + } + + g_object_unref (contact); + } + + db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT); + } + + dbc->c_close (dbc); + + if (card_failed) { + g_warning ("failed to update %d cards", card_failed); + return FALSE; + } + } + + string_to_dbt (E_BOOK_BACKEND_FILE_VERSION_NAME, &version_name_dbt); + string_to_dbt (E_BOOK_BACKEND_FILE_LAST_BDB_VERSION, &version_dbt); + + db_error = db->put (db, NULL, &version_name_dbt, &version_dbt, 0); + if (db_error == 0) + return TRUE; + else + return FALSE; +} + +static gboolean +e_book_backend_file_maybe_upgrade_db (DB *db) +{ + DBT version_name_dbt, version_dbt; + gint db_error; + gchar *version; + gboolean ret_val = TRUE; + + if (!db) { + g_warning (G_STRLOC ": No DB opened"); + return FALSE; + } + + string_to_dbt (E_BOOK_BACKEND_FILE_VERSION_NAME, &version_name_dbt); + memset (&version_dbt, 0, sizeof (version_dbt)); + version_dbt.flags = DB_DBT_MALLOC; + + db_error = db->get (db, NULL, &version_name_dbt, &version_dbt, 0); + if (db_error == 0) { + /* success */ + version = version_dbt.data; + } + else { + /* key was not in file */ + version = g_strdup ("0.0"); + } + + if (strcmp (version, E_BOOK_BACKEND_FILE_LAST_BDB_VERSION)) + ret_val = e_book_backend_file_upgrade_db (db, version); + + g_free (version); + + return ret_val; +} + + +static gboolean +migrate_bdb_to_sqlitedb (EBookBackendSqliteDB *sqlitedb, + const gchar *sqlite_folder_id, + DB *db, + GError **error) +{ + DBC *dbc; + DBT id_dbt, vcard_dbt; + gint db_error; + gboolean skipped_version = FALSE; + gboolean skipped_revision = FALSE; + GSList *contacts = NULL; + + db_error = db->cursor (db, NULL, &dbc, 0); + + if (db_error != 0) { + g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error)); + db_error_to_gerror (db_error, error); + return FALSE; + } + + memset (&vcard_dbt, 0, sizeof (vcard_dbt)); + memset (&id_dbt, 0, sizeof (id_dbt)); + db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST); + + while (db_error == 0) { + gboolean skip = FALSE; + + /* don't include the version and revision in the list of cards */ + if (!skipped_version && !strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) { + skipped_version = TRUE; + skip = TRUE; + } else if (!skipped_revision && !strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME)) { + skipped_revision = TRUE; + skip = TRUE; + } + + if (!skip) { + EContact *contact = e_contact_new_from_vcard_with_uid (vcard_dbt.data, id_dbt.data); + + contacts = g_slist_prepend (contacts, contact); + } + + db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT); + } + + dbc->c_close (dbc); + + /* Detect error case */ + if (db_error != DB_NOTFOUND) { + g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error)); + e_util_free_object_slist (contacts); + db_error_to_gerror (db_error, error); + return FALSE; + } + + /* Add the contacts to the SQLite (only if there are any contacts to add) */ + if (contacts && + !e_book_backend_sqlitedb_add_contacts (sqlitedb, + sqlite_folder_id, + contacts, FALSE, error)) { + if (error && *error) { + g_warning ("Failed to add contacts to sqlite db: %s", (*error)->message); + } else { + g_warning ("Failed to add contacts to sqlite db: unknown error"); + } + + e_util_free_object_slist (contacts); + return FALSE; + } + + e_util_free_object_slist (contacts); + + if (!e_book_backend_sqlitedb_set_is_populated (sqlitedb, sqlite_folder_id, TRUE, error)) { + if (error && *error) { + g_warning ("Failed to set the sqlitedb populated flag: %s", (*error)->message); + } else { + g_warning ("Failed to set the sqlitedb populated flag: unknown error"); + } + return FALSE; + } + + return TRUE; +} + +gboolean +e_book_backend_file_migrate_bdb (EBookBackendSqliteDB *sqlitedb, + const gchar *sqlite_folder_id, + const gchar *dirname, + const gchar *filename, + GError **error) +{ + DB *db = NULL; + gint db_error; + gboolean status = FALSE; + + + G_LOCK (db_env); + +#ifdef G_OS_WIN32 + if (!db_env_initialized) { + /* Use the gstdio wrappers to open, check, rename and unlink + * files from libdb. + */ + db_env_set_func_open (my_open); + db_env_set_func_close (close); + db_env_set_func_exists (my_exists); + db_env_set_func_rename (my_rename); + db_env_set_func_unlink (my_unlink); + + db_env_initialized = TRUE; + } +#endif + + db_error = e_db3_utils_maybe_recover (filename); + if (db_error != 0) { + g_warning ("db recovery failed with %s", db_strerror (db_error)); + db_error_to_gerror (db_error, error); + goto unlock_env; + } + + db_error = db_env_create (&db_env, 0); + if (db_error != 0) { + g_warning ("db_env_create failed with %s", db_strerror (db_error)); + db_error_to_gerror (db_error, error); + goto unlock_env; + } + + db_env->set_errcall (db_env, file_errcall); + + /* Set the allocation routines to the non-aborting GLib functions */ + db_env->set_alloc (db_env, (gpointer (*)(gsize)) g_try_malloc, + (gpointer (*)(gpointer , gsize)) g_try_realloc, + g_free); + + /* + * DB_INIT_TXN enables transaction support. It requires DB_INIT_LOCK to + * initialize the locking subsystem and DB_INIT_LOG for the logging + * subsystem. + * + * DB_INIT_MPOOL enables the in-memory cache. + * + * Note that historically we needed either DB_INIT_CDB or DB_INIT_LOCK, because we + * had multiple threads reading and writing concurrently without + * any locking above libdb. DB_INIT_LOCK was used because + * DB_INIT_TXN conflicts with DB_INIT_CDB. + * + * Currently we leave this in place because it is known to work. + */ + db_error = (*db_env->open) (db_env, dirname, DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG | DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_THREAD, 0); + if (db_error != 0) { + g_warning ("db_env_open failed with %s", db_strerror (db_error)); + db_error_to_gerror (db_error, error); + goto close_env; + } + + db_error = db_create (&db, db_env, 0); + if (db_error != 0) { + g_warning ("db_create failed with %s", db_strerror (db_error)); + db_error_to_gerror (db_error, error); + goto close_env; + } + + /* First try opening the DB... we want write permission because it's possible we need to update + * an out of date DB before actually migrating it to the SQLite DB */ + db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD | DB_AUTO_COMMIT, 0666); + + /* Was an old version, lets upgrade the format ... */ + if (db_error == DB_OLD_VERSION) { + db_error = e_db3_utils_upgrade_format (filename); + + if (db_error != 0) { + g_warning ("db format upgrade failed with %s", db_strerror (db_error)); + db_error_to_gerror (db_error, error); + goto close_env; + } + + db->close (db, 0); + db_error = db_create (&db, db_env, 0); + if (db_error != 0) { + g_warning ("db_create failed with %s", db_strerror (db_error)); + db_error_to_gerror (db_error, error); + goto close_env; + } + + /* Try again after the upgrade... */ + db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD | DB_AUTO_COMMIT, 0666); + + if (db_error != 0) { + /* Another error, clean up and get out of here */ + goto close_db; + } + } + + /* Unable to open the DB for some reason */ + if (db_error != 0) { + db_error_to_gerror (db_error, error); + goto close_env; + } + + /* Try another upgrade */ + if (!e_book_backend_file_maybe_upgrade_db (db)) { + g_propagate_error (error, EDB_ERROR_EX (OTHER_ERROR, "e_book_backend_file_maybe_upgrade_db failed")); + goto close_db; + } + + /* Now we have our old BDB up and running and migrated to the latest known BDB version, + * lets go ahead and now migrate it to the sqlite DB + */ + if (migrate_bdb_to_sqlitedb (sqlitedb, sqlite_folder_id, db, error)) + status = TRUE; + + close_db: + db->close (db, 0); + db = NULL; + + close_env: + db_env->close (db_env, 0); + db_env = NULL; + + unlock_env: + + G_UNLOCK (db_env); + + return status; +} diff --git a/addressbook/backends/file/e-book-backend-file-migrate-bdb.h b/addressbook/backends/file/e-book-backend-file-migrate-bdb.h new file mode 100644 index 000000000..e108d1364 --- /dev/null +++ b/addressbook/backends/file/e-book-backend-file-migrate-bdb.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* e-book-backend-file-migrate-bdb.c - Migration of old BDB database to the new sqlite DB. + * + * Copyright (C) 2012 Openismus GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Authors: Tristan Van Berkom <tristanvb@openismus.com> + * + * Based on work by Nat Friedman, Chris Toshok and Hans Petter Jansson. + */ + +#ifndef E_BOOK_BACKEND_MIGRATE_BDB_H +#define E_BOOK_BACKEND_MIGRATE_BDB_H + +G_BEGIN_DECLS + +gboolean e_book_backend_file_migrate_bdb (EBookBackendSqliteDB *sqlitedb, + const gchar *sqlite_folder_id, + const gchar *dirname, + const gchar *filename, + GError **error); + + + +G_END_DECLS + + + +#endif /* E_BOOK_BACKEND_MIGRATE_BDB_H */ diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c index b0213f95e..6327853b6 100644 --- a/addressbook/backends/file/e-book-backend-file.c +++ b/addressbook/backends/file/e-book-backend-file.c @@ -21,6 +21,7 @@ * Authors: Nat Friedman <nat@novell.com> * Chris Toshok <toshok@ximian.com> * Hans Petter Jansson <hpj@novell.com> + * Tristan Van Berkom <tristanvb@openismus.com> */ #include <config.h> @@ -32,7 +33,6 @@ #include <fcntl.h> #include <time.h> #include <errno.h> -#include "db.h" #include <sys/stat.h> #include <sys/time.h> @@ -40,14 +40,13 @@ #include <glib/gi18n-lib.h> #include "e-book-backend-file.h" +#include "e-book-backend-file-migrate-bdb.h" #define E_BOOK_BACKEND_FILE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_BOOK_BACKEND_FILE, EBookBackendFilePrivate)) -#define d(x) - -#define CHANGES_DB_SUFFIX ".changes.db" +#define d(x) x #define E_BOOK_BACKEND_FILE_VERSION_NAME "PAS-DB-VERSION" #define E_BOOK_BACKEND_FILE_VERSION "0.2" @@ -60,25 +59,26 @@ #define SQLITEDB_FOLDER_ID "folder_id" #define SQLITEDB_FOLDER_NAME "folder" -#define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL) +#define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL) #define EDB_ERROR_EX(_code, _msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg) -#define EDB_NOT_OPENED_ERROR EDB_ERROR(NOT_OPENED) +#define EDB_NOT_OPENED_ERROR EDB_ERROR(NOT_OPENED) + G_DEFINE_TYPE (EBookBackendFile, e_book_backend_file, E_TYPE_BOOK_BACKEND_SYNC) struct _EBookBackendFilePrivate { - gchar *dirname; - gchar *filename; gchar *photo_dirname; gchar *revision; gint rev_counter; GRecMutex revision_mutex; - DB *file_db; - DB_ENV *env; EBookBackendSqliteDB *sqlitedb; }; + +/**************************************************************** + * File Management helper APIs * + ****************************************************************/ typedef enum { GET_PATH_DB_DIR, GET_PATH_PHOTO_DIR @@ -90,45 +90,6 @@ typedef enum { STATUS_ERROR } PhotoModifiedStatus; -G_LOCK_DEFINE_STATIC (db_environments); -static GHashTable *db_environments = NULL; -typedef struct { - gint ref_count; - DB_ENV *env; -} global_env; - -static void -db_error_to_gerror (const gint db_error, - GError **perror) -{ - if (db_error && perror && *perror) - g_clear_error (perror); - - switch (db_error) { - case 0: - return; - case DB_NOTFOUND: - g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND)); - return; - case EACCES: - g_propagate_error (perror, EDB_ERROR (PERMISSION_DENIED)); - return; - default: - 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"))); - return; - } -} - -static void -string_to_dbt (const gchar *str, - DBT *dbt) -{ - memset (dbt, 0, sizeof (*dbt)); - dbt->data = (gpointer) str; - dbt->size = strlen (str) + 1; - dbt->flags = DB_DBT_USERMEM; -} - static gboolean remove_file (const gchar *filename, GError **error) @@ -171,59 +132,6 @@ create_directory (const gchar *dirname, return TRUE; } -static EContact * -create_contact (const gchar *uid, - const gchar *vcard) -{ - return e_contact_new_from_vcard_with_uid (vcard, uid); -} - -static gchar * -load_vcard (EBookBackendFile *bf, - DB_TXN *txn, - const gchar *uid, - GError **error) -{ - DB *db = bf->priv->file_db; - DBT id_dbt, vcard_dbt; - gchar *vcard; - gint db_error; - - /* Get the old contact from the db and compare the photo fields */ - string_to_dbt (uid, &id_dbt); - memset (&vcard_dbt, 0, sizeof (vcard_dbt)); - vcard_dbt.flags = DB_DBT_MALLOC; - - db_error = db->get (db, txn, &id_dbt, &vcard_dbt, 0); - - if (db_error == 0) { - vcard = vcard_dbt.data; - } else { - g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error)); - g_propagate_error (error, EDB_ERROR (CONTACT_NOT_FOUND)); - return NULL; - } - - return vcard; -} - -static EContact * -load_contact (EBookBackendFile *bf, - DB_TXN *txn, - const gchar *uid, - GError **error) -{ - EContact *contact = NULL; - gchar *vcard; - - if ((vcard = load_vcard (bf, txn, uid, error)) != NULL) { - contact = create_contact (uid, vcard); - g_free (vcard); - } - - return contact; -} - static gchar * check_remove_uri_for_field (EContact *old_contact, EContact *new_contact, @@ -650,7 +558,6 @@ static PhotoModifiedStatus maybe_transform_vcard_for_photo (EBookBackendFile *bf, EContact *old_contact, EContact *contact, - gchar **vcard_ret, GError **error) { PhotoModifiedStatus status; @@ -668,96 +575,16 @@ maybe_transform_vcard_for_photo (EBookBackendFile *bf, modified = modified || (status == STATUS_MODIFIED); } - if (status != STATUS_ERROR) { - if (modified) { - if (vcard_ret) - *vcard_ret = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - status = STATUS_MODIFIED; - } - } + if (status != STATUS_ERROR && modified) + status = STATUS_MODIFIED; return status; } -static gboolean -build_sqlitedb (EBookBackendFilePrivate *bfpriv) -{ - DB *db = bfpriv->file_db; - DBC *dbc; - gint db_error; - DBT id_dbt, vcard_dbt; - GSList *contacts = NULL; - GError *error = NULL; - gboolean skipped_version = FALSE; - gboolean skipped_revision = FALSE; - - if (!db) { - g_warning (G_STRLOC ": Not opened yet"); - return FALSE; - } - - db_error = db->cursor (db, NULL, &dbc, 0); - - if (db_error != 0) { - g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error)); - return FALSE; - } - - memset (&vcard_dbt, 0, sizeof (vcard_dbt)); - memset (&id_dbt, 0, sizeof (id_dbt)); - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST); - - while (db_error == 0) { - gboolean skip = FALSE; - - /* don't include the version and revision in the list of cards */ - if (!skipped_version && !strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) { - skipped_version = TRUE; - skip = TRUE; - } else if (!skipped_revision && !strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME)) { - skipped_revision = TRUE; - skip = TRUE; - } - - if (!skip) { - EContact *contact = create_contact (id_dbt.data, vcard_dbt.data); - - contacts = g_slist_prepend (contacts, contact); - } - - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT); - - } - - dbc->c_close (dbc); - - /* Detect error case */ - if (db_error != DB_NOTFOUND) { - g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error)); - e_util_free_object_slist (contacts); - return FALSE; - } - - if (contacts && !e_book_backend_sqlitedb_add_contacts (bfpriv->sqlitedb, - SQLITEDB_FOLDER_ID, - contacts, FALSE, &error)) { - g_warning ("Failed to build contact summary: %s", error ? error->message : "Unknown error"); - g_clear_error (&error); - e_util_free_object_slist (contacts); - return FALSE; - } - - e_util_free_object_slist (contacts); - - if (!e_book_backend_sqlitedb_set_is_populated (bfpriv->sqlitedb, SQLITEDB_FOLDER_ID, TRUE, &error)) { - g_warning ("Failed to set the sqlitedb populated flag: %s", error->message); - g_error_free (error); - return FALSE; - } - - return TRUE; -} +/**************************************************************** + * Global Revisioning Tools * + ****************************************************************/ static gchar * e_book_backend_file_create_unique_id (void) { @@ -791,23 +618,21 @@ e_book_backend_file_new_revision (EBookBackendFile *bf) static void e_book_backend_file_bump_revision (EBookBackendFile *bf) { - DB *db = bf->priv->file_db; - DBT revision_name_dbt, revision_dbt; - gint db_error; + GError *error = NULL; g_rec_mutex_lock (&bf->priv->revision_mutex); g_free (bf->priv->revision); bf->priv->revision = e_book_backend_file_new_revision (bf); - string_to_dbt (E_BOOK_BACKEND_FILE_REVISION_NAME, &revision_name_dbt); - string_to_dbt (bf->priv->revision, &revision_dbt); - db_error = db->put (db, NULL, &revision_name_dbt, &revision_dbt, 0); - - if (db_error != 0) - g_warning ( - G_STRLOC ": db->put failed while bumping the revision string: %s", - db_strerror (db_error)); + if (!e_book_backend_sqlitedb_set_revision (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, + bf->priv->revision, + &error)) { + g_warning (G_STRLOC ": Error setting database revision: %s", + error->message); + g_error_free (error); + } e_book_backend_notify_property_changed (E_BOOK_BACKEND (bf), BOOK_BACKEND_PROPERTY_REVISION, @@ -819,24 +644,19 @@ e_book_backend_file_bump_revision (EBookBackendFile *bf) static void e_book_backend_file_load_revision (EBookBackendFile *bf) { - DB *db = bf->priv->file_db; - DBT version_name_dbt, version_dbt; - gint db_error; + GError *error = NULL; g_rec_mutex_lock (&bf->priv->revision_mutex); - string_to_dbt (E_BOOK_BACKEND_FILE_REVISION_NAME, &version_name_dbt); - memset (&version_dbt, 0, sizeof (version_dbt)); - version_dbt.flags = DB_DBT_MALLOC; - - db_error = db->get (db, NULL, &version_name_dbt, &version_dbt, 0); - if (db_error == 0) { - /* success */ - bf->priv->revision = version_dbt.data; - } - else { - /* key was not in file */ - bf->priv->revision = e_book_backend_file_new_revision (bf); + if (!e_book_backend_sqlitedb_get_revision (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, + &bf->priv->revision, + &error)) { + g_warning (G_STRLOC ": Error loading database revision: %s", + error->message); + g_error_free (error); + } else if (bf->priv->revision == NULL) { + e_book_backend_file_bump_revision (bf); } g_rec_mutex_unlock (&bf->priv->revision_mutex); @@ -857,6 +677,11 @@ set_revision (EContact *contact) } + +/**************************************************************** + * Main Backend Implementation * + ****************************************************************/ + /** * This method will return TRUE if all the contacts were properly created. * If at least one contact fails, the method will return FALSE, all @@ -865,38 +690,25 @@ set_revision (EContact *contact) */ static gboolean do_create (EBookBackendFile *bf, - const GSList *vcards_req, - GSList **contacts, - GError **perror) + const GSList *vcards_req, + GSList **contacts, + GError **perror) { - DB *db = bf->priv->file_db; - DB_ENV *env = bf->priv->env; - DB_TXN *txn = NULL; GSList *slist = NULL; const GSList *l; - gint db_error = 0; PhotoModifiedStatus status = STATUS_NORMAL; + GError *local_error = NULL; g_assert (bf); g_assert (vcards_req); - if (!db) { + if (!bf || !bf->priv || !bf->priv->sqlitedb) { g_propagate_error (perror, EDB_NOT_OPENED_ERROR); return FALSE; } - /* Begin transaction */ - db_error = env->txn_begin (env, NULL, &txn, 0); - if (db_error != 0) { - g_warning (G_STRLOC ": env->txn_begin failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - return FALSE; - } - for (l = vcards_req; l != NULL; l = l->next) { - DBT id_dbt, vcard_dbt; gchar *id; - gchar *vcard; const gchar *rev; const gchar *vcard_req; EContact *contact; @@ -910,37 +722,17 @@ do_create (EBookBackendFile *bf, if (!(rev && *rev)) set_revision (contact); - status = maybe_transform_vcard_for_photo (bf, NULL, contact, NULL, perror); - - if (status != STATUS_ERROR) { - vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - string_to_dbt (id, &id_dbt); - string_to_dbt (vcard, &vcard_dbt); - - db_error = db->put (db, txn, &id_dbt, &vcard_dbt, 0); - - g_free (vcard); - } + status = maybe_transform_vcard_for_photo (bf, NULL, contact, perror); g_free (id); - if (db_error == 0 && status != STATUS_ERROR) { - /* Contact was added successfully, add it to the return list */ - if (contacts != NULL) - slist = g_slist_prepend (slist, contact); - } else if (db_error != 0) { - /* Contact could not be added */ - g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error)); - g_object_unref (contact); - db_error_to_gerror (db_error, perror); + if (status != STATUS_ERROR) { - /* Abort as soon as an error occurs */ - break; - } else if (status == STATUS_ERROR) { - /* Contact could not be added */ - g_warning ( - G_STRLOC ": db->put failed with %s", + /* Contact was added successfully, add it to the local list */ + slist = g_slist_prepend (slist, contact); + } else { + /* Contact could not be transformed */ + g_warning (G_STRLOC ": Error transforming vcard with image data %s", (perror && *perror) ? (*perror)->message : "Unknown error transforming vcard"); g_object_unref (contact); @@ -950,36 +742,31 @@ do_create (EBookBackendFile *bf, } } - if (db_error == 0 && status != STATUS_ERROR) { - /* Commit transaction */ - db_error = txn->commit (txn, 0); - if (db_error == 0) { - /* Flush cache information to disk */ - if (db->sync (db, 0) != 0) { - g_warning ("db->sync failed with %s", db_strerror (db_error)); - } - } else { - g_warning (G_STRLOC ": txn->commit failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - } + if (status != STATUS_ERROR) { - } else { - /* Rollback transaction */ - txn->abort (txn); - } + if (!e_book_backend_sqlitedb_add_contacts (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, + slist, FALSE, + &local_error)) { + + g_warning ("Failed to add contacts: %s", local_error->message); + g_propagate_error (perror, local_error); - if (db_error == 0 && status != STATUS_ERROR) { - if (contacts != NULL) - *contacts = g_slist_reverse (slist); + status = STATUS_ERROR; + } + } - return TRUE; + if (status != STATUS_ERROR && contacts != NULL) { + *contacts = g_slist_reverse (slist); } else { - if (contacts != NULL) + + if (contacts) *contacts = NULL; e_util_free_object_slist (slist); - return FALSE; } + + return (status != STATUS_ERROR); } static void @@ -993,15 +780,6 @@ e_book_backend_file_create_contacts (EBookBackendSync *backend, EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend); if (do_create (bf, vcards, added_contacts, perror)) { - GError *error = NULL; - - if (!e_book_backend_sqlitedb_add_contacts (bf->priv->sqlitedb, - SQLITEDB_FOLDER_ID, - *added_contacts, FALSE, &error)) { - g_warning ("Failed to add contacts to summary: %s", error->message); - g_error_free (error); - } - e_book_backend_file_bump_revision (bf); } } @@ -1015,70 +793,42 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend, GError **perror) { EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend); - DB *db = bf->priv->file_db; - DB_ENV *env = bf->priv->env; - DB_TXN *txn = NULL; - gint db_error; GSList *removed_ids = NULL, *removed_contacts = NULL; + GError *local_error = NULL; + gboolean delete_failed = FALSE; const GSList *l; - if (!db) { - g_propagate_error (perror, EDB_NOT_OPENED_ERROR); - return; - } - - /* Begin transaction */ - db_error = env->txn_begin (env, NULL, &txn, 0); - if (db_error != 0) { - g_warning (G_STRLOC ": env->txn_begin failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - return; - } - for (l = id_list; l != NULL; l = l->next) { const gchar *id; EContact *contact; - DBT id_dbt; id = l->data; - contact = load_contact (bf, txn, id, NULL); - if (contact) - removed_contacts = g_slist_prepend (removed_contacts, contact); + /* First load the EContacts which need to be removed, we might delete some + * photos from disk because of this... + * + * Note: sqlitedb backend can probably make this faster by executing a + * single query to fetch a list of contacts for a list of ids, the + * current method makes a query for each UID. + */ + contact = e_book_backend_sqlitedb_get_contact (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, id, + NULL, NULL, &local_error); - /* Then go on to delete from the db */ - string_to_dbt (id, &id_dbt); + if (contact) { + removed_ids = g_slist_prepend (removed_ids, g_strdup (id)); + removed_contacts = g_slist_prepend (removed_contacts, contact); + } else { + g_warning ("Failed to fetch contact to be removed: %s", local_error->message); + g_propagate_error (perror, local_error); - db_error = db->del (db, txn, &id_dbt, 0); - if (db_error != 0) { - g_warning (G_STRLOC ": db->del failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - /* Abort as soon as a removal fails */ + /* Abort as soon as missing contact is to be deleted */ + delete_failed = TRUE; break; } - - removed_ids = g_slist_prepend (removed_ids, g_strdup (id)); } - if (db_error == 0) { - /* Commit transaction */ - db_error = txn->commit (txn, 0); - if (db_error == 0) { - /* Flush cache information to disk */ - if (db->sync (db, 0) != 0) { - g_warning ("db->sync failed with %s", db_strerror (db_error)); - } - } else { - g_warning (G_STRLOC ": txn->commit failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - } - } else { - /* Rollback transaction */ - txn->abort (txn); - } - - if (db_error == 0) { - GError *error = NULL; + if (!delete_failed) { /* Delete URI associated to those contacts */ for (l = removed_contacts; l; l = l->next) { @@ -1088,9 +838,9 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend, /* Remove from summary as well */ if (!e_book_backend_sqlitedb_remove_contacts (bf->priv->sqlitedb, SQLITEDB_FOLDER_ID, - removed_ids, &error)) { - g_warning ("Failed to remove contacts from the summary: %s", error->message); - g_error_free (error); + removed_ids, &local_error)) { + g_warning ("Failed to remove contacts: %s", local_error->message); + g_propagate_error (perror, local_error); } *ids = removed_ids; @@ -1100,7 +850,7 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend, } e_book_backend_file_bump_revision (bf); - g_slist_free_full (removed_contacts, g_object_unref); + e_util_free_object_slist (removed_contacts); } static void @@ -1112,46 +862,39 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend, GError **perror) { EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend); - DB *db = bf->priv->file_db; - DB_ENV *env = bf->priv->env; - DB_TXN *txn = NULL; - gint db_error; const GSList *lold, *l; GSList *old_contacts = NULL, *modified_contacts = NULL; GSList *ids = NULL; + GError *local_error = NULL; PhotoModifiedStatus status = STATUS_NORMAL; - if (!db) { + if (!bf || !bf->priv || !bf->priv->sqlitedb) { g_propagate_error (perror, EDB_NOT_OPENED_ERROR); return; } - /* Begin transaction */ - db_error = env->txn_begin (env, NULL, &txn, 0); - if (db_error != 0) { - g_warning (G_STRLOC ": env->txn_begin failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - return; - } - for (l = vcards; l != NULL; l = l->next) { - gchar *id, *lookup_id; - gchar *vcard_with_rev; - DBT id_dbt, vcard_dbt; + gchar *id; EContact *contact, *old_contact; contact = e_contact_new_from_vcard (l->data); id = e_contact_get (contact, E_CONTACT_UID); if (id == NULL) { + status = STATUS_ERROR; + g_propagate_error (perror, EDB_ERROR_EX (OTHER_ERROR, _("No UID in the contact"))); g_object_unref (contact); break; } - old_contact = load_contact (bf, txn, id, perror); + old_contact = e_book_backend_sqlitedb_get_contact (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, id, + NULL, NULL, &local_error); if (!old_contact) { - g_warning (G_STRLOC ": Failed to load contact %s", id); + g_warning (G_STRLOC ": Failed to load contact %s: %s", id, local_error->message); + g_propagate_error (perror, local_error); + status = STATUS_ERROR; g_free (id); @@ -1161,11 +904,10 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend, old_contacts = g_slist_prepend (old_contacts, old_contact); /* Transform incomming photo blobs to uris before storing this to the DB */ - status = maybe_transform_vcard_for_photo (bf, old_contact, contact, NULL, perror); + status = maybe_transform_vcard_for_photo (bf, old_contact, contact, &local_error); if (status == STATUS_ERROR) { - g_warning ( - G_STRLOC ": Error transforming contact %s: %s", - id, (perror && *perror) ? (*perror)->message : "Unknown Error"); + g_warning (G_STRLOC ": Error transforming contact %s: %s", id, local_error->message); + g_propagate_error (perror, local_error); g_free (id); g_object_unref (old_contact); @@ -1175,63 +917,17 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend, /* update the revision (modified time of contact) */ set_revision (contact); - vcard_with_rev = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - /* This is disgusting, but for a time cards were added with - * ID's that are no longer used (they contained both the uri - * and the id.) If we recognize it as a uri (file:///...) trim - * off everything before the last '/', and use that as the - * id.*/ - if (!strncmp (id, "file:///", strlen ("file:///"))) { - lookup_id = strrchr (id, '/') + 1; - } - else - lookup_id = id; - - string_to_dbt (lookup_id, &id_dbt); - string_to_dbt (vcard_with_rev, &vcard_dbt); - - db_error = db->put (db, txn, &id_dbt, &vcard_dbt, 0); - g_free (vcard_with_rev); - - if (db_error != 0) { - g_warning (G_STRLOC ": db->put failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - - /* Abort as soon as a modification fails */ - g_free (id); - g_object_unref (contact); - break; - } modified_contacts = g_slist_prepend (modified_contacts, contact); - ids = g_slist_prepend (ids, id); + ids = g_slist_prepend (ids, id); } - if (db_error == 0 && status != STATUS_ERROR) { - /* Commit transaction */ - db_error = txn->commit (txn, 0); - if (db_error == 0) { - /* Flush cache information to disk */ - if (db->sync (db, 0) != 0) { - g_warning ("db->sync failed with %s", db_strerror (db_error)); - } - } else { - g_warning (G_STRLOC ": txn->commit failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - } - } else { - /* Rollback transaction */ - txn->abort (txn); - } - - if (db_error == 0 && status != STATUS_ERROR) { - GError *error = NULL; + if (status != STATUS_ERROR) { /* Delete old photo file uris if need be (this will compare the new contact * with the current copy in the BDB to extract the uris to delete) */ lold = old_contacts; - l = modified_contacts; + l = modified_contacts; while (lold && l) { maybe_delete_unused_uris (bf, E_CONTACT (lold->data), E_CONTACT (l->data)); lold = lold->next; @@ -1239,18 +935,17 @@ e_book_backend_file_modify_contacts (EBookBackendSync *backend, } /* Update summary as well */ - if (!e_book_backend_sqlitedb_remove_contacts (bf->priv->sqlitedb, - SQLITEDB_FOLDER_ID, - ids, &error)) { - g_warning ("Failed to remove contacts from the summary: %s", error->message); - g_error_free (error); - } else if (!e_book_backend_sqlitedb_add_contacts (bf->priv->sqlitedb, - SQLITEDB_FOLDER_ID, - modified_contacts, FALSE, &error)) { - g_warning ("Failed to add contacts to summary: %s", error->message); - g_error_free (error); + if (!e_book_backend_sqlitedb_add_contacts (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, + modified_contacts, FALSE, &local_error)) { + g_warning ("Failed to modify contacts: %s", local_error->message); + g_propagate_error (perror, local_error); + + status = STATUS_ERROR; } + } + if (status != STATUS_ERROR) { *contacts = g_slist_reverse (modified_contacts); } else { *contacts = NULL; @@ -1273,12 +968,14 @@ e_book_backend_file_get_contact (EBookBackendSync *backend, { EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend); - if (!bf || !bf->priv || !bf->priv->file_db) { + if (!bf || !bf->priv || !bf->priv->sqlitedb) { g_propagate_error (perror, EDB_NOT_OPENED_ERROR); return; } - *vcard = load_vcard (bf, NULL, id, perror); + *vcard = e_book_backend_sqlitedb_get_vcard_string (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, id, + NULL, NULL, perror); } static void @@ -1290,117 +987,38 @@ e_book_backend_file_get_contact_list (EBookBackendSync *backend, GError **perror) { EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend); - DB *db = bf->priv->file_db; - DBC *dbc; - gint db_error; - DBT id_dbt, vcard_dbt; - EBookBackendSExp *card_sexp = NULL; - gboolean search_needed; - const gchar *search = query; - GSList *contact_list = NULL, *l; - GSList *summary_list = NULL; - gboolean searched_summary = FALSE; - gboolean with_all_required_fields = FALSE; - - d (printf ("e_book_backend_file_get_contact_list (%s)\n", search)); - - if (!db) { + GSList *contact_list = NULL, *l; + GSList *summary_list = NULL; + GError *local_error = NULL; + + d (printf ("e_book_backend_file_get_contact_list (%s)\n", query)); + + if (!bf || !bf->priv || !bf->priv->sqlitedb) { g_propagate_error (perror, EDB_NOT_OPENED_ERROR); return; } summary_list = e_book_backend_sqlitedb_search ( - bf->priv->sqlitedb, - SQLITEDB_FOLDER_ID, - search, NULL, - &searched_summary, - &with_all_required_fields, NULL); + bf->priv->sqlitedb, SQLITEDB_FOLDER_ID, + query, NULL, + NULL, NULL, &local_error); if (summary_list) { for (l = summary_list; l; l = l->next) { EbSdbSearchData *data = l->data; - if (with_all_required_fields) { - contact_list = g_slist_prepend (contact_list, data->vcard); - data->vcard = NULL; - } else { - /* In this case the sqlitedb helped us with the query, but - * the return information is incomplete so we need to load it up. - */ - gchar *vcard; - - vcard = load_vcard (bf, NULL, data->uid, perror); - - /* Break out on the first BDB error */ - if (!vcard) - break; - - contact_list = g_slist_prepend (contact_list, vcard); - } + /* Steal the memory directly from the returned query */ + contact_list = g_slist_prepend (contact_list, data->vcard); + data->vcard = NULL; } g_slist_foreach (summary_list, (GFunc) e_book_backend_sqlitedb_search_data_free, NULL); g_slist_free (summary_list); - } else { - search_needed = TRUE; - if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")")) - search_needed = FALSE; - - card_sexp = e_book_backend_sexp_new (search); - if (!card_sexp) { - g_propagate_error (perror, EDB_ERROR (INVALID_QUERY)); - return; - } - - db_error = db->cursor (db, NULL, &dbc, 0); - - if (db_error != 0) { - g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error)); - /* XXX this needs to be some CouldNotOpen error */ - db_error_to_gerror (db_error, perror); - return; - } - - memset (&vcard_dbt, 0, sizeof (vcard_dbt)); - vcard_dbt.flags = DB_DBT_MALLOC; - memset (&id_dbt, 0, sizeof (id_dbt)); - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST); - - while (db_error == 0) { - - /* don't include the version or revision in the list of cards */ - if ((id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1 - || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) && - (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1 - || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) { - - if ((!search_needed) || (card_sexp != NULL && e_book_backend_sexp_match_vcard (card_sexp, vcard_dbt.data))) { - contact_list = g_slist_prepend (contact_list, vcard_dbt.data); - } else { - free (vcard_dbt.data); - } - } else { - free (vcard_dbt.data); - } - - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT); - - } - g_object_unref (card_sexp); - - if (db_error == DB_NOTFOUND) { - /* Success */ - } else { - g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - } - - db_error = dbc->c_close (dbc); - if (db_error != 0) { - g_warning (G_STRLOC ": dbc->c_close failed with %s", db_strerror (db_error)); - } + } else if (local_error != NULL) { + g_warning ("Failed to fetch contacts: %s", local_error->message); + g_propagate_error (perror, local_error); } *contacts = contact_list; @@ -1415,19 +1033,12 @@ e_book_backend_file_get_contact_list_uids (EBookBackendSync *backend, GError **perror) { EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend); - DB *db = bf->priv->file_db; - DBC *dbc; - gint db_error; - DBT id_dbt, vcard_dbt; - EBookBackendSExp *card_sexp = NULL; - gboolean search_needed; - const gchar *search = query; - GSList *uids = NULL; - gboolean searched = FALSE; - - d (printf ("e_book_backend_file_get_contact_list (%s)\n", search)); - - if (!db) { + GSList *uids = NULL; + GError *local_error = NULL; + + d (printf ("e_book_backend_file_get_contact_list (%s)\n", query)); + + if (!bf || !bf->priv || !bf->priv->sqlitedb) { g_propagate_error (perror, EDB_NOT_OPENED_ERROR); return; } @@ -1435,64 +1046,11 @@ e_book_backend_file_get_contact_list_uids (EBookBackendSync *backend, uids = e_book_backend_sqlitedb_search_uids ( bf->priv->sqlitedb, SQLITEDB_FOLDER_ID, - search, &searched, NULL); + query, NULL, &local_error); - if (!searched) { - search_needed = TRUE; - if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")")) - search_needed = FALSE; - - card_sexp = e_book_backend_sexp_new (search); - if (!card_sexp) { - g_propagate_error (perror, EDB_ERROR (INVALID_QUERY)); - return; - } - - db_error = db->cursor (db, NULL, &dbc, 0); - - if (db_error != 0) { - g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error)); - /* XXX this needs to be some CouldNotOpen error */ - db_error_to_gerror (db_error, perror); - return; - } - - memset (&vcard_dbt, 0, sizeof (vcard_dbt)); - vcard_dbt.flags = DB_DBT_MALLOC; - memset (&id_dbt, 0, sizeof (id_dbt)); - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST); - - while (db_error == 0) { - - /* don't include the version or revision in the list of cards */ - if ((id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1 - || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) && - (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1 - || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) { - - if ((!search_needed) || (card_sexp != NULL && e_book_backend_sexp_match_vcard (card_sexp, vcard_dbt.data))) { - uids = g_slist_prepend (uids, g_strdup (id_dbt.data)); - } - } - - g_free (vcard_dbt.data); - - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT); - - } - g_object_unref (card_sexp); - - if (db_error == DB_NOTFOUND) { - /* Success */ - } else { - g_warning (G_STRLOC ": dbc->c_get failed with %s", db_strerror (db_error)); - db_error_to_gerror (db_error, perror); - } - - db_error = dbc->c_close (dbc); - if (db_error != 0) { - g_warning (G_STRLOC ": dbc->c_close failed with %s", db_strerror (db_error)); - } + if (uids == NULL && local_error != NULL) { + g_warning ("Failed to fetch contact ids: %s", local_error->message); + g_propagate_error (perror, local_error); } *contacts_uids = g_slist_reverse (uids); @@ -1557,14 +1115,9 @@ book_view_thread (gpointer data) FileBackendSearchClosure *closure; EBookBackendFile *bf; const gchar *query; - DB *db; - DBT id_dbt, vcard_dbt; - gint db_error; - gboolean allcontacts; GSList *summary_list, *l; GHashTable *fields_of_interest; - gboolean searched = FALSE; - gboolean with_all_required_fields = FALSE; + GError *local_error = NULL; g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (data), NULL); @@ -1581,12 +1134,10 @@ book_view_thread (gpointer data) /* ref the book view because it'll be removed and unrefed * when/if it's stopped */ e_data_book_view_ref (book_view); - - db = bf->priv->file_db; query = e_data_book_view_get_card_query (book_view); fields_of_interest = e_data_book_view_get_fields_of_interest (book_view); - if (!db) { + if (!bf->priv->sqlitedb) { e_data_book_view_notify_complete (book_view, EDB_NOT_OPENED_ERROR); e_data_book_view_unref (book_view); return NULL; @@ -1594,10 +1145,8 @@ book_view_thread (gpointer data) if ( !strcmp (query, "(contains \"x-evolution-any-field\" \"\")")) { e_data_book_view_notify_progress (book_view, -1, _("Loading...")); - allcontacts = TRUE; } else { e_data_book_view_notify_progress (book_view, -1, _("Searching...")); - allcontacts = FALSE; } d (printf ("signalling parent thread\n")); @@ -1607,84 +1156,29 @@ book_view_thread (gpointer data) bf->priv->sqlitedb, SQLITEDB_FOLDER_ID, query, fields_of_interest, - &searched, &with_all_required_fields, NULL); - - if (searched) { - - for (l = summary_list; l; l = l->next) { - EbSdbSearchData *data = l->data; - gchar *vcard = NULL; + NULL, NULL, &local_error); - if (with_all_required_fields) { - vcard = data->vcard; - data->vcard = NULL; - } else { - GError *error = NULL; - - /* The sqlitedb summary did not satisfy 'fields-of-interest', - * load the complete vcard here. */ - vcard = load_vcard (bf, NULL, data->uid, &error); - - if (error) { - g_warning ( - "Error loading contact %s: %s", - data->uid, error->message); - g_error_free (error); - } - - if (!vcard) - continue; - - } - - notify_update_vcard (book_view, TRUE, data->uid, vcard); - g_free (vcard); - } - - g_slist_foreach (summary_list, (GFunc) e_book_backend_sqlitedb_search_data_free, NULL); - g_slist_free (summary_list); - } else { - /* iterate over the db and do the query there */ - DBC *dbc; - - memset (&id_dbt, 0, sizeof (id_dbt)); - memset (&vcard_dbt, 0, sizeof (vcard_dbt)); - vcard_dbt.flags = DB_DBT_MALLOC; - - db_error = db->cursor (db, NULL, &dbc, 0); - if (db_error == 0) { - - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST); - while (db_error == 0) { - - if (!e_flag_is_set (closure->running)) - break; - - /* don't include the version in the list of cards */ - if (strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME) && - strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME)) { - notify_update_vcard (book_view, allcontacts, - id_dbt.data, vcard_dbt.data); - } - - g_free (vcard_dbt.data); - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT); - } + if (!summary_list && local_error != NULL) { + g_warning (G_STRLOC ": Failed to query initial contacts: %s", local_error->message); + g_error_free (local_error); + e_data_book_view_notify_complete (book_view, EDB_NOT_OPENED_ERROR); + g_object_unref (book_view); + return NULL; + } - dbc->c_close (dbc); - if (db_error && db_error != DB_NOTFOUND) - g_warning ( - "e_book_backend_file_search: error building list: %s", - db_strerror (db_error)); - } - else if (db_error == DB_RUNRECOVERY) { - g_warning ( - "e_book_backend_file_search: error getting the cursor for %s", - bf->priv->filename); - abort (); - } + for (l = summary_list; l; l = l->next) { + EbSdbSearchData *data = l->data; + gchar *vcard = NULL; - } + vcard = data->vcard; + data->vcard = NULL; + + notify_update_vcard (book_view, TRUE, data->uid, vcard); + g_free (vcard); + } + + g_slist_foreach (summary_list, (GFunc) e_book_backend_sqlitedb_search_data_free, NULL); + g_slist_free (summary_list); if (e_flag_is_set (closure->running)) e_data_book_view_notify_complete (book_view, NULL /* Success */); @@ -1729,167 +1223,12 @@ e_book_backend_file_stop_book_view (EBookBackend *backend, g_thread_join (closure->thread); } -/* -** versions: -** -** 0.0 just a list of cards -** -** 0.1 same as 0.0, but with the version tag -** -** 0.2 not a real format upgrade, just a hack to fix broken ids caused -** by a bug in early betas, but we only need to convert them if -** the previous version is 0.1, since the bug existed after 0.1 -** came about. -*/ -static gboolean -e_book_backend_file_upgrade_db (EBookBackendFile *bf, - gchar *old_version) -{ - DB *db = bf->priv->file_db; - gint db_error; - DBT version_name_dbt, version_dbt; - - if (!db) { - g_warning (G_STRLOC ": No DB opened"); - return FALSE; - } - - if (strcmp (old_version, "0.0") - && strcmp (old_version, "0.1")) { - g_warning ( - "unsupported version '%s' found in PAS backend file\n", - old_version); - return FALSE; - } - - if (!strcmp (old_version, "0.1")) { - /* we just loop through all the cards in the db, - * giving them valid ids if they don't have them */ - DBT id_dbt, vcard_dbt; - DBC *dbc; - gint card_failed = 0; - - db_error = db->cursor (db, NULL, &dbc, 0); - if (db_error != 0) { - g_warning (G_STRLOC ": db->cursor failed with %s", db_strerror (db_error)); - return FALSE; - } - - memset (&id_dbt, 0, sizeof (id_dbt)); - memset (&vcard_dbt, 0, sizeof (vcard_dbt)); - - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_FIRST); - - while (db_error == 0) { - if ((id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1 - || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) && - (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_REVISION_NAME) + 1 - || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_REVISION_NAME))) { - EContact *contact; - - contact = create_contact (id_dbt.data, vcard_dbt.data); - - /* the cards we're looking for are - * created with a normal id dbt, but - * with the id field in the vcard set - * to something that doesn't match. - * so, we need to modify the card to - * have the same id as the the dbt. */ - if (strcmp (id_dbt.data, e_contact_get_const (contact, E_CONTACT_UID))) { - gchar *vcard; - - e_contact_set (contact, E_CONTACT_UID, id_dbt.data); - - vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - string_to_dbt (vcard, &vcard_dbt); - - db_error = db->put (db, NULL, - &id_dbt, &vcard_dbt, 0); - - g_free (vcard); - - if (db_error != 0) - card_failed++; - } - - g_object_unref (contact); - } - - db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT); - } - - dbc->c_close (dbc); - - if (card_failed) { - g_warning ("failed to update %d cards", card_failed); - return FALSE; - } - } - - string_to_dbt (E_BOOK_BACKEND_FILE_VERSION_NAME, &version_name_dbt); - string_to_dbt (E_BOOK_BACKEND_FILE_VERSION, &version_dbt); - - db_error = db->put (db, NULL, &version_name_dbt, &version_dbt, 0); - if (db_error == 0) - return TRUE; - else - return FALSE; -} - -static gboolean -e_book_backend_file_maybe_upgrade_db (EBookBackendFile *bf) -{ - DB *db = bf->priv->file_db; - DBT version_name_dbt, version_dbt; - gint db_error; - gchar *version; - gboolean ret_val = TRUE; - - if (!db) { - g_warning (G_STRLOC ": No DB opened"); - return FALSE; - } - - string_to_dbt (E_BOOK_BACKEND_FILE_VERSION_NAME, &version_name_dbt); - memset (&version_dbt, 0, sizeof (version_dbt)); - version_dbt.flags = DB_DBT_MALLOC; - - db_error = db->get (db, NULL, &version_name_dbt, &version_dbt, 0); - if (db_error == 0) { - /* success */ - version = version_dbt.data; - } - else { - /* key was not in file */ - version = g_strdup ("0.0"); - } - - if (strcmp (version, E_BOOK_BACKEND_FILE_VERSION)) - ret_val = e_book_backend_file_upgrade_db (bf, version); - - g_free (version); - - return ret_val; -} #ifdef CREATE_DEFAULT_VCARD # include <libedata-book/ximian-vcard.h> #endif static void -#if (DB_VERSION_MAJOR > 4) || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) -file_errcall (const DB_ENV *env, - const gchar *buf1, - const gchar *buf2) -#else -file_errcall (const gchar *buf1, - gchar *buf2) -#endif -{ - g_warning ("libdb error: %s", buf2); -} - -static void e_book_backend_file_open (EBookBackendSync *backend, EDataBook *book, GCancellable *cancellable, @@ -1897,260 +1236,168 @@ e_book_backend_file_open (EBookBackendSync *backend, GError **perror) { EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend); - gchar *dirname, *filename; - gboolean readonly = TRUE; + gchar *dirname, *filename, *backup; ESourceRegistry *registry; ESource *source; - gint db_error; - DB *db; - DB_ENV *env; GError *local_error = NULL; - global_env *genv = NULL; - -#ifdef CREATE_DEFAULT_VCARD - gboolean create_default_vcard = FALSE; -#endif + gboolean populated; source = e_backend_get_source (E_BACKEND (backend)); registry = e_book_backend_get_registry (E_BOOK_BACKEND (backend)); dirname = e_book_backend_file_extract_path_from_source ( registry, source, GET_PATH_DB_DIR); - - if (only_if_exists && !g_file_test (dirname, G_FILE_TEST_IS_DIR)) { - g_free (dirname); - g_propagate_error (perror, EDB_ERROR (NO_SUCH_BOOK)); - return; - } - filename = g_build_filename (dirname, "addressbook.db", NULL); + backup = g_build_filename (dirname, "addressbook.db.old", NULL); - db_error = e_db3_utils_maybe_recover (filename); - if (db_error != 0) { - g_warning ("db recovery failed with %s", db_strerror (db_error)); - g_free (dirname); - g_free (filename); - db_error_to_gerror (db_error, perror); - return; - } + /* The old BDB exists, lets migrate that to sqlite right away + */ + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + bf->priv->sqlitedb = e_book_backend_sqlitedb_new (dirname, + SQLITEDB_EMAIL_ID, + SQLITEDB_FOLDER_ID, + SQLITEDB_FOLDER_NAME, + TRUE, + &local_error); - G_LOCK (db_environments); - if (db_environments) { - genv = g_hash_table_lookup (db_environments, dirname); - } - if (genv && genv->ref_count > 0) { - genv->ref_count++; - env = genv->env; - } else { - db_error = db_env_create (&env, 0); - if (db_error != 0) { - g_warning ("db_env_create failed with %s", db_strerror (db_error)); - G_UNLOCK (db_environments); + if (!bf->priv->sqlitedb) { + g_warning (G_STRLOC ": Failed to open sqlitedb: %s", local_error->message); + g_propagate_error (perror, local_error); g_free (dirname); g_free (filename); - db_error_to_gerror (db_error, perror); + g_free (backup); return; } - env->set_errcall (env, file_errcall); - - /* Set the allocation routines to the non-aborting GLib functions */ - env->set_alloc (env, (gpointer (*)(gsize)) g_try_malloc, - (gpointer (*)(gpointer , gsize)) g_try_realloc, - g_free); - - /* Make sure the database directory is created - * or env->open will fail */ - if (!only_if_exists) { - if (!create_directory (dirname, perror)) { - g_warning ("failed to create directory at %s", dirname); - G_UNLOCK (db_environments); - g_free (dirname); - g_free (filename); - return; - } - } + if (!e_book_backend_file_migrate_bdb (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, + dirname, filename, &local_error)) { - /* - * DB_INIT_TXN enables transaction support. It requires DB_INIT_LOCK to - * initialize the locking subsystem and DB_INIT_LOG for the logging - * subsystem. - * - * DB_INIT_MPOOL enables the in-memory cache. - * - * Note that we need either DB_INIT_CDB or DB_INIT_LOCK, because we will - * have multiple threads reading and writing concurrently without - * any locking above libdb. Right now DB_INIT_LOCK is used because - * DB_INIT_TXN conflicts with DB_INIT_CDB. - */ - 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); - if (db_error != 0) { - env->close (env, 0); - g_warning ("db_env_open failed with %s", db_strerror (db_error)); - G_UNLOCK (db_environments); + /* Perhaps this error should not be fatal */ + g_warning (G_STRLOC ": Failed to migrate old BDB to sqlitedb: %s", local_error->message); + g_propagate_error (perror, local_error); g_free (dirname); g_free (filename); - db_error_to_gerror (db_error, perror); + g_free (backup); + + g_object_unref (bf->priv->sqlitedb); + bf->priv->sqlitedb = NULL; return; } - /* Insert in the db_environments hash table */ - if (!db_environments) { - db_environments = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - } - genv = g_malloc0 (sizeof (global_env)); - genv->ref_count = 1; - genv->env = env; - g_hash_table_insert (db_environments, g_strdup (dirname), genv); - } - G_UNLOCK (db_environments); - - bf->priv->env = env; + /* Now we've migrated the database, lets rename it instead of unlinking it */ + if (g_rename (filename, backup) < 0) { - db_error = db_create (&db, env, 0); - if (db_error != 0) { - g_warning ("db_create failed with %s", db_strerror (db_error)); - g_free (dirname); - g_free (filename); - db_error_to_gerror (db_error, perror); - return; - } + g_warning (G_STRLOC ": Failed to rename old database from '%s' to '%s': %s", + filename, backup, g_strerror (errno)); - db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD | DB_AUTO_COMMIT, 0666); - - if (db_error == DB_OLD_VERSION) { - db_error = e_db3_utils_upgrade_format (filename); - - if (db_error != 0) { - g_warning ("db format upgrade failed with %s", db_strerror (db_error)); - g_free (dirname); - g_free (filename); - db_error_to_gerror (db_error, perror); - return; - } + g_propagate_error (perror, e_data_book_create_error_fmt + (E_DATA_BOOK_STATUS_OTHER_ERROR, + _("Failed to rename old database from '%s' to '%s': %s"), + filename, backup, g_strerror (errno))); - db->close (db, 0); - db_error = db_create (&db, env, 0); - if (db_error != 0) { - g_warning ("db_create failed with %s", db_strerror (db_error)); g_free (dirname); g_free (filename); - db_error_to_gerror (db_error, perror); + g_free (backup); + bf->priv->sqlitedb = NULL; return; - } - - db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_THREAD | DB_AUTO_COMMIT, 0666); + } } - if (db_error == 0) { - readonly = FALSE; - } else { - db->close (db, 0); - db_error = db_create (&db, env, 0); - if (db_error != 0) { - g_warning ("db_create failed with %s", db_strerror (db_error)); - g_free (dirname); - g_free (filename); - db_error_to_gerror (db_error, perror); - return; - } + /* If we already have a handle on this, it means there was an old BDB migrated + * and no need to reopen it + */ + if (bf->priv->sqlitedb == NULL) { - db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_RDONLY | DB_THREAD | DB_AUTO_COMMIT, 0666); + /* ensure the directory exists first */ + if (!only_if_exists && !create_directory (dirname, &local_error)) { - if (db_error != 0 && !only_if_exists) { + g_warning (G_STRLOC ": Failed to create directory for sqlite db: %s", local_error->message); + g_propagate_error (perror, local_error); - /* the database didn't exist, so we create the - * directory then the .db */ - db->close (db, 0); + g_free (dirname); + g_free (filename); + g_free (backup); + return; + } - if (!create_directory (dirname, perror)) { - g_free (dirname); - g_free (filename); - return; - } + /* Create the sqlitedb */ + bf->priv->sqlitedb = e_book_backend_sqlitedb_new (dirname, + SQLITEDB_EMAIL_ID, + SQLITEDB_FOLDER_ID, + SQLITEDB_FOLDER_NAME, + TRUE, &local_error); - db_error = db_create (&db, env, 0); - if (db_error != 0) { - g_warning ("db_create failed with %s", db_strerror (db_error)); - g_free (dirname); - g_free (filename); - db_error_to_gerror (db_error, perror); + if (!bf->priv->sqlitedb) { + g_warning (G_STRLOC ": Failed to open sqlitedb: %s", local_error->message); + g_propagate_error (perror, local_error); + g_free (dirname); + g_free (filename); + g_free (backup); + return; + } + + /* An sqlite DB only 'exists' if the populated flag is set */ + populated = e_book_backend_sqlitedb_get_is_populated (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, + &local_error); + + if (local_error != NULL) { + /* Perhaps this error should not be fatal */ + g_warning (G_STRLOC ": Failed to check populated flag in sqlite db: %s", local_error->message); + g_propagate_error (perror, local_error); + g_free (dirname); + g_free (filename); + g_free (backup); + + g_object_unref (bf->priv->sqlitedb); + bf->priv->sqlitedb = NULL; + return; + } + + if (!populated) { + /* Shutdown, no such book ! */ + if (only_if_exists) { + g_free (dirname); + g_free (filename); + g_free (backup); + g_object_unref (bf->priv->sqlitedb); + bf->priv->sqlitedb = NULL; + g_propagate_error (perror, EDB_ERROR (NO_SUCH_BOOK)); return; - } + } - db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_CREATE | DB_THREAD | DB_AUTO_COMMIT, 0666); - if (db_error != 0) { - db->close (db, 0); - g_warning ("db->open (... %s ... DB_CREATE ...) failed with %s", filename, db_strerror (db_error)); - } - else { #ifdef CREATE_DEFAULT_VCARD - create_default_vcard = TRUE; -#endif + { + GSList l; + l.data = XIMIAN_VCARD; + l.next = NULL; - readonly = FALSE; + if (!do_create (bf, &l, NULL, NULL)) + g_warning ("Cannot create default contact"); } - } - } - - bf->priv->file_db = db; - - if (db_error != 0) { - bf->priv->file_db = NULL; - g_free (dirname); - g_free (filename); - db_error_to_gerror (db_error, perror); - return; - } - -#ifdef CREATE_DEFAULT_VCARD - if (create_default_vcard) { - GSList l; - l.data = XIMIAN_VCARD; - l.next = NULL; - - if (!do_create (bf, &l, NULL, NULL)) - g_warning ("Cannot create default contact"); - } #endif - if (!e_book_backend_file_maybe_upgrade_db (bf)) { - db->close (db, 0); - bf->priv->file_db = NULL; - g_free (dirname); - g_free (filename); - g_propagate_error (perror, EDB_ERROR_EX (OTHER_ERROR, "e_book_backend_file_maybe_upgrade_db failed")); - return; - } - - g_free (bf->priv->dirname); - g_free (bf->priv->filename); - bf->priv->dirname = dirname; - bf->priv->filename = filename; - - bf->priv->sqlitedb = e_book_backend_sqlitedb_new ( - bf->priv->dirname, - SQLITEDB_EMAIL_ID, - SQLITEDB_FOLDER_ID, - SQLITEDB_FOLDER_NAME, - FALSE, - perror); - if (!bf->priv->sqlitedb) - return; + /* Set the populated flag */ + if (!e_book_backend_sqlitedb_set_is_populated (bf->priv->sqlitedb, + SQLITEDB_FOLDER_ID, + TRUE, + &local_error)) { + g_warning (G_STRLOC ": Failed to set populated flag in sqlite db: %s", + local_error->message); + g_propagate_error (perror, local_error); + g_free (dirname); + g_free (filename); + g_free (backup); + g_object_unref (bf->priv->sqlitedb); + bf->priv->sqlitedb = NULL; + return; + } + } + } - if (!e_book_backend_sqlitedb_get_is_populated (bf->priv->sqlitedb, - SQLITEDB_FOLDER_ID, - &local_error)) { - if (local_error) { - g_propagate_error (perror, local_error); - return; - } else if (!build_sqlitedb (bf->priv)) { - g_propagate_error ( - perror, e_data_book_create_error_fmt ( - E_DATA_BOOK_STATUS_OTHER_ERROR, - _("Failed to build summary for an address book %s"), - bf->priv->filename)); - } - } + g_free (dirname); + g_free (filename); + g_free (backup); /* Resolve the photo directory here */ dirname = e_book_backend_file_extract_path_from_source ( @@ -2162,7 +1409,7 @@ e_book_backend_file_open (EBookBackendSync *backend, e_book_backend_file_load_revision (bf); e_book_backend_notify_online (E_BOOK_BACKEND (backend), TRUE); - e_book_backend_notify_readonly (E_BOOK_BACKEND (backend), readonly); + e_book_backend_notify_readonly (E_BOOK_BACKEND (backend), FALSE); e_book_backend_notify_opened (E_BOOK_BACKEND (backend), NULL /* Success */); g_rec_mutex_lock (&bf->priv->revision_mutex); @@ -2226,15 +1473,10 @@ static void e_book_backend_file_sync (EBookBackend *backend) { EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend); - gint db_error; g_return_if_fail (bf != NULL); - if (bf->priv->file_db) { - db_error = bf->priv->file_db->sync (bf->priv->file_db, 0); - if (db_error != 0) - g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error)); - } + /* FIXME: Tell sqlite to dump NOW ! */ } typedef struct { @@ -2290,33 +1532,9 @@ static void e_book_backend_file_dispose (GObject *object) { EBookBackendFile *bf; - global_env *genv; bf = E_BOOK_BACKEND_FILE (object); - if (bf->priv->file_db) { - bf->priv->file_db->close (bf->priv->file_db, 0); - bf->priv->file_db = NULL; - } - - G_LOCK (db_environments); - if (bf->priv->dirname) { - genv = g_hash_table_lookup (db_environments, bf->priv->dirname); - if (genv) { - genv->ref_count--; - if (genv->ref_count == 0) { - genv->env->close (genv->env, 0); - g_free (genv); - g_hash_table_remove (db_environments, bf->priv->dirname); - } - if (g_hash_table_size (db_environments) == 0) { - g_hash_table_destroy (db_environments); - db_environments = NULL; - } - } - } - G_UNLOCK (db_environments); - if (bf->priv->sqlitedb) { g_object_unref (bf->priv->sqlitedb); bf->priv->sqlitedb = NULL; @@ -2332,8 +1550,6 @@ e_book_backend_file_finalize (GObject *object) priv = E_BOOK_BACKEND_FILE_GET_PRIVATE (object); - g_free (priv->filename); - g_free (priv->dirname); g_free (priv->photo_dirname); g_free (priv->revision); g_rec_mutex_clear (&priv->revision_mutex); @@ -2342,54 +1558,6 @@ e_book_backend_file_finalize (GObject *object) G_OBJECT_CLASS (e_book_backend_file_parent_class)->finalize (object); } -#ifdef G_OS_WIN32 -/* Avoid compiler warning by providing a function with exactly the - * prototype that db_env_set_func_open() wants for the open method. - */ - -static gint -my_open (const gchar *name, - gint oflag, - ...) -{ - gint mode = 0; - - if (oflag & O_CREAT) { - va_list arg; - va_start (arg, oflag); - mode = va_arg (arg, gint); - va_end (arg); - } - - return g_open (name, oflag, mode); -} - -gint -my_rename (const gchar *oldname, - const gchar *newname) -{ - return g_rename (oldname, newname); -} - -gint -my_exists (const gchar *name, - gint *isdirp) -{ - if (!g_file_test (name, G_FILE_TEST_EXISTS)) - return ENOENT; - if (isdirp != NULL) - *isdirp = g_file_test (name, G_FILE_TEST_IS_DIR); - return 0; -} - -gint -my_unlink (const gchar *name) -{ - return g_unlink (name); -} - -#endif - static void e_book_backend_file_class_init (EBookBackendFileClass *class) { @@ -2419,17 +1587,6 @@ e_book_backend_file_class_init (EBookBackendFileClass *class) object_class->dispose = e_book_backend_file_dispose; object_class->finalize = e_book_backend_file_finalize; - -#ifdef G_OS_WIN32 - /* Use the gstdio wrappers to open, check, rename and unlink - * files from libdb. - */ - db_env_set_func_open (my_open); - db_env_set_func_close (close); - db_env_set_func_exists (my_exists); - db_env_set_func_rename (my_rename); - db_env_set_func_unlink (my_unlink); -#endif } static void |