Store the Evolution version used to create the backup file in the evolution.dir file which is part of the tarball. After restoring the backup file we need to reset the last-used-version key in GSettings to the version listed in the evolution.dir file so the proper migration routines run when Evolution is restarted.
1021 lines
25 KiB
C
1021 lines
25 KiB
C
/*
|
|
* evolution-backup-tool.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/>
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <glib/gi18n.h>
|
|
#include <glib/gstdio.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <libedataserver/libedataserver.h>
|
|
|
|
#ifdef G_OS_WIN32
|
|
#ifdef DATADIR
|
|
#undef DATADIR
|
|
#endif
|
|
#include <windows.h>
|
|
#include <conio.h>
|
|
#ifndef PROCESS_DEP_ENABLE
|
|
#define PROCESS_DEP_ENABLE 0x00000001
|
|
#endif
|
|
#ifndef PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
|
|
#define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION 0x00000002
|
|
#endif
|
|
#endif
|
|
|
|
#include "e-util/e-util-private.h"
|
|
#include "e-util/e-util.h"
|
|
|
|
#define EVOUSERDATADIR_MAGIC "#EVO_USERDATADIR#"
|
|
|
|
#define EVOLUTION "evolution"
|
|
#define EVOLUTION_DIR "$DATADIR/"
|
|
#define EVOLUTION_DIR_FILE EVOLUTION ".dir"
|
|
|
|
#define ANCIENT_GCONF_DUMP_FILE "backup-restore-gconf.xml"
|
|
|
|
#define DCONF_DUMP_FILE_EDS "backup-restore-dconf-eds.ini"
|
|
#define DCONF_DUMP_FILE_EVO "backup-restore-dconf-evo.ini"
|
|
|
|
#define DCONF_PATH_EDS "/org/gnome/evolution-data-server/"
|
|
#define DCONF_PATH_EVO "/org/gnome/evolution/"
|
|
|
|
#define KEY_FILE_GROUP "Evolution Backup"
|
|
|
|
static gboolean backup_op = FALSE;
|
|
static gchar *bk_file = NULL;
|
|
static gboolean restore_op = FALSE;
|
|
static gchar *res_file = NULL;
|
|
static gboolean check_op = FALSE;
|
|
static gchar *chk_file = NULL;
|
|
static gboolean restart_arg = FALSE;
|
|
static gboolean gui_arg = FALSE;
|
|
static gchar **opt_remaining = NULL;
|
|
static gint result = 0;
|
|
static GtkWidget *progress_dialog;
|
|
static GtkWidget *pbar;
|
|
static gchar *txt = NULL;
|
|
|
|
static GOptionEntry options[] = {
|
|
{ "backup", '\0', 0, G_OPTION_ARG_NONE, &backup_op,
|
|
N_("Back up Evolution directory"), NULL },
|
|
{ "restore", '\0', 0, G_OPTION_ARG_NONE, &restore_op,
|
|
N_("Restore Evolution directory"), NULL },
|
|
{ "check", '\0', 0, G_OPTION_ARG_NONE, &check_op,
|
|
N_("Check Evolution Back up"), NULL },
|
|
{ "restart", '\0', 0, G_OPTION_ARG_NONE, &restart_arg,
|
|
N_("Restart Evolution"), NULL },
|
|
{ "gui", '\0', 0, G_OPTION_ARG_NONE, &gui_arg,
|
|
N_("With Graphical User Interface"), NULL },
|
|
{ G_OPTION_REMAINING, '\0', 0,
|
|
G_OPTION_ARG_STRING_ARRAY, &opt_remaining },
|
|
{ NULL }
|
|
};
|
|
|
|
#define d(x)
|
|
|
|
#define print_and_run(x) \
|
|
G_STMT_START { g_message ("%s", x); system (x); } G_STMT_END
|
|
|
|
static gboolean check (const gchar *filename, gboolean *is_new_format);
|
|
|
|
static GString *
|
|
replace_string (const gchar *text,
|
|
const gchar *find,
|
|
const gchar *replace)
|
|
{
|
|
const gchar *p, *next;
|
|
GString *str;
|
|
gint find_len;
|
|
|
|
g_return_val_if_fail (text != NULL, NULL);
|
|
g_return_val_if_fail (find != NULL, NULL);
|
|
g_return_val_if_fail (*find, NULL);
|
|
|
|
find_len = strlen (find);
|
|
str = g_string_new ("");
|
|
|
|
p = text;
|
|
while (next = strstr (p, find), next) {
|
|
if (p < next)
|
|
g_string_append_len (str, p, next - p);
|
|
|
|
if (replace && *replace)
|
|
g_string_append (str, replace);
|
|
|
|
p = next + find_len;
|
|
}
|
|
|
|
g_string_append (str, p);
|
|
|
|
return str;
|
|
}
|
|
|
|
static const gchar *
|
|
strip_home_dir (const gchar *dir)
|
|
{
|
|
const gchar *home_dir, *res;
|
|
|
|
g_return_val_if_fail (dir != NULL, NULL);
|
|
|
|
home_dir = g_get_home_dir ();
|
|
g_return_val_if_fail (home_dir != NULL, dir);
|
|
g_return_val_if_fail (*home_dir != '\0', dir);
|
|
|
|
res = dir;
|
|
if (g_str_has_prefix (res, home_dir))
|
|
res += strlen (home_dir);
|
|
|
|
if (*res == G_DIR_SEPARATOR)
|
|
res++;
|
|
|
|
return res;
|
|
}
|
|
|
|
static GString *
|
|
replace_variables (const gchar *str)
|
|
{
|
|
GString *res = NULL, *use;
|
|
const gchar *strip_datadir, *strip_configdir;
|
|
|
|
g_return_val_if_fail (str != NULL, NULL);
|
|
|
|
strip_datadir = strip_home_dir (e_get_user_data_dir ());
|
|
strip_configdir = strip_home_dir (e_get_user_config_dir ());
|
|
|
|
#define repl(_find, _replace) \
|
|
use = replace_string (res ? res->str : str, _find, _replace); \
|
|
g_return_val_if_fail (use != NULL, NULL); \
|
|
if (res) \
|
|
g_string_free (res, TRUE); \
|
|
res = use;
|
|
|
|
repl ("$HOME", g_get_home_dir ());
|
|
repl ("$TMP", g_get_tmp_dir ());
|
|
repl ("$DATADIR", e_get_user_data_dir ());
|
|
repl ("$CONFIGDIR", e_get_user_config_dir ());
|
|
repl ("$STRIPDATADIR", strip_datadir);
|
|
repl ("$STRIPCONFIGDIR", strip_configdir);
|
|
|
|
#undef repl
|
|
|
|
g_return_val_if_fail (res != NULL, NULL);
|
|
|
|
/* remove trailing dir separator */
|
|
while (res->len > 0 && res->str[res->len - 1] == G_DIR_SEPARATOR) {
|
|
g_string_truncate (res, res->len - 1);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
replace_in_file (const gchar *filename,
|
|
const gchar *find,
|
|
const gchar *replace)
|
|
{
|
|
gchar *content = NULL;
|
|
GError *error = NULL;
|
|
GString *filenamestr = NULL;
|
|
|
|
g_return_if_fail (filename != NULL);
|
|
g_return_if_fail (find != NULL);
|
|
g_return_if_fail (*find);
|
|
g_return_if_fail (replace != NULL);
|
|
|
|
if (strstr (filename, "$")) {
|
|
filenamestr = replace_variables (filename);
|
|
|
|
if (!filenamestr) {
|
|
g_warning (
|
|
"%s: Replace variables in '%s' failed!",
|
|
G_STRFUNC, filename);
|
|
return;
|
|
}
|
|
|
|
filename = filenamestr->str;
|
|
}
|
|
|
|
if (g_file_get_contents (filename, &content, NULL, &error)) {
|
|
GString *str = replace_string (content, find, replace);
|
|
|
|
if (str) {
|
|
if (!g_file_set_contents (filename, str->str, -1, &error) && error) {
|
|
g_warning (
|
|
"%s: cannot write file content, "
|
|
"error: %s", G_STRFUNC, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
g_string_free (str, TRUE);
|
|
} else {
|
|
g_warning (
|
|
"%s: Replace of '%s' to '%s' failed!",
|
|
G_STRFUNC, find, replace);
|
|
}
|
|
|
|
g_free (content);
|
|
} else if (error) {
|
|
g_warning (
|
|
"%s: Cannot read file content, error: %s",
|
|
G_STRFUNC, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
if (filenamestr)
|
|
g_string_free (filenamestr, TRUE);
|
|
}
|
|
|
|
static void
|
|
run_cmd (const gchar *cmd)
|
|
{
|
|
if (!cmd)
|
|
return;
|
|
|
|
if (strstr (cmd, "$") != NULL) {
|
|
/* read the doc for g_get_home_dir to know why replacing it here */
|
|
GString *str = replace_variables (cmd);
|
|
|
|
if (str) {
|
|
print_and_run (str->str);
|
|
g_string_free (str, TRUE);
|
|
}
|
|
} else
|
|
print_and_run (cmd);
|
|
}
|
|
|
|
static void
|
|
run_evolution_no_wait (void)
|
|
{
|
|
g_spawn_command_line_async (EVOLUTION, NULL);
|
|
}
|
|
|
|
static void
|
|
write_dir_file (void)
|
|
{
|
|
GString *content, *filename;
|
|
GError *error = NULL;
|
|
|
|
filename = replace_variables ("$HOME/" EVOLUTION_DIR_FILE);
|
|
g_return_if_fail (filename != NULL);
|
|
|
|
content = replace_variables (
|
|
"[" KEY_FILE_GROUP "]\n"
|
|
"Version=" VERSION "\n"
|
|
"UserDataDir=$STRIPDATADIR\n"
|
|
"UserConfigDir=$STRIPCONFIGDIR\n");
|
|
g_return_if_fail (content != NULL);
|
|
|
|
g_file_set_contents (filename->str, content->str, content->len, &error);
|
|
|
|
if (error) {
|
|
g_warning ("Failed to write file '%s': %s\n", filename->str, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
g_string_free (filename, TRUE);
|
|
g_string_free (content, TRUE);
|
|
}
|
|
|
|
static void
|
|
backup (const gchar *filename,
|
|
GCancellable *cancellable)
|
|
{
|
|
gchar *command;
|
|
gchar *quotedfname;
|
|
|
|
g_return_if_fail (filename && *filename);
|
|
quotedfname = g_shell_quote (filename);
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Shutting down Evolution");
|
|
/* FIXME Will the versioned setting always work? */
|
|
run_cmd (EVOLUTION " --quit");
|
|
|
|
run_cmd ("rm $DATADIR/.running");
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Backing Evolution accounts and settings");
|
|
run_cmd ("dconf dump " DCONF_PATH_EDS " >" EVOLUTION_DIR DCONF_DUMP_FILE_EDS);
|
|
run_cmd ("dconf dump " DCONF_PATH_EVO " >" EVOLUTION_DIR DCONF_DUMP_FILE_EVO);
|
|
|
|
replace_in_file (
|
|
EVOLUTION_DIR DCONF_DUMP_FILE_EDS,
|
|
e_get_user_data_dir (), EVOUSERDATADIR_MAGIC);
|
|
|
|
replace_in_file (
|
|
EVOLUTION_DIR DCONF_DUMP_FILE_EVO,
|
|
e_get_user_data_dir (), EVOUSERDATADIR_MAGIC);
|
|
|
|
write_dir_file ();
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Backing Evolution data (Mails, Contacts, Calendar, Tasks, Memos)");
|
|
|
|
/* FIXME stay on this file system ,other options?" */
|
|
/* FIXME compression type?" */
|
|
/* FIXME date/time stamp?" */
|
|
/* FIXME backup location?" */
|
|
command = g_strdup_printf (
|
|
"cd $HOME && tar chf - $STRIPDATADIR "
|
|
"$STRIPCONFIGDIR .camel_certs " EVOLUTION_DIR_FILE " | "
|
|
"gzip > %s", quotedfname);
|
|
run_cmd (command);
|
|
g_free (command);
|
|
g_free (quotedfname);
|
|
|
|
run_cmd ("rm $HOME/" EVOLUTION_DIR_FILE);
|
|
|
|
txt = _("Back up complete");
|
|
|
|
if (restart_arg) {
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Restarting Evolution");
|
|
run_evolution_no_wait ();
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
extract_backup_data (const gchar *filename,
|
|
gchar **restored_version,
|
|
gchar **data_dir,
|
|
gchar **config_dir)
|
|
{
|
|
GKeyFile *key_file;
|
|
GError *error = NULL;
|
|
|
|
g_return_if_fail (filename != NULL);
|
|
g_return_if_fail (data_dir != NULL);
|
|
g_return_if_fail (config_dir != NULL);
|
|
|
|
key_file = g_key_file_new ();
|
|
g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error);
|
|
|
|
if (error) {
|
|
g_warning ("Failed to read '%s': %s", filename, error->message);
|
|
g_error_free (error);
|
|
|
|
/* This is the current format as of Evolution 3.6. */
|
|
} else if (g_key_file_has_group (key_file, KEY_FILE_GROUP)) {
|
|
gchar *tmp;
|
|
|
|
tmp = g_key_file_get_value (
|
|
key_file, KEY_FILE_GROUP, "Version", NULL);
|
|
if (tmp != NULL)
|
|
*restored_version = g_strstrip (g_strdup (tmp));
|
|
g_free (tmp);
|
|
|
|
tmp = g_key_file_get_value (
|
|
key_file, KEY_FILE_GROUP, "UserDataDir", NULL);
|
|
if (tmp != NULL)
|
|
*data_dir = g_shell_quote (tmp);
|
|
g_free (tmp);
|
|
|
|
tmp = g_key_file_get_value (
|
|
key_file, KEY_FILE_GROUP, "UserConfigDir", NULL);
|
|
if (tmp != NULL)
|
|
*config_dir = g_shell_quote (tmp);
|
|
g_free (tmp);
|
|
|
|
/* This is the legacy format with no version information. */
|
|
} else if (g_key_file_has_group (key_file, "dirs")) {
|
|
gchar *tmp;
|
|
|
|
tmp = g_key_file_get_value (key_file, "dirs", "data", NULL);
|
|
if (tmp)
|
|
*data_dir = g_shell_quote (tmp);
|
|
g_free (tmp);
|
|
|
|
tmp = g_key_file_get_value (key_file, "dirs", "config", NULL);
|
|
if (tmp)
|
|
*config_dir = g_shell_quote (tmp);
|
|
g_free (tmp);
|
|
}
|
|
|
|
g_key_file_free (key_file);
|
|
}
|
|
|
|
static gint
|
|
get_dir_level (const gchar *dir)
|
|
{
|
|
gint res = 0, i;
|
|
|
|
g_return_val_if_fail (dir != NULL, -1);
|
|
|
|
for (i = 0; dir[i]; i++) {
|
|
if (dir[i] == '/' || dir[i] == '\\')
|
|
res++;
|
|
}
|
|
|
|
if (i > 0)
|
|
res++;
|
|
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
restore (const gchar *filename,
|
|
GCancellable *cancellable)
|
|
{
|
|
gchar *command;
|
|
gchar *quotedfname;
|
|
gboolean is_new_format = FALSE;
|
|
|
|
g_return_if_fail (filename && *filename);
|
|
|
|
if (!check (filename, &is_new_format)) {
|
|
g_message ("Cannot restore from an incorrect archive '%s'.", filename);
|
|
goto end;
|
|
}
|
|
|
|
quotedfname = g_shell_quote (filename);
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
/* FIXME Will the versioned setting always work? */
|
|
txt = _("Shutting down Evolution");
|
|
run_cmd (EVOLUTION " --quit");
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Back up current Evolution data");
|
|
run_cmd ("mv $DATADIR $DATADIR_old");
|
|
run_cmd ("mv $CONFIGDIR $CONFIGDIR_old");
|
|
run_cmd ("mv $HOME/.camel_certs $HOME/.camel_certs_old");
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Extracting files from back up");
|
|
|
|
if (is_new_format) {
|
|
GString *dir_fn;
|
|
gchar *data_dir = NULL;
|
|
gchar *config_dir = NULL;
|
|
gchar *restored_version = NULL;
|
|
|
|
command = g_strdup_printf (
|
|
"cd $TMP && tar xzf %s "
|
|
EVOLUTION_DIR_FILE, quotedfname);
|
|
run_cmd (command);
|
|
g_free (command);
|
|
|
|
dir_fn = replace_variables ("$TMP" G_DIR_SEPARATOR_S EVOLUTION_DIR_FILE);
|
|
if (!dir_fn) {
|
|
g_warning ("Failed to create evolution's dir filename");
|
|
goto end;
|
|
}
|
|
|
|
/* data_dir and config_dir are quoted inside extract_backup_data */
|
|
extract_backup_data (
|
|
dir_fn->str,
|
|
&restored_version,
|
|
&data_dir,
|
|
&config_dir);
|
|
|
|
g_unlink (dir_fn->str);
|
|
g_string_free (dir_fn, TRUE);
|
|
|
|
if (!data_dir || !config_dir) {
|
|
g_warning (
|
|
"Failed to get old data_dir (%p)/"
|
|
"config_dir (%p)", data_dir, config_dir);
|
|
g_free (data_dir);
|
|
g_free (config_dir);
|
|
goto end;
|
|
}
|
|
|
|
g_mkdir_with_parents (e_get_user_data_dir (), 0700);
|
|
g_mkdir_with_parents (e_get_user_config_dir (), 0700);
|
|
|
|
command = g_strdup_printf (
|
|
"cd $DATADIR && tar xzf %s %s --strip-components=%d",
|
|
quotedfname, data_dir, get_dir_level (data_dir));
|
|
run_cmd (command);
|
|
g_free (command);
|
|
|
|
command = g_strdup_printf (
|
|
"cd $CONFIGDIR && tar xzf %s %s --strip-components=%d",
|
|
quotedfname, config_dir, get_dir_level (config_dir));
|
|
run_cmd (command);
|
|
g_free (command);
|
|
|
|
command = g_strdup_printf (
|
|
"cd $HOME && tar xzf %s .camel_certs", quotedfname);
|
|
run_cmd (command);
|
|
g_free (command);
|
|
|
|
/* If the back file had version information, set the last
|
|
* used version in GSettings before restarting Evolution. */
|
|
if (restored_version != NULL && *restored_version != '\0') {
|
|
GSettings *settings;
|
|
|
|
settings = g_settings_new ("org.gnome.evolution");
|
|
g_settings_set_string (
|
|
settings, "version", restored_version);
|
|
g_object_unref (settings);
|
|
}
|
|
|
|
g_free (data_dir);
|
|
g_free (config_dir);
|
|
g_free (restored_version);
|
|
} else {
|
|
run_cmd ("mv $HOME/.evolution $HOME/.evolution_old");
|
|
|
|
command = g_strdup_printf (
|
|
"cd $HOME && gzip -cd %s | tar xf -", quotedfname);
|
|
run_cmd (command);
|
|
g_free (command);
|
|
}
|
|
|
|
g_free (quotedfname);
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Loading Evolution settings");
|
|
|
|
if (is_new_format) {
|
|
/* new format has it in DATADIR... */
|
|
GString *file = replace_variables (EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE);
|
|
if (file && g_file_test (file->str, G_FILE_TEST_EXISTS)) {
|
|
/* ancient backup */
|
|
replace_in_file (
|
|
EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE,
|
|
EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
|
|
run_cmd ("gconftool-2 --load " EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE);
|
|
run_cmd ("rm " EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE);
|
|
} else {
|
|
replace_in_file (
|
|
EVOLUTION_DIR DCONF_DUMP_FILE_EDS,
|
|
EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
|
|
run_cmd ("cat " EVOLUTION_DIR DCONF_DUMP_FILE_EDS " | dconf load " DCONF_PATH_EDS);
|
|
run_cmd ("rm " EVOLUTION_DIR DCONF_DUMP_FILE_EDS);
|
|
|
|
replace_in_file (
|
|
EVOLUTION_DIR DCONF_DUMP_FILE_EVO,
|
|
EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
|
|
run_cmd ("cat " EVOLUTION_DIR DCONF_DUMP_FILE_EVO " | dconf load " DCONF_PATH_EVO);
|
|
run_cmd ("rm " EVOLUTION_DIR DCONF_DUMP_FILE_EVO);
|
|
}
|
|
|
|
g_string_free (file, TRUE);
|
|
} else {
|
|
gchar *gconf_dump_file;
|
|
|
|
/* ... old format in ~/.evolution */
|
|
gconf_dump_file = g_build_filename (
|
|
"$HOME", ".evolution", ANCIENT_GCONF_DUMP_FILE, NULL);
|
|
|
|
replace_in_file (
|
|
gconf_dump_file,
|
|
EVOUSERDATADIR_MAGIC,
|
|
e_get_user_data_dir ());
|
|
|
|
command = g_strconcat (
|
|
"gconftool-2 --load ", gconf_dump_file, NULL);
|
|
run_cmd (command);
|
|
g_free (command);
|
|
|
|
command = g_strconcat ("rm ", gconf_dump_file, NULL);
|
|
run_cmd (command);
|
|
g_free (command);
|
|
|
|
g_free (gconf_dump_file);
|
|
}
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Removing temporary back up files");
|
|
run_cmd ("rm -rf $DATADIR_old");
|
|
run_cmd ("rm -rf $CONFIGDIR_old");
|
|
run_cmd ("rm -rf $HOME/.camel_certs_old");
|
|
run_cmd ("rm $DATADIR/.running");
|
|
|
|
if (!is_new_format)
|
|
run_cmd ("rm -rf $HOME/.evolution_old");
|
|
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Reloading registry service");
|
|
|
|
/* This runs migration routines on the newly-restored data.
|
|
*
|
|
* XXX Hard-coding the whole command like this is not ideal
|
|
* because the "SourcesX" interface name will occasionally
|
|
* change and I guarantee we'll forget to update this. */
|
|
run_cmd (
|
|
"gdbus call --session "
|
|
"--dest org.gnome.evolution.dataserver.Sources0 "
|
|
"--object-path /org/gnome/evolution/dataserver/SourceManager "
|
|
"--method org.gnome.evolution.dataserver.SourceManager.Reload");
|
|
|
|
end:
|
|
if (restart_arg) {
|
|
if (g_cancellable_is_cancelled (cancellable))
|
|
return;
|
|
|
|
txt = _("Restarting Evolution");
|
|
run_evolution_no_wait ();
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
check (const gchar *filename,
|
|
gboolean *is_new_format)
|
|
{
|
|
gchar *command;
|
|
gchar *quotedfname;
|
|
gboolean is_new = TRUE;
|
|
|
|
g_return_val_if_fail (filename && *filename, FALSE);
|
|
quotedfname = g_shell_quote (filename);
|
|
|
|
if (is_new_format)
|
|
*is_new_format = FALSE;
|
|
|
|
command = g_strdup_printf ("tar ztf %s 1>/dev/null", quotedfname);
|
|
result = system (command);
|
|
g_free (command);
|
|
|
|
g_message ("First result %d", result);
|
|
if (result) {
|
|
g_free (quotedfname);
|
|
return FALSE;
|
|
}
|
|
|
|
command = g_strdup_printf (
|
|
"tar ztf %s | grep -e \"%s$\"",
|
|
quotedfname, EVOLUTION_DIR_FILE);
|
|
result = system (command);
|
|
g_free (command);
|
|
|
|
if (result) {
|
|
command = g_strdup_printf (
|
|
"tar ztf %s | grep -e \"^\\.evolution/$\"",
|
|
quotedfname);
|
|
result = system (command);
|
|
g_free (command);
|
|
is_new = FALSE;
|
|
}
|
|
|
|
g_message ("Second result %d", result);
|
|
if (result) {
|
|
g_free (quotedfname);
|
|
return FALSE;
|
|
}
|
|
|
|
if (is_new) {
|
|
if (is_new_format)
|
|
*is_new_format = TRUE;
|
|
g_free (quotedfname);
|
|
return TRUE;
|
|
}
|
|
|
|
command = g_strdup_printf (
|
|
"tar ztf %s | grep -e \"^\\.evolution/%s$\"",
|
|
quotedfname, ANCIENT_GCONF_DUMP_FILE);
|
|
result = system (command);
|
|
g_free (command);
|
|
|
|
if (result != 0) {
|
|
/* maybe it's an ancient backup */
|
|
command = g_strdup_printf (
|
|
"tar ztf %s | grep -e \"^\\.evolution/%s$\"",
|
|
quotedfname, ANCIENT_GCONF_DUMP_FILE);
|
|
result = system (command);
|
|
g_free (command);
|
|
}
|
|
|
|
g_free (quotedfname);
|
|
|
|
g_message ("Third result %d", result);
|
|
|
|
return result == 0;
|
|
}
|
|
|
|
static gboolean
|
|
pbar_update (GCancellable *cancellable)
|
|
{
|
|
gtk_progress_bar_pulse ((GtkProgressBar *) pbar);
|
|
gtk_progress_bar_set_text ((GtkProgressBar *) pbar, txt);
|
|
|
|
/* Return TRUE to reschedule the timeout. */
|
|
return !g_cancellable_is_cancelled (cancellable);
|
|
}
|
|
|
|
static gboolean
|
|
finish_job (gpointer user_data)
|
|
{
|
|
gtk_main_quit ();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
start_job (GIOSchedulerJob *job,
|
|
GCancellable *cancellable,
|
|
gpointer user_data)
|
|
{
|
|
if (backup_op)
|
|
backup (bk_file, cancellable);
|
|
else if (restore_op)
|
|
restore (res_file, cancellable);
|
|
else if (check_op)
|
|
check (chk_file, NULL); /* not cancellable */
|
|
|
|
g_io_scheduler_job_send_to_mainloop_async (
|
|
job, finish_job, NULL, (GDestroyNotify) NULL);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
dlg_response (GtkWidget *dlg,
|
|
gint response,
|
|
GCancellable *cancellable)
|
|
{
|
|
/* We will cancel only backup/restore
|
|
* operations and not the check operation. */
|
|
g_cancellable_cancel (cancellable);
|
|
|
|
/* If the response is not of delete_event then destroy the event. */
|
|
if (response != GTK_RESPONSE_NONE)
|
|
gtk_widget_destroy (dlg);
|
|
|
|
/* We will kill just the tar operation. Rest of
|
|
* them will be just a second of microseconds.*/
|
|
run_cmd ("pkill tar");
|
|
|
|
if (bk_file && backup_op && response == GTK_RESPONSE_REJECT) {
|
|
/* Backup was canceled, delete the
|
|
* backup file as it is not needed now. */
|
|
gchar *cmd, *filename;
|
|
|
|
g_message ("Back up canceled, removing partial back up file.");
|
|
|
|
filename = g_shell_quote (bk_file);
|
|
cmd = g_strconcat ("rm ", filename, NULL);
|
|
|
|
run_cmd (cmd);
|
|
|
|
g_free (cmd);
|
|
g_free (filename);
|
|
}
|
|
|
|
gtk_main_quit ();
|
|
}
|
|
|
|
gint
|
|
main (gint argc,
|
|
gchar **argv)
|
|
{
|
|
GCancellable *cancellable;
|
|
gchar *file = NULL, *oper = NULL;
|
|
const gchar *title = NULL;
|
|
gint ii;
|
|
GError *error = NULL;
|
|
|
|
#ifdef G_OS_WIN32
|
|
/* Reduce risks */
|
|
{
|
|
typedef BOOL (WINAPI *t_SetDllDirectoryA) (LPCSTR lpPathName);
|
|
t_SetDllDirectoryA p_SetDllDirectoryA;
|
|
|
|
p_SetDllDirectoryA = GetProcAddress (
|
|
GetModuleHandle ("kernel32.dll"),
|
|
"SetDllDirectoryA");
|
|
|
|
if (p_SetDllDirectoryA != NULL)
|
|
p_SetDllDirectoryA ("");
|
|
}
|
|
#ifndef _WIN64
|
|
{
|
|
typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags);
|
|
t_SetProcessDEPPolicy p_SetProcessDEPPolicy;
|
|
|
|
p_SetProcessDEPPolicy = GetProcAddress (
|
|
GetModuleHandle ("kernel32.dll"),
|
|
"SetProcessDEPPolicy");
|
|
|
|
if (p_SetProcessDEPPolicy)
|
|
p_SetProcessDEPPolicy (
|
|
PROCESS_DEP_ENABLE |
|
|
PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
|
|
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
|
|
textdomain (GETTEXT_PACKAGE);
|
|
|
|
gtk_init_with_args (
|
|
&argc, &argv, NULL, options, GETTEXT_PACKAGE, &error);
|
|
|
|
if (error != NULL) {
|
|
g_printerr ("%s\n", error->message);
|
|
g_error_free (error);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
if (opt_remaining != NULL) {
|
|
for (ii = 0; ii < g_strv_length (opt_remaining); ii++) {
|
|
if (backup_op) {
|
|
title = _("Evolution Back Up");
|
|
oper = _("Backing up to the folder %s");
|
|
bk_file = g_strdup ((gchar *) opt_remaining[ii]);
|
|
file = bk_file;
|
|
} else if (restore_op) {
|
|
title = _("Evolution Restore");
|
|
oper = _("Restoring from the folder %s");
|
|
res_file = g_strdup ((gchar *) opt_remaining[ii]);
|
|
file = res_file;
|
|
} else if (check_op) {
|
|
d(g_message ("Checking %s", (gchar *) opt_remaining[ii]));
|
|
chk_file = g_strdup ((gchar *) opt_remaining[ii]);
|
|
}
|
|
}
|
|
}
|
|
|
|
cancellable = g_cancellable_new ();
|
|
|
|
if (gui_arg && !check_op) {
|
|
GtkWidget *widget, *container;
|
|
GtkWidget *action_area;
|
|
GtkWidget *content_area;
|
|
const gchar *txt, *txt2;
|
|
gchar *str = NULL;
|
|
gchar *markup;
|
|
|
|
gtk_window_set_default_icon_name ("evolution");
|
|
|
|
/* Backup / Restore only can have GUI.
|
|
* We should restrict the rest. */
|
|
progress_dialog = gtk_dialog_new_with_buttons (
|
|
title, NULL,
|
|
GTK_DIALOG_MODAL,
|
|
GTK_STOCK_CANCEL,
|
|
GTK_RESPONSE_REJECT,
|
|
NULL);
|
|
|
|
gtk_container_set_border_width (
|
|
GTK_CONTAINER (progress_dialog), 12);
|
|
gtk_window_set_default_size (
|
|
GTK_WINDOW (progress_dialog), 450, 120);
|
|
|
|
action_area = gtk_dialog_get_action_area (
|
|
GTK_DIALOG (progress_dialog));
|
|
content_area = gtk_dialog_get_content_area (
|
|
GTK_DIALOG (progress_dialog));
|
|
|
|
/* Override GtkDialog defaults */
|
|
gtk_box_set_spacing (GTK_BOX (content_area), 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
|
|
gtk_box_set_spacing (GTK_BOX (action_area), 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);
|
|
|
|
if (oper && file)
|
|
str = g_strdup_printf (oper, file);
|
|
|
|
container = gtk_table_new (2, 3, FALSE);
|
|
gtk_table_set_col_spacings (GTK_TABLE (container), 12);
|
|
gtk_table_set_row_spacings (GTK_TABLE (container), 12);
|
|
gtk_widget_show (container);
|
|
|
|
gtk_box_pack_start (
|
|
GTK_BOX (content_area), container, FALSE, TRUE, 0);
|
|
|
|
widget = gtk_image_new_from_stock (
|
|
GTK_STOCK_COPY, GTK_ICON_SIZE_DIALOG);
|
|
gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
|
|
gtk_widget_show (widget);
|
|
|
|
gtk_table_attach (
|
|
GTK_TABLE (container), widget, 0, 1, 0, 3,
|
|
GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
|
|
|
|
if (backup_op) {
|
|
txt = _("Backing up Evolution Data");
|
|
txt2 = _("Please wait while Evolution is backing up your data.");
|
|
} else if (restore_op) {
|
|
txt = _("Restoring Evolution Data");
|
|
txt2 = _("Please wait while Evolution is restoring your data.");
|
|
} else {
|
|
g_return_val_if_reached (EXIT_FAILURE);
|
|
}
|
|
|
|
markup = g_markup_printf_escaped ("<b><big>%s</big></b>", txt);
|
|
widget = gtk_label_new (markup);
|
|
gtk_label_set_line_wrap (GTK_LABEL (widget), FALSE);
|
|
gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
|
|
gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
|
|
gtk_widget_show (widget);
|
|
g_free (markup);
|
|
|
|
gtk_table_attach (
|
|
GTK_TABLE (container), widget, 1, 2, 0, 1,
|
|
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
|
|
|
|
markup = g_strconcat (
|
|
txt2, " ", _("This may take a while depending "
|
|
"on the amount of data in your account."), NULL);
|
|
widget = gtk_label_new (markup);
|
|
gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
|
|
gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
|
|
gtk_widget_show (widget);
|
|
g_free (markup);
|
|
|
|
gtk_table_attach (
|
|
GTK_TABLE (container), widget, 1, 2, 1, 2,
|
|
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
|
|
|
|
pbar = gtk_progress_bar_new ();
|
|
|
|
if (str != NULL) {
|
|
markup = g_markup_printf_escaped ("<i>%s</i>", str);
|
|
widget = gtk_label_new (markup);
|
|
gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
|
|
gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
|
|
g_free (markup);
|
|
g_free (str);
|
|
|
|
gtk_table_attach (
|
|
GTK_TABLE (container), widget, 1, 2, 2, 3,
|
|
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
|
|
gtk_table_set_row_spacing (GTK_TABLE (container), 2, 6);
|
|
|
|
gtk_table_attach (
|
|
GTK_TABLE (container), pbar, 1, 2, 3, 4,
|
|
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
|
|
} else
|
|
gtk_table_attach (
|
|
GTK_TABLE (container), pbar, 1, 2, 2, 3,
|
|
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
|
|
|
|
g_signal_connect (
|
|
progress_dialog, "response",
|
|
G_CALLBACK (dlg_response), cancellable);
|
|
gtk_widget_show_all (progress_dialog);
|
|
|
|
} else if (check_op) {
|
|
/* For sanity we don't need gui */
|
|
check (chk_file, NULL);
|
|
exit (result == 0 ? 0 : 1);
|
|
}
|
|
|
|
if (gui_arg)
|
|
g_timeout_add_full (
|
|
G_PRIORITY_DEFAULT, 50,
|
|
(GSourceFunc) pbar_update,
|
|
g_object_ref (cancellable),
|
|
(GDestroyNotify) g_object_unref);
|
|
|
|
g_io_scheduler_push_job (
|
|
start_job, NULL,
|
|
(GDestroyNotify) NULL,
|
|
G_PRIORITY_DEFAULT, cancellable);
|
|
|
|
gtk_main ();
|
|
|
|
g_object_unref (cancellable);
|
|
|
|
return result;
|
|
}
|