/* * 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 DBUS_SOURCE_REGISTRY_SERVICE_FILE "$DBUSDATADIR/org.gnome.evolution.dataserver.Sources.service" #define ANCIENT_GCONF_DUMP_FILE "backup-restore-gconf.xml" #define DCONF_DUMP_FILE_EDS "backup-restore-dconf-eds.ini" #define DCONF_DUMP_FILE_EVO "backup-restore-dconf-evo.ini" #define DCONF_PATH_EDS "/org/gnome/evolution-data-server/" #define DCONF_PATH_EVO "/org/gnome/evolution/" #define KEY_FILE_GROUP "Evolution Backup" static gboolean backup_op = FALSE; static gchar *bk_file = NULL; static gboolean restore_op = FALSE; static gchar *res_file = NULL; static gboolean check_op = FALSE; static gchar *chk_file = NULL; static gboolean restart_arg = FALSE; static gboolean gui_arg = FALSE; static gchar **opt_remaining = NULL; static gint result = 0; static GtkWidget *progress_dialog; static GtkWidget *pbar; static gchar *txt = NULL; static GOptionEntry options[] = { { "backup", '\0', 0, G_OPTION_ARG_NONE, &backup_op, N_("Back up Evolution directory"), NULL }, { "restore", '\0', 0, G_OPTION_ARG_NONE, &restore_op, N_("Restore Evolution directory"), NULL }, { "check", '\0', 0, G_OPTION_ARG_NONE, &check_op, N_("Check Evolution Back up"), NULL }, { "restart", '\0', 0, G_OPTION_ARG_NONE, &restart_arg, N_("Restart Evolution"), NULL }, { "gui", '\0', 0, G_OPTION_ARG_NONE, &gui_arg, N_("With Graphical User Interface"), NULL }, { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &opt_remaining }, { NULL } }; #define d(x) #define print_and_run(x) \ G_STMT_START { g_message ("%s", x); if (system (x) == -1) g_warning ("%s: Failed to execute '%s'", G_STRFUNC, (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, gboolean remove_dir_sep) { 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); repl ("$DBUSDATADIR", DBUS_SERVICES_DIR); #undef repl g_return_val_if_fail (res != NULL, NULL); if (remove_dir_sep) { /* 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, TRUE); 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 != NULL) { 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, FALSE); 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, TRUE); g_return_if_fail (filename != NULL); content = replace_variables ( "[" KEY_FILE_GROUP "]\n" "Version=" VERSION "\n" "UserDataDir=$STRIPDATADIR\n" "UserConfigDir=$STRIPCONFIGDIR\n" , TRUE); g_return_if_fail (content != NULL); g_file_set_contents (filename->str, content->str, content->len, &error); if (error != NULL) { g_warning ("Failed to write file '%s': %s\n", filename->str, error->message); g_error_free (error); } g_string_free (filename, TRUE); g_string_free (content, TRUE); } static void backup (const gchar *filename, GCancellable *cancellable) { gchar *command; gchar *quotedfname; g_return_if_fail (filename && *filename); quotedfname = g_shell_quote (filename); if (g_cancellable_is_cancelled (cancellable)) return; txt = _("Shutting down Evolution"); /* FIXME Will the versioned setting always work? */ run_cmd (EVOLUTION " --quit"); run_cmd ("rm $DATADIR/.running"); if (g_cancellable_is_cancelled (cancellable)) return; txt = _("Backing Evolution accounts and settings"); run_cmd ("dconf dump " DCONF_PATH_EDS " >" EVOLUTION_DIR DCONF_DUMP_FILE_EDS); run_cmd ("dconf dump " DCONF_PATH_EVO " >" EVOLUTION_DIR DCONF_DUMP_FILE_EVO); replace_in_file ( EVOLUTION_DIR DCONF_DUMP_FILE_EDS, e_get_user_data_dir (), EVOUSERDATADIR_MAGIC); replace_in_file ( EVOLUTION_DIR DCONF_DUMP_FILE_EVO, e_get_user_data_dir (), EVOUSERDATADIR_MAGIC); write_dir_file (); if (g_cancellable_is_cancelled (cancellable)) return; txt = _("Backing Evolution data (Mails, Contacts, Calendar, Tasks, Memos)"); /* FIXME stay on this file system ,other options?" */ /* FIXME compression type?" */ /* FIXME date/time stamp?" */ /* FIXME backup location?" */ command = g_strdup_printf ( "cd $HOME && tar chf - $STRIPDATADIR " "$STRIPCONFIGDIR " EVOLUTION_DIR_FILE " | " "gzip > %s", quotedfname); run_cmd (command); g_free (command); g_free (quotedfname); run_cmd ("rm $HOME/" EVOLUTION_DIR_FILE); txt = _("Back up complete"); if (restart_arg) { if (g_cancellable_is_cancelled (cancellable)) return; txt = _("Restarting Evolution"); run_evolution_no_wait (); } } static void extract_backup_data (const gchar *filename, gchar **restored_version, gchar **data_dir, gchar **config_dir) { GKeyFile *key_file; GError *error = NULL; g_return_if_fail (filename != NULL); g_return_if_fail (data_dir != NULL); g_return_if_fail (config_dir != NULL); key_file = g_key_file_new (); g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error); if (error != NULL) { g_warning ("Failed to read '%s': %s", filename, error->message); g_error_free (error); /* This is the current format as of Evolution 3.6. */ } else if (g_key_file_has_group (key_file, KEY_FILE_GROUP)) { gchar *tmp; tmp = g_key_file_get_value ( key_file, KEY_FILE_GROUP, "Version", NULL); if (tmp != NULL) *restored_version = g_strstrip (g_strdup (tmp)); g_free (tmp); tmp = g_key_file_get_value ( key_file, KEY_FILE_GROUP, "UserDataDir", NULL); if (tmp != NULL) *data_dir = g_shell_quote (tmp); g_free (tmp); tmp = g_key_file_get_value ( key_file, KEY_FILE_GROUP, "UserConfigDir", NULL); if (tmp != NULL) *config_dir = g_shell_quote (tmp); g_free (tmp); /* This is the legacy format with no version information. */ } else if (g_key_file_has_group (key_file, "dirs")) { gchar *tmp; tmp = g_key_file_get_value (key_file, "dirs", "data", NULL); if (tmp) *data_dir = g_shell_quote (tmp); g_free (tmp); tmp = g_key_file_get_value (key_file, "dirs", "config", NULL); if (tmp) *config_dir = g_shell_quote (tmp); g_free (tmp); } g_key_file_free (key_file); } static gint get_dir_level (const gchar *dir) { gint res = 0, i; g_return_val_if_fail (dir != NULL, -1); for (i = 0; dir[i]; i++) { if (dir[i] == '/' || dir[i] == '\\') res++; } if (i > 0) res++; return res; } static gchar * get_source_manager_reload_command (void) { GString *tmp; gchar *command; tmp = replace_variables (DBUS_SOURCE_REGISTRY_SERVICE_FILE, TRUE); if (tmp) { GKeyFile *key_file; gchar *str = NULL; key_file = g_key_file_new (); if (g_key_file_load_from_file (key_file, tmp->str, G_KEY_FILE_NONE, NULL)) { str = g_key_file_get_string (key_file, "D-BUS Service", "Name", NULL); } g_key_file_free (key_file); if (str && *str) { g_string_assign (tmp, str); } else { g_string_free (tmp, TRUE); tmp = NULL; } g_free (str); } if (!tmp) tmp = g_string_new ("org.gnome.evolution.dataserver.Sources0"); command = g_strdup_printf ("gdbus call --session --dest %s " "--object-path /org/gnome/evolution/dataserver/SourceManager " "--method org.gnome.evolution.dataserver.SourceManager.Reload", tmp->str); g_string_free (tmp, TRUE); return command; } 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"); if (g_cancellable_is_cancelled (cancellable)) return; txt = _("Extracting files from back up"); if (is_new_format) { GString *dir_fn; gchar *data_dir = NULL; gchar *config_dir = NULL; gchar *restored_version = NULL; command = g_strdup_printf ( "cd $TMP && tar xzf %s " EVOLUTION_DIR_FILE, quotedfname); run_cmd (command); g_free (command); dir_fn = replace_variables ("$TMP" G_DIR_SEPARATOR_S EVOLUTION_DIR_FILE, TRUE); if (!dir_fn) { g_warning ("Failed to create evolution's dir filename"); goto end; } /* data_dir and config_dir are quoted inside extract_backup_data */ extract_backup_data ( dir_fn->str, &restored_version, &data_dir, &config_dir); g_unlink (dir_fn->str); g_string_free (dir_fn, TRUE); if (!data_dir || !config_dir) { g_warning ( "Failed to get old data_dir (%p)/" "config_dir (%p)", data_dir, config_dir); g_free (data_dir); g_free (config_dir); goto end; } g_mkdir_with_parents (e_get_user_data_dir (), 0700); g_mkdir_with_parents (e_get_user_config_dir (), 0700); command = g_strdup_printf ( "cd $DATADIR && tar xzf %s %s --strip-components=%d", quotedfname, data_dir, get_dir_level (data_dir)); run_cmd (command); g_free (command); command = g_strdup_printf ( "cd $CONFIGDIR && tar xzf %s %s --strip-components=%d", quotedfname, config_dir, get_dir_level (config_dir)); run_cmd (command); g_free (command); /* If the back file had version information, set the last * used version in GSettings before restarting Evolution. */ if (restored_version != NULL && *restored_version != '\0') { GSettings *settings; settings = g_settings_new ("org.gnome.evolution"); g_settings_set_string ( settings, "version", restored_version); g_object_unref (settings); } g_free (data_dir); g_free (config_dir); g_free (restored_version); } else { run_cmd ("mv $HOME/.evolution $HOME/.evolution_old"); command = g_strdup_printf ( "cd $HOME && gzip -cd %s | tar xf -", quotedfname); run_cmd (command); g_free (command); } g_free (quotedfname); if (g_cancellable_is_cancelled (cancellable)) return; txt = _("Loading Evolution settings"); if (is_new_format) { /* new format has it in DATADIR... */ GString *file = replace_variables (EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE, TRUE); if (file && g_file_test (file->str, G_FILE_TEST_EXISTS)) { /* ancient backup */ replace_in_file ( EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE, EVOUSERDATADIR_MAGIC, e_get_user_data_dir ()); run_cmd ("gconftool-2 --load " EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE); /* give a chance to GConf to save what was loaded into a disk */ g_usleep (G_USEC_PER_SEC * 5); /* do not forget to convert GConf keys into GSettings */ run_cmd ("gsettings-data-convert"); run_cmd ("rm " EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE); } else { replace_in_file ( EVOLUTION_DIR DCONF_DUMP_FILE_EDS, EVOUSERDATADIR_MAGIC, e_get_user_data_dir ()); run_cmd ("cat " EVOLUTION_DIR DCONF_DUMP_FILE_EDS " | dconf load " DCONF_PATH_EDS); run_cmd ("rm " EVOLUTION_DIR DCONF_DUMP_FILE_EDS); replace_in_file ( EVOLUTION_DIR DCONF_DUMP_FILE_EVO, EVOUSERDATADIR_MAGIC, e_get_user_data_dir ()); run_cmd ("cat " EVOLUTION_DIR DCONF_DUMP_FILE_EVO " | dconf load " DCONF_PATH_EVO); run_cmd ("rm " EVOLUTION_DIR DCONF_DUMP_FILE_EVO); } g_string_free (file, TRUE); } else { gchar *gconf_dump_file; /* ... old format in ~/.evolution */ gconf_dump_file = g_build_filename ( "$HOME", ".evolution", ANCIENT_GCONF_DUMP_FILE, NULL); replace_in_file ( gconf_dump_file, EVOUSERDATADIR_MAGIC, e_get_user_data_dir ()); command = g_strconcat ( "gconftool-2 --load ", gconf_dump_file, NULL); run_cmd (command); g_free (command); /* give a chance to GConf to save what was loaded into a disk */ g_usleep (G_USEC_PER_SEC * 5); /* do not forget to convert GConf keys into GSettings */ run_cmd ("gsettings-data-convert"); 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 $DATADIR/.running"); if (!is_new_format) run_cmd ("rm -rf $HOME/.evolution_old"); if (g_cancellable_is_cancelled (cancellable)) return; txt = _("Reloading registry service"); /* wait few seconds, till changes settle */ g_usleep (G_USEC_PER_SEC * 5); command = get_source_manager_reload_command (); /* This runs migration routines on the newly-restored data. */ run_cmd (command); g_free (command); end: if (restart_arg) { if (g_cancellable_is_cancelled (cancellable)) return; txt = _("Restarting Evolution"); /* wait 5 seconds before restarting evolution, thus any * changes being done are updated in source registry too */ g_usleep (G_USEC_PER_SEC * 5); run_evolution_no_wait (); } } static gboolean check (const gchar *filename, gboolean *is_new_format) { gchar *command; gchar *quotedfname; gboolean is_new = TRUE; g_return_val_if_fail (filename && *filename, FALSE); quotedfname = g_shell_quote (filename); if (is_new_format) *is_new_format = FALSE; command = g_strdup_printf ("tar ztf %s 1>/dev/null", quotedfname); result = system (command); g_free (command); g_message ("First result %d", result); if (result) { g_free (quotedfname); return FALSE; } command = g_strdup_printf ( "tar ztf %s | grep -e \"%s$\"", quotedfname, EVOLUTION_DIR_FILE); result = system (command); g_free (command); if (result) { command = g_strdup_printf ( "tar ztf %s | grep -e \"^\\.evolution/$\"", quotedfname); result = system (command); g_free (command); is_new = FALSE; } g_message ("Second result %d", result); if (result) { g_free (quotedfname); return FALSE; } if (is_new) { if (is_new_format) *is_new_format = TRUE; g_free (quotedfname); return TRUE; } command = g_strdup_printf ( "tar ztf %s | grep -e \"^\\.evolution/%s$\"", quotedfname, ANCIENT_GCONF_DUMP_FILE); result = system (command); g_free (command); if (result != 0) { /* maybe it's an ancient backup */ command = g_strdup_printf ( "tar ztf %s | grep -e \"^\\.evolution/%s$\"", quotedfname, ANCIENT_GCONF_DUMP_FILE); result = system (command); g_free (command); } g_free (quotedfname); g_message ("Third result %d", result); return result == 0; } static gboolean pbar_update (gpointer user_data) { GCancellable *cancellable = G_CANCELLABLE (user_data); 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); 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_grid_new (); gtk_grid_set_column_spacing (GTK_GRID (container), 6); gtk_grid_set_row_spacing (GTK_GRID (container), 0); 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_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 3); g_object_set ( G_OBJECT (widget), "halign", GTK_ALIGN_FILL, "valign", GTK_ALIGN_FILL, "vexpand", TRUE, NULL); 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_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1); g_object_set ( G_OBJECT (widget), "halign", GTK_ALIGN_FILL, "hexpand", TRUE, "valign", GTK_ALIGN_FILL, NULL); 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_grid_attach (GTK_GRID (container), widget, 1, 1, 1, 1); g_object_set ( G_OBJECT (widget), "halign", GTK_ALIGN_FILL, "hexpand", TRUE, "valign", GTK_ALIGN_FILL, NULL); 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_grid_attach (GTK_GRID (container), widget, 1, 2, 1, 1); g_object_set ( G_OBJECT (widget), "halign", GTK_ALIGN_FILL, "hexpand", TRUE, "valign", GTK_ALIGN_FILL, NULL); gtk_grid_attach (GTK_GRID (container), pbar, 1, 3, 1, 1); } else gtk_grid_attach (GTK_GRID (container), pbar, 1, 2, 1, 1); g_object_set ( G_OBJECT (pbar), "halign", GTK_ALIGN_FILL, "hexpand", TRUE, "valign", GTK_ALIGN_FILL, NULL); 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) { e_named_timeout_add_full ( G_PRIORITY_DEFAULT, 50, 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; }