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



commit b44a797f95532c4da253b50b7cfc79e5c3b455ec
Author: Matthew Barnes <mbarnes redhat com>
Date:   Tue Jun 8 08:58:10 2010 -0400

    Add a migration routine to EDataCalFactory.
    
    Migration runs just before the main loop starts.
    It's just a sequence of local directory renames.
    
    ~/.evolution/cache/calendar  -->  $XDG_CACHE_HOME/evolution/calendar
    ~/.evolution/cache/memos     -->  $XDG_CACHE_HOME/evolution/memos
    ~/.evolution/cache/tasks     -->  $XDG_CACHE_HOME/evolution/tasks
    ~/.evolution/calendar/local  -->  $XDG_DATA_HOME/evolution/calendar
    ~/.evolution/memos/local     -->  $XDG_DATA_HOME/evolution/memos
    ~/.evolution/tasks/local     -->  $XDG_DATA_HOME/evolution/tasks
    
    We also migrate Evolution-Exchange account storage:
    
    ~/.evolution/exchange        --> $XDG_DATA_HOME/evolution/exchange
    
    (After first cleaning up Exchange attachment cache directories.)

 calendar/libedata-cal/Makefile.am          |    1 +
 calendar/libedata-cal/e-data-cal-factory.c |    6 +
 calendar/libedata-cal/e-data-cal-migrate.c |  353 ++++++++++++++++++++++++++++
 3 files changed, 360 insertions(+), 0 deletions(-)
---
diff --git a/calendar/libedata-cal/Makefile.am b/calendar/libedata-cal/Makefile.am
index c32c681..ef5f904 100644
--- a/calendar/libedata-cal/Makefile.am
+++ b/calendar/libedata-cal/Makefile.am
@@ -98,6 +98,7 @@ factory_PROGRAMS = e-calendar-factory
 e_calendar_factory_SOURCES = \
 	e-data-cal-factory.c \
 	e-data-cal-factory.h \
+	e-data-cal-migrate.c \
 	e-cal-backend-loader-factory.c \
 	e-cal-backend-loader-factory.h
 
diff --git a/calendar/libedata-cal/e-data-cal-factory.c b/calendar/libedata-cal/e-data-cal-factory.c
index a69cf38..cd3e20a 100644
--- a/calendar/libedata-cal/e-data-cal-factory.c
+++ b/calendar/libedata-cal/e-data-cal-factory.c
@@ -91,6 +91,9 @@ struct _EDataCalFactoryPrivate {
 	guint exit_timeout;
 };
 
+/* Forward Declarations */
+void e_data_cal_migrate (void);
+
 /* Create the EDataCalFactory error quark */
 GQuark
 e_data_cal_factory_error_quark (void)
@@ -846,6 +849,9 @@ main (gint argc, gchar **argv)
 
 	printf ("Server is up and running...\n");
 
+	/* Migrate user data from ~/.evolution to XDG base directories. */
+	e_data_cal_migrate ();
+
 	g_main_loop_run (loop);
 
 	dbus_g_connection_unref (connection);
diff --git a/calendar/libedata-cal/e-data-cal-migrate.c b/calendar/libedata-cal/e-data-cal-migrate.c
new file mode 100644
index 0000000..5fce9ee
--- /dev/null
+++ b/calendar/libedata-cal/e-data-cal-migrate.c
@@ -0,0 +1,353 @@
+/*
+ * e-data-cal-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_cal_migrate (void);
+
+static gboolean
+data_cal_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_cal_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_cal_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_cal_migrate_rename (old_filename, new_filename);
+		g_hash_table_iter_remove (&iter);
+	}
+}
+
+static gboolean
+data_cal_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_cal_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_cal_migrate_fix_exchange_bug (const gchar *old_base_dir)
+{
+	GDir *dir;
+	GHashTable *corrections;
+	const gchar *basename;
+	gchar *exchange_dir;
+	gchar *old_cache_dir;
+
+	/* The exchange backend mistakenly cached calendar attachments in
+	 * ~/.evolution/exchange instead of ~/.evolution/cache/calendar.
+	 * Fix that before we migrate the cache directory. */
+
+	exchange_dir = g_build_filename (old_base_dir, "exchange", NULL);
+	old_cache_dir = g_build_filename (old_base_dir, "cache", "calendar", NULL);
+
+	dir = g_dir_open (exchange_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, "exchange___"))
+			continue;
+
+		old_filename = g_build_filename (exchange_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_cal_migrate_process_corrections (corrections);
+	g_hash_table_destroy (corrections);
+
+exit:
+	g_free (exchange_dir);
+	g_free (old_cache_dir);
+}
+
+static void
+data_cal_migrate_fix_memos_cache_bug (const gchar *old_base_dir)
+{
+	gchar *src_directory;
+	gchar *dst_directory;
+
+	/* Some calendar backends cached memo data under
+	 * ~/.evolution/cache/journal instead of ~/.evolution/cache/memos.
+	 * Fix that before we migrate the cache directory. */
+
+	src_directory = g_build_filename (old_base_dir, "cache", "journal", NULL);
+	dst_directory = g_build_filename (old_base_dir, "cache", "memos", NULL);
+
+	data_cal_migrate_move_contents (src_directory, dst_directory);
+	data_cal_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+}
+
+static void
+data_cal_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, "calendar", NULL);
+	dst_directory = g_build_filename (new_cache_dir, "calendar", NULL);
+
+	data_cal_migrate_move_contents (src_directory, dst_directory);
+	data_cal_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+
+	src_directory = g_build_filename (old_cache_dir, "memos", NULL);
+	dst_directory = g_build_filename (new_cache_dir, "memos", NULL);
+
+	data_cal_migrate_move_contents (src_directory, dst_directory);
+	data_cal_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+
+	src_directory = g_build_filename (old_cache_dir, "tasks", NULL);
+	dst_directory = g_build_filename (new_cache_dir, "tasks", NULL);
+
+	data_cal_migrate_move_contents (src_directory, dst_directory);
+	data_cal_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_cal_migrate_rmdir (old_cache_dir);
+
+	g_free (old_cache_dir);
+}
+
+static void
+data_cal_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, "calendar", "local", NULL);
+	dst_directory = g_build_filename (new_data_dir, "calendar", NULL);
+
+	data_cal_migrate_move_contents (src_directory, dst_directory);
+	data_cal_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+
+	src_directory = g_build_filename (old_base_dir, "memos", "local", NULL);
+	dst_directory = g_build_filename (new_data_dir, "memos", NULL);
+
+	data_cal_migrate_move_contents (src_directory, dst_directory);
+	data_cal_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+
+	src_directory = g_build_filename (old_base_dir, "tasks", "local", NULL);
+	dst_directory = g_build_filename (new_data_dir, "tasks", NULL);
+
+	data_cal_migrate_move_contents (src_directory, dst_directory);
+	data_cal_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+
+	/* XXX This is not really the right place to be migrating
+	 *     exchange data, but since we already cleaned out the
+	 *     cached attachment files from this directory, may as
+	 *     well move the user accounts too while we're at it. */
+
+	src_directory = g_build_filename (old_base_dir, "exchange", NULL);
+	dst_directory = g_build_filename (new_data_dir, "exchange", NULL);
+
+	data_cal_migrate_move_contents (src_directory, dst_directory);
+	data_cal_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+}
+
+void
+e_data_cal_migrate (void)
+{
+	const gchar *home_dir;
+	gchar *old_base_dir;
+
+	/* XXX This blocks, but it's all just local file
+	 *     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_cal_migrate_fix_exchange_bug (old_base_dir);
+	data_cal_migrate_fix_memos_cache_bug (old_base_dir);
+
+	data_cal_migrate_to_user_cache_dir (old_base_dir);
+	data_cal_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_cal_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]