aboutsummaryrefslogblamecommitdiffstats
path: root/modules/backup-restore/evolution-backup-tool.c
blob: d09efa36289dfa15ee7fbfcfd4dd5077932bc31c (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 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/"

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 ("dconf dump " DCONF_PATH_EDS " >" EVOLUTION_DIR DCONF_DUMP_FILE_EDS);
    run_cmd ("dconf dump " DCONF_PATH_EVO " >" EVOLUTION_DIR DCONF_DUMP_FILE_EVO);

    replace_in_file (
        EVOLUTION_DIR DCONF_DUMP_FILE_EDS,
        e_get_user_data_dir (), EVOUSERDATADIR_MAGIC);

    replace_in_file (
        EVOLUTION_DIR DCONF_DUMP_FILE_EVO,
        e_get_user_data_dir (), EVOUSERDATADIR_MAGIC);

    write_dir_file ();

    if (g_cancellable_is_cancelled (cancellable))
        return;

    txt = _("Backing Evolution data (Mails, Contacts, Calendar, Tasks, Memos)");

    /* FIXME stay on this file system ,other options?" */
    /* FIXME compression type?" */
    /* FIXME date/time stamp?" */
    /* FIXME backup location?" */
    command = g_strdup_printf (
        "cd $HOME && tar chf - $STRIPDATADIR "
        "$STRIPCONFIGDIR .camel_certs " EVOLUTION_DIR_FILE " | "
        "gzip > %s", quotedfname);
    run_cmd (command);
    g_free (command);
    g_free (quotedfname);

    run_cmd ("rm $HOME/" EVOLUTION_DIR_FILE);

    txt = _("Back up complete");

    if (restart_arg) {

        if (g_cancellable_is_cancelled (cancellable))
            return;

        txt = _("Restarting Evolution");
        run_evolution_no_wait ();
    }

}

static void
extract_backup_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... */
        GString *file = replace_variables (EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE);
        if (file && g_file_test (file->str, G_FILE_TEST_EXISTS)) {
            /* ancient backup */
            replace_in_file (
                EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE,
                EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
            run_cmd ("gconftool-2 --load " EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE);
            run_cmd ("rm " EVOLUTION_DIR ANCIENT_GCONF_DUMP_FILE);
        } else {
            replace_in_file (
                EVOLUTION_DIR DCONF_DUMP_FILE_EDS,
                EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
            run_cmd ("cat " EVOLUTION_DIR DCONF_DUMP_FILE_EDS " | dconf load " DCONF_PATH_EDS);
            run_cmd ("rm " EVOLUTION_DIR DCONF_DUMP_FILE_EDS);

            replace_in_file (
                EVOLUTION_DIR DCONF_DUMP_FILE_EVO,
                EVOUSERDATADIR_MAGIC, e_get_user_data_dir ());
            run_cmd ("cat " EVOLUTION_DIR DCONF_DUMP_FILE_EVO " | dconf load " DCONF_PATH_EVO);
            run_cmd ("rm " EVOLUTION_DIR DCONF_DUMP_FILE_EVO);
        }

        g_string_free (file, TRUE);
    } else {
        gchar *gconf_dump_file;

        /* ... old format in ~/.evolution */
        gconf_dump_file = g_build_filename (
            "$HOME", ".evolution", ANCIENT_GCONF_DUMP_FILE, NULL);

        replace_in_file (
            gconf_dump_file,
            EVOUSERDATADIR_MAGIC,
            e_get_user_data_dir ());

        command = g_strconcat (
            "gconftool-2 --load ", gconf_dump_file, NULL);
        run_cmd (command);
        g_free (command);

        command = g_strconcat ("rm ", gconf_dump_file, NULL);
        run_cmd (command);
        g_free (command);

        g_free (gconf_dump_file);
    }

    if (g_cancellable_is_cancelled (cancellable))
        return;

    txt = _("Removing temporary back up files");
    run_cmd ("rm -rf $DATADIR_old");
    run_cmd ("rm -rf $CONFIGDIR_old");
    run_cmd ("rm -rf $HOME/.camel_certs_old");
    run_cmd ("rm $DATADIR/.running");

    if (!is_new_format)
        run_cmd ("rm -rf $HOME/.evolution_old");

    if (g_cancellable_is_cancelled (cancellable))
        return;

    txt = _("Reloading registry service");

    /* This runs migration routines on the newly-restored data.
     *
     * XXX Hard-coding the whole command like this is not ideal
     *     because the "SourcesX" interface name will occasionally
     *     change and I guarantee we'll forget to update this. */
    run_cmd (
        "gdbus call --session "
        "--dest org.gnome.evolution.dataserver.Sources0 "
        "--object-path /org/gnome/evolution/dataserver/SourceManager "
        "--method org.gnome.evolution.dataserver.SourceManager.Reload");

end:
    if (restart_arg) {
        if (g_cancellable_is_cancelled (cancellable))
            return;

        txt = _("Restarting Evolution");
        run_evolution_no_wait ();
    }
}

static gboolean
check (const gchar *filename,
       gboolean *is_new_format)
{
    gchar *command;
    gchar *quotedfname;
    gboolean is_new = TRUE;

    g_return_val_if_fail (filename && *filename, FALSE);
    quotedfname = g_shell_quote (filename);

    if (is_new_format)
        *is_new_format = FALSE;

    command = g_strdup_printf ("tar ztf %s 1>/dev/null", quotedfname);
    result = system (command);
    g_free (command);

    g_message ("First result %d", result);
    if (result) {
        g_free (quotedfname);
        return FALSE;
    }

    command = g_strdup_printf (
        "tar ztf %s | grep -e \"%s$\"",
        quotedfname, EVOLUTION_DIR_FILE);
    result = system (command);
    g_free (command);

    if (result) {
        command = g_strdup_printf (
            "tar ztf %s | grep -e \"^\\.evolution/$\"",
            quotedfname);
        result = system (command);
        g_free (command);
        is_new = FALSE;
    }

    g_message ("Second result %d", result);
    if (result) {
        g_free (quotedfname);
        return FALSE;
    }

    if (is_new) {
        if (is_new_format)
            *is_new_format = TRUE;
        g_free (quotedfname);
        return TRUE;
    }

    command = g_strdup_printf (
        "tar ztf %s | grep -e \"^\\.evolution/%s$\"",
        quotedfname, ANCIENT_GCONF_DUMP_FILE);
    result = system (command);
    g_free (command);

    if (result != 0) {
        /* maybe it's an ancient backup */
        command = g_strdup_printf (
            "tar ztf %s | grep -e \"^\\.evolution/%s$\"",
            quotedfname, ANCIENT_GCONF_DUMP_FILE);
        result = system (command);
        g_free (command);
    }

    g_free (quotedfname);

    g_message ("Third result %d", result);

    return result == 0;
}

static gboolean
pbar_update (GCancellable *cancellable)
{
    gtk_progress_bar_pulse ((GtkProgressBar *) pbar);
    gtk_progress_bar_set_text ((GtkProgressBar *) pbar, txt);

    /* Return TRUE to reschedule the timeout. */
    return !g_cancellable_is_cancelled (cancellable);
}

static gboolean
finish_job (gpointer user_data)
{
    gtk_main_quit ();

    return FALSE;
}

static gboolean
start_job (GIOSchedulerJob *job,
           GCancellable *cancellable,
           gpointer user_data)
{
    if (backup_op)
        backup (bk_file, cancellable);
    else if (restore_op)
        restore (res_file, cancellable);
    else if (check_op)
        check (chk_file, NULL);  /* not cancellable */

    g_io_scheduler_job_send_to_mainloop_async (
        job, finish_job, NULL, (GDestroyNotify) NULL);

    return FALSE;
}

static void
dlg_response (GtkWidget *dlg,
              gint response,
              GCancellable *cancellable)
{
    /* We will cancel only backup/restore
     * operations and not the check operation. */
    g_cancellable_cancel (cancellable);

    /* If the response is not of delete_event then destroy the event. */
    if (response != GTK_RESPONSE_NONE)
        gtk_widget_destroy (dlg);

    /* We will kill just the tar operation. Rest of
     * them will be just a second of microseconds.*/
    run_cmd ("pkill tar");

    if (bk_file && backup_op && response == GTK_RESPONSE_REJECT) {
        /* Backup was canceled, delete the
         * backup file as it is not needed now. */
        gchar *cmd, *filename;

        g_message ("Back up canceled, removing partial back up file.");

        filename = g_shell_quote (bk_file);
        cmd = g_strconcat ("rm ", filename, NULL);

        run_cmd (cmd);

        g_free (cmd);
        g_free (filename);
    }

    gtk_main_quit ();
}

gint
main (gint argc,
      gchar **argv)
{
    GCancellable *cancellable;
    gchar *file = NULL, *oper = NULL;
    const gchar *title = NULL;
    gint ii;
    GError *error = NULL;

#ifdef G_OS_WIN32
    /* Reduce risks */
    {
        typedef BOOL (WINAPI *t_SetDllDirectoryA) (LPCSTR lpPathName);
        t_SetDllDirectoryA p_SetDllDirectoryA;

        p_SetDllDirectoryA = GetProcAddress (
            GetModuleHandle ("kernel32.dll"),
            "SetDllDirectoryA");

        if (p_SetDllDirectoryA != NULL)
            p_SetDllDirectoryA ("");
    }
#ifndef _WIN64
    {
        typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags);
        t_SetProcessDEPPolicy p_SetProcessDEPPolicy;

        p_SetProcessDEPPolicy = GetProcAddress (
            GetModuleHandle ("kernel32.dll"),
            "SetProcessDEPPolicy");

        if (p_SetProcessDEPPolicy)
            p_SetProcessDEPPolicy (
                PROCESS_DEP_ENABLE |
                PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
    }
#endif
#endif

    bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
    textdomain (GETTEXT_PACKAGE);

    gtk_init_with_args (
        &argc, &argv, NULL, options, GETTEXT_PACKAGE, &error);

    if (error != NULL) {
        g_printerr ("%s\n", error->message);
        g_error_free (error);
        exit (EXIT_FAILURE);
    }

    if (opt_remaining != NULL) {
        for (ii = 0; ii < g_strv_length (opt_remaining); ii++) {
            if (backup_op) {
                title = _("Evolution Back Up");
                oper = _("Backing up to the folder %s");
                bk_file = g_strdup ((gchar *) opt_remaining[ii]);
                file = bk_file;
            } else if (restore_op) {
                title = _("Evolution Restore");
                oper = _("Restoring from the folder %s");
                res_file = g_strdup ((gchar *) opt_remaining[ii]);
                file = res_file;
            } else if (check_op) {
                d(g_message ("Checking %s", (gchar *) opt_remaining[ii]));
                chk_file = g_strdup ((gchar *) opt_remaining[ii]);
            }
        }
    }

    cancellable = g_cancellable_new ();

    if (gui_arg && !check_op) {
        GtkWidget *widget, *container;
        GtkWidget *action_area;
        GtkWidget *content_area;
        const gchar *txt, *txt2;
        gchar *str = NULL;
        gchar *markup;

        gtk_window_set_default_icon_name ("evolution");

        /* Backup / Restore only can have GUI.
         * We should restrict the rest. */
        progress_dialog = gtk_dialog_new_with_buttons (
            title, NULL,
            GTK_DIALOG_MODAL,
            GTK_STOCK_CANCEL,
            GTK_RESPONSE_REJECT,
            NULL);

        gtk_container_set_border_width (
            GTK_CONTAINER (progress_dialog), 12);
        gtk_window_set_default_size (
            GTK_WINDOW (progress_dialog), 450, 120);

        action_area = gtk_dialog_get_action_area (
            GTK_DIALOG (progress_dialog));
        content_area = gtk_dialog_get_content_area (
            GTK_DIALOG (progress_dialog));

        /* Override GtkDialog defaults */
        gtk_box_set_spacing (GTK_BOX (content_area), 12);
        gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
        gtk_box_set_spacing (GTK_BOX (action_area), 12);
        gtk_container_set_border_width (GTK_CONTAINER (action_area), 0);

        if (oper && file)
            str = g_strdup_printf (oper, file);

        container = gtk_table_new (2, 3, FALSE);
        gtk_table_set_col_spacings (GTK_TABLE (container), 12);
        gtk_table_set_row_spacings (GTK_TABLE (container), 12);
        gtk_widget_show (container);

        gtk_box_pack_start (
            GTK_BOX (content_area), container, FALSE, TRUE, 0);

        widget = gtk_image_new_from_stock (
            GTK_STOCK_COPY, GTK_ICON_SIZE_DIALOG);
        gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
        gtk_widget_show (widget);

        gtk_table_attach (
            GTK_TABLE (container), widget, 0, 1, 0, 3,
            GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);

        if (backup_op) {
            txt = _("Backing up Evolution Data");
            txt2 = _("Please wait while Evolution is backing up your data.");
        } else if (restore_op) {
            txt = _("Restoring Evolution Data");
            txt2 = _("Please wait while Evolution is restoring your data.");
        } else {
            g_return_val_if_reached (EXIT_FAILURE);
        }

        markup = g_markup_printf_escaped ("<b><big>%s</big></b>", txt);
        widget = gtk_label_new (markup);
        gtk_label_set_line_wrap (GTK_LABEL (widget), FALSE);
        gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
        gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
        gtk_widget_show (widget);
        g_free (markup);

        gtk_table_attach (
            GTK_TABLE (container), widget, 1, 2, 0, 1,
            GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);

        markup = g_strconcat (
            txt2, " ", _("This may take a while depending "
            "on the amount of data in your account."), NULL);
        widget = gtk_label_new (markup);
        gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
        gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
        gtk_widget_show (widget);
        g_free (markup);

        gtk_table_attach (
            GTK_TABLE (container), widget, 1, 2, 1, 2,
            GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);

        pbar = gtk_progress_bar_new ();

        if (str != NULL) {
            markup = g_markup_printf_escaped ("<i>%s</i>", str);
            widget = gtk_label_new (markup);
            gtk_label_set_use_markup (GTK_LABEL (widget), TRUE);
            gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
            g_free (markup);
            g_free (str);

            gtk_table_attach (
                GTK_TABLE (container), widget, 1, 2, 2, 3,
                GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
            gtk_table_set_row_spacing (GTK_TABLE (container), 2, 6);

            gtk_table_attach (
                GTK_TABLE (container), pbar, 1, 2, 3, 4,
                GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
        } else
            gtk_table_attach (
                GTK_TABLE (container), pbar, 1, 2, 2, 3,
                GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);

        g_signal_connect (
            progress_dialog, "response",
            G_CALLBACK (dlg_response), cancellable);
        gtk_widget_show_all (progress_dialog);

    } else if (check_op) {
        /* For sanity we don't need gui */
        check (chk_file, NULL);
        exit (result == 0 ? 0 : 1);
    }

    if (gui_arg)
        g_timeout_add_full (
            G_PRIORITY_DEFAULT, 50,
            (GSourceFunc) pbar_update,
            g_object_ref (cancellable),
            (GDestroyNotify) g_object_unref);

    g_io_scheduler_push_job (
        start_job, NULL,
        (GDestroyNotify) NULL,
        G_PRIORITY_DEFAULT, cancellable);

    gtk_main ();

    g_object_unref (cancellable);

    return result;
}