[evolution] Migrate ~/.evolution to XDG base directories.



commit 6affbe33e90b4157380eff3dad6a9654c9a24443
Author: Matthew Barnes <mbarnes redhat com>
Date:   Thu Jul 22 17:07:52 2010 -0400

    Migrate ~/.evolution to XDG base directories.
    
    Migration runs just before the main loop starts.
    It's just a sequence of local directory and file renames.

 e-util/e-datetime-format.c             |    2 +-
 e-util/e-print.c                       |    2 +-
 e-util/e-signature.c                   |   39 +++-
 e-util/e-util.c                        |    4 +-
 mail/e-mail-backend.c                  |   16 +-
 mail/em-utils.c                        |    2 +-
 mail/mail-config.c                     |    2 +-
 mail/mail-session.c                    |   10 +-
 modules/calendar/e-cal-shell-content.c |   15 +-
 shell/e-shell-backend.c                |   39 ++--
 shell/e-shell-content.c                |    6 +-
 shell/e-shell-migrate.c                |  492 ++++++++++++++++++++++++++++++++
 shell/e-shell-migrate.h                |    4 +-
 shell/e-shell-view.c                   |   14 +-
 14 files changed, 589 insertions(+), 58 deletions(-)
---
diff --git a/e-util/e-datetime-format.c b/e-util/e-datetime-format.c
index 37226d4..f927a27 100644
--- a/e-util/e-datetime-format.c
+++ b/e-util/e-datetime-format.c
@@ -25,7 +25,7 @@
 #include "e-datetime-format.h"
 #include "e-util.h"
 
-#define KEYS_FILENAME "datetime-formats"
+#define KEYS_FILENAME "datetime-formats.ini"
 #define KEYS_GROUPNAME "formats"
 
 #ifdef G_OS_WIN32
diff --git a/e-util/e-print.c b/e-util/e-print.c
index bb5ce90..756381f 100644
--- a/e-util/e-print.c
+++ b/e-util/e-print.c
@@ -37,7 +37,7 @@
 static gchar *
 key_file_get_filename (void)
 {
-	return g_build_filename (e_get_user_data_dir (), "printing", NULL);
+	return g_build_filename (e_get_user_data_dir (), "printing.ini", NULL);
 }
 
 static void
diff --git a/e-util/e-signature.c b/e-util/e-signature.c
index c4ae7ed..6eb84c3 100644
--- a/e-util/e-signature.c
+++ b/e-util/e-signature.c
@@ -32,6 +32,7 @@
 #include <gconf/gconf-client.h>
 
 #include <libedataserver/e-uid.h>
+#include <libedataserver/e-data-server-util.h>
 
 #include "e-signature.h"
 
@@ -493,6 +494,24 @@ e_signature_set_from_xml (ESignature *signature, const gchar *xml)
 			break;
 		}
 
+		/* If the signature is not a script, replace the directory
+		 * part with the current signatures directory.  This makes
+		 * moving the signatures directory transparent. */
+		if (!e_signature_get_is_script (signature)) {
+			const gchar *user_data_dir;
+			gchar *basename;
+			gchar *filename;
+
+			user_data_dir = e_get_user_data_dir ();
+
+			filename = signature->priv->filename;
+			basename = g_path_get_basename (filename);
+			signature->priv->filename = g_build_filename (
+				user_data_dir, "signatures", basename, NULL);
+			g_free (basename);
+			g_free (filename);
+		}
+
 		cur = cur->next;
 	}
 
@@ -544,13 +563,25 @@ e_signature_to_xml (ESignature *signature)
 
 		string = e_signature_get_filename (signature);
 		if (string != NULL) {
-			node = xmlNewTextChild (
-				root, NULL, (xmlChar *) "filename",
-				(xmlChar *) string);
-			if (e_signature_get_is_script (signature))
+
+			/* For scripts we save the full filename,
+			 * for normal signatures just the basename. */
+			if (e_signature_get_is_script (signature)) {
+				node = xmlNewTextChild (
+					root, NULL, (xmlChar *) "filename",
+					(xmlChar *) string);
 				xmlSetProp (
 					node, (xmlChar *) "script",
 					(xmlChar *) "true");
+			} else {
+				gchar *basename;
+
+				basename = g_path_get_basename (string);
+				node = xmlNewTextChild (
+					root, NULL, (xmlChar *) "filename",
+					(xmlChar *) basename);
+				g_free (basename);
+			}
 		}
 	} else {
 		/* this is to make Evolution-1.4 and older 1.5 versions happy */
diff --git a/e-util/e-util.c b/e-util/e-util.c
index 7d45709..3f27c52 100644
--- a/e-util/e-util.c
+++ b/e-util/e-util.c
@@ -1301,8 +1301,6 @@ get_font_options (void)
 
 /* Evolution Locks for crash recovery */
 
-#define LOCK_FILE ".running"
-
 static const gchar *
 get_lock_filename (void)
 {
@@ -1310,7 +1308,7 @@ get_lock_filename (void)
 
 	if (G_UNLIKELY (filename == NULL))
 		filename = g_build_filename (
-			e_get_user_data_dir (), LOCK_FILE, NULL);
+			e_get_user_config_dir (), ".running", NULL);
 
 	return filename;
 }
diff --git a/mail/e-mail-backend.c b/mail/e-mail-backend.c
index b2e22e3..14a7d22 100644
--- a/mail/e-mail-backend.c
+++ b/mail/e-mail-backend.c
@@ -368,6 +368,17 @@ mail_backend_folder_changed_cb (MailFolderCache *folder_cache,
 		(EEventTarget *) target);
 }
 
+static gboolean
+mail_backend_idle_cb (EShellBackend *shell_backend)
+{
+	const gchar *data_dir;
+
+	data_dir = e_shell_backend_get_data_dir (shell_backend);
+	e_mail_store_init (data_dir);
+
+	return FALSE;
+}
+
 static void
 mail_backend_constructed (GObject *object)
 {
@@ -422,8 +433,9 @@ mail_backend_constructed (GObject *object)
 	mail_config_init ();
 	mail_msg_init ();
 
-	data_dir = e_shell_backend_get_data_dir (shell_backend);
-	e_mail_store_init (data_dir);
+	/* Defer initializing CamelStores until after the main loop
+	 * has started, so migration has a chance to run first. */
+	g_idle_add ((GSourceFunc) mail_backend_idle_cb, shell_backend);
 }
 
 static void
diff --git a/mail/em-utils.c b/mail/em-utils.c
index e182e61..80699ec 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -2342,7 +2342,7 @@ emu_restore_folder_tree_state (EMFolderTree *folder_tree)
 	config_dir = e_shell_backend_get_config_dir (backend);
 	g_return_if_fail (config_dir != NULL);
 
-	filename = g_build_filename (config_dir, "state", NULL);
+	filename = g_build_filename (config_dir, "state.ini", NULL);
 
 	key_file = g_key_file_new ();
 	g_key_file_load_from_file (key_file, filename, 0, &error);
diff --git a/mail/mail-config.c b/mail/mail-config.c
index 8c76353..8bbd9fc 100644
--- a/mail/mail-config.c
+++ b/mail/mail-config.c
@@ -535,7 +535,7 @@ mail_config_folder_to_cachename (CamelFolder *folder, const gchar *prefix)
 	config_dir = mail_session_get_config_dir ();
 	url = mail_config_folder_to_safe_url (folder);
 	basename = g_strdup_printf ("%s%s", prefix, url);
-	filename = g_build_filename (config_dir, basename, NULL);
+	filename = g_build_filename (config_dir, "folders", basename, NULL);
 	g_free (basename);
 	g_free (url);
 
diff --git a/mail/mail-session.c b/mail/mail-session.c
index 1ccb87c..0396401 100644
--- a/mail/mail-session.c
+++ b/mail/mail-session.c
@@ -1007,15 +1007,9 @@ mail_session_get_data_dir (void)
 const gchar *
 mail_session_get_config_dir (void)
 {
-	if (G_UNLIKELY (mail_config_dir == NULL)) {
+	if (G_UNLIKELY (mail_config_dir == NULL))
 		mail_config_dir = g_build_filename (
-			mail_session_get_data_dir (), "config", NULL);
-
-		if (g_mkdir_with_parents (mail_config_dir, 0777) != 0)
-			g_critical (
-				"Cannot create directory %s: %s",
-				mail_config_dir, g_strerror (errno));
-	}
+			e_get_user_config_dir (), "mail", NULL);
 
 	return mail_config_dir;
 }
diff --git a/modules/calendar/e-cal-shell-content.c b/modules/calendar/e-cal-shell-content.c
index 146d9b9..737c387 100644
--- a/modules/calendar/e-cal-shell-content.c
+++ b/modules/calendar/e-cal-shell-content.c
@@ -141,7 +141,8 @@ cal_shell_content_notify_view_id_cb (ECalShellContent *cal_shell_content)
 }
 
 static gchar *
-cal_chell_content_get_pad_state_filename (EShellContent *shell_content, ETable *table)
+cal_shell_content_get_pad_state_filename (EShellContent *shell_content,
+                                          ETable *table)
 {
 	EShellBackend *shell_backend;
 	EShellView *shell_view;
@@ -167,11 +168,13 @@ cal_chell_content_get_pad_state_filename (EShellContent *shell_content, ETable *
 }
 
 static void
-cal_shell_content_save_table_state (EShellContent *shell_content, ETable *table)
+cal_shell_content_save_table_state (EShellContent *shell_content,
+                                    ETable *table)
 {
 	gchar *filename;
 
-	filename = cal_chell_content_get_pad_state_filename (shell_content, table);
+	filename = cal_shell_content_get_pad_state_filename (
+		shell_content, table);
 	g_return_if_fail (filename != NULL);
 
 	e_table_save_state (table, filename);
@@ -179,11 +182,13 @@ cal_shell_content_save_table_state (EShellContent *shell_content, ETable *table)
 }
 
 static void
-cal_shell_content_load_table_state (EShellContent *shell_content, ETable *table)
+cal_shell_content_load_table_state (EShellContent *shell_content,
+                                    ETable *table)
 {
 	gchar *filename;
 
-	filename = cal_chell_content_get_pad_state_filename (shell_content, table);
+	filename = cal_shell_content_get_pad_state_filename (
+		shell_content, table);
 	g_return_if_fail (filename != NULL);
 
 	e_table_load_state (table, filename);
diff --git a/shell/e-shell-backend.c b/shell/e-shell-backend.c
index 570e58d..c0fd14a 100644
--- a/shell/e-shell-backend.c
+++ b/shell/e-shell-backend.c
@@ -123,22 +123,18 @@ shell_backend_finalize (GObject *object)
 static const gchar *
 shell_backend_get_config_dir (EShellBackend *shell_backend)
 {
+	EShellBackendClass *class;
+
+	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
+
+	/* Determine the user config directory for this backend. */
 	if (G_UNLIKELY (shell_backend->priv->config_dir == NULL)) {
-		const gchar *data_dir;
-		gchar *config_dir;
-
-		/* Determine the user configuration directory
-		 * for this backend. */
-		data_dir = e_shell_backend_get_data_dir (shell_backend);
-		config_dir = g_build_filename (data_dir, "config", NULL);
-		shell_backend->priv->config_dir = config_dir;
-
-		/* Create the user configuration directory for this backend,
-		 * which should also create the user data directory. */
-		if (g_mkdir_with_parents (config_dir, 0700) != 0)
-			g_critical (
-				"Cannot create directory %s: %s",
-				config_dir, g_strerror (errno));
+		const gchar *user_config_dir;
+
+		user_config_dir = e_get_user_config_dir ();
+		shell_backend->priv->config_dir =
+			g_build_filename (user_config_dir, class->name, NULL);
+		g_mkdir_with_parents (shell_backend->priv->config_dir, 0700);
 	}
 
 	return shell_backend->priv->config_dir;
@@ -149,14 +145,17 @@ shell_backend_get_data_dir (EShellBackend *shell_backend)
 {
 	EShellBackendClass *class;
 
-	g_return_val_if_fail (E_IS_SHELL_BACKEND (shell_backend), NULL);
-
 	class = E_SHELL_BACKEND_GET_CLASS (shell_backend);
 
 	/* Determine the user data directory for this backend. */
-	if (G_UNLIKELY (shell_backend->priv->data_dir == NULL))
-		shell_backend->priv->data_dir = g_build_filename (
-			e_get_user_data_dir (), class->name, NULL);
+	if (G_UNLIKELY (shell_backend->priv->data_dir == NULL)) {
+		const gchar *user_data_dir;
+
+		user_data_dir = e_get_user_data_dir ();
+		shell_backend->priv->data_dir =
+			g_build_filename (user_data_dir, class->name, NULL);
+		g_mkdir_with_parents (shell_backend->priv->data_dir, 0700);
+	}
 
 	return shell_backend->priv->data_dir;
 }
diff --git a/shell/e-shell-content.c b/shell/e-shell-content.c
index d897e11..7b1fe4e 100644
--- a/shell/e-shell-content.c
+++ b/shell/e-shell-content.c
@@ -149,7 +149,7 @@ shell_content_constructed (GObject *object)
 	EShellContent *shell_content;
 	EShellBackend *shell_backend;
 	EShellView *shell_view;
-	const gchar *data_dir;
+	const gchar *config_dir;
 
 	shell_content = E_SHELL_CONTENT (object);
 	shell_view = e_shell_content_get_shell_view (shell_content);
@@ -159,9 +159,9 @@ shell_content_constructed (GObject *object)
 	 *     in shell_view_init_search_context().  ERuleContext ought
 	 *     to remember the filename when loading rules so you don't
 	 *     have to keep passing it in when saving rules. */
-	data_dir = e_shell_backend_get_data_dir (shell_backend);
+	config_dir = e_shell_backend_get_config_dir (shell_backend);
 	shell_content->priv->user_filename =
-		g_build_filename (data_dir, "searches.xml", NULL);
+		g_build_filename (config_dir, "searches.xml", NULL);
 
 	e_extensible_load_extensions (E_EXTENSIBLE (object));
 }
diff --git a/shell/e-shell-migrate.c b/shell/e-shell-migrate.c
index 45ed5a1..cc30366 100644
--- a/shell/e-shell-migrate.c
+++ b/shell/e-shell-migrate.c
@@ -21,6 +21,7 @@
 
 #include "e-shell-migrate.h"
 
+#include <errno.h>
 #include <string.h>
 #include <unistd.h>
 #include <glib/gi18n.h>
@@ -36,6 +37,493 @@
 #define GCONF_VERSION_KEY	"/apps/evolution/version"
 #define GCONF_LAST_VERSION_KEY	"/apps/evolution/last_version"
 
+/******************** Begin XDG Base Directory Migration ********************/
+
+/* These are the known EShellBackend names as of Evolution 3.0 */
+static const gchar *shell_backend_names[] =
+	{ "addressbook", "calendar", "mail", "memos", "tasks", NULL };
+
+static gboolean
+shell_xdg_migrate_rename (const gchar *old_filename,
+                          const gchar *new_filename)
+{
+	gboolean success = TRUE;
+
+	if (g_file_test (old_filename, G_FILE_TEST_EXISTS)) {
+		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
+shell_xdg_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
+shell_xdg_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)) {
+		gboolean is_directory;
+
+		is_directory = g_file_test (old_filename, G_FILE_TEST_IS_DIR);
+
+		/* If the old filename is a directory and the new filename
+		 * is NULL, treat it as a request to remove the directory. */
+		if (is_directory && new_filename == NULL)
+			shell_xdg_migrate_rmdir (old_filename);
+		else
+			shell_xdg_migrate_rename (old_filename, new_filename);
+
+		g_hash_table_iter_remove (&iter);
+	}
+}
+
+static gboolean
+shell_xdg_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 which 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);
+
+	shell_xdg_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
+shell_xdg_migrate_cache_dir (EShell *shell,
+                             const gchar *old_base_dir)
+{
+	const gchar *new_cache_dir;
+	gchar *old_cache_dir;
+	gchar *old_filename;
+	gchar *new_filename;
+
+	old_cache_dir = g_build_filename (old_base_dir, "cache", NULL);
+	new_cache_dir = e_get_user_cache_dir ();
+
+	g_print ("Migrating cached data\n");
+
+	g_mkdir_with_parents (new_cache_dir, 0700);
+
+	old_filename = g_build_filename (old_cache_dir, "http", NULL);
+	new_filename = g_build_filename (new_cache_dir, "http", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	/* Try to remove the old cache directory.  Good chance this will
+	 * fail on the first try, since E-D-S puts stuff here too. */
+	shell_xdg_migrate_rmdir (old_cache_dir);
+
+	g_free (old_cache_dir);
+}
+
+static void
+shell_xdg_migrate_config_dir_common (EShell *shell,
+                                     const gchar *old_base_dir,
+                                     const gchar *backend_name)
+{
+	const gchar *user_config_dir;
+	gchar *old_config_dir;
+	gchar *new_config_dir;
+	gchar *old_filename;
+	gchar *new_filename;
+
+	user_config_dir = e_get_user_config_dir ();
+
+	old_config_dir = g_build_filename (old_base_dir, backend_name, NULL);
+	new_config_dir = g_build_filename (user_config_dir, backend_name, NULL);
+
+	g_mkdir_with_parents (new_config_dir, 0700);
+
+	old_filename = g_build_filename (old_config_dir, "views", NULL);
+	new_filename = g_build_filename (new_config_dir, "views", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	old_filename = g_build_filename (old_config_dir, "searches.xml", NULL);
+	new_filename = g_build_filename (new_config_dir, "searches.xml", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	/* Subtle name change: config/state --> state.ini */
+	old_filename = g_build_filename (old_config_dir, "config", "state", NULL);
+	new_filename = g_build_filename (new_config_dir, "state.ini", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	g_free (old_config_dir);
+	g_free (new_config_dir);
+}
+
+static void
+shell_xdg_migrate_config_dir_calendar (EShell *shell,
+                                       const gchar *old_base_dir)
+{
+	const gchar *user_config_dir;
+	gchar *old_config_dir;
+	gchar *new_config_dir;
+	gchar *old_filename;
+	gchar *new_filename;
+
+	user_config_dir = e_get_user_config_dir ();
+
+	old_config_dir = g_build_filename (old_base_dir, "calendar", NULL);
+	new_config_dir = g_build_filename (user_config_dir, "calendar", NULL);
+
+	old_filename = g_build_filename (old_config_dir, "config", "TaskPad", NULL);
+	new_filename = g_build_filename (new_config_dir, "TaskPad", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	old_filename = g_build_filename (old_config_dir, "config", "MemoPad", NULL);
+	new_filename = g_build_filename (new_config_dir, "MemoPad", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	g_free (old_config_dir);
+	g_free (new_config_dir);
+}
+
+static void
+shell_xdg_migrate_config_dir_mail (EShell *shell,
+                                   const gchar *old_base_dir)
+{
+	const gchar *user_config_dir;
+	gchar *old_config_dir;
+	gchar *new_config_dir;
+	gchar *old_filename;
+	gchar *new_filename;
+
+	user_config_dir = e_get_user_config_dir ();
+
+	old_config_dir = g_build_filename (old_base_dir, "mail", NULL);
+	new_config_dir = g_build_filename (user_config_dir, "mail", NULL);
+
+	old_filename = g_build_filename (old_config_dir, "filters.xml", NULL);
+	new_filename = g_build_filename (new_config_dir, "filters.xml", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	old_filename = g_build_filename (old_config_dir, "vfolders.xml", NULL);
+	new_filename = g_build_filename (new_config_dir, "vfolders.xml", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	/* I hate this file.  GtkHtml uses style properties for fonts. */
+	old_filename = g_build_filename (old_config_dir, "config", "gtkrc-mail-fonts", NULL);
+	new_filename = g_build_filename (new_config_dir, "gtkrc-mail-fonts", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	/* Everything else in the "config" directory just should be
+	 * per-folder ETree files recording the expanded state of mail
+	 * threads.  Rename this directory to "folders". */
+	old_filename = g_build_filename (old_config_dir, "config", NULL);
+	new_filename = g_build_filename (new_config_dir, "folders", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	g_free (old_config_dir);
+	g_free (new_config_dir);
+}
+
+static void
+shell_xdg_migrate_config_dir_cleanup (EShell *shell,
+                                      const gchar *old_base_dir,
+                                      const gchar *backend_name)
+{
+	gchar *dirname;
+
+	dirname = g_build_filename (
+		old_base_dir, backend_name, "config", NULL);
+
+	shell_xdg_migrate_rmdir (dirname);
+
+	g_free (dirname);
+}
+
+static void
+shell_xdg_migrate_config_dir (EShell *shell,
+                              const gchar *old_base_dir)
+{
+	const gchar *old_config_dir;
+	const gchar *new_config_dir;
+	gchar *old_filename;
+	gchar *new_filename;
+	gint ii;
+
+	g_print ("Migrating config data\n");
+
+	/* Some files are common to all shell backends. */
+	for (ii = 0; shell_backend_names[ii] != NULL; ii++)
+		shell_xdg_migrate_config_dir_common (
+			shell, old_base_dir, shell_backend_names[ii]);
+
+	/* Handle backend-specific files. */
+	shell_xdg_migrate_config_dir_calendar (shell, old_base_dir);
+	shell_xdg_migrate_config_dir_mail (shell, old_base_dir);
+
+	/* Remove leftover "config" directories. */
+	for (ii = 0; shell_backend_names[ii] != NULL; ii++)
+		shell_xdg_migrate_config_dir_cleanup (
+			shell, old_base_dir, shell_backend_names[ii]);
+
+	/*** Miscellaneous configuration files. ***/
+
+	old_config_dir = old_base_dir;
+	new_config_dir = e_get_user_config_dir ();
+
+	/* Subtle name change: datetime-formats --> datefime-formats.ini */
+	old_filename = g_build_filename (old_config_dir, "datetime-formats", NULL);
+	new_filename = g_build_filename (new_config_dir, "datetime-formats.ini", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+
+	/* Subtle name change: printing --> printing.ini */
+	old_filename = g_build_filename (old_config_dir, "printing", NULL);
+	new_filename = g_build_filename (new_config_dir, "printing.ini", NULL);
+	shell_xdg_migrate_rename (old_filename, new_filename);
+	g_free (old_filename);
+	g_free (new_filename);
+}
+
+static void
+shell_xdg_migrate_data_dir (EShell *shell,
+                            const gchar *old_base_dir)
+{
+	GDir *dir;
+	GHashTable *corrections;
+	const gchar *basename;
+	const gchar *old_data_dir;
+	const gchar *new_data_dir;
+	gchar *src_directory;
+	gchar *dst_directory;
+
+	g_print ("Migrating local user data\n");
+
+	old_data_dir = old_base_dir;
+	new_data_dir = e_get_user_data_dir ();
+
+	/* The mail hierarchy is complex and Camel doesn't distinguish
+	 * between user data files and disposable cache files, so just
+	 * move everything to the data directory for now.  We'll sort
+	 * it out sometime down the road. */
+
+	src_directory = g_build_filename (old_data_dir, "mail", NULL);
+	dst_directory = g_build_filename (new_data_dir, "mail", NULL);
+
+	dir = g_dir_open (src_directory, 0, NULL);
+	if (dir == NULL)
+		goto skip_mail;
+
+	/* This is to avoid removing directories while we're iterating
+	 * over the parent 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);
+
+	/* Iterate over the base CamelProvider directories. */
+	while ((basename = g_dir_read_name (dir)) != NULL) {
+		gchar *provider_src_directory;
+		gchar *provider_dst_directory;
+
+		provider_src_directory =
+			g_build_filename (src_directory, basename, NULL);
+		provider_dst_directory =
+			g_build_filename (dst_directory, basename, NULL);
+
+		if (!g_file_test (provider_src_directory, G_FILE_TEST_IS_DIR)) {
+			g_free (provider_src_directory);
+			g_free (provider_dst_directory);
+			continue;
+		}
+
+		shell_xdg_migrate_move_contents (
+			provider_src_directory, provider_dst_directory);
+
+		g_hash_table_insert (corrections, provider_src_directory, NULL);
+		g_free (provider_dst_directory);
+	}
+
+	g_dir_close (dir);
+
+	/* Remove the old base CamelProvider directories. */
+	shell_xdg_migrate_process_corrections (corrections);
+	g_hash_table_destroy (corrections);
+
+skip_mail:
+
+	g_free (src_directory);
+	g_free (dst_directory);
+
+	/* 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_data_dir, "signatures", NULL);
+	dst_directory = g_build_filename (new_data_dir, "signatures", NULL);
+
+	shell_xdg_migrate_move_contents (src_directory, dst_directory);
+	shell_xdg_migrate_rmdir (src_directory);
+
+	g_free (src_directory);
+	g_free (dst_directory);
+
+	/* Move all remaining regular files to the new data directory. */
+
+	dir = g_dir_open (old_data_dir, 0, NULL);
+	if (dir == NULL)
+		return;
+
+	/* 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;
+
+		old_filename = g_build_filename (old_data_dir, basename, NULL);
+		new_filename = g_build_filename (new_data_dir, basename, NULL);
+
+		/* If we encounter a directory, try removing it.  This
+		 * will only work if the directory is empty, so there's
+		 * no risk of data loss. */
+		if (g_file_test (old_filename, G_FILE_TEST_IS_DIR)) {
+			shell_xdg_migrate_rmdir (old_filename);
+			g_free (old_filename);
+			g_free (new_filename);
+			continue;
+		}
+
+		g_hash_table_insert (corrections, old_filename, new_filename);
+	}
+
+	g_dir_close (dir);
+
+	shell_xdg_migrate_process_corrections (corrections);
+	g_hash_table_destroy (corrections);
+}
+
+static void
+shell_migrate_to_xdg_base_dirs (EShell *shell)
+{
+	const gchar *home_dir;
+	gchar *old_base_dir;
+
+	g_return_if_fail (E_IS_SHELL (shell));
+
+	/* 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;
+
+	shell_xdg_migrate_cache_dir (shell, old_base_dir);
+	shell_xdg_migrate_config_dir (shell, old_base_dir);
+	shell_xdg_migrate_data_dir (shell, 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. */
+	g_rmdir (old_base_dir);
+
+exit:
+	g_free (old_base_dir);
+}
+
+/********************* End XDG Base Directory Migration *********************/
+
 static gboolean
 shell_migrate_attempt (EShell *shell,
                        gint major,
@@ -188,6 +676,10 @@ e_shell_migrate_attempt (EShell *shell)
 
 	shell_migrate_get_version (shell, &major, &minor, &micro);
 
+	/* Migrate to XDG Base Directories first, so shell backends
+	 * don't have to deal with legacy data and cache directories. */
+	shell_migrate_to_xdg_base_dirs (shell);
+
 	/* This sets the folder permissions to S_IRWXU if needed */
 	if (curr_major <= 2 && curr_minor <= 30)
 		fix_folder_permissions (e_get_user_data_dir ());
diff --git a/shell/e-shell-migrate.h b/shell/e-shell-migrate.h
index 8ebe697..54656ba 100644
--- a/shell/e-shell-migrate.h
+++ b/shell/e-shell-migrate.h
@@ -44,8 +44,8 @@ typedef enum {
 	E_SHELL_MIGRATE_ERROR_FAILED
 } EShellMigrateError;
 
-gboolean	e_shell_migrate_attempt		(EShell *shell);
-GQuark		e_shell_migrate_error_quark	(void);
+gboolean	e_shell_migrate_attempt			(EShell *shell);
+GQuark		e_shell_migrate_error_quark		(void);
 
 G_END_DECLS
 
diff --git a/shell/e-shell-view.c b/shell/e-shell-view.c
index 5d1c44e..e6731af 100644
--- a/shell/e-shell-view.c
+++ b/shell/e-shell-view.c
@@ -111,7 +111,7 @@ shell_view_init_search_context (EShellViewClass *class)
 	ERuleContext *search_context;
 	EFilterRule *rule;
 	EFilterPart *part;
-	const gchar *data_dir;
+	const gchar *config_dir;
 	gchar *system_filename;
 	gchar *user_filename;
 
@@ -128,9 +128,9 @@ shell_view_init_search_context (EShellViewClass *class)
 		EVOLUTION_RULEDIR, class->search_rules, NULL);
 
 	/* The filename for custom saved searches is always of
-	 * the form "$(shell_backend_data_dir)/searches.xml". */
-	data_dir = e_shell_backend_get_data_dir (shell_backend);
-	user_filename = g_build_filename (data_dir, "searches.xml", NULL);
+	 * the form "$(shell_backend_config_dir)/searches.xml". */
+	config_dir = e_shell_backend_get_config_dir (shell_backend);
+	user_filename = g_build_filename (config_dir, "searches.xml", NULL);
 
 	/* Create the search context instance.  Subclasses may override
 	 * the GType so check that it's really an ERuleContext instance. */
@@ -175,7 +175,7 @@ shell_view_init_view_collection (EShellViewClass *class)
 	base_dir = EVOLUTION_GALVIEWSDIR;
 	system_dir = g_build_filename (base_dir, backend_name, NULL);
 
-	base_dir = e_shell_backend_get_data_dir (shell_backend);
+	base_dir = e_shell_backend_get_config_dir (shell_backend);
 	local_dir = g_build_filename (base_dir, "views", NULL);
 
 	/* The view collection is never destroyed. */
@@ -216,7 +216,7 @@ shell_view_load_state (EShellView *shell_view)
 
 	shell_backend = e_shell_view_get_shell_backend (shell_view);
 	config_dir = e_shell_backend_get_config_dir (shell_backend);
-	filename = g_build_filename (config_dir, "state", NULL);
+	filename = g_build_filename (config_dir, "state.ini", NULL);
 
 	/* XXX Should do this asynchronously. */
 	key_file = shell_view->priv->state_key_file;
@@ -277,7 +277,7 @@ shell_view_save_state (EShellView *shell_view)
 	contents = g_key_file_to_data (key_file, NULL, NULL);
 	g_return_val_if_fail (contents != NULL, NULL);
 
-	path = g_build_filename (config_dir, "state", NULL);
+	path = g_build_filename (config_dir, "state.ini", NULL);
 	file = g_file_new_for_path (path);
 	g_free (path);
 



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