[evolution-data-server] Add a migration routine to EDataBookFactory.



commit 31e8a90beaa049f48cba7b7c8632c36d6b125772
Author: Matthew Barnes <mbarnes redhat com>
Date:   Tue Jun 8 09:21:05 2010 -0400

    Add a migration routine to EDataBookFactory.
    
    Migration runs just before the main loop starts.
    It's just a sequence of local directory renames.
    
    ~/.evolution/cache/addressbook  -->  $XDG_CACHE_HOME/evolution/addressbook
    ~/.evolution/addressbook/local  -->  $XDG_DATA_HOME/evolution/addressbook

 addressbook/libedata-book/Makefile.am           |    5 +-
 addressbook/libedata-book/e-data-book-factory.c |    6 +
 addressbook/libedata-book/e-data-book-migrate.c |  283 +++++++++++++++++++++++
 3 files changed, 293 insertions(+), 1 deletions(-)
---
diff --git a/addressbook/libedata-book/Makefile.am b/addressbook/libedata-book/Makefile.am
index 26351b0..1e27886 100644
--- a/addressbook/libedata-book/Makefile.am
+++ b/addressbook/libedata-book/Makefile.am
@@ -82,7 +82,10 @@ e_addressbook_factory_CPPFLAGS = \
 	-I$(top_srcdir)/addressbook		\
 	-I$(top_builddir)/addressbook
 
-e_addressbook_factory_SOURCES = e-data-book-factory.c e-data-book-factory.h
+e_addressbook_factory_SOURCES = \
+	e-data-book-factory.c \
+	e-data-book-factory.h \
+	e-data-book-migrate.c
 
 e_addressbook_factory_LDADD =					\
 	libedata-book-1.2.la					\
diff --git a/addressbook/libedata-book/e-data-book-factory.c b/addressbook/libedata-book/e-data-book-factory.c
index 230d730..fded31a 100644
--- a/addressbook/libedata-book/e-data-book-factory.c
+++ b/addressbook/libedata-book/e-data-book-factory.c
@@ -81,6 +81,9 @@ struct _EDataBookFactoryPrivate {
         gint mode;
 };
 
+/* Forward Declarations */
+void e_data_book_migrate (void);
+
 /* Create the EDataBookFactory error quark */
 GQuark
 e_data_book_factory_error_quark (void)
@@ -528,6 +531,9 @@ main (gint argc, gchar **argv)
 
 	printf ("Server is up and running...\n");
 
+	/* Migrate user data from ~/.evolution to XDG base directories. */
+	e_data_book_migrate ();
+
 	g_main_loop_run (loop);
 
 	g_object_unref (eol);
diff --git a/addressbook/libedata-book/e-data-book-migrate.c b/addressbook/libedata-book/e-data-book-migrate.c
new file mode 100644
index 0000000..3b006a0
--- /dev/null
+++ b/addressbook/libedata-book/e-data-book-migrate.c
@@ -0,0 +1,283 @@
+/*
+ * e-data-book-migrate.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <errno.h>
+#include <glib/gstdio.h>
+#include <libedataserver/e-data-server-util.h>
+
+void e_data_book_migrate (void);
+
+static gboolean
+data_book_migrate_rename (const gchar *old_filename,
+                          const gchar *new_filename)
+{
+	gboolean success = TRUE;
+
+	if (g_file_test (old_filename, G_FILE_TEST_IS_DIR)) {
+		g_print ("  mv %s %s\n", old_filename, new_filename);
+		if (g_rename (old_filename, new_filename) < 0) {
+			g_printerr ("  FAILED: %s\n", g_strerror (errno));
+			success = FALSE;
+		}
+	}
+
+	return success;
+}
+
+static gboolean
+data_book_migrate_rmdir (const gchar *dirname)
+{
+	gboolean success = TRUE;
+
+	if (g_file_test (dirname, G_FILE_TEST_IS_DIR)) {
+		g_print ("  rmdir %s\n", dirname);
+		if (g_rmdir (dirname) < 0) {
+			g_printerr ("  FAILED: %s\n", g_strerror (errno));
+			success = FALSE;
+		}
+	}
+
+	return success;
+}
+
+static void
+data_book_migrate_process_corrections (GHashTable *corrections)
+{
+	GHashTableIter iter;
+	gpointer old_filename;
+	gpointer new_filename;
+
+	g_hash_table_iter_init (&iter, corrections);
+
+	while (g_hash_table_iter_next (&iter, &old_filename, &new_filename)) {
+		data_book_migrate_rename (old_filename, new_filename);
+		g_hash_table_iter_remove (&iter);
+	}
+}
+
+static gboolean
+data_book_migrate_move_contents (const gchar *src_directory,
+                                 const gchar *dst_directory)
+{
+	GDir *dir;
+	GHashTable *corrections;
+	const gchar *basename;
+
+	dir = g_dir_open (src_directory, 0, NULL);
+	if (dir == NULL)
+		return FALSE;
+
+	/* This is to avoid renaming files while we're iterating over the
+	 * directory.  POSIX says the outcome of that is unspecified. */
+	corrections = g_hash_table_new_full (
+		g_str_hash, g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_free);
+
+	g_mkdir_with_parents (dst_directory, 0700);
+
+	while ((basename = g_dir_read_name (dir)) != NULL) {
+		gchar *old_filename;
+		gchar *new_filename;
+
+		old_filename = g_build_filename (src_directory, basename, NULL);
+		new_filename = g_build_filename (dst_directory, basename, NULL);
+
+		g_hash_table_insert (corrections, old_filename, new_filename);
+	}
+
+	g_dir_close (dir);
+
+	data_book_migrate_process_corrections (corrections);
+	g_hash_table_destroy (corrections);
+
+	/* It's tempting to want to remove the source directory here.
+	 * Don't.  We might be iterating over the source directory's
+	 * parent directory, and removing the source directory would
+	 * screw up the iteration. */
+
+	return TRUE;
+}
+
+static void
+data_book_migrate_fix_groupwise_bug (const gchar *old_base_dir)
+{
+	GDir *dir;
+	GHashTable *corrections;
+	const gchar *basename;
+	gchar *old_data_dir;
+	gchar *old_cache_dir;
+
+	/* The groupwise backend mistakenly put its addressbook
+	 * cache files in ~/.evolution/addressbook instead of
+	 * ~/.evolution/cache/addressbook.  Fix that before
+	 * we migrate the cache directory. */
+
+	old_data_dir = g_build_filename (old_base_dir, "addressbook", NULL);
+	old_cache_dir = g_build_filename (old_base_dir, "cache", "addressbook", NULL);
+
+	dir = g_dir_open (old_data_dir, 0, NULL);
+	if (dir == NULL)
+		goto exit;
+
+	/* This is to avoid renaming files while we're iterating over the
+	 * directory.  POSIX says the outcome of that is unspecified. */
+	corrections = g_hash_table_new_full (
+		g_str_hash, g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_free);
+
+	while ((basename = g_dir_read_name (dir)) != NULL) {
+		gchar *old_filename;
+		gchar *new_filename;
+
+		if (!g_str_has_prefix (basename, "groupwise___"))
+			continue;
+
+		old_filename = g_build_filename (old_data_dir, basename, NULL);
+		new_filename = g_build_filename (old_cache_dir, basename, NULL);
+
+		g_hash_table_insert (corrections, old_filename, new_filename);
+	}
+
+	g_dir_close (dir);
+
+	data_book_migrate_process_corrections (corrections);
+	g_hash_table_destroy (corrections);
+
+exit:
+	g_free (old_data_dir);
+	g_free (old_cache_dir);
+}
+
+static void
+data_book_migrate_to_user_cache_dir (const gchar *old_base_dir)
+{
+	const gchar *new_cache_dir;
+	gchar *old_cache_dir;
+	gchar *src_directory;
+	gchar *dst_directory;
+
+	old_cache_dir = g_build_filename (old_base_dir, "cache", NULL);
+	new_cache_dir = e_get_user_cache_dir ();
+
+	g_print ("Migrating cached backend data\n");
+
+	/* We don't want to move the source directory directly because the
+	 * destination directory may already exist with content.  Instead
+	 * we want to merge the content of the source directory into the
+	 * destination directory.
+	 *
+	 * For example, given:
+	 *
+	 *    $(src_directory)/A   and   $(dst_directory)/B
+	 *    $(src_directory)/C
+	 *
+	 * we want to end up with:
+	 *
+	 *    $(dst_directory)/A
+	 *    $(dst_directory)/B
+	 *    $(dst_directory)/C
+	 *
+	 * Any name collisions will be left in the source directory.
+	 */
+
+	src_directory = g_build_filename (old_cache_dir, "addressbook", NULL);
+	dst_directory = g_build_filename (new_cache_dir, "addressbook", NULL);
+
+	data_book_migrate_move_contents (src_directory, dst_directory);
+	data_book_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+
+	/* Try to remove the old cache directory.  Good chance this will
+	 * fail on the first try, since Evolution puts stuff here too. */
+	data_book_migrate_rmdir (old_cache_dir);
+
+	g_free (old_cache_dir);
+}
+
+static void
+data_book_migrate_to_user_data_dir (const gchar *old_base_dir)
+{
+	const gchar *new_data_dir;
+	gchar *src_directory;
+	gchar *dst_directory;
+
+	new_data_dir = e_get_user_data_dir ();
+
+	g_print ("Migrating local backend data\n");
+
+	/* We don't want to move the source directory directly because the
+	 * destination directory may already exist with content.  Instead
+	 * we want to merge the content of the source directory into the
+	 * destination directory.
+	 *
+	 * For example, given:
+	 *
+	 *    $(src_directory)/A   and   $(dst_directory)/B
+	 *    $(src_directory)/C
+	 *
+	 * we want to end up with:
+	 *
+	 *    $(dst_directory)/A
+	 *    $(dst_directory)/B
+	 *    $(dst_directory)/C
+	 *
+	 * Any name collisions will be left in the source directory.
+	 */
+
+	src_directory = g_build_filename (old_base_dir, "addressbook", "local", NULL);
+	dst_directory = g_build_filename (new_data_dir, "addressbook", NULL);
+
+	data_book_migrate_move_contents (src_directory, dst_directory);
+	data_book_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+}
+
+void
+e_data_book_migrate (void)
+{
+	const gchar *home_dir;
+	gchar *old_base_dir;
+
+	/* XXX This blocks, but it's all just local directory
+	 *     renames so it should be nearly instantaneous. */
+
+	home_dir = g_get_home_dir ();
+	old_base_dir = g_build_filename (home_dir, ".evolution", NULL);
+
+	/* Is there even anything to migrate? */
+	if (!g_file_test (old_base_dir, G_FILE_TEST_IS_DIR))
+		goto exit;
+
+	data_book_migrate_fix_groupwise_bug (old_base_dir);
+
+	data_book_migrate_to_user_cache_dir (old_base_dir);
+	data_book_migrate_to_user_data_dir (old_base_dir);
+
+	/* Try to remove the old base directory.  Good chance this will
+	 * fail on the first try, since Evolution puts stuff here too. */
+	data_book_migrate_rmdir (old_base_dir);
+
+exit:
+	g_free (old_base_dir);
+}



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