summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Van Berkom <tristanvb@openismus.com>2012-10-29 15:33:09 +0900
committerTristan Van Berkom <tristanvb@openismus.com>2013-01-27 16:32:50 +0900
commit7ff1bb6702183a35215e630855cb2c85e95735a4 (patch)
treeb501846357a5dde1aade081bbb7cb78c6a8de40d
parentcf928bc74248e893fdcf3a82ebeed9a531f75416 (diff)
downloadevolution-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.am2
-rw-r--r--addressbook/backends/file/e-book-backend-file-migrate-bdb.c528
-rw-r--r--addressbook/backends/file/e-book-backend-file-migrate-bdb.h43
-rw-r--r--addressbook/backends/file/e-book-backend-file.c1427
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