[evolution-data-server/openismus-work: 1/2] Make local addressbook backend store image data as URIs.
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/openismus-work: 1/2] Make local addressbook backend store image data as URIs.
- Date: Fri, 15 Jul 2011 23:02:35 +0000 (UTC)
commit cb9c59ba5812a07086b87c1638ac7a456f5e1b74
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date: Fri Jul 15 19:00:51 2011 -0400
Make local addressbook backend store image data as URIs.
Whenever the local address book receives a uri as a binary
blob it proceeds to transform it to a uri accessbile on
the system somewhere under $XDG_DATA_HOME. The local backend
cleans up old photo uris when contacts are removed or modified
to use new photos.
Additionally, whenever it is detected that the user is
cross-referencing a uri which belongs to another contact,
a hard-link will be created to the addressbook owned file
on disk and a new uri will be assigned to any additional
contacts which try to share a uri owned by the addressbook.
addressbook/backends/file/e-book-backend-file.c | 768 +++++++++++++++++++----
1 files changed, 647 insertions(+), 121 deletions(-)
---
diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index a65e126..e2a6118 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -74,18 +74,70 @@ struct _EBookBackendFilePrivate {
gchar *dirname;
gchar *filename;
gchar *summary_filename;
- DB *file_db;
- DB_ENV *env;
+ gchar *photo_dirname;
+ DB *file_db;
+ DB_ENV *env;
EBookBackendSummary *summary;
guint id;
};
+typedef enum {
+ GET_PATH_DB_DIR,
+ GET_PATH_PHOTO_DIR
+} GetPathType;
+
+typedef enum {
+ STATUS_NORMAL = 0,
+ STATUS_MODIFIED,
+ STATUS_ERROR
+} PhotoModifiedStatus;
+
G_LOCK_DEFINE_STATIC (global_env);
static struct {
gint ref_count;
DB_ENV *env;
} global_env;
+static gboolean
+remove_file (const gchar *filename, GError **error)
+{
+ if (-1 == g_unlink (filename)) {
+ if (errno == EACCES || errno == EPERM) {
+ g_propagate_error (error, EDB_ERROR (PERMISSION_DENIED));
+ } else {
+ g_propagate_error (error, e_data_book_create_error_fmt
+ (E_DATA_BOOK_STATUS_OTHER_ERROR,
+ "Failed to remove file '%s': %s",
+ filename, g_strerror (errno)));
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+create_directory (const gchar *dirname,
+ GError **error)
+{
+ gint rv;
+
+ rv = g_mkdir_with_parents (dirname, 0700);
+ if (rv == -1 && errno != EEXIST) {
+ g_warning ("failed to make directory %s: %s", dirname, g_strerror (errno));
+ if (errno == EACCES || errno == EPERM)
+ g_propagate_error (error, EDB_ERROR (PERMISSION_DENIED));
+ else
+ g_propagate_error (error,
+ e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
+ "Failed to make directory %s: %s",
+ dirname, g_strerror (errno)));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
static void
db_error_to_gerror (const gint db_error, GError **perror)
{
@@ -117,7 +169,7 @@ string_to_dbt(const gchar *str, DBT *dbt)
}
static EContact*
-create_contact (gchar *uid, const gchar *vcard)
+create_contact (const gchar *uid, const gchar *vcard)
{
EContact *contact = e_contact_new_from_vcard (vcard);
if (!e_contact_get_const (contact, E_CONTACT_UID))
@@ -126,6 +178,463 @@ create_contact (gchar *uid, const gchar *vcard)
return contact;
}
+static gchar *
+load_vcard (EBookBackendFile *bf,
+ 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, NULL, &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,
+ const gchar *uid,
+ GError **error)
+{
+ EContact *contact = NULL;
+ gchar *vcard;
+
+ if ((vcard = load_vcard (bf, uid, error)) != NULL) {
+ contact = create_contact (uid, vcard);
+ g_free (vcard);
+ }
+
+ return contact;
+}
+
+static gchar *
+collect_uri_for_field (EContact *old_contact,
+ EContact *new_contact,
+ EContactField field)
+{
+ EContactPhoto *old_photo, *new_photo;
+
+ old_photo = e_contact_get (old_contact, field);
+ if (!old_photo)
+ return NULL;
+
+ if (new_contact) {
+
+ new_photo = e_contact_get (new_contact, field);
+
+ if (new_photo == NULL ||
+ strcmp (old_photo->data.uri, new_photo->data.uri))
+ return g_strdup (old_photo->data.uri);
+ } else {
+ return g_strdup (old_photo->data.uri);
+ }
+
+ return NULL;
+}
+
+static void
+maybe_delete_uri (EBookBackendFile *bf,
+ const gchar *uri)
+{
+ GError *error = NULL;
+ gchar *filename;
+
+ /* A uri that does not give us a filename is certainly not
+ * a uri that we created for a local file, just skip it */
+ if ((filename = g_filename_from_uri (uri, NULL, NULL)) == NULL)
+ return;
+
+ /* If the file is in our path it belongs to us and we need to delete it.
+ */
+ if (!strncmp (bf->priv->photo_dirname, filename, strlen (bf->priv->photo_dirname))) {
+
+ /* Deleting uris should not cause the backend to fail to update
+ * a contact so the best we can do from here is log warnings
+ * when we fail to unlink a file from the disk.
+ */
+ if (!remove_file (filename, &error)) {
+ g_warning ("Unable to cleanup photo uri: %s", error->message);
+ g_error_free (error);
+ }
+ }
+
+ g_free (filename);
+}
+
+static void
+maybe_delete_unused_uris (EBookBackendFile *bf,
+ const gchar *id,
+ EContact *old_contact,
+ EContact *new_contact)
+{
+ GError *error = NULL;
+ gchar *uri_photo, *uri_logo;
+
+ if (!old_contact) {
+ old_contact = load_contact (bf, id, &error);
+
+ if (!old_contact) {
+ g_warning (G_STRLOC ": failed to load contact %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ } else
+ g_object_ref (old_contact);
+
+ /* If there is no new contact, collect all the uris to delete from old_contact
+ *
+ * Otherwise, if any of the photo uri fields have changed in new_contact, then collect the
+ * old uris for those fields from old_contact to delete
+ */
+ uri_photo = collect_uri_for_field (old_contact, new_contact, E_CONTACT_PHOTO);
+ uri_logo = collect_uri_for_field (old_contact, new_contact, E_CONTACT_LOGO);
+
+ if (uri_photo) {
+ maybe_delete_uri (bf, uri_photo);
+ g_free (uri_photo);
+ }
+
+ if (uri_logo) {
+ maybe_delete_uri (bf, uri_logo);
+ g_free (uri_logo);
+ }
+
+ g_object_unref (old_contact);
+}
+
+static gchar *
+e_book_backend_file_extract_path_from_source (ESource *source,
+ GetPathType path_type)
+{
+ const gchar *user_data_dir;
+ const gchar *source_dir;
+ gchar *mangled_source_dir;
+ gchar *filename = NULL;
+
+ user_data_dir = e_get_user_data_dir ();
+ source_dir = e_source_peek_relative_uri (source);
+
+ if (!source_dir || !g_str_equal (source_dir, "system"))
+ source_dir = e_source_peek_uid (source);
+
+ /* Mangle the URI to not contain invalid characters. */
+ mangled_source_dir = g_strdelimit (g_strdup (source_dir), ":/", '_');
+
+ switch (path_type) {
+ case GET_PATH_DB_DIR:
+ filename = g_build_filename
+ (user_data_dir, "addressbook", mangled_source_dir, NULL);
+ break;
+ case GET_PATH_PHOTO_DIR:
+ filename = g_build_filename
+ (user_data_dir, "addressbook", mangled_source_dir, "photos", NULL);
+ break;
+ default:
+ break;
+ }
+ g_free (mangled_source_dir);
+
+ return filename;
+}
+
+static gchar *
+safe_name_for_photo (EBookBackendFile *bf,
+ EContact *contact,
+ EContactField field)
+{
+ gchar *fullname = NULL, *uid, *str, *final;
+ gint i = 0;
+
+ uid = g_strdup (e_contact_get_const (contact, E_CONTACT_UID));
+ uid = g_strdelimit (uid, NULL, '_');
+
+ str = g_strdup (e_contact_field_name (field));
+ str = g_strdelimit (str, NULL, '_');
+
+ final = g_strdup_printf ("%s_%s", uid, str);
+
+ do {
+ gchar *filename = NULL;
+
+ g_free (fullname);
+
+ filename = g_strdup_printf ("%s_%02d.data", final, i);
+ fullname = g_build_filename (bf->priv->photo_dirname, filename, NULL);
+ g_free (filename);
+
+ i++;
+ } while (g_file_test (fullname, G_FILE_TEST_EXISTS));
+
+ g_free (str);
+ g_free (uid);
+ g_free (final);
+
+ return fullname;
+}
+
+static gchar *
+hard_link_photo (EBookBackendFile *bf,
+ EContact *contact,
+ EContactField field,
+ const gchar *src_filename,
+ GError **error)
+{
+ gchar *fullname = NULL, *uid, *str, *final;
+ gint i = 0, ret;
+
+ uid = g_strdup (e_contact_get_const (contact, E_CONTACT_UID));
+ uid = g_strdelimit (uid, NULL, '_');
+
+ str = g_strdup (e_contact_field_name (field));
+ str = g_strdelimit (str, NULL, '_');
+
+ final = g_strdup_printf ("%s_%s", uid, str);
+
+ do {
+ gchar *filename = NULL;
+
+ g_free (fullname);
+
+ filename = g_strdup_printf ("%s_%02d.data", final, i);
+ fullname = g_build_filename (bf->priv->photo_dirname, filename, NULL);
+ g_free (filename);
+
+ i++;
+
+ ret = link (src_filename, fullname);
+
+ } while (ret < 0 && errno == EEXIST);
+
+ if (ret < 0) {
+ if (errno == EACCES || errno == EPERM) {
+ g_propagate_error (error, EDB_ERROR (PERMISSION_DENIED));
+ } else {
+ g_propagate_error (error, e_data_book_create_error_fmt
+ (E_DATA_BOOK_STATUS_OTHER_ERROR,
+ "Failed to create hardlink for resource '%s': %s",
+ src_filename, g_strerror (errno)));
+ }
+ g_free (fullname);
+ fullname = NULL;
+ }
+
+ g_free (str);
+ g_free (uid);
+ g_free (final);
+
+ return fullname;
+}
+
+
+static gboolean
+is_backend_owned_uri (EBookBackendFile *bf,
+ const gchar *uri)
+{
+ gchar *filename;
+ gchar *dirname;
+ gboolean owned_uri;
+
+ /* Errors converting from uri definitily indicate it was
+ * not our uri to begin with, so just disregard this error. */
+ filename = g_filename_from_uri (uri, NULL, NULL);
+ if (!filename)
+ return FALSE;
+
+ dirname = g_path_get_dirname (filename);
+
+ owned_uri = (strcmp (dirname, bf->priv->photo_dirname) == 0);
+
+ g_free (filename);
+ g_free (dirname);
+
+ return owned_uri;
+}
+
+
+static PhotoModifiedStatus
+maybe_transform_vcard_field_for_photo (EBookBackendFile *bf,
+ EContact *old_contact,
+ EContact *contact,
+ EContactField field,
+ GError **error)
+{
+ PhotoModifiedStatus status = STATUS_NORMAL;
+ EContactPhoto *photo;
+
+ if (field != E_CONTACT_PHOTO && field != E_CONTACT_LOGO)
+ return status;
+
+ photo = e_contact_get (contact, field);
+ if (!photo)
+ return status;
+
+ if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
+ EContactPhoto *new_photo;
+ gchar *new_photo_path;
+ gchar *uri;
+
+ /* XXX Create a good file extension based on mime type */
+ new_photo_path = safe_name_for_photo (bf, contact, field);
+
+ if ((uri =
+ g_filename_to_uri (new_photo_path, NULL, error)) == NULL) {
+
+ status = STATUS_ERROR;
+ } else if (!g_file_set_contents (new_photo_path,
+ (const gchar *)photo->data.inlined.data,
+ photo->data.inlined.length,
+ error)) {
+
+ status = STATUS_ERROR;
+ } else {
+ new_photo = g_slice_new0 (EContactPhoto);
+ new_photo->type = E_CONTACT_PHOTO_TYPE_URI;
+ new_photo->data.uri = uri;
+
+ e_contact_set (contact, field, new_photo);
+
+ d(g_print ("Backend modified incomming binary blob to be %s:\n", uri));
+
+ status = STATUS_MODIFIED;
+
+ g_slice_free (EContactPhoto, new_photo);
+ }
+
+ g_free (uri);
+ g_free (new_photo_path);
+
+ } else { /* E_CONTACT_PHOTO_TYPE_URI */
+ const gchar *uid;
+ EContactPhoto *old_photo = NULL, *new_photo;
+
+ /* First determine that the new contact uri points to our 'photos' directory,
+ * if not then we do nothing
+ */
+ if (!is_backend_owned_uri (bf, photo->data.uri))
+ return status;
+
+ /* Now check if the uri is changed from the BDB copy
+ */
+ uid = e_contact_get_const (contact, E_CONTACT_UID);
+ if (uid == NULL) {
+ g_propagate_error (error, EDB_ERROR_EX (OTHER_ERROR, "No UID in the contact"));
+ return STATUS_ERROR;
+ }
+
+ if (old_contact)
+ old_photo = e_contact_get (old_contact, field);
+
+ /* Unless we are receiving the same uri that we already have
+ * stored in the BDB... */
+ if (!old_photo || old_photo->type == E_CONTACT_PHOTO_TYPE_INLINED ||
+ strcmp (old_photo->data.uri, photo->data.uri) != 0) {
+ gchar *filename;
+ gchar *new_filename;
+ gchar *new_uri = NULL;
+
+ /* ... Assume that the incomming uri belongs to another contact
+ * still in the BDB. Lets go ahead and create a hard link to the
+ * photo file and create a new name for the incomming uri, and
+ * use that in the incomming contact to save in place.
+ *
+ * This piece of code is here to ensure there are no problems if
+ * the libebook user decides to cross-reference and start "sharing"
+ * uris that we've previously stored in the photo directory.
+ *
+ * We use the hard-link here to off-load the necessary ref-counting
+ * logic to the file-system.
+ */
+ filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
+ g_assert (filename); /* we already checked this with 'is_backend_owned_uri()' */
+
+ new_filename = hard_link_photo (bf, contact, field, filename, error);
+
+ if (!new_filename)
+ status = STATUS_ERROR;
+ else if ((new_uri = g_filename_to_uri (new_filename, NULL, error)) == NULL) {
+ /* If we fail here... we need to clean up the hardlink we just created */
+ GError *local_err = NULL;
+ if (!remove_file (new_filename, &local_err)) {
+ g_warning ("Unable to cleanup photo uri: %s", local_err->message);
+ g_error_free (local_err);
+ }
+ status = STATUS_ERROR;
+ } else {
+
+ new_photo = g_slice_new0 (EContactPhoto);
+ new_photo->type = E_CONTACT_PHOTO_TYPE_URI;
+ new_photo->data.uri = new_uri;
+
+ e_contact_set (contact, field, new_photo);
+
+ d(g_print ("Backend modified incomming shared uri to be %s:\n", new_uri));
+
+ g_slice_free (EContactPhoto, new_photo);
+ status = STATUS_MODIFIED;
+ }
+ g_free (new_uri);
+ g_free (new_filename);
+ g_free (filename);
+ }
+ }
+
+ return status;
+}
+
+/*
+ * When a contact is added or modified we receive a vCard,
+ * this function checks if we've received inline data
+ * and replaces it with a uri notation.
+ *
+ * Returns the actual data and stores the size in 'len'
+ */
+static PhotoModifiedStatus
+maybe_transform_vcard_for_photo (EBookBackendFile *bf,
+ EContact *old_contact,
+ EContact *contact,
+ gchar **vcard_ret,
+ GError **error)
+{
+ PhotoModifiedStatus status;
+ gboolean modified = FALSE;
+
+ status = maybe_transform_vcard_field_for_photo (bf, old_contact, contact,
+ E_CONTACT_PHOTO, error);
+ modified = (status == STATUS_MODIFIED);
+
+ if (status != STATUS_ERROR) {
+ status = maybe_transform_vcard_field_for_photo (bf, old_contact, contact,
+ E_CONTACT_LOGO, error);
+ 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;
+ }
+ }
+
+ return status;
+}
+
+
static gboolean
build_summary (EBookBackendFilePrivate *bfpriv)
{
@@ -195,18 +704,22 @@ do_create(EBookBackendFile *bf,
EContact **contact,
GError **perror)
{
- DB *db = bf->priv->file_db;
- DBT id_dbt, vcard_dbt;
- gint db_error;
- gchar *id;
- gchar *vcard;
- const gchar *rev;
+ DB *db = bf->priv->file_db;
+ DBT id_dbt, vcard_dbt;
+ gint db_error;
+ gchar *id = NULL;
+ gchar *vcard = NULL;
+ const gchar *rev;
+ PhotoModifiedStatus status = STATUS_NORMAL;
g_assert (bf);
g_assert (vcard_req);
g_assert (contact);
retry:
+ g_free (id);
+ g_free (vcard);
+
id = e_book_backend_file_create_next_id (bf);
string_to_dbt (id, &id_dbt);
@@ -223,9 +736,6 @@ do_create(EBookBackendFile *bf,
db_error = db->put (db, NULL, &id_dbt, &vcard_dbt, DB_NOOVERWRITE);
- g_free (vcard);
- g_free (id);
-
if (0 == db_error) {
db_error = db->sync (db, 0);
if (db_error != 0) {
@@ -242,7 +752,40 @@ do_create(EBookBackendFile *bf,
}
}
- db_error_to_gerror (db_error, perror);
+ /* If we havent failed yet, now we might have to overwrite
+ * the entry with a transformed contact.
+ *
+ * We wait until the last minute to re-enter any new contact
+ * with photo data into the database because we need to know
+ * the contact UID in order to know where to store the data
+ * (and in order to derive the correct uri for the vCard).
+ */
+ if (0 == db_error) {
+
+ gchar *transformed_vcard = NULL;
+
+ status = maybe_transform_vcard_for_photo (bf, NULL, *contact, &transformed_vcard, perror);
+
+ if (status == STATUS_MODIFIED && transformed_vcard) {
+
+ /* Here we just dump the new version of the vcard into
+ * the DB, the returned EContact is correct and stores
+ * images by uri already so the following notifications
+ * to the views should be correct.
+ */
+ string_to_dbt (id, &id_dbt);
+ string_to_dbt (transformed_vcard, &vcard_dbt);
+
+ db_error = db->put (db, NULL, &id_dbt, &vcard_dbt, 0);
+ }
+ g_free (transformed_vcard);
+ }
+
+ g_free (vcard);
+ g_free (id);
+
+ if (perror && !perror)
+ db_error_to_gerror (db_error, perror);
return db_error == 0;
}
@@ -275,12 +818,16 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
DBT id_dbt;
gint db_error;
gchar *id;
- GList *l;
- GList *removed_cards = NULL;
+ GList *l;
+ GList *removed_cards = NULL;
for (l = id_list; l; l = l->next) {
id = l->data;
+ /* First collect uris to delete */
+ maybe_delete_unused_uris (bf, id, NULL, NULL);
+
+ /* Then go on to delete from the db */
string_to_dbt (id, &id_dbt);
db_error = db->del (db, NULL, &id_dbt, 0);
@@ -322,6 +869,8 @@ e_book_backend_file_modify_contact (EBookBackendSync *backend,
gint db_error;
const gchar *id, *lookup_id;
gchar *vcard_with_rev;
+ PhotoModifiedStatus status;
+ EContact *old_contact;
*contact = e_contact_new_from_vcard (vcard);
id = e_contact_get_const (*contact, E_CONTACT_UID);
@@ -331,6 +880,24 @@ e_book_backend_file_modify_contact (EBookBackendSync *backend,
return;
}
+ old_contact = load_contact (bf, id, perror);
+ if (!old_contact) {
+ g_warning (G_STRLOC ": Failed to load contact %s", id);
+ return;
+ }
+
+ /* Transform incomming photo blobs to uris before storing this to the DB */
+ status = maybe_transform_vcard_for_photo (bf, old_contact, *contact, NULL, perror);
+ if (status == STATUS_ERROR) {
+ g_object_unref (old_contact);
+ return;
+ }
+
+ /* 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) */
+ maybe_delete_unused_uris (bf, id, old_contact, *contact);
+ g_object_unref (old_contact);
+
/* update the revision (modified time of contact) */
set_revision (*contact);
vcard_with_rev = e_vcard_to_string (E_VCARD (*contact), EVC_FORMAT_VCARD_30);
@@ -375,28 +942,12 @@ e_book_backend_file_get_contact (EBookBackendSync *backend,
gchar **vcard,
GError **perror)
{
- EBookBackendFile *bf;
- DB *db;
- DBT id_dbt, vcard_dbt;
- gint db_error = 0;
-
- bf = E_BOOK_BACKEND_FILE (backend);
- db = bf->priv->file_db;
-
- string_to_dbt (id, &id_dbt);
- memset (&vcard_dbt, 0, sizeof (vcard_dbt));
- vcard_dbt.flags = DB_DBT_MALLOC;
+ EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
- db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
+ *vcard = load_vcard (bf, id, perror);
- if (db_error == 0) {
- *vcard = vcard_dbt.data;
- } else {
- g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
+ if (!*vcard)
*vcard = g_strdup ("");
-
- g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND));
- }
}
static void
@@ -431,18 +982,13 @@ e_book_backend_file_get_contact_list (EBookBackendSync *backend,
for (i = 0; i < ids->len; i++) {
gchar *id = g_ptr_array_index (ids, i);
- string_to_dbt (id, &id_dbt);
- memset (&vcard_dbt, 0, sizeof (vcard_dbt));
- vcard_dbt.flags = DB_DBT_MALLOC;
+ gchar *vcard;
- db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
- if (db_error == 0) {
- contact_list = g_list_prepend (contact_list, vcard_dbt.data);
- } else {
- g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
- db_error_to_gerror (db_error, perror);
+ vcard = load_vcard (bf, id, perror);
+ if (!vcard)
break;
- }
+
+ contact_list = g_list_prepend (contact_list, vcard);
}
g_ptr_array_free (ids, TRUE);
} else {
@@ -721,6 +1267,7 @@ book_view_thread (gpointer data)
for (i = 0; i < ids->len; i++) {
gchar *id = g_ptr_array_index (ids, i);
+ gchar *vcard;
if (!e_flag_is_set (closure->running))
break;
@@ -730,18 +1277,9 @@ book_view_thread (gpointer data)
continue;
}
- string_to_dbt (id, &id_dbt);
- memset (&vcard_dbt, 0, sizeof (vcard_dbt));
- vcard_dbt.flags = DB_DBT_MALLOC;
-
- db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
-
- if (db_error == 0) {
- notify_update_vcard (book_view, uid_only, TRUE, id, vcard_dbt.data);
- }
- else {
- g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
- }
+ vcard = load_vcard (bf, id, NULL);
+ if (vcard)
+ notify_update_vcard (book_view, uid_only, TRUE, id, vcard);
}
g_ptr_array_free (ids, TRUE);
@@ -787,8 +1325,9 @@ book_view_thread (gpointer data)
}
done:
- if (e_flag_is_set (closure->running))
+ if (e_flag_is_set (closure->running)) {
e_data_book_view_notify_complete (book_view, NULL /* Success */);
+ }
/* unref the */
printf("book_view file uref \n");
@@ -833,7 +1372,7 @@ e_book_backend_file_stop_book_view (EBookBackend *backend,
}
typedef struct {
- DB *db;
+ EBookBackendFile *bf;
GList *add_cards;
GList *add_ids;
@@ -847,35 +1386,26 @@ static void
e_book_backend_file_changes_foreach_key (const gchar *key, gpointer user_data)
{
EBookBackendFileChangeContext *ctx = user_data;
- DB *db = ctx->db;
- DBT id_dbt, vcard_dbt;
- gint db_error = 0;
-
- string_to_dbt (key, &id_dbt);
- memset (&vcard_dbt, 0, sizeof (vcard_dbt));
- vcard_dbt.flags = DB_DBT_MALLOC;
+ EBookBackendFile *bf = ctx->bf;
+ gchar *vcard;
- db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
+ vcard = load_vcard (bf, key, NULL);
- if (db_error != 0) {
+ if (vcard) {
EContact *contact;
- gchar *id = id_dbt.data;
- gchar *vcard_string;
+ gchar *vcard_string;
contact = e_contact_new ();
- e_contact_set (contact, E_CONTACT_UID, id);
+ e_contact_set (contact, E_CONTACT_UID, key);
vcard_string = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
- ctx->del_ids = g_list_append (ctx->del_ids,
- g_strdup (id));
- ctx->del_cards = g_list_append (ctx->del_cards,
- vcard_string);
+ ctx->del_ids = g_list_append (ctx->del_ids, g_strdup (key));
+ ctx->del_cards = g_list_append (ctx->del_cards, vcard_string);
g_object_unref (contact);
+ g_free (vcard);
}
-
- g_free (vcard_dbt.data);
}
static void
@@ -902,7 +1432,7 @@ e_book_backend_file_get_changes (EBookBackendSync *backend,
memset (&ctx, 0, sizeof (ctx));
- ctx.db = db;
+ ctx.bf = bf;
/* Find the changed ids */
filename = g_strdup_printf ("%s/%s" CHANGES_DB_SUFFIX, bf->priv->dirname, change_id);
@@ -1018,31 +1548,6 @@ e_book_backend_file_get_changes (EBookBackendSync *backend,
e_dbhash_destroy (ehash);
}
-static gchar *
-e_book_backend_file_extract_path_from_source (ESource *source)
-{
- const gchar *user_data_dir;
- const gchar *source_dir;
- gchar *mangled_source_dir;
- gchar *filename;
-
- user_data_dir = e_get_user_data_dir ();
- source_dir = e_source_peek_relative_uri (source);
-
- if (!source_dir || !g_str_equal (source_dir, "system"))
- source_dir = e_source_peek_uid (source);
-
- /* Mangle the URI to not contain invalid characters. */
- mangled_source_dir = g_strdelimit (g_strdup (source_dir), ":/", '_');
-
- filename = g_build_filename (
- user_data_dir, "addressbook", mangled_source_dir, NULL);
-
- g_free (mangled_source_dir);
-
- return filename;
-}
-
static void
e_book_backend_file_authenticate_user (EBookBackendSync *backend,
EDataBook *book,
@@ -1255,7 +1760,7 @@ e_book_backend_file_load_source (EBookBackend *backend,
struct stat sb;
DB_HASH_STAT *hashstat;
- dirname = e_book_backend_file_extract_path_from_source (source);
+ dirname = e_book_backend_file_extract_path_from_source (source, GET_PATH_DB_DIR);
filename = g_build_filename (dirname, "addressbook.db", NULL);
db_error = e_db3_utils_maybe_recover (filename);
@@ -1369,18 +1874,11 @@ e_book_backend_file_load_source (EBookBackend *backend,
db_error = (*db->open) (db, NULL, filename, NULL, DB_HASH, DB_RDONLY | DB_THREAD, 0666);
if (db_error != 0 && !only_if_exists) {
- gint rv;
-
/* the database didn't exist, so we create the
directory then the .db */
db->close (db, 0);
- rv = g_mkdir_with_parents (dirname, 0700);
- if (rv == -1 && errno != EEXIST) {
- g_warning ("failed to make directory %s: %s", dirname, g_strerror (errno));
- if (errno == EACCES || errno == EPERM)
- g_propagate_error (perror, EDB_ERROR (PERMISSION_DENIED));
- else
- g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "Failed to make directory %s: %s", dirname, g_strerror (errno)));
+
+ if (!create_directory (dirname, perror)) {
g_free (dirname);
g_free (filename);
return;
@@ -1474,6 +1972,12 @@ e_book_backend_file_load_source (EBookBackend *backend,
}
}
+ /* Resolve the photo directory here */
+ dirname = e_book_backend_file_extract_path_from_source (source, GET_PATH_PHOTO_DIR);
+ if (!only_if_exists && !create_directory (dirname, perror))
+ return;
+ bf->priv->photo_dirname = dirname;
+
e_book_backend_set_is_loaded (backend, TRUE);
e_book_backend_set_is_writable (backend, writable);
}
@@ -1497,6 +2001,28 @@ select_changes (const gchar *name)
}
static void
+remove_photos (const gchar *dirname)
+{
+ GDir *dir;
+
+ dir = g_dir_open (dirname, 0, NULL);
+ if (dir) {
+ const gchar *name;
+
+ while ((name = g_dir_read_name (dir))) {
+ gchar *full_path = g_build_filename (dirname, name, NULL);
+ if (-1 == g_unlink (full_path)) {
+ g_warning ("failed to remove photo file %s: %s",
+ full_path, g_strerror (errno));
+ }
+ g_free (full_path);
+ }
+
+ g_dir_close (dir);
+ }
+}
+
+static void
e_book_backend_file_remove (EBookBackendSync *backend,
EDataBook *book,
guint32 opid,
@@ -1505,14 +2031,8 @@ e_book_backend_file_remove (EBookBackendSync *backend,
EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
GDir *dir;
- if (-1 == g_unlink (bf->priv->filename)) {
- if (errno == EACCES || errno == EPERM) {
- g_propagate_error (perror, EDB_ERROR (PERMISSION_DENIED));
- } else {
- g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "Failed to remove file '%s': %s", bf->priv->filename, g_strerror (errno)));
- }
+ if (!remove_file (bf->priv->filename, perror))
return;
- }
/* unref the summary before we remove the file so it's not written out again */
g_object_unref (bf->priv->summary);
@@ -1537,6 +2057,12 @@ e_book_backend_file_remove (EBookBackendSync *backend,
g_dir_close (dir);
}
+ /* Remove photos and the photo directory */
+ remove_photos (bf->priv->photo_dirname);
+ if (-1 == g_rmdir (bf->priv->photo_dirname))
+ g_warning ("failed to remove directory `%s`: %s", bf->priv->dirname, g_strerror (errno));
+
+ /* Try removing the base directory now */
if (-1 == g_rmdir (bf->priv->dirname))
g_warning ("failed to remove directory `%s`: %s", bf->priv->dirname, g_strerror (errno));
@@ -1630,6 +2156,7 @@ e_book_backend_file_finalize (GObject *object)
g_free (bf->priv->filename);
g_free (bf->priv->dirname);
g_free (bf->priv->summary_filename);
+ g_free (bf->priv->photo_dirname);
g_free (bf->priv);
@@ -1731,7 +2258,6 @@ e_book_backend_file_init (EBookBackendFile *backend)
{
EBookBackendFilePrivate *priv;
- priv = g_new0 (EBookBackendFilePrivate, 1);
-
+ priv = g_new0 (EBookBackendFilePrivate, 1);
backend->priv = priv;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]