From cd3759369b675e754dbed5ba19894cdd87a63a88 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Sun, 25 Mar 2012 18:19:01 -0400 Subject: Add 'backup-restore' module. Replaces the 'backup-restore' plugin. --- modules/backup-restore/Makefile.am | 84 ++ .../backup-restore/e-mail-config-restore-page.c | 382 +++++++++ .../backup-restore/e-mail-config-restore-page.h | 81 ++ .../e-mail-config-restore-ready-page.c | 80 ++ .../e-mail-config-restore-ready-page.h | 76 ++ modules/backup-restore/evolution-backup-restore.c | 494 +++++++++++ modules/backup-restore/evolution-backup-tool.c | 935 +++++++++++++++++++++ .../org-gnome-backup-restore.error.xml | 24 + 8 files changed, 2156 insertions(+) create mode 100644 modules/backup-restore/Makefile.am create mode 100644 modules/backup-restore/e-mail-config-restore-page.c create mode 100644 modules/backup-restore/e-mail-config-restore-page.h create mode 100644 modules/backup-restore/e-mail-config-restore-ready-page.c create mode 100644 modules/backup-restore/e-mail-config-restore-ready-page.h create mode 100644 modules/backup-restore/evolution-backup-restore.c create mode 100644 modules/backup-restore/evolution-backup-tool.c create mode 100644 modules/backup-restore/org-gnome-backup-restore.error.xml (limited to 'modules/backup-restore') diff --git a/modules/backup-restore/Makefile.am b/modules/backup-restore/Makefile.am new file mode 100644 index 0000000000..42f2477904 --- /dev/null +++ b/modules/backup-restore/Makefile.am @@ -0,0 +1,84 @@ +NULL = + +module_LTLIBRARIES = module-backup-restore.la + +module_backup_restore_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/widgets \ + -DG_LOG_DOMAIN=\"evolution-backup-restore\" \ + -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \ + -DEVOLUTION_TOOLSDIR=\""$(privlibexecdir)"\" \ + -DPREFIX=\""$(prefix)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DDATADIR=\""$(datadir)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(GTKHTML_CFLAGS) \ + $(NULL) + +module_backup_restore_la_SOURCES = \ + evolution-backup-restore.c \ + e-mail-config-restore-page.c \ + e-mail-config-restore-page.h \ + e-mail-config-restore-ready-page.c \ + e-mail-config-restore-ready-page.h \ + $(NULL) + +module_backup_restore_la_LIBADD = \ + $(top_builddir)/e-util/libeutil.la \ + $(top_builddir)/shell/libeshell.la \ + $(top_builddir)/mail/libevolution-mail.la \ + $(top_builddir)/widgets/misc/libemiscwidgets.la \ + $(top_builddir)/libemail-engine/libemail-engine.la \ + $(top_builddir)/libevolution-utils/libevolution-utils.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(GTKHTML_LIBS) \ + $(NULL) + +module_backup_restore_la_LDFLAGS = \ + -module -avoid-version $(NO_UNDEFINED) + +privlibexec_PROGRAMS = evolution-backup + +evolution_backup_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/widgets \ + -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \ + -DEVOLUTION_TOOLSDIR=\""$(privlibexecdir)"\" \ + -DPREFIX=\""$(prefix)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DDATADIR=\""$(datadir)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(NULL) + +evolution_backup_SOURCES = \ + evolution-backup-tool.c \ + $(NULL) + +evolution_backup_LDADD = \ + $(top_builddir)/e-util/libeutil.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(NULL) + +if OS_WIN32 +evolution_backup_LDFLAGS = -mwindows +endif + +error_DATA = org-gnome-backup-restore.error +errordir = $(privdatadir)/errors +@EVO_PLUGIN_RULE@ + +BUILT_SOURCES = $(error_DATA) + +EXTRA_DIST = \ + org-gnome-backup-restore.error.xml \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/modules/backup-restore/e-mail-config-restore-page.c b/modules/backup-restore/e-mail-config-restore-page.c new file mode 100644 index 0000000000..fa1a6fd25e --- /dev/null +++ b/modules/backup-restore/e-mail-config-restore-page.c @@ -0,0 +1,382 @@ +/* + * e-mail-config-restore-page.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 + * + */ + +#include "e-mail-config-restore-page.h" + +#include +#include +#include +#include +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#include + +#include +#include +#include + +#define E_MAIL_CONFIG_RESTORE_PAGE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_MAIL_CONFIG_RESTORE_PAGE, EMailConfigRestorePagePrivate)) + +struct _EMailConfigRestorePagePrivate { + GtkWidget *toggle_button; /* not referenced */ + GtkWidget *file_chooser; /* not referenced */ + GtkWidget *alert_bar; /* not referenced */ + gchar *filename; +}; + +enum { + PROP_0, + PROP_FILENAME +}; + +/* Forward Declarations */ +static void e_mail_config_restore_page_alert_sink_init + (EAlertSinkInterface *interface); +static void e_mail_config_restore_page_interface_init + (EMailConfigPageInterface *interface); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED ( + EMailConfigRestorePage, + e_mail_config_restore_page, + GTK_TYPE_BOX, + 0, + G_IMPLEMENT_INTERFACE_DYNAMIC ( + E_TYPE_ALERT_SINK, + e_mail_config_restore_page_alert_sink_init) + G_IMPLEMENT_INTERFACE_DYNAMIC ( + E_TYPE_MAIL_CONFIG_PAGE, + e_mail_config_restore_page_interface_init)) + +static void +mail_config_restore_page_update_filename (EMailConfigRestorePage *page) +{ + GtkToggleButton *toggle_button; + GtkFileChooser *file_chooser; + gchar *filename = NULL; + + file_chooser = GTK_FILE_CHOOSER (page->priv->file_chooser); + toggle_button = GTK_TOGGLE_BUTTON (page->priv->toggle_button); + + e_alert_bar_clear (E_ALERT_BAR (page->priv->alert_bar)); + + if (gtk_toggle_button_get_active (toggle_button)) + filename = gtk_file_chooser_get_filename (file_chooser); + + if (!evolution_backup_restore_validate_backup_file (filename)) { + if (filename != NULL) { + e_alert_submit ( + E_ALERT_SINK (page), + "org.gnome.backup-restore:invalid-backup", + NULL); + g_free (filename); + filename = NULL; + } + } + + g_free (page->priv->filename); + page->priv->filename = filename; + + g_object_notify (G_OBJECT (page), "filename"); + + e_mail_config_page_changed (E_MAIL_CONFIG_PAGE (page)); +} + +static void +mail_config_restore_page_toggled_cb (GtkToggleButton *toggle_button, + EMailConfigRestorePage *page) +{ + mail_config_restore_page_update_filename (page); +} + +static void +mail_config_restore_page_file_set_cb (GtkFileChooser *file_chooser, + EMailConfigRestorePage *page) +{ + mail_config_restore_page_update_filename (page); +} + +static void +mail_config_restore_page_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FILENAME: + g_value_set_string ( + value, + e_mail_config_restore_page_get_filename ( + E_MAIL_CONFIG_RESTORE_PAGE (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +mail_config_restore_page_finalize (GObject *object) +{ + EMailConfigRestorePagePrivate *priv; + + priv = E_MAIL_CONFIG_RESTORE_PAGE_GET_PRIVATE (object); + + g_free (priv->filename); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_mail_config_restore_page_parent_class)-> + finalize (object); +} + +static void +mail_config_restore_page_constructed (GObject *object) +{ + EMailConfigRestorePage *page; + GtkWidget *widget; + GtkWidget *container; + const gchar *text; + + page = E_MAIL_CONFIG_RESTORE_PAGE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_mail_config_restore_page_parent_class)-> + constructed (object); + + gtk_orientable_set_orientation ( + GTK_ORIENTABLE (page), GTK_ORIENTATION_VERTICAL); + + gtk_box_set_spacing (GTK_BOX (page), 24); + + text = _("You can restore Evolution from a backup file.\n\n" + "This will restore all your personal data, settings " + "mail filters, etc."); + widget = gtk_label_new (text); + gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE); + gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + container = widget; + + text = _("_Restore from a backup file:"); + widget = gtk_check_button_new_with_mnemonic (text); + gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); + page->priv->toggle_button = widget; /* not referenced */ + gtk_widget_show (widget); + + g_signal_connect ( + widget, "toggled", + G_CALLBACK (mail_config_restore_page_toggled_cb), page); + + widget = gtk_file_chooser_button_new ( + _("Choose a backup file to restore"), + GTK_FILE_CHOOSER_ACTION_OPEN); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (widget), TRUE); + gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); + page->priv->file_chooser = widget; /* not referenced */ + gtk_widget_show (widget); + + g_signal_connect ( + widget, "file-set", + G_CALLBACK (mail_config_restore_page_file_set_cb), page); + + widget = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0); + /* Visibility is bound to the EActivityBar. */ + + container = widget; + + widget = e_alert_bar_new (); + gtk_container_add (GTK_CONTAINER (container), widget); + page->priv->alert_bar = widget; /* not referenced */ + /* EActivityBar controls its own visibility. */ + + g_object_bind_property ( + widget, "visible", + container, "visible", + G_BINDING_SYNC_CREATE); + + g_object_bind_property ( + page->priv->toggle_button, "active", + page->priv->file_chooser, "sensitive", + G_BINDING_SYNC_CREATE); +} + +static void +mail_config_restore_page_submit_alert (EAlertSink *alert_sink, + EAlert *alert) +{ + EMailConfigRestorePagePrivate *priv; + EAlertBar *alert_bar; + GtkWidget *dialog; + gpointer parent; + + priv = E_MAIL_CONFIG_RESTORE_PAGE_GET_PRIVATE (alert_sink); + + parent = gtk_widget_get_toplevel (GTK_WIDGET (alert_sink)); + parent = gtk_widget_is_toplevel (parent) ? parent : NULL; + + switch (e_alert_get_message_type (alert)) { + case GTK_MESSAGE_INFO: + case GTK_MESSAGE_WARNING: + case GTK_MESSAGE_ERROR: + alert_bar = E_ALERT_BAR (priv->alert_bar); + e_alert_bar_add_alert (alert_bar, alert); + break; + + default: + dialog = e_alert_dialog_new (parent, alert); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + break; + } +} + +static gboolean +mail_config_restore_page_check_complete (EMailConfigPage *page) +{ + EMailConfigRestorePagePrivate *priv; + GtkToggleButton *toggle_button; + gboolean complete; + + priv = E_MAIL_CONFIG_RESTORE_PAGE_GET_PRIVATE (page); + + toggle_button = GTK_TOGGLE_BUTTON (priv->toggle_button); + + complete = + !gtk_toggle_button_get_active (toggle_button) || + (priv->filename != NULL && *priv->filename != '\0'); + + return complete; +} + +static void +e_mail_config_restore_page_class_init (EMailConfigRestorePageClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private ( + class, sizeof (EMailConfigRestorePagePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = mail_config_restore_page_get_property; + object_class->finalize = mail_config_restore_page_finalize; + object_class->constructed = mail_config_restore_page_constructed; + + g_object_class_install_property ( + object_class, + PROP_FILENAME, + g_param_spec_string ( + "filename", + "Filename", + "Selected filename to restore from", + NULL, + G_PARAM_READABLE)); +} + +static void +e_mail_config_restore_page_class_finalize (EMailConfigRestorePageClass *class) +{ +} + +static void +e_mail_config_restore_page_alert_sink_init (EAlertSinkInterface *interface) +{ + interface->submit_alert = mail_config_restore_page_submit_alert; +} + +static void +e_mail_config_restore_page_interface_init (EMailConfigPageInterface *interface) +{ + interface->title = _("Restore from Backup"); + interface->sort_order = E_MAIL_CONFIG_RESTORE_PAGE_SORT_ORDER; + interface->check_complete = mail_config_restore_page_check_complete; +} + +static void +e_mail_config_restore_page_init (EMailConfigRestorePage *page) +{ + page->priv = E_MAIL_CONFIG_RESTORE_PAGE_GET_PRIVATE (page); +} + +void +e_mail_config_restore_page_type_register (GTypeModule *type_module) +{ + /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration + * function, so we have to wrap it with a public function in + * order to register types from a separate compilation unit. */ + e_mail_config_restore_page_register_type (type_module); +} + +EMailConfigPage * +e_mail_config_restore_page_new (void) +{ + return g_object_new (E_TYPE_MAIL_CONFIG_RESTORE_PAGE, NULL); +} + +const gchar * +e_mail_config_restore_page_get_filename (EMailConfigRestorePage *page) +{ + g_return_val_if_fail (E_IS_MAIL_CONFIG_RESTORE_PAGE (page), NULL); + + return page->priv->filename; +} + +gboolean +evolution_backup_restore_validate_backup_file (const gchar *filename) +{ + gchar *command; + gint result; + gchar *quotedfname; + gchar *toolfname; + const gchar *basedir; + + if (filename == NULL || *filename == '\0') + return FALSE; + + /* FIXME We should be using g_spawn_command_line_sync() here. */ + + basedir = EVOLUTION_TOOLSDIR; + quotedfname = g_shell_quote (filename); + toolfname = g_build_filename (basedir, "evolution-backup", NULL); + + command = g_strdup_printf("%s --check %s", toolfname, quotedfname); + result = system (command); + + g_free (command); + g_free (quotedfname); + g_free (toolfname); + +#ifdef HAVE_SYS_WAIT_H + g_message ( + "Sanity check result %d:%d %d", + WIFEXITED (result), WEXITSTATUS (result), result); + + return WIFEXITED (result) && (WEXITSTATUS (result) == 0); +#else + return (result == 0); +#endif +} + diff --git a/modules/backup-restore/e-mail-config-restore-page.h b/modules/backup-restore/e-mail-config-restore-page.h new file mode 100644 index 0000000000..c6d4221a54 --- /dev/null +++ b/modules/backup-restore/e-mail-config-restore-page.h @@ -0,0 +1,81 @@ +/* + * e-mail-config-restore-page.h + * + * 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 + * + */ + +#ifndef E_MAIL_CONFIG_RESTORE_PAGE_H +#define E_MAIL_CONFIG_RESTORE_PAGE_H + +#include + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_MAIL_CONFIG_RESTORE_PAGE \ + (e_mail_config_restore_page_get_type ()) +#define E_MAIL_CONFIG_RESTORE_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_CONFIG_RESTORE_PAGE, EMailConfigRestorePage)) +#define E_MAIL_CONFIG_RESTORE_PAGE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_CONFIG_RESTORE_PAGE, EMailConfigRestorePageClass)) +#define E_IS_MAIL_CONFIG_RESTORE_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_CONFIG_RESTORE_PAGE)) +#define E_IS_MAIL_CONFIG_RESTORE_PAGE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_CONFIG_RESTORE_PAGE)) +#define E_MAIL_CONFIG_RESTORE_PAGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_CONFIG_RESTORE_PAGE, EMailConfigRestorePageClass)) + +#define E_MAIL_CONFIG_RESTORE_PAGE_SORT_ORDER \ + (E_MAIL_CONFIG_WELCOME_PAGE_SORT_ORDER + 10) + +G_BEGIN_DECLS + +typedef struct _EMailConfigRestorePage EMailConfigRestorePage; +typedef struct _EMailConfigRestorePageClass EMailConfigRestorePageClass; +typedef struct _EMailConfigRestorePagePrivate EMailConfigRestorePagePrivate; + +struct _EMailConfigRestorePage { + GtkBox parent; + EMailConfigRestorePagePrivate *priv; +}; + +struct _EMailConfigRestorePageClass { + GtkBoxClass parent_class; +}; + +GType e_mail_config_restore_page_get_type + (void) G_GNUC_CONST; +void e_mail_config_restore_page_type_register + (GTypeModule *type_module); +EMailConfigPage * + e_mail_config_restore_page_new (void); +const gchar * e_mail_config_restore_page_get_filename + (EMailConfigRestorePage *page); + +/* This is a stand-alone function to validate the given backup file. + * It resides in this file because EMailConfigRestorePage uses it. */ +gboolean evolution_backup_restore_validate_backup_file + (const gchar *filename); + +G_END_DECLS + +#endif /* E_MAIL_CONFIG_RESTORE_PAGE_H */ + diff --git a/modules/backup-restore/e-mail-config-restore-ready-page.c b/modules/backup-restore/e-mail-config-restore-ready-page.c new file mode 100644 index 0000000000..2827e67e5b --- /dev/null +++ b/modules/backup-restore/e-mail-config-restore-ready-page.c @@ -0,0 +1,80 @@ +/* + * e-mail-config-restore-ready-page.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 + * + */ + +/* NOTE: This page is never actually shown to the user. It works as a + * placeholder, visible only when the user chooses a backup file + * to restore. As soon as we arrive on this page we execl() the + * "evolution-backup" tool, and the startup wizard disappears. */ + +#include "e-mail-config-restore-ready-page.h" + +#include +#include + +/* Forward Declarations */ +static void e_mail_config_restore_ready_page_interface_init + (EMailConfigPageInterface *interface); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED ( + EMailConfigRestoreReadyPage, + e_mail_config_restore_ready_page, + GTK_TYPE_BOX, + 0, + G_IMPLEMENT_INTERFACE_DYNAMIC ( + E_TYPE_MAIL_CONFIG_PAGE, + e_mail_config_restore_ready_page_interface_init)) + +static void +e_mail_config_restore_ready_page_class_init (EMailConfigRestoreReadyPageClass *class) +{ +} + +static void +e_mail_config_restore_ready_page_class_finalize (EMailConfigRestoreReadyPageClass *class) +{ +} + +static void +e_mail_config_restore_ready_page_interface_init (EMailConfigPageInterface *interface) +{ + /* Keep the title identical to EMailConfigRestorePage + * so it's only shown once in the assistant sidebar. */ + interface->title = _("Restore from Backup"); + interface->sort_order = E_MAIL_CONFIG_RESTORE_READY_PAGE_SORT_ORDER; +} + +static void +e_mail_config_restore_ready_page_init (EMailConfigRestoreReadyPage *page) +{ +} + +void +e_mail_config_restore_ready_page_type_register (GTypeModule *type_module) +{ + /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration + * function, so we have to wrap it with a public function in + * order to register types from a separate compilation unit. */ + e_mail_config_restore_ready_page_register_type (type_module); +} + +EMailConfigPage * +e_mail_config_restore_ready_page_new (void) +{ + return g_object_new (E_TYPE_MAIL_CONFIG_RESTORE_READY_PAGE, NULL); +} + diff --git a/modules/backup-restore/e-mail-config-restore-ready-page.h b/modules/backup-restore/e-mail-config-restore-ready-page.h new file mode 100644 index 0000000000..fb196db1f5 --- /dev/null +++ b/modules/backup-restore/e-mail-config-restore-ready-page.h @@ -0,0 +1,76 @@ +/* + * e-mail-config-restore-ready-page.h + * + * 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 + * + */ + +#ifndef E_MAIL_CONFIG_RESTORE_READY_PAGE_H +#define E_MAIL_CONFIG_RESTORE_READY_PAGE_H + +#include + +#include + +#include "e-mail-config-restore-page.h" + +/* Standard GObject macros */ +#define E_TYPE_MAIL_CONFIG_RESTORE_READY_PAGE \ + (e_mail_config_restore_ready_page_get_type ()) +#define E_MAIL_CONFIG_RESTORE_READY_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_CONFIG_RESTORE_READY_PAGE, EMailConfigRestoreReadyPage)) +#define E_MAIL_CONFIG_RESTORE_READY_PAGE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_CONFIG_RESTORE_READY_PAGE, EMailConfigRestoreReadyPageClass)) +#define E_IS_MAIL_CONFIG_RESTORE_READY_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_CONFIG_RESTORE_READY_PAGE)) +#define E_IS_MAIL_CONFIG_RESTORE_READY_PAGE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_CONFIG_RESTORE_READY_PAGE)) +#define E_MAIL_CONFIG_RESTORE_READY_PAGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_CONFIG_RESTORE_READY_PAGE, EMailConfigRestoreReadyPageClass)) + +#define E_MAIL_CONFIG_RESTORE_READY_PAGE_SORT_ORDER \ + (E_MAIL_CONFIG_RESTORE_PAGE_SORT_ORDER + 1) + +G_BEGIN_DECLS + +typedef struct _EMailConfigRestoreReadyPage EMailConfigRestoreReadyPage; +typedef struct _EMailConfigRestoreReadyPageClass EMailConfigRestoreReadyPageClass; +typedef struct _EMailConfigRestoreReadyPagePrivate EMailConfigRestoreReadyPagePrivate; + +struct _EMailConfigRestoreReadyPage { + GtkBox parent; + EMailConfigRestoreReadyPagePrivate *priv; +}; + +struct _EMailConfigRestoreReadyPageClass { + GtkBoxClass parent_class; +}; + +GType e_mail_config_restore_ready_page_get_type + (void) G_GNUC_CONST; +void e_mail_config_restore_ready_page_type_register + (GTypeModule *type_module); +EMailConfigPage * + e_mail_config_restore_ready_page_new + (void); + +G_END_DECLS + +#endif /* E_MAIL_CONFIG_RESTORE_READY_PAGE_H */ + diff --git a/modules/backup-restore/evolution-backup-restore.c b/modules/backup-restore/evolution-backup-restore.c new file mode 100644 index 0000000000..75a070a353 --- /dev/null +++ b/modules/backup-restore/evolution-backup-restore.c @@ -0,0 +1,494 @@ +/* + * evolution-backup-restore.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 + * + */ + +#include +#include +#include +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "e-mail-config-restore-page.h" +#include "e-mail-config-restore-ready-page.h" + +#ifdef G_OS_WIN32 +#ifdef localtime_r +#undef localtime_r +#endif +/* The localtime() in Microsoft's C library *is* thread-safe */ +#define localtime_r(timep, result) \ + (localtime (timep) ? memcpy ( \ + (result), localtime (timep), sizeof (*(result))) : 0) +#endif + +typedef EExtension EvolutionBackupRestoreAssistant; +typedef EExtensionClass EvolutionBackupRestoreAssistantClass; + +typedef EExtension EvolutionBackupRestoreMenuItems; +typedef EExtensionClass EvolutionBackupRestoreMenuItemsClass; + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); + +/* Forward Declarations */ +GType evolution_backup_restore_assistant_get_type (void); +GType evolution_backup_restore_menu_items_get_type (void); + +static const gchar *ui = +"" +" " +" " +" " +" " +" " +" " +" " +" " +""; + +G_DEFINE_DYNAMIC_TYPE ( + EvolutionBackupRestoreAssistant, + evolution_backup_restore_assistant, + E_TYPE_EXTENSION) + +G_DEFINE_DYNAMIC_TYPE ( + EvolutionBackupRestoreMenuItems, + evolution_backup_restore_menu_items, + E_TYPE_EXTENSION) + +enum { + BR_OK = 1 << 0, + BR_START = 1 << 1 +}; + +static void +backup (const gchar *filename, + gboolean restart) +{ + if (restart) + execl ( + EVOLUTION_TOOLSDIR "/evolution-backup", + "evolution-backup", + "--gui", + "--backup", + "--restart", + filename, + NULL); + else + execl ( + EVOLUTION_TOOLSDIR "/evolution-backup", + "evolution-backup", + "--gui", + "--backup", + filename, + NULL); +} + +static void +restore (const gchar *filename, + gboolean restart) +{ + if (restart) + execl ( + EVOLUTION_TOOLSDIR "/evolution-backup", + "evolution-backup", + "--gui", + "--restore", + "--restart", + filename, + NULL); + else + execl ( + EVOLUTION_TOOLSDIR "/evolution-backup", + "evolution-backup", + "--gui", + "--restore", + filename, + NULL); +} + +static guint32 +dialog_prompt_user (GtkWindow *parent, + const gchar *string, + const gchar *tag, + ...) +{ + GtkWidget *dialog; + GtkWidget *check = NULL; + GtkWidget *container; + va_list ap; + gint button; + guint32 mask = 0; + EAlert *alert = NULL; + + va_start (ap, tag); + alert = e_alert_new_valist (tag, ap); + va_end (ap); + + dialog = e_alert_dialog_new (parent, alert); + g_object_unref (alert); + + container = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog)); + + check = gtk_check_button_new_with_mnemonic (string); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE); + gtk_box_pack_start (GTK_BOX (container), check, FALSE, FALSE, 0); + gtk_widget_show (check); + + button = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (button == GTK_RESPONSE_YES) + mask |= BR_OK; + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check))) + mask |= BR_START; + + gtk_widget_destroy (dialog); + + return mask; +} + +static void +set_local_only (GtkFileChooser *file_chooser) +{ + /* XXX Has to be a local file, since the backup utility + * takes a filename argument, not a URI. */ + gtk_file_chooser_set_local_only (file_chooser, TRUE); +} + +static gchar * +suggest_file_name (void) +{ + time_t t; + struct tm tm; + + t = time (NULL); + localtime_r (&t, &tm); + + return g_strdup_printf ( + "evolution-backup-%04d%02d%02d.tar.gz", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); +} + +static void +action_settings_backup_cb (GtkAction *action, + EShellWindow *shell_window) +{ + GFile *file; + GFile *parent; + GFileInfo *file_info; + const gchar *attribute; + GError *error = NULL; + gchar *suggest; + + suggest = suggest_file_name (); + + file = e_shell_run_save_dialog ( + e_shell_window_get_shell (shell_window), + _("Select name of the Evolution backup file"), + suggest, "*.tar.gz", (GtkCallback) + set_local_only, NULL); + + g_free (suggest); + + if (file == NULL) + return; + + /* Make sure the parent directory can be written to. */ + + parent = g_file_get_parent (file); + attribute = G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE; + + /* XXX The query operation blocks the main loop but we + * know it's a local file, so let it slide for now. */ + file_info = g_file_query_info ( + parent, attribute, G_FILE_QUERY_INFO_NONE, NULL, &error); + + g_object_unref (parent); + + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + return; + } + + if (g_file_info_get_attribute_boolean (file_info, attribute)) { + guint32 mask; + gchar *path; + + mask = dialog_prompt_user ( + GTK_WINDOW (shell_window), + _("_Restart Evolution after backup"), + "org.gnome.backup-restore:backup-confirm", NULL); + if (mask & BR_OK) { + path = g_file_get_path (file); + backup (path, (mask & BR_START) ? TRUE: FALSE); + g_free (path); + } + } else { + e_alert_run_dialog_for_args ( + GTK_WINDOW (shell_window), + "org.gnome.backup-restore:insufficient-permissions", + NULL); + } + + g_object_unref (file_info); + g_object_unref (file); +} + +static void +action_settings_restore_cb (GtkAction *action, + EShellWindow *shell_window) +{ + GFile *file; + gchar *path; + + file = e_shell_run_open_dialog ( + e_shell_window_get_shell (shell_window), + _("Select name of the Evolution backup file to restore"), + (GtkCallback) set_local_only, NULL); + + if (file == NULL) + return; + + path = g_file_get_path (file); + + if (evolution_backup_restore_validate_backup_file (path)) { + guint32 mask; + + mask = dialog_prompt_user ( + GTK_WINDOW (shell_window), + _("_Restart Evolution after restore"), + "org.gnome.backup-restore:restore-confirm", NULL); + if (mask & BR_OK) + restore (path, mask & BR_START); + } else { + e_alert_run_dialog_for_args ( + GTK_WINDOW (shell_window), + "org.gnome.backup-restore:invalid-backup", NULL); + } + + g_object_unref (file); + g_free (path); +} + +static GtkActionEntry entries[] = { + + { "settings-backup", + NULL, + N_("_Back up Evolution Data..."), + NULL, + N_("Back up Evolution data and settings to an archive file"), + G_CALLBACK (action_settings_backup_cb) }, + + { "settings-restore", + NULL, + N_("R_estore Evolution Data..."), + NULL, + N_("Restore Evolution data and settings from an archive file"), + G_CALLBACK (action_settings_restore_cb) } +}; + +static gboolean +evolution_backup_restore_filename_to_visible (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer unused) +{ + const gchar *filename; + gboolean visible; + + filename = g_value_get_string (source_value); + visible = (filename != NULL && *filename != '\0'); + g_value_set_boolean (target_value, visible); + + return TRUE; +} + +static void +evolution_backup_restore_prepare_cb (GtkAssistant *assistant, + GtkWidget *page, + EMailConfigRestorePage *restore_page) +{ + const gchar *filename; + + /* If we've landed on the EMailConfigRestoreReadyPage, that + * means the user has chosen a valid backup file to restore + * so start the "evolution-backup" tool immediately. */ + + filename = e_mail_config_restore_page_get_filename (restore_page); + + if (E_IS_MAIL_CONFIG_RESTORE_READY_PAGE (page)) + restore (filename, TRUE); +} + +static void +evolution_backup_restore_assistant_constructed (GObject *object) +{ + EExtension *extension; + EExtensible *extensible; + EMailConfigAssistant *assistant; + const gchar *type_name; + + extension = E_EXTENSION (object); + extensible = e_extension_get_extensible (extension); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (evolution_backup_restore_assistant_parent_class)-> + constructed (object); + + assistant = E_MAIL_CONFIG_ASSISTANT (extensible); + + /* XXX We only want to add the EMailConfigRestorePage to an + * EStartupAssistant instance, not a normal EMailConfigAssistant. + * But EStartupAssistant is defined in the "startup-wizard" module + * and we can't access its GType without knowing its type name, so + * just hard-code the type name. */ + type_name = G_OBJECT_TYPE_NAME (assistant); + if (g_strcmp0 (type_name, "EStartupAssistant") == 0) { + EMailConfigPage *restore_page; + EMailConfigPage *ready_page; + + restore_page = e_mail_config_restore_page_new (); + e_mail_config_assistant_add_page (assistant, restore_page); + + ready_page = e_mail_config_restore_ready_page_new (); + e_mail_config_assistant_add_page (assistant, ready_page); + + g_object_bind_property_full ( + restore_page, "filename", + ready_page, "visible", + G_BINDING_SYNC_CREATE, + evolution_backup_restore_filename_to_visible, + NULL, + NULL, (GDestroyNotify) NULL); + + g_signal_connect ( + assistant, "prepare", + G_CALLBACK (evolution_backup_restore_prepare_cb), + restore_page); + } +} + +static void +evolution_backup_restore_assistant_class_init (EExtensionClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = evolution_backup_restore_assistant_constructed; + + class->extensible_type = E_TYPE_MAIL_CONFIG_ASSISTANT; +} + +static void +evolution_backup_restore_assistant_class_finalize (EExtensionClass *class) +{ +} + +static void +evolution_backup_restore_assistant_init (EExtension *extension) +{ +} + +static void +evolution_backup_restore_menu_items_constructed (GObject *object) +{ + EExtension *extension; + EExtensible *extensible; + EShellWindow *shell_window; + GtkActionGroup *action_group; + GtkUIManager *ui_manager; + GError *error = NULL; + + extension = E_EXTENSION (object); + extensible = e_extension_get_extensible (extension); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (evolution_backup_restore_menu_items_parent_class)-> + constructed (object); + + shell_window = E_SHELL_WINDOW (extensible); + action_group = e_shell_window_get_action_group (shell_window, "shell"); + + /* Add actions to the "shell" action group. */ + gtk_action_group_add_actions ( + action_group, entries, + G_N_ELEMENTS (entries), shell_window); + + /* Because we are loading from a hard-coded string, there is + * no chance of I/O errors. Failure here implies a malformed + * UI definition. Full stop. */ + ui_manager = e_shell_window_get_ui_manager (shell_window); + gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error); + if (error != NULL) + g_error ("%s", error->message); +} + +static void +evolution_backup_restore_menu_items_class_init (EExtensionClass *class) +{ + GObjectClass *object_class; + EExtensionClass *extension_class; + + object_class = G_OBJECT_CLASS (class); + object_class->constructed = evolution_backup_restore_menu_items_constructed; + + extension_class = E_EXTENSION_CLASS (class); + extension_class->extensible_type = E_TYPE_SHELL_WINDOW; +} + +static void +evolution_backup_restore_menu_items_class_finalize (EExtensionClass *class) +{ +} + +static void +evolution_backup_restore_menu_items_init (EExtension *extension) +{ +} + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + evolution_backup_restore_assistant_register_type (type_module); + evolution_backup_restore_menu_items_register_type (type_module); + + e_mail_config_restore_page_type_register (type_module); + e_mail_config_restore_ready_page_type_register (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} + diff --git a/modules/backup-restore/evolution-backup-tool.c b/modules/backup-restore/evolution-backup-tool.c new file mode 100644 index 0000000000..38a477e00d --- /dev/null +++ b/modules/backup-restore/evolution-backup-tool.c @@ -0,0 +1,935 @@ +/* + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef G_OS_WIN32 +#ifdef DATADIR +#undef DATADIR +#endif +#include +#include +#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 GCONF_DUMP_FILE "backup-restore-gconf.xml" +#define GCONF_DIR "/apps/evolution" + +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 ( + "[dirs]\n" + "data=$STRIPDATADIR\n" + "config=$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 ("gconftool-2 --dump " GCONF_DIR " > " EVOLUTION_DIR GCONF_DUMP_FILE); + + replace_in_file ( + EVOLUTION_DIR GCONF_DUMP_FILE, + 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_dirs (const gchar *filename, + 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); + } else { + 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, *config_dir = 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_dirs */ + extract_backup_dirs (dir_fn->str, &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); + + g_free (data_dir); + g_free (config_dir); + } 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... */ + replace_in_file ( + EVOLUTION_DIR GCONF_DUMP_FILE, + EVOUSERDATADIR_MAGIC, e_get_user_data_dir ()); + run_cmd ("gconftool-2 --load " EVOLUTION_DIR GCONF_DUMP_FILE); + run_cmd ("rm " EVOLUTION_DIR GCONF_DUMP_FILE); + } else { + gchar *gconf_dump_file; + + /* ... old format in ~/.evolution */ + gconf_dump_file = g_build_filename ( + "$HOME", ".evolution", 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, 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 ("%s", 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 ("%s", 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; +} diff --git a/modules/backup-restore/org-gnome-backup-restore.error.xml b/modules/backup-restore/org-gnome-backup-restore.error.xml new file mode 100644 index 0000000000..b38a134132 --- /dev/null +++ b/modules/backup-restore/org-gnome-backup-restore.error.xml @@ -0,0 +1,24 @@ + + + + + <_primary>Invalid Evolution backup file + <_secondary>Please select a valid backup file to restore. + + + <_primary>Are you sure you want to close Evolution? + <_secondary xml:space="preserve">To back up your data and settings, you must first close Evolution. Please make sure that you save any unsaved data before proceeding. +