Files
evolution/shell/e-shell-migrate.c
Matthew Barnes a0957f73f6 Delay saving Evolution version until later in initialization.
Save the version after the startup wizard has had a chance to
run.  If the user chooses to restore data and settings from a
backup, Evolution will restart and the restored data may need
to be migrated.

If we save the version before the restart, then Evolution will
think it has already migrated data and settings to the current
version and the restored data may not be handled properly.
2012-07-02 18:42:16 -04:00

916 lines
25 KiB
C

/*
* e-shell-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/>
*
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "e-shell-migrate.h"
#include <libedataserver/libedataserver.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include "libevolution-utils/e-alert-dialog.h"
#include "e-util/e-file-utils.h"
#include "e-util/e-util.h"
#include "es-event.h"
#include "evo-version.h"
/******************** 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 old_filename_is_dir;
gboolean old_filename_exists;
gboolean new_filename_exists;
gboolean success = TRUE;
old_filename_is_dir = g_file_test (old_filename, G_FILE_TEST_IS_DIR);
old_filename_exists = g_file_test (old_filename, G_FILE_TEST_EXISTS);
new_filename_exists = g_file_test (new_filename, G_FILE_TEST_EXISTS);
if (!old_filename_exists)
return TRUE;
g_print (" mv %s %s\n", old_filename, new_filename);
/* It's safe to go ahead and move directories because rename ()
* will fail if the new directory already exists with content.
* With regular files we have to be careful not to overwrite
* new files with old files. */
if (old_filename_is_dir || !new_filename_exists) {
if (g_rename (old_filename, new_filename) < 0) {
g_printerr (" FAILED: %s\n", g_strerror (errno));
success = FALSE;
}
} else {
g_printerr (" FAILED: Destination file already exists\n");
success = FALSE;
}
return success;
}
static gboolean
shell_xdg_migrate_rmdir (const gchar *dirname)
{
GDir *dir = NULL;
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", g_strerror (errno));
if (errno == ENOTEMPTY) {
dir = g_dir_open (dirname, 0, NULL);
g_printerr (" (contents follows)");
}
g_printerr ("\n");
success = FALSE;
}
}
/* List the directory's contents to aid debugging. */
if (dir != NULL) {
const gchar *basename;
/* Align the filenames beneath the error message. */
while ((basename = g_dir_read_name (dir)) != NULL)
g_print (" %s\n", basename);
g_dir_close (dir);
}
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_rename_files (const gchar *src_directory,
const gchar *dst_directory)
{
GDir *dir;
GHashTable *corrections;
const gchar *basename;
const gchar *home_dir;
gchar *old_base_dir;
gchar *new_base_dir;
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);
home_dir = g_get_home_dir ();
old_base_dir = g_build_filename (home_dir, ".evolution", NULL);
e_filename_make_safe (old_base_dir);
new_base_dir = g_strdup (e_get_user_data_dir ());
e_filename_make_safe (new_base_dir);
while ((basename = g_dir_read_name (dir)) != NULL) {
GString *buffer;
gchar *old_filename;
gchar *new_filename;
gchar *cp;
buffer = g_string_new (basename);
if ((cp = strstr (basename, old_base_dir)) != NULL) {
g_string_erase (
buffer, cp - basename,
strlen (old_base_dir));
g_string_insert (
buffer, cp - basename, new_base_dir);
}
old_filename = g_build_filename (
src_directory, basename, NULL);
new_filename = g_build_filename (
dst_directory, buffer->str, NULL);
g_string_free (buffer, TRUE);
g_hash_table_insert (corrections, old_filename, new_filename);
}
g_free (old_base_dir);
g_free (new_base_dir);
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 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);
old_filename = g_build_filename (old_cache_dir, "tmp", NULL);
new_filename = g_build_filename (new_cache_dir, "tmp", 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)
{
GDir *dir;
const gchar *user_config_dir;
gchar *old_config_dir;
gchar *new_config_dir;
gchar *old_filename;
gchar *new_filename;
gchar *dirname;
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_files (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);
/* This one only occurs in calendar and memos.
* For other backends this will just be a no-op. */
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);
/* This one only occurs in calendar and tasks.
* For other backends this will just be a no-op. */
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);
/* 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);
/* GIO had a bug for awhile where it would leave behind an empty
* temp file with the pattern .goutputstream-XXXXXX if an output
* stream operation was cancelled. We've had several reports of
* these files in config directories, so remove any we find. */
dirname = g_build_filename (old_config_dir, "config", NULL);
dir = g_dir_open (dirname, 0, NULL);
if (dir != NULL) {
const gchar *basename;
while ((basename = g_dir_read_name (dir)) != NULL) {
gchar *filename;
struct stat st;
if (!g_str_has_prefix (basename, ".goutputstream"))
continue;
filename = g_build_filename (dirname, basename, NULL);
/* Verify the file is indeed empty. */
if (g_stat (filename, &st) == 0 && st.st_size == 0)
g_unlink (filename);
g_free (filename);
}
g_dir_close (dir);
}
g_free (dirname);
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);
/* This file is no longer used. Try removing it. */
old_filename = g_build_filename (
old_config_dir, "config",
"folder-tree-expand-state.xml", NULL);
g_unlink (old_filename);
g_free (old_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_files (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_dir_cleanup (EShell *shell,
const gchar *old_base_dir,
const gchar *backend_name,
const gchar *dir_name)
{
gchar *dirname;
dirname = g_build_filename (
old_base_dir, backend_name, dir_name, 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_mail (shell, old_base_dir);
/* Remove leftover config directories. */
for (ii = 0; shell_backend_names[ii] != NULL; ii++) {
shell_xdg_migrate_dir_cleanup (
shell, old_base_dir, shell_backend_names[ii], "config");
shell_xdg_migrate_dir_cleanup (
shell, old_base_dir, shell_backend_names[ii], "views");
}
/*** Miscellaneous configuration files. ***/
old_config_dir = old_base_dir;
new_config_dir = e_get_user_config_dir ();
/* Subtle name change: datetime-formats --> datetime-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,
gint minor,
gint micro)
{
GtkWindow *parent;
GList *backends;
gboolean success = TRUE;
parent = e_shell_get_active_window (shell);
backends = e_shell_get_shell_backends (shell);
/* New user accounts have nothing to migrate. */
if (major == 0 && minor == 0 && micro == 0)
return TRUE;
/* We only support migrating from version 2 now. */
if (major < 2) {
gchar *version;
gint response;
version = g_strdup_printf ("%d.%d", major, minor);
response = e_alert_run_dialog_for_args (
parent, "shell:upgrade-version-too-old",
version, NULL);
g_free (version);
return (response == GTK_RESPONSE_OK);
}
/* Ask each of the shell backends to migrate their own data.
* XXX If something fails the user may end up with only partially
* migrated data. Need transaction semantics here, but how? */
while (success && backends != NULL) {
EShellBackend *shell_backend = backends->data;
GError *error = NULL;
success = e_shell_backend_migrate (
shell_backend, major, minor, micro, &error);
if (error != NULL) {
gint response;
response = e_alert_run_dialog_for_args (
parent, "shell:upgrade-failed",
error->message, NULL);
success = (response == GTK_RESPONSE_OK);
g_error_free (error);
}
backends = g_list_next (backends);
}
return success;
}
static void
shell_migrate_get_version (EShell *shell,
gint *major,
gint *minor,
gint *micro)
{
GSettings *settings;
gchar *string;
*major = 0;
*minor = 0;
*micro = 0;
settings = g_settings_new ("org.gnome.evolution");
string = g_settings_get_string (settings, "version");
if (string != NULL) {
/* Since 1.4.0 we've kept the version key in GSettings. */
sscanf (string, "%d.%d.%d", major, minor, micro);
g_free (string);
}
g_object_unref (settings);
}
static gboolean
shell_migrate_downgraded (gint previous_major,
gint previous_minor,
gint previous_micro)
{
gboolean downgraded;
/* This could just be a single boolean expression,
* but I find this form easier to understand. */
if (previous_major == EVO_MAJOR_VERSION) {
if (previous_minor == EVO_MINOR_VERSION) {
downgraded = (previous_micro > EVO_MICRO_VERSION);
} else {
downgraded = (previous_minor > EVO_MINOR_VERSION);
}
} else {
downgraded = (previous_major > EVO_MAJOR_VERSION);
}
return downgraded;
}
static void
change_dir_modes (const gchar *path)
{
GDir *dir;
GError *err = NULL;
const gchar *file = NULL;
dir = g_dir_open (path, 0, &err);
if (err) {
g_warning ("Error opening directory %s: %s \n", path, err->message);
g_clear_error (&err);
return;
}
while ((file = g_dir_read_name (dir))) {
gchar *full_path = g_build_filename (path, file, NULL);
if (g_file_test (full_path, G_FILE_TEST_IS_DIR))
change_dir_modes (full_path);
g_free (full_path);
}
g_chmod (path, 0700);
g_dir_close (dir);
}
static void
fix_folder_permissions (const gchar *data_dir)
{
struct stat sb;
if (g_stat (data_dir, &sb) == -1) {
g_warning ("error stat: %s \n", data_dir);
return;
}
if (((guint32) sb.st_mode & 0777) != 0700)
change_dir_modes (data_dir);
}
static void
shell_migrate_save_current_version (void)
{
GSettings *settings;
gchar *version;
/* Save the version after the startup wizard has had a chance to
* run. If the user chooses to restore data and settings from a
* backup, Evolution will restart and the restored data may need
* to be migrated.
*
* If we save the version before the restart, then Evolution will
* think it has already migrated data and settings to the current
* version and the restored data may not be handled properly.
*
* This implies an awareness of module behavior from within the
* application core, but practical considerations overrule here. */
settings = g_settings_new ("org.gnome.evolution");
version = g_strdup_printf (
"%d.%d.%d",
EVO_MAJOR_VERSION,
EVO_MINOR_VERSION,
EVO_MICRO_VERSION);
g_settings_set_string (settings, "version", version);
g_free (version);
g_object_unref (settings);
}
static void
shell_migrate_ready_to_start_event_cb (EShell *shell)
{
shell_migrate_save_current_version ();
}
gboolean
e_shell_migrate_attempt (EShell *shell)
{
ESEvent *ese;
gint major, minor, micro;
g_return_val_if_fail (E_IS_SHELL (shell), FALSE);
shell_migrate_get_version (shell, &major, &minor, &micro);
/* Abort all migration if the user downgraded. */
if (shell_migrate_downgraded (major, minor, micro))
return TRUE;
/* 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 (major <= 2 && minor <= 30)
fix_folder_permissions (e_get_user_data_dir ());
/* Attempt to run migration all the time and let the backend
* make the choice */
if (!shell_migrate_attempt (shell, major, minor, micro))
_exit (EXIT_SUCCESS);
/* We want our handler to run last, hence g_signal_connect_after(). */
g_signal_connect_after (
shell, "event::ready-to-start",
G_CALLBACK (shell_migrate_ready_to_start_event_cb), NULL);
/** @Event: Shell attempted upgrade
* @Id: upgrade.done
* @Target: ESMenuTargetState
*
* This event is emitted whenever the shell successfully attempts
* an upgrade.
**/
ese = es_event_peek ();
e_event_emit (
(EEvent *) ese, "upgrade.done",
(EEventTarget *) es_event_target_new_upgrade (
ese, EVO_MAJOR_VERSION, EVO_MINOR_VERSION, EVO_MICRO_VERSION));
return TRUE;
}
GQuark
e_shell_migrate_error_quark (void)
{
static GQuark quark = 0;
if (G_UNLIKELY (quark == 0))
quark = g_quark_from_static_string (
"e-shell-migrate-error-quark");
return quark;
}