aboutsummaryrefslogblamecommitdiffstats
path: root/modules/backup-restore/evolution-backup-tool.c
blob: 93a56bd672d949b9c627ac2b77e28509daffbdb2 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  
                          











                                                                    
                                                                             
  
   
 







                    
 
                       
                        
                    
 
                                          
 













                                                          
                                  

                          

                                                
                             

                                           
                                                                                                       







                                                          
 

                                         
                                  
                             
                                   
                              
                                 
                              
                                    
                                
                                    
                       

                                  
                         
 
                                 
                                                           
                                                    


                                                             
                                                


                                                              
                                                      




                                                      
            
 

                                                                    
 
                                                                       
 
                


                                     
 
                              
                     
                      









                                                  
                             












                                                               





















                                                      
                                    
                                           





















                                                                                 
                                                 




                                                 




                                                                                   




                   
           


                                       
 
                              







                                            
                                     
                                                                 

                                   


                                                                        










                                                                                             


                                                                                




                                                     


                                                                      



                                 


                                                                  







                                                  
                          



                       
                                        
                                                                                   
                                                              
 


                                                  
                 
              
                                    


           





                                                     




                                    
                                                                         


                                            


                                             

                                                 













                                                                                             

                                  
 

                           
 
                                                 
                                               
 


                                                     
                                           
                                                           
                                      
 
                                         
 


                                                     
                                                           





                                                                                      
 
                         
                                                  
                                                              

                          
 


                                                     

                                                                                    


                                                             
                                     

                                                      
                                                           
                                          
                          
                         
                             
 

                                                 
                                    


                          

                                                             
 
                                                
                                         

         


           

                                              

                                        













                                                                                
























                                                                         

































                                                                              





                                        
                                                                          
































                                                                                              
           

                                   
 

                           
                                       
 
                                                 
 
                                                
                                                                                       
                         

         
                                               
 


                                                     
                                                           
                                           
                                      
 


                                                     
                                                  

                                                 
 


                                                     
                                                 


                                


                                               
 


                                                         


                                  
                                                                                               




                                                                                





                                                                                   




                                               


                                                                         







                                                                      


                                                                             


                                  


                                                                               


                                  










                                                                            

                                    
                                          


                                                                     

                                                                           



                                  
                             
 


                                                     
                                              
 

                                                     
                                                                                                





                                                                                              



                                                                                        

                                                                                















                                                                                                           
                

                                       
                                                    
                                                    
                                                                              










                                                                      


                                                                                


                                                                        




                                                                     
         
 


                                                     
                                                    

                                          



                                                        
 


                                                     

                                              






                                                                      
 
    
                          


                                                             
                                                

                                                                       
                                                                           

                                              
                                         
         

 
               

                               
 

                           
                               
 
                                                            
                                               
 


                                       
                                                                          

                                  
 
                                              




                                     


                                                 


                                  
                     


                                                                   




                                          




                                               
 






                                              

                                                             
                                                      

                                  









                                                                     
                             
 
                                              
 
                           

 
               
                                       
 

                                                                 
 

                                                         

 

                               
 
                         
 
                     

 
               


                                     
 





                                                              
 

                                                              




                     


                                        
 

                                                     
                                           
 
                                                                            

                                          
 

                                                        
                              
 
                                                                      

                                                          

                                      
                                                                               









                                                          

                         
 
    

                   
 
                                  
                                         

                                  
                             
 





                                                                              





                                                         





                                                                             



                                                         
                                          


                                                                         



         



                                                              
                            

                                                                      

                                                    
                                     
                                    
         
 

                                                                        
                                        
                                                               
                                                                        
                                                                                 

                                                
                                                               
                                                                         
                                                                                  

                                                
                                                                                           
                                                                                  
                         


                 

                                           

                                              

                                        


                                        
 

                                                               







                                                               
 

                                                             
 




                                                            
                                                 



                                                                                 
 
                                 
                                                           
 


                                                                      

                                            

                                                                           
 

                                                              


                                                                     
                                                                           

                                          



                                                 







                                                                                         
                                                               
                 
 

                                                                               
                                                                    
                                                                    

                                                                     
                                
 
                                                                           

                                          



                                                 
 



                                                                         
                                                                   

                                                                     
                                
 
                                                                           

                                          



                                                 
 
                                               
 



                                                                            
                                                                             
                                        
                                     
 
                                                                                   

                                                  



                                                         
 
                                                                                 
                      

                                                                                 

                                        



                                                 
 


                                                                
                                                      
 
                              
                                                  
                                       
                                           

         











                                                         
                    
 

                                     
                      
 
/*
 * evolution-backup-tool.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>

#include <libedataserver/libedataserver.h>

#ifdef G_OS_WIN32
#ifdef DATADIR
#undef DATADIR
#endif
#include <windows.h>
#include <conio.h>
#ifndef PROCESS_DEP_ENABLE
#define PROCESS_DEP_ENABLE 0x00000001
#endif
#ifndef PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
#define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION 0x00000002
#endif
#endif

#include "e-util/e-util-private.h"
#include "e-util/e-util.h"

#define EVOUSERDATADIR_MAGIC "#EVO_USERDATADIR#"

#define EVOLUTION "evolution"
#define EVOLUTION_DIR "$DATADIR/"
#define EVOLUTION_DIR_FILE EVOLUTION ".dir"
#define 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); 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,
                   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) {
        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) {
        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) {
        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 (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);

        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 ("<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_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 ("<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_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)
        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;
}