diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2012-03-26 06:19:01 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2012-06-03 11:00:41 +0800 |
commit | cd3759369b675e754dbed5ba19894cdd87a63a88 (patch) | |
tree | 3eaca93bd8630675c57896da239f5ade32300e40 /modules | |
parent | 71f5369ebfe5ee1d06b0bd1936cca80abc58e60a (diff) | |
download | gsoc2013-evolution-cd3759369b675e754dbed5ba19894cdd87a63a88.tar.gz gsoc2013-evolution-cd3759369b675e754dbed5ba19894cdd87a63a88.tar.zst gsoc2013-evolution-cd3759369b675e754dbed5ba19894cdd87a63a88.zip |
Add 'backup-restore' module.
Replaces the 'backup-restore' plugin.
Diffstat (limited to 'modules')
-rw-r--r-- | modules/Makefile.am | 1 | ||||
-rw-r--r-- | modules/backup-restore/Makefile.am | 84 | ||||
-rw-r--r-- | modules/backup-restore/e-mail-config-restore-page.c | 382 | ||||
-rw-r--r-- | modules/backup-restore/e-mail-config-restore-page.h | 81 | ||||
-rw-r--r-- | modules/backup-restore/e-mail-config-restore-ready-page.c | 80 | ||||
-rw-r--r-- | modules/backup-restore/e-mail-config-restore-ready-page.h | 76 | ||||
-rw-r--r-- | modules/backup-restore/evolution-backup-restore.c | 494 | ||||
-rw-r--r-- | modules/backup-restore/evolution-backup-tool.c | 935 | ||||
-rw-r--r-- | modules/backup-restore/org-gnome-backup-restore.error.xml | 24 |
9 files changed, 2157 insertions, 0 deletions
diff --git a/modules/Makefile.am b/modules/Makefile.am index ce6c0aa94a..e52034bc42 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -15,6 +15,7 @@ SUBDIRS = \ bogofilter \ calendar \ mail \ + backup-restore \ composer-autosave \ mailto-handler \ mdn \ 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 <http://www.gnu.org/licenses/> + * + */ + +#include "e-mail-config-restore-page.h" + +#include <config.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif +#include <glib/gi18n-lib.h> + +#include <libevolution-utils/e-alert-sink.h> +#include <libevolution-utils/e-alert-dialog.h> +#include <misc/e-alert-bar.h> + +#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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_MAIL_CONFIG_RESTORE_PAGE_H +#define E_MAIL_CONFIG_RESTORE_PAGE_H + +#include <gtk/gtk.h> + +#include <mail/e-mail-config-page.h> +#include <mail/e-mail-config-welcome-page.h> + +/* 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 <http://www.gnu.org/licenses/> + * + */ + +/* 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 <config.h> +#include <glib/gi18n-lib.h> + +/* 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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef E_MAIL_CONFIG_RESTORE_READY_PAGE_H +#define E_MAIL_CONFIG_RESTORE_READY_PAGE_H + +#include <gtk/gtk.h> + +#include <mail/e-mail-config-page.h> + +#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 <http://www.gnu.org/licenses/> + * + */ + +#include <config.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif +#include <stdlib.h> + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <libebackend/e-extension.h> + +#include <mail/e-mail-config-assistant.h> +#include <libevolution-utils/e-alert-dialog.h> +#include <e-util/e-util.h> +#include <e-util/e-dialog-utils.h> +#include <shell/e-shell-utils.h> +#include <shell/e-shell-window.h> + +#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 = +"<ui>" +" <menubar name='main-menu'>" +" <menu action='file-menu'>" +" <placeholder name='file-actions'>" +" <menuitem action='settings-backup'/>" +" <menuitem action='settings-restore'/>" +" </placeholder>" +" </menu>" +" </menubar>" +"</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 <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/e-data-server-util.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 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 ("<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; +} 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<error-list domain="org.gnome.backup-restore"> + + <error id="invalid-backup" type="warning"> + <_primary>Invalid Evolution backup file</_primary> + <_secondary>Please select a valid backup file to restore.</_secondary> + </error> + <error id="backup-confirm" type="warning" default="GTK_RESPONSE_CANCEL"> + <_primary>Are you sure you want to close Evolution?</_primary> + <_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.</_secondary> + <button _label="Close and Back up Evolution" response="GTK_RESPONSE_YES"/> + <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/> + </error> + <error id="restore-confirm" type="warning" default="GTK_RESPONSE_CANCEL"> + <_primary>Are you sure you want to restore Evolution from the selected backup file?</_primary> + <_secondary xml:space="preserve">To restore your data and settings, you must first close Evolution. Please make sure that you save any unsaved data before proceeding. This will delete all your current Evolution data and settings and restore them from your backup.</_secondary> + <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/> + <button _label="Close and Restore Evolution" response="GTK_RESPONSE_YES"/> + </error> + <error id="insufficient-permissions" type="error"> + <_primary>Insufficient Permissions</_primary> + <_secondary>The selected folder is not writable.</_secondary> + </error> +</error-list> |