aboutsummaryrefslogblamecommitdiffstats
path: root/mail/e-mail-migrate.c
blob: 9ec894a4b9f93567249a0f836782f06f7acf3dde (plain) (tree)
1
2
3
4
5
6
7
8
  
                   
  



                                                                
  



                                                                    
  
                                                                   
                                                                             
  

                                                        


   
                           

                  
                   


                      
                  

                   
                  
                  
                  
 
                 
                       

                        

                    

                               



                             
                          
                                       
                                              
                               
 
                                   
                                  
                                  
                            
                                     
 
                          

                                  
                         
                           
                     


              

                            
                         
  
 
               
                                                                      
 
                           
 

                                        


                                   
 
                                                        
                                                        


                                       
                                       
 
                        
 



                                                                        
                                                                
 
           
                                                            
 

 

                                                   
 
 
 



                                          
 
                                                               
                                                
 
                       

 


                                
 

                                                                        
 

                                   
 



                                                                       
 


                                                          
 
                                 
 


                                                               
 


                                                                  
 


                                                                                 
 


                                                                                    
 



                                                                 
 





                                                                                              
 



                                                              
 


                                                              
 




                                                                            
 


                                                                               
 



                                                         
 



                                                  

 
           
                                                     
 
                    
 



                                                                   
 



                                        
 
                                                                          
 

                                                          
 


                                      
 




                      
 




                                      
 
               
                                                                           
 



                                       
                       
                          
 




                                                                             
         
 

                                                                     
                             
 



                                                                                 
                             





                                                                         
 



                                       
 


                                                                   
 

                                       
 



                                                                                           
 

                                  
 


                                  
 



                                 

                    














                         
 
 




                                           






                                 
                                                







                                                                                         
                                                                                                         












                                                              
                                               

 
                         
 
               
                                         
 
                  

                                   
                                            



                                                                      
                                                    
 
                                                         

                                                                         
                             


                                         



                                                         
                                                                


                                                            
                                 


                                     
                                                              
 
                                          
                  
                                                     
                                          
 

                                                                
 


                                                         
                 
                                  

         

                       
 
                    
 
 



















































































                                                                                                                                   










                                                                                          
                                                                












                                                                                                                           

                  
               
                                                                      
 







                                                               
                     
 

           






                                     



                            
 



                                                                           
 
                                              
 
                                                                        
                                                                              

                             
                                                              

                                                                      
                    
                                                              
                                                                     
 

                                                                             
                                                                                                   


                              
                                                 


                             

                       

                                    
                                                


                                    
                                                 




                              
                                                
                                             

                      
                              
                   
                          
 
                                           
                                                                
                                                         




                                                                                                                   

                     
 
 

                  
                                        




                                
  
 

                                                                                  
 
                                                        
                                                                   



                                                                           
 
 
           
                                            
 
                                  
                               

                                   
                        
                      

                                 

                              
                                                
                       
 

                                                                 



                                                                       
 

                                                        
 
                                                                         
                                                                   




                                                                                        

                                                     
                                                           
                                                 




                                                   
                   



                                                                                                   
                    
                                                           




                                                                 
                                                                                                       

                                                              



                                                     


                                                                       
                                  
 

                                          
                                                             




                                                                

                                                                  
 
                                                                                                                                             
                                                                 




                                                                   
                                   

                                                                                   
                                                                         



                                                          
                                                                                                                       
                                                         
                                                                              

                                                                                                    









                                                                    

                                 

 

      
               
                                                               
 

                            
                              
                                          
 








                                                                       
 


                                                                 
 



                                

 
                                                




                                                 

                                                     










                                                                                                                    
                                                       





                                                                        


                                             






















                                                                                                                
 




                                                                                                    
 
























































                                                                                     
 



















                                                          
 


                                                                        
 











                                               


                                                                        


                                                               
 


                                                            

                                                  


                                 

 
               
                                                                             
 






                                                   
 

                                                                 




                                                                       


                                   





                                                               












                                                                           





                                                                       


                                                  
                                                                




                                                  
                                                                  

                            
                                               
                                                          

                                       
     







                             



                                                  
                              



                                                              
                            







                                                 

                                                                
                                                                 
 

                                                           
 

                                                                         
 
                                                         
                                                     
 

                                 


                             

 
























































                                                                                                                        
        




                                             

                       
                              
 
                                                
                                                                
                                           
                                                                                     



                                                                           
                                                                           
                                     

                 
 
                       
                                                    
 
                  
                                                      

                                           



                                                          
                                                      
                                                  
                                              
         
     

                                                                                                         
      
 



                                                           

                                                 
 
                    
 
/*
 * e-mail-migrate.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/>
 *
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#include "e-mail-migrate.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>
#include <unistd.h>
#include <dirent.h>
#include <regex.h>
#include <errno.h>
#include <ctype.h>

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

#include <gtk/gtk.h>

#include <gconf/gconf-client.h>

#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>

#include <e-util/e-util.h>
#include <libedataserver/e-xml-utils.h>
#include <libedataserver/e-data-server-util.h>
#include <e-util/e-xml-utils.h>

#include "e-util/e-account-utils.h"
#include "e-util/e-alert-dialog.h"
#include "e-util/e-util-private.h"
#include "e-util/e-plugin.h"
#include "e-util/e-signature-utils.h"

#include "shell/e-shell.h"
#include "shell/e-shell-migrate.h"

#include "e-mail-store.h"
#include "e-mail-backend.h"
#include "em-utils.h"

#define d(x) x

struct _migrate_state_info {
    gchar *label_name;
    gdouble progress;
};

static gboolean
update_states_in_main_thread (const struct _migrate_state_info *info);

/* 1.4 upgrade functions */

#define EM_TYPE_MIGRATE_SESSION \
    (em_migrate_session_get_type ())

typedef struct _EMMigrateSession {
    CamelSession parent_object;

    CamelStore *store;   /* new folder tree store */
    gchar *srcdir;        /* old folder tree path */
} EMMigrateSession;

typedef struct _EMMigrateSessionClass {
    CamelSessionClass parent_class;

} EMMigrateSessionClass;

GType em_migrate_session_get_type (void);

G_DEFINE_TYPE (EMMigrateSession, em_migrate_session, CAMEL_TYPE_SESSION)

static CamelSession *em_migrate_session_new (const gchar *path);

static void
em_migrate_session_class_init (EMMigrateSessionClass *class)
{
}

static void
em_migrate_session_init (EMMigrateSession *session)
{
}

static CamelSession *
em_migrate_session_new (const gchar *path)
{
    CamelSession *session;

    session = g_object_new (EM_TYPE_MIGRATE_SESSION, NULL);
    camel_session_construct (session, path);

    return session;
}

static GtkWidget *window;
static GtkLabel *label;
static GtkProgressBar *progress;

static void
em_migrate_setup_progress_dialog (const gchar *title, const gchar *desc)
{
    GtkWidget *vbox, *hbox, *w;
    gchar *markup;

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title ((GtkWindow *) window, _("Migrating..."));
    gtk_window_set_modal ((GtkWindow *) window, TRUE);
    gtk_container_set_border_width ((GtkContainer *) window, 6);

    vbox = gtk_vbox_new (FALSE, 6);
    gtk_widget_show (vbox);
    gtk_container_add ((GtkContainer *) window, vbox);

    w = gtk_label_new (desc);

    gtk_label_set_line_wrap ((GtkLabel *) w, TRUE);
    gtk_widget_show (w);
    gtk_box_pack_start ((GtkBox *) vbox, w, TRUE, TRUE, 0);

    hbox = gtk_hbox_new (FALSE, 6);
    gtk_widget_show (hbox);
    gtk_box_pack_start ((GtkBox *) vbox, hbox, TRUE, TRUE, 0);

    label = (GtkLabel *) gtk_label_new ("");
    gtk_widget_show ((GtkWidget *) label);
    gtk_box_pack_start ((GtkBox *) hbox, (GtkWidget *) label, TRUE, TRUE, 0);

    progress = (GtkProgressBar *) gtk_progress_bar_new ();
    gtk_widget_show ((GtkWidget *) progress);
    gtk_box_pack_start ((GtkBox *) hbox, (GtkWidget *) progress, TRUE, TRUE, 0);

    /* Prepare the message */
    vbox = gtk_vbox_new (FALSE, 12);
    gtk_widget_show (vbox);
    gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);

    w = gtk_label_new (NULL);
    gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0);
    markup = g_strconcat ("<big><b>", title ? title : _("Migration"), "</b></big>", NULL);
    gtk_label_set_markup (GTK_LABEL (w), markup);
    gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0);
    g_free (markup);

    w = gtk_label_new (desc);
    gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0);
    gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
    gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0);

    /* Progress bar */
    w = gtk_vbox_new (FALSE, 6);
    gtk_box_pack_start (GTK_BOX (vbox), w, TRUE, TRUE, 0);

    label = GTK_LABEL (gtk_label_new (""));
    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
    gtk_label_set_line_wrap (label, TRUE);
    gtk_widget_show (GTK_WIDGET (label));
    gtk_box_pack_start (GTK_BOX (w), GTK_WIDGET (label), TRUE, TRUE, 0);

    progress = GTK_PROGRESS_BAR (gtk_progress_bar_new ());
    gtk_widget_show (GTK_WIDGET (progress));
    gtk_box_pack_start (GTK_BOX (w), GTK_WIDGET (progress), TRUE, TRUE, 0);

    gtk_container_add (GTK_CONTAINER (window), hbox);
    gtk_widget_show_all (hbox);
    gtk_widget_show (window);
}

static void
em_migrate_close_progress_dialog (void)
{
    gtk_widget_destroy ((GtkWidget *) window);
}

static void
em_migrate_set_folder_name (const gchar *folder_name)
{
    gchar *text;

    text = g_strdup_printf (_("Migrating '%s':"), folder_name);
    gtk_label_set_text (label, text);
    g_free (text);
}

static void
em_migrate_set_progress (double percent)
{
    gchar text[5];

    snprintf (text, sizeof (text), "%d%%", (gint) (percent * 100.0f));

    gtk_progress_bar_set_fraction (progress, percent);
    gtk_progress_bar_set_text (progress, text);

    while (gtk_events_pending ())
        gtk_main_iteration ();
}

enum {
    CP_UNIQUE = 0,
    CP_OVERWRITE,
    CP_APPEND
};

static gint open_flags[3] = {
    O_WRONLY | O_CREAT | O_TRUNC,
    O_WRONLY | O_CREAT | O_TRUNC,
    O_WRONLY | O_CREAT | O_APPEND,
};

static gboolean
cp (const gchar *src, const gchar *dest, gboolean show_progress, gint mode)
{
    guchar readbuf[65536];
    gssize nread, nwritten;
    gint errnosav, readfd, writefd;
    gsize total = 0;
    struct stat st;
    struct utimbuf ut;

    /* if the dest file exists and has content, abort - we don't
     * want to corrupt their existing data */
    if (g_stat (dest, &st) == 0 && st.st_size > 0 && mode == CP_UNIQUE) {
        errno = EEXIST;
        return FALSE;
    }

    if (g_stat (src, &st) == -1
        || (readfd = g_open (src, O_RDONLY | O_BINARY, 0)) == -1)
        return FALSE;

    if ((writefd = g_open (dest, open_flags[mode] | O_BINARY, 0666)) == -1) {
        errnosav = errno;
        close (readfd);
        errno = errnosav;
        return FALSE;
    }

    do {
        do {
            nread = read (readfd, readbuf, sizeof (readbuf));
        } while (nread == -1 && errno == EINTR);

        if (nread == 0)
            break;
        else if (nread < 0)
            goto exception;

        do {
            nwritten = write (writefd, readbuf, nread);
        } while (nwritten == -1 && errno == EINTR);

        if (nwritten < nread)
            goto exception;

        total += nwritten;
        if (show_progress)
            em_migrate_set_progress (((double) total) / ((double) st.st_size));
    } while (total < st.st_size);

    if (fsync (writefd) == -1)
        goto exception;

    close (readfd);
    if (close (writefd) == -1)
        goto failclose;

    ut.actime = st.st_atime;
    ut.modtime = st.st_mtime;
    utime (dest, &ut);
    chmod (dest, st.st_mode);

    return TRUE;

 exception:

    errnosav = errno;
    close (readfd);
    close (writefd);
    errno = errnosav;

 failclose:

    errnosav = errno;
    unlink (dest);
    errno = errnosav;

    return FALSE;
}

#ifndef G_OS_WIN32

#define SUBFOLDER_DIR_NAME     "subfolders"
#define SUBFOLDER_DIR_NAME_LEN 10

static void
em_update_accounts_2_11 (void)
{
    EAccountList *accounts;
    EIterator *iter;
    gboolean changed = FALSE;

    if (!(accounts = e_get_account_list ()))
        return;

    iter = e_list_get_iterator ((EList *) accounts);
    while (e_iterator_is_valid (iter)) {
        EAccount *account = (EAccount *) e_iterator_get (iter);

        if (g_str_has_prefix (account->source->url, "spool://")) {
            if (g_file_test (account->source->url + 8, G_FILE_TEST_IS_DIR)) {
                gchar *str = g_strdup_printf ("spooldir://%s", account->source->url + 8);

                g_free (account->source->url);
                account->source->url = str;
                changed = TRUE;
            }
        }

        e_iterator_next (iter);
    }

    g_object_unref (iter);

    if (changed)
        e_account_list_save (accounts);
}

#endif  /* !G_OS_WIN32 */

static gboolean
emm_setup_initial (const gchar *data_dir)
{
    GDir *dir;
    const gchar *d;
    gchar *local = NULL, *base;
    const gchar * const *language_names;

    /* special-case - this means brand new install of evolution */
    /* FIXME: create default folders and stuff... */

    d(printf("Setting up initial mail tree\n"));

    base = g_build_filename(data_dir, "local", NULL);
    if (g_mkdir_with_parents (base, 0700) == -1 && errno != EEXIST) {
        g_free (base);
        return FALSE;
    }

    /* e.g. try en-AU then en, etc */
    language_names = g_get_language_names ();
    while (*language_names != NULL) {
        local = g_build_filename (
            EVOLUTION_PRIVDATADIR, "default",
            *language_names, "mail", "local", NULL);
        if (g_file_test (local, G_FILE_TEST_EXISTS))
            break;
        g_free (local);
        language_names++;
    }

    /* Make sure we found one. */
    g_return_val_if_fail (*language_names != NULL, FALSE);

    dir = g_dir_open (local, 0, NULL);
    if (dir) {
        while ((d = g_dir_read_name (dir))) {
            gchar *src, *dest;

            src = g_build_filename (local, d, NULL);
            dest = g_build_filename (base, d, NULL);

            cp (src, dest, FALSE, CP_UNIQUE);
            g_free (dest);
            g_free (src);
        }
        g_dir_close (dir);
    }

    g_free (local);
    g_free (base);

    return TRUE;
}

static gboolean
is_in_plugs_list (GSList *list, const gchar *value)
{
    GSList *l;

    for (l = list; l; l = l->next) {
        if (l->data && !strcmp (l->data, value))
            return TRUE;
    }

    return FALSE;
}

/*
 * em_update_message_notify_settings_2_21
 * DBus plugin and sound email notification was merged to mail-notification plugin,
 * so move these options to new locations.
 */
static void
em_update_message_notify_settings_2_21 (void)
{
    GConfClient *client;
    GConfValue  *is_key;
    gboolean dbus, status;
    GSList *list;
    gchar *str;
    gint val;

    client = gconf_client_get_default ();

    is_key = gconf_client_get (client, "/apps/evolution/eplugin/mail-notification/dbus-enabled", NULL);
    if (is_key) {
        /* already migrated, so do not migrate again */
        gconf_value_free (is_key);
        g_object_unref (client);

        return;
    }

    gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-blink-icon",
                gconf_client_get_bool (client, "/apps/evolution/mail/notification/blink-status-icon", NULL), NULL);
    gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-notification",
                gconf_client_get_bool (client, "/apps/evolution/mail/notification/notification", NULL), NULL);

    list = gconf_client_get_list (client, "/apps/evolution/eplugin/disabled", GCONF_VALUE_STRING, NULL);
    dbus = !is_in_plugs_list (list, "org.gnome.evolution.new_mail_notify");
    status = !is_in_plugs_list (list, "org.gnome.evolution.mail_notification");

    gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/dbus-enabled", dbus, NULL);
    gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/status-enabled", status, NULL);

    if (!status) {
        /* enable this plugin, because it holds all those other things */
        GSList *plugins, *l;

        plugins = e_plugin_list_plugins ();

        for (l = plugins; l; l = l->next) {
            EPlugin *p = l->data;

            if (p && p->id && !strcmp (p->id, "org.gnome.evolution.mail_notification")) {
                e_plugin_enable (p, 1);
                break;
            }
        }

        g_slist_foreach (plugins, (GFunc)g_object_unref, NULL);
        g_slist_free (plugins);
    }

    g_slist_foreach (list, (GFunc) g_free, NULL);
    g_slist_free (list);

    val = gconf_client_get_int (client, "/apps/evolution/mail/notify/type", NULL);
    gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/sound-enabled", val == 1 || val == 2, NULL);
    gconf_client_set_bool (client, "/apps/evolution/eplugin/mail-notification/sound-beep", val == 0 || val == 1, NULL);

    str = gconf_client_get_string (client, "/apps/evolution/mail/notify/sound", NULL);
    gconf_client_set_string (client, "/apps/evolution/eplugin/mail-notification/sound-file", str ? str : "", NULL);
    g_free (str);

    g_object_unref (client);
}

/* fixing typo in SpamAssassin name */
static void
em_update_sa_junk_setting_2_23 (void)
{
    GConfClient *client;
    GConfValue  *key;

    client = gconf_client_get_default ();

    key = gconf_client_get (client, "/apps/evolution/mail/junk/default_plugin", NULL);
    if (key) {
        const gchar *str = gconf_value_get_string (key);

        if (str && strcmp (str, "Spamassasin") == 0)
            gconf_client_set_string (client, "/apps/evolution/mail/junk/default_plugin", "SpamAssassin", NULL);

        gconf_value_free (key);
        g_object_unref (client);

        return;
    }

    g_object_unref (client);
}

#ifndef G_OS_WIN32

static gboolean
update_states_in_main_thread (const struct _migrate_state_info * info)
{
    g_return_val_if_fail (info != NULL, FALSE);
    g_return_val_if_fail (info->label_name != NULL, FALSE);
    em_migrate_set_progress (info->progress);
    em_migrate_set_folder_name (info->label_name);
    g_free (info->label_name);
    g_free ( (gpointer)info);
    while (gtk_events_pending ())
        gtk_main_iteration ();
    return FALSE;
}

static void
migrate_folders (CamelStore *store,
                 gboolean is_local,
                 CamelFolderInfo *fi,
                 const gchar *acc,
                 gboolean *done,
                 gint *nth_folder,
                 gint total_folders)
{
    CamelFolder *folder;

    while (fi) {

        struct _migrate_state_info *info = g_malloc (sizeof (struct
                    _migrate_state_info));
        info->label_name = g_strdup_printf ("%s/%s", acc,
                fi->full_name);

        *nth_folder = *nth_folder + 1;

        info->progress = (double) (*nth_folder) / total_folders;
        g_idle_add ((GSourceFunc) update_states_in_main_thread, info);

        if (is_local)
            folder = camel_store_get_folder_sync (
                store, fi->full_name,
                CAMEL_STORE_IS_MIGRATING, NULL, NULL);
        else
            folder = camel_store_get_folder_sync (
                store, fi->full_name, 0, NULL, NULL);

        if (folder != NULL)
            camel_folder_summary_migrate_infos (folder->summary);
        migrate_folders (store, is_local, fi->child, acc, done, nth_folder, total_folders);
        fi = fi->next;
    }

    if ((*nth_folder) == (total_folders - 1))
        *done = TRUE;
}

#endif /* G_OS_WIN32 */

/* This could be in CamelStore.ch */
static void
count_folders (CamelFolderInfo *fi, gint *count)
{
    while (fi) {
        *count = *count + 1;
        count_folders (fi->child, count);
        fi = fi->next;
    }
}

static CamelStore *
setup_local_store (EShellBackend *shell_backend,
                   EMMigrateSession *session)
{
    CamelURL *url;
    const gchar *data_dir;
    gchar *tmp;
    CamelStore *store;

    url = camel_url_new("mbox:", NULL);
    data_dir = e_shell_backend_get_data_dir (shell_backend);
    tmp = g_build_filename (data_dir, "local", NULL);
    camel_url_set_path (url, tmp);
    g_free (tmp);
    tmp = camel_url_to_string (url, 0);
    store = (CamelStore *)camel_session_get_service (CAMEL_SESSION (session), tmp, CAMEL_PROVIDER_STORE, NULL);
    g_free (tmp);

    return store;
}

#ifndef G_OS_WIN32

struct migrate_folders_to_db_structure {
    gchar *account_name;
    CamelStore *store;
    CamelFolderInfo *info;
    gboolean done;
    gboolean is_local_store;
};

static void
migrate_folders_to_db_thread (struct migrate_folders_to_db_structure *migrate_dbs)
{
        gint num_of_folders = 0, nth_folder = 0;
        count_folders (migrate_dbs->info, &num_of_folders);
        migrate_folders (
            migrate_dbs->store, migrate_dbs->is_local_store,
            migrate_dbs->info, migrate_dbs->account_name,
            &(migrate_dbs->done), &nth_folder, num_of_folders);
}

static void
migrate_to_db (EShellBackend *shell_backend)
{
    EMMigrateSession *session;
    EAccountList *accounts;
    EMailBackend *mail_backend;
    EMailSession *mail_session;
    EIterator *iter;
    gint i=0, len;
    CamelStore *store = NULL;
    CamelFolderInfo *info;
    const gchar *data_dir;

    if (!(accounts = e_get_account_list ()))
        return;

    mail_backend = E_MAIL_BACKEND (shell_backend);
    mail_session = e_mail_backend_get_session (mail_backend);
    data_dir = e_shell_backend_get_data_dir (shell_backend);

    /* Initialize the mail stores early so we can add a new one. */
    e_mail_store_init (mail_session, data_dir);

    iter = e_list_get_iterator ((EList *) accounts);
    len = e_list_length ((EList *) accounts);

    session = (EMMigrateSession *) em_migrate_session_new (data_dir);
    camel_session_set_online ((CamelSession *) session, FALSE);
    em_migrate_setup_progress_dialog (
        _("Migrating Folders"),
        _("The summary format of the Evolution mailbox "
          "folders has been moved to SQLite since Evolution 2.24.\n\nPlease be "
          "patient while Evolution migrates your folders..."));

    em_migrate_set_progress ( (double)i/(len+1));
    store = setup_local_store (shell_backend, session);
    info = camel_store_get_folder_info_sync (
        store, NULL,
        CAMEL_STORE_FOLDER_INFO_RECURSIVE |
        CAMEL_STORE_FOLDER_INFO_FAST |
        CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
        NULL, NULL);
    if (info) {
        struct migrate_folders_to_db_structure migrate_dbs;

        if (g_str_has_suffix (((CamelService *)store)->url->path, ".evolution/mail/local"))
            migrate_dbs.is_local_store = TRUE;
        else
            migrate_dbs.is_local_store = FALSE;
        migrate_dbs.account_name = _("On This Computer");
        migrate_dbs.info = info;
        migrate_dbs.store = store;
        migrate_dbs.done = FALSE;

        g_thread_create ((GThreadFunc) migrate_folders_to_db_thread, &migrate_dbs, TRUE, NULL);
        while (!migrate_dbs.done)
            g_main_context_iteration (NULL, TRUE);
    }
    i++;
    em_migrate_set_progress ( (double)i/(len+1));

    while (e_iterator_is_valid (iter)) {
        EAccount *account = (EAccount *) e_iterator_get (iter);
        EAccountService *service;
        const gchar *name;

        service = account->source;
        name = account->name;
        em_migrate_set_progress ( (double)i/(len+1));
        if (account->enabled
            && service->url != NULL
            && service->url[0]
            && strncmp(service->url, "mbox:", 5) != 0) {

            e_mail_store_add_by_uri (
                mail_session, service->url, name);

            store = (CamelStore *) camel_session_get_service (CAMEL_SESSION (session), service->url, CAMEL_PROVIDER_STORE, NULL);
            info = camel_store_get_folder_info_sync (
                store, NULL,
                CAMEL_STORE_FOLDER_INFO_RECURSIVE |
                CAMEL_STORE_FOLDER_INFO_FAST |
                CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
                NULL, NULL);
            if (info) {
                struct migrate_folders_to_db_structure migrate_dbs;

                migrate_dbs.account_name = account->name;
                migrate_dbs.info = info;
                migrate_dbs.store = store;
                migrate_dbs.done = FALSE;

                g_thread_create ((GThreadFunc) migrate_folders_to_db_thread, &migrate_dbs, TRUE, NULL);
                while (!migrate_dbs.done)
                    g_main_context_iteration (NULL, TRUE);
            } else
                printf("%s:%s: failed to get folder infos \n", G_STRLOC, G_STRFUNC);
        }
        i++;
        e_iterator_next (iter);

    }

    //camel_session_set_online ((CamelSession *) session, TRUE);

    g_object_unref (iter);
    em_migrate_close_progress_dialog ();

    g_object_unref (session);
}

#endif

static gboolean
mbox_to_maildir_migration_needed (EShellBackend *shell_backend)
{
    gchar *local_store;
    gchar *local_outbox;
    const gchar *data_dir;
    gboolean migration_needed = FALSE;

    data_dir = e_shell_backend_get_data_dir (shell_backend);

    local_store = g_build_filename (data_dir, "local", NULL);
    local_outbox = g_build_filename (local_store, ".Outbox", NULL);

    /* If this is a fresh install (no local store exists yet)
     * then obviously there's nothing to migrate to Maildir. */
    if (!g_file_test (local_store, G_FILE_TEST_IS_DIR))
        migration_needed = FALSE;

    /* Look for a Maildir Outbox folder. */
    else if (!g_file_test (local_outbox, G_FILE_TEST_IS_DIR))
        migration_needed = TRUE;

    g_free (local_store);
    g_free (local_outbox);

    return migration_needed;
}

/* Folder names with '.' are converted to '_' */
static gchar *
sanitize_maildir_folder_name (gchar *folder_name)
{
    gchar *maildir_folder_name;

    maildir_folder_name = g_strdup (folder_name);
    g_strdelimit (maildir_folder_name, ".", '_');

     return maildir_folder_name;
}

static void
copy_folder (CamelStore *mbox_store, CamelStore *maildir_store, const gchar *mbox_fname, const gchar *maildir_fname)
{
    CamelFolder *fromfolder, *tofolder;
    GPtrArray *uids;

    fromfolder = camel_store_get_folder_sync (
        mbox_store, mbox_fname, 0, NULL, NULL);
    if (fromfolder == NULL) {
        g_warning ("Cannot find mbox folder %s \n", mbox_fname);
        return;
    }

    tofolder = camel_store_get_folder_sync (
        maildir_store, maildir_fname,
        CAMEL_STORE_FOLDER_CREATE,
        NULL, NULL);
    if (tofolder == NULL) {
        g_warning ("Cannot create maildir folder %s \n", maildir_fname);
        g_object_unref (fromfolder);
        return;
    }

    uids = camel_folder_get_uids (fromfolder);
    camel_folder_transfer_messages_to_sync (
            fromfolder, uids, tofolder,
            FALSE, NULL,
            NULL, NULL);
    camel_folder_free_uids (fromfolder, uids);

    g_object_unref (fromfolder);
    g_object_unref (tofolder);
}

static void
copy_folders (CamelStore *mbox_store, CamelStore *maildir_store, CamelFolderInfo *fi, EMMigrateSession *session)
{
    if (fi) {
        if (!g_str_has_prefix (fi->full_name, ".#evolution")) {
            gchar *maildir_folder_name;

            /* sanitize folder names and copy folders */
            maildir_folder_name = sanitize_maildir_folder_name (fi->full_name);
            copy_folder (mbox_store, maildir_store, fi->full_name, maildir_folder_name);
            g_free (maildir_folder_name);
        }

        if (fi->child)
            copy_folders (mbox_store, maildir_store, fi->child, session);

        copy_folders (mbox_store, maildir_store, fi->next, session);
    }
}

struct MigrateStore {
    EMMigrateSession *session;
    CamelStore *mbox_store;
    CamelStore *maildir_store;
    gboolean complete;
};

static void
migrate_stores (struct MigrateStore *ms)
{
    CamelFolderInfo *mbox_fi;
    CamelStore *mbox_store = ms->mbox_store;
    CamelStore *maildir_store = ms->maildir_store;

    mbox_fi = camel_store_get_folder_info_sync (
        mbox_store, NULL,
        CAMEL_STORE_FOLDER_INFO_RECURSIVE |
        CAMEL_STORE_FOLDER_INFO_FAST |
        CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
        NULL, NULL);

    /* FIXME progres dialog */
    copy_folders (mbox_store, maildir_store, mbox_fi, ms->session);
    ms->complete = TRUE;

    return;
}

static gboolean
migrate_mbox_to_maildir (EShellBackend *shell_backend, EMMigrateSession *session)
{
    CamelService *mbox_service, *maildir_service;
    CamelStore *mbox_store, *maildir_store;
    CamelURL *url;
    const gchar *data_dir;
    gchar *temp;
    struct MigrateStore ms;

    data_dir = e_shell_backend_get_data_dir (shell_backend);
    url = camel_url_new ("mbox:", NULL);
    temp = g_build_filename (data_dir, "local_mbox", NULL);
    camel_url_set_path (url, temp);
    g_free (temp);

    temp = camel_url_to_string (url, 0);
    mbox_service = camel_session_get_service (
        CAMEL_SESSION (session), temp,
        CAMEL_PROVIDER_STORE, NULL);
    g_free (temp);
    camel_url_free (url);

    url = camel_url_new ("maildir:", NULL);
    temp = g_build_filename (data_dir, "local", NULL);
    g_mkdir (temp, 0700);
    camel_url_set_path (url, temp);
    g_free (temp);

    temp = camel_url_to_string (url, 0);
    maildir_service = camel_session_get_service (
        CAMEL_SESSION (session), temp,
        CAMEL_PROVIDER_STORE, NULL);
    g_free (temp);
    camel_url_free (url);

    mbox_store = CAMEL_STORE (mbox_service);
    maildir_store = CAMEL_STORE (maildir_service);

    ms.mbox_store = mbox_store;
    ms.maildir_store = maildir_store;
    ms.session = session;
    ms.complete = FALSE;

    g_thread_create ((GThreadFunc) migrate_stores, &ms, TRUE, NULL);
    while (!ms.complete)
        g_main_context_iteration (NULL, TRUE);

    g_object_unref (mbox_store);
    g_object_unref (maildir_store);

    return TRUE;
}

static void
rename_mbox_dir (EShellBackend *shell_backend)
{
    gchar *local_mbox_path, *new_mbox_path;
    const gchar *data_dir;

    data_dir = e_shell_backend_get_data_dir (shell_backend);
    local_mbox_path = g_build_filename (data_dir, "local", NULL);
    new_mbox_path = g_build_filename (data_dir, "local_mbox", NULL);

    if (!g_file_test (local_mbox_path, G_FILE_TEST_EXISTS))
        goto exit;

    if (g_file_test (new_mbox_path, G_FILE_TEST_EXISTS))
        goto exit;

    g_rename (local_mbox_path, new_mbox_path);

exit:
    g_free (local_mbox_path);
    g_free (new_mbox_path);
}

static gboolean
create_mbox_account (EShellBackend *shell_backend, EMMigrateSession *session)
{
    EMailBackend *mail_backend;
    EMailSession *mail_session;
    CamelURL *url;
    EAccountList *accounts;
    EAccount *account;
    const gchar *data_dir;
    gchar *name, *id, *temp, *uri, *folder_uri;

    mail_backend = E_MAIL_BACKEND (shell_backend);
    mail_session = e_mail_backend_get_session (mail_backend);
    data_dir = e_shell_backend_get_data_dir (shell_backend);

    /* Initialize the mail stores early so we can add a new one. */
    e_mail_store_init (mail_session, data_dir);

    account = e_account_new ();
    account->enabled = TRUE;

    url = camel_url_new ("mbox:", NULL);
    temp = g_build_filename (data_dir, "local_mbox", NULL);
    camel_url_set_path (url, temp);
    g_free (temp);

    uri = camel_url_to_string (url, 0);
    e_account_set_string (account, E_ACCOUNT_SOURCE_URL, uri);

#ifndef G_OS_WIN32
    name = g_locale_to_utf8 (g_get_user_name (), -1, NULL, NULL, NULL);
#else
    name = g_strdup (g_get_user_name ());
#endif

    id = g_strconcat (name, "@", "localhost", NULL);
    e_account_set_string (account, E_ACCOUNT_ID_NAME, name);
    e_account_set_string (account, E_ACCOUNT_ID_ADDRESS, id);
    e_account_set_string (account, E_ACCOUNT_NAME, id);

    accounts = e_get_account_list ();
    if (e_account_list_find (accounts, E_ACCOUNT_ID_ADDRESS, id)) {
        g_object_unref (account);
        goto exit;
    }

    camel_url_set_fragment (url, _("Sent"));
    folder_uri = camel_url_to_string (url, 0);
    e_account_set_string (
        account, E_ACCOUNT_SENT_FOLDER_URI, folder_uri);
    g_free (folder_uri);

    camel_url_set_fragment (url, _("Drafts"));
    folder_uri = camel_url_to_string (url, 0);
    e_account_set_string (
        account, E_ACCOUNT_DRAFTS_FOLDER_URI, folder_uri);
    g_free (folder_uri);

    e_account_list_add (accounts, account);
    e_mail_store_add_by_uri (mail_session, uri, name);
    e_account_list_save (accounts);

exit:
    camel_url_free (url);
    g_free (uri);
    g_free (name);
    g_free (id);

    return TRUE;
}

static gboolean
migrate_local_store (EShellBackend *shell_backend)
{
    EMMigrateSession *session;
    const gchar *data_dir;
    gchar *local_store;
    gint response;

    if (!mbox_to_maildir_migration_needed (shell_backend))
        return TRUE;

    response = e_alert_run_dialog_for_args (
        e_shell_get_active_window (NULL),
        "mail:ask-migrate-store", NULL);

    if (response == GTK_RESPONSE_CANCEL)
        exit (EXIT_SUCCESS);

    rename_mbox_dir (shell_backend);
    data_dir = e_shell_backend_get_data_dir (shell_backend);
    local_store = g_build_filename (data_dir, "local", NULL);

    if (!g_file_test (local_store, G_FILE_TEST_EXISTS))
        g_mkdir_with_parents (local_store, 0700);

    session = (EMMigrateSession *) em_migrate_session_new (data_dir);
    camel_session_set_online (CAMEL_SESSION (session), FALSE);

    migrate_mbox_to_maildir (shell_backend, session);
    create_mbox_account (shell_backend, session);

    g_object_unref (session);

    g_free (local_store);

    return TRUE;
}

static void
em_ensure_proxy_ignore_hosts_being_list (void)
{
    const gchar *key = "/apps/evolution/shell/network_config/ignore_hosts";
    GConfClient *client;
    GConfValue  *key_value;

    /* makes sure the 'key' is a list of strings, not a string, as set by previous versions */

    client = gconf_client_get_default ();
    key_value = gconf_client_get (client, key, NULL);
    if (key_value && key_value->type == GCONF_VALUE_STRING) {
        gchar *value = gconf_client_get_string (client, key, NULL);
        GSList *lst = NULL;
        GError *error = NULL;

        if (value && *value) {
            gchar **split = g_strsplit (value, ",", -1);

            if (split) {
                gint ii;

                for (ii = 0; split[ii]; ii++) {
                    const gchar *tmp = split[ii];

                    if (tmp && *tmp) {
                        gchar *val = g_strstrip (g_strdup (tmp));

                        if (val && *val)
                            lst = g_slist_append (lst, val);
                        else
                            g_free (val);
                    }
                }
            }

            g_strfreev (split);
        }

        gconf_client_unset (client, key, NULL);
        gconf_client_set_list (client, key, GCONF_VALUE_STRING, lst, &error);

        g_slist_foreach (lst, (GFunc) g_free, NULL);
        g_slist_free (lst);
        g_free (value);

        if (error) {
            fprintf (stderr, "%s: Failed to set a list values with error: %s\n", G_STRFUNC, error->message);
            g_error_free (error);
        }
    }

    if (key_value)
        gconf_value_free (key_value);
    g_object_unref (client);
}

gboolean
e_mail_migrate (EShellBackend *shell_backend,
                gint major,
                gint minor,
                gint micro,
                GError **error)
{
    struct stat st;
    const gchar *data_dir;

    /* make sure ~/.evolution/mail exists */
    data_dir = e_shell_backend_get_data_dir (shell_backend);
    if (g_stat (data_dir, &st) == -1) {
        if (errno != ENOENT || g_mkdir_with_parents (data_dir, 0700) == -1) {
            g_set_error (
                error, E_SHELL_MIGRATE_ERROR,
                E_SHELL_MIGRATE_ERROR_FAILED,
                _("Unable to create local mail folders at "
                "'%s': %s"), data_dir, g_strerror (errno));
            return FALSE;
        }
    }

    if (major == 0)
        return emm_setup_initial (data_dir);

#ifndef G_OS_WIN32
    if (major < 2 || (major == 2 && minor < 12)) {
        em_update_accounts_2_11 ();
    }

    if (major < 2 || (major == 2 && minor < 22))
        em_update_message_notify_settings_2_21 ();

    if (major < 2 || (major == 2 && minor < 24)) {
        em_update_sa_junk_setting_2_23 ();
        migrate_to_db (shell_backend);
    }
#else
    if (major < 2 || (major == 2 && minor < 24))
        g_debug ("Upgrading from ancient versions %d.%d not supported on Windows", major, minor);
#endif

    if (major < 2 || (major == 2 && minor < 32)) {
        em_ensure_proxy_ignore_hosts_being_list ();
    }

    if (!migrate_local_store (shell_backend))
        return FALSE;

    return TRUE;
}