[evolution-data-server/openismus-work] Initially make the local addressbook store received binary photos as files and report the uris.



commit df703351641f2725f63d1c82854ff1be93df625d
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Fri Jul 1 16:00:48 2011 -0400

    Initially make the local addressbook store received binary photos as files and report the uris.

 addressbook/backends/file/e-book-backend-file.c |  311 +++++++++++++++++++----
 1 files changed, 263 insertions(+), 48 deletions(-)
---
diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index a65e126..2c50426 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -80,12 +80,219 @@ struct _EBookBackendFilePrivate {
 	guint     id;
 };
 
+typedef enum {
+	GET_PATH_DB_DIR,
+	GET_PATH_PHOTO_DIR,
+	GET_PATH_PHOTO
+} 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
+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 gchar *
+e_book_backend_file_extract_path_from_source (ESource      *source, 
+					      GetPathType   path_type,
+					      EContact     *contact,
+					      EContactField field)
+{
+	const gchar *user_data_dir;
+	const gchar *source_dir;
+	gchar *mangled_source_dir;
+	gchar *filename = NULL, *str;
+	gchar *uid = NULL, *final = 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), ":/", '_');
+
+	if (contact && field) {
+		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);
+		g_free (str);
+	}
+
+	switch (path_type) {
+	case GET_PATH_DB_DIR:
+		filename = g_build_filename 
+			(user_data_dir, "addressbook", mangled_source_dir, "db", NULL);
+		break;
+	case GET_PATH_PHOTO_DIR:
+		filename = g_build_filename 
+			(user_data_dir, "addressbook", mangled_source_dir, "photos", NULL);
+		break;
+	case GET_PATH_PHOTO:
+		filename = g_build_filename 
+			(user_data_dir, "addressbook", mangled_source_dir, "photos", final, NULL);
+		break;
+	default:
+		break;
+	}
+	g_free (mangled_source_dir);
+	g_free (final);
+	g_free (uid);
+
+	return filename;
+}
+
+static gchar *
+safe_name_for_photo (EBookBackendFile *bf,
+		     ESource          *source,
+		     EContact         *contact,
+		     EContactField     field)
+{
+	gchar *filename = NULL, *tmp;
+	gint   i = 0;
+
+	do {
+		g_free (filename);
+		tmp = e_book_backend_file_extract_path_from_source (source, GET_PATH_PHOTO, contact, field);
+		filename = g_strdup_printf ("%s_%02d.data", tmp, i);
+		g_free (tmp);
+		i++;
+	} while (g_file_test (filename, G_FILE_TEST_EXISTS));
+
+	return filename;
+}
+
+static PhotoModifiedStatus
+maybe_transform_vcard_field_for_photo (EBookBackendFile *bf,
+				       ESource          *source,
+				       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         *directory;
+		gchar         *uri;
+
+		/* XXX Create a good file extension based on mime type */
+		new_photo_path = safe_name_for_photo (bf, source, contact, field);
+		directory      = g_path_get_dirname (new_photo_path);
+		g_free (directory);
+
+		if ((uri = 
+		     g_filename_to_uri (new_photo_path, NULL, error)) == NULL) {
+
+			status = STATUS_ERROR;
+		} else if (!create_directory (directory, error)) {
+
+			g_free (uri);
+			status = STATUS_ERROR;
+		} else if (!g_file_set_contents (new_photo_path,
+						 (const gchar *)photo->data.inlined.data,
+						 photo->data.inlined.length,
+						 error)) {
+
+			g_free (uri);
+			status = STATUS_ERROR;
+		} else {
+			g_message ("Transforming vcard photo to uri %s\n", uri);
+
+			new_photo           = g_new (EContactPhoto, 1);
+			new_photo->type     = E_CONTACT_PHOTO_TYPE_URI;
+			new_photo->data.uri = uri;
+
+			e_contact_set (contact, field, new_photo);
+
+			status = STATUS_MODIFIED;
+		}
+
+		g_free (new_photo_path);
+	}
+
+	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         *contact,
+				 gchar           **vcard_ret,
+				 GError          **error)
+{
+	EBookBackend *backend = E_BOOK_BACKEND (bf);
+	ESource      *source  = e_book_backend_get_source (backend);
+	gboolean      modified = FALSE;
+	PhotoModifiedStatus status;
+
+	status   = maybe_transform_vcard_field_for_photo (bf, source, contact, E_CONTACT_PHOTO, error);
+	modified = (status == STATUS_MODIFIED);
+
+	if (status != STATUS_ERROR) {
+		status   = maybe_transform_vcard_field_for_photo (bf, source, contact, E_CONTACT_LOGO, error);
+		modified = modified || (status == STATUS_MODIFIED);
+	}
+
+	if (status != STATUS_ERROR) {
+		if (modified) {
+			*vcard_ret = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+			status = STATUS_MODIFIED;
+		}
+	}
+
+	return status;
+}
+
+
+
 static void
 db_error_to_gerror (const gint db_error, GError **perror)
 {
@@ -195,18 +402,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 +434,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,9 +450,43 @@ 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) {
 
-	return db_error == 0;
+		gchar *transformed_vcard = NULL;
+
+		status = maybe_transform_vcard_for_photo (bf, *contact, &transformed_vcard, perror);
+
+		if (status == STATUS_MODIFIED && transformed_vcard) {
+			/* XXX
+			 *
+			 * Here we need to mark the newly entered contact as
+			 * modified and tell the sender that it's contact was
+			 * implicitly modified.
+			 *
+			 */
+			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 && status == STATUS_NORMAL;
 }
 
 static void
@@ -275,8 +517,8 @@ 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;
@@ -306,6 +548,11 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 		gchar *id = l->data;
 		e_book_backend_summary_remove_contact (bf->priv->summary, id);
 	}
+
+	/* XXX Collect the uris that need to be removed and track the views
+	 * until they've received the remove notification before actually
+	 * deleting the photo fields.
+	 */
 }
 
 static void
@@ -1018,31 +1265,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 +1477,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, NULL, 0);
 	filename = g_build_filename (dirname, "addressbook.db", NULL);
 
 	db_error = e_db3_utils_maybe_recover (filename);
@@ -1369,18 +1591,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;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]