aboutsummaryrefslogblamecommitdiffstats
path: root/mail/mail-send-recv.c
blob: 6530043e53a78dd5012f7e2fe6644e22d30036be (plain) (tree)
1
2
3
4
5
6
7
8
9
  


                                                                           
  



                                                                             
  

                                                                           
  




                                                        

   
 
                           
 
                   

                   
                       
 
                          


                                  
                          
 

                                 

                           
                     
 



                                          
 


                                        

                                                       

                        



                                                                               
                                                                          
                     
                   

                            



                                             




                     
                      
                       
 


                                            

                            
                    
                            

                                                        

  



                                                                         
                    
              






                       
                   
                                                                 
                                  
                              
                              
                                
                           

                                 
 
                                                            
 


                        
 
                                      
                        
 
                          


                                




                                                                      
                                          
 
                                           
                                          
 
           
                                            

                                                  
                                                           
                                      

                           


           
                                        
 
                                      
                                                   

                                               

                                               
                                  

                                                   
                                
                      

 
                          
                                       

                                
 
                                
                                                              
                                           



                                                           


                                                                  
                                           

                                                      
                                                
                                                         
         
 


                         
           

                                        
 
                                         
                                                         


                                                                      
                                                   

                                             
                                
                                                                      


           
                     
 

                                            
                                                                 
 
                          
                                                                  
                                                                  
                                             
         
 


                                             
                                    
                      


                         
           


                                          
 
                                                                

 
           


                                        
 

                                   

                                    
                                                   




                                     

                                           
 

                                                                           
                                


           


                                         
 
                         
                                 
                                                       

                                               
                                                                                             
                 
                                                                                   
                      
                
                                               
                                                                                   
                                
                                                        



                      
                          
                                                          
 
           
                                        



                                       

                                             
                                            
 




                                                                               
                                                                        
                                                                         

                                              
                                                                           



                                     
                                              
 






                            


                                         
 
                                    
 

                                     
                      
 
                                      


           

                                                  
 

                              
                                    
 
                                                                           





                                                       
 


                                         
                                      



                                



                                          
 
                                         
 
 
              
                                           
 

                                
                                   
                                  
                                 

                           

                           


                                   
 
                                                        

                                                                

                                                        
                                                   
                                                        
                                                           

                                                              
                                                        


                                                              
 
                                                 
                                                      
                                                         

                                                              
 

                                  






                                                               

                                                      

                                                                  



                                                                      
 
                                     
                                                      


                                                           
                                                      






                                                        

                                                      
         
 
                              

                      
                      
 
                          

 
                          
 



                            
 




                                                     
 



                                                                 

































                                                                                          
                                                                                                        





                                                              
                                                                                                                        

         





































                                                                                


                     
                  
                                        
 
                                
                         
 



                                          

                                                        
                                                           

                                    
                                                        
 
                             
                                    
 

                                              


                                                             

                                    




                                                                
         
 


                                                             
                            

 






                                          
                                                        







                                                                           

                                  


                              
                          
                                
                                    
                                  
                                      
                                  
 
                      


                         

                                
                             
                             
                             
                                   
                         

                                 
                                         
                                
                          
                                         

                                    
                                                                                          
 
                                             
 
                                           


                                                                        

                                                                         
 



                                                              
 




                                                                      
 
                                                                                         

                                                                              
 


                                                                 
                                              
 
                                                               

                                                    

                                                      
                                                        
                                                              
 
                                                     
                                               
                                                              
                            
                                                                     
                                          
 
                                                                                         
                                         
 
                



                                                                            
 

                                                      
 
                                                                   
                                                               
                                   

                                                        
                                                          
 
                                                                      
                                         
 
                                                          
                                          
                                                               

                                                                            
                                                                   
                                                                               
                                                                
                                                                                
 
                                          
                                                            

                                                                     
                                             
                                                                    
                                                           
 
                                                        
                                                                        
                                 
 

                                                                
                                                                                
                 
 
                                                          



                                                                   
                                             

                                                                
                                                                     
                                    
 
                                                       



                                                               
                                                     
                                                            

                                                                
                                                                                             

                                                                     
 
                                                                    
                                                                 
 


                                                              

                                                                

                                                                       
 
                                                  
                                                    
                                  
 


                                                           
 
                              
         
 

                                                                           
                                                   
                                                                           


                                                                
 

                                                               
                                                                  

                                                                        
 
                                                                        
                                   
                                                          
                                               
                                                               
                                                                 
                                                     
                                                                   
                                                  
                                                                
                                                                                
 
                                          
                                                            

                                                                     

                                                                             
                                                           

                                                                
                                                                                
                 
 
                                                          



                                                                   
                                             

                                                                
                                                                     
                                    
 
                                                       




                                                                          
 
                                                                                             
                                                                     
 
                                                                 
 


                                                              

                                                                

                                                                       
 

                                                    
                                  
                                                 
 


                                                           
         
 
                                    

                           
                                                  
 


                                                    
 
                                                                                  
 
                           
 



                    


                                          
 
                                        
 
                                                                   


                                                                 
                                                       
                                                                 
                                                  
                                                       




                                   
           




                                                  

                                       
                                 

                                                                        
                                                                                  
 
                                                                       
                                                    
                                                        
                                                       
                                                            



                                                                      

                                                                               

                                                                    

                                       
                                                 
                      
                                        
                                                   
                      




                      
                                   
           
                            

                                       



                                                    
 

                                                                                   
                                          
 

                                                         

                                                       
 
                                                                      
 
                                
                                 
                                                       
                                     
                                                        



                                                 
                                         


                       

                                  
 
                                               


                                                                    
                                             
                      
                                             
                                                    
                 
 

                                                                     
         
 

                                                                      
 
                                              
                                                              




                                                                                                   
                                                                      





                                                                                          
                                                             

                             
                                                                    
 
                                                          
                                   
                                                                          
                                  
         
 
                              

 


                         
                            
 
                                                                                      


                                                                                       
                    



                                         

                                       

                                     
                                    
 
                                         
                                                                 
                                           
 
                      
                                               
                                       
         
 
                                                    
                                                    
                                                                     


                            
                                                                                    
                                                                                   
                                         
 

                                                                          
                                                             
                                                 

                                         
                                                        
                                         

                                                                                 
         
 
                              
 
                                           
 


                      

                                                                            























































































































                                                                                                                     
           


                                   
 
                      
                                                                         

                                                                         
 



                                                                      
                 





                                                          
           

                                               



                                          
                             
                     



                                
                               

  

                                                     
 

                                                   
                                                                           


           


                                                     
 
                            
               
                         
                                                      
                                   


                              


                                                                        
 




                                                              

                                                     
                                                                              
 






                                                                              
                                               
                                                            
                                                          
                                                
                                                   
                                                                                                         
                                                                                           

                                          
                                                                                               

                                                             

                                             
                                                                                       
                                                                                        

                                                         
                                                                                         
                                 
 
                                                                                                   

                         
                                                     
                 
 


                                                

                                                                        
                              

                                                     
                                                  
                                                                                 
         
 
                                                           
 

                           
                                                                               


           
                                                     
 
                               


           
                                                     
 
               
 
                                             

                                              
 
                                          
                                  

 





                                               

  



                                                      
 









                                                                              
                                              

                                           

                                         

                                                         
                                              
                                                                           

                                           

                                         

                                                                    
                                                        
                                               
 
                                                         
                                                             
                                     

                                                       
 
                                            
 
                
                                         
         


           

                                                  
 
                                      
 

                                                        
 
                            









                                                                                    
                                              
                                                               
                                                             
                
                                    


         














































































                                                                                                      
                     
                                             

































                                                                             
                                                                           





                                
                  
                                
                                    
                                  
 
                                  
                                
                                
                            
 
                                       
                                                                                   
                                                                           
                 
                                        
         
 
                                                    
 



                                                     
                             
                                                                      
 


                                           

                                                           



                                                                
 
                                                      
                                 
 
                                     
                                  
                                         
                                                            
                                                         
                                                 



                                                         


                                                             
                                         
                                                      
                                                                



                                                         
                                                 

                                 
                                                  
                                                                   
                              
                        
                              
                 
         
 

                           
                                

 
           
                                     
                                         
 
                                                    


           
                                
                                    
 
                                                     

 

                                                           
    
                                            
 
                                
                                
                              
                                  
                         
                                        
 


                                                      
                                                      
 
                                                          
                                                       

                         
                          
 
                                          
 
                                                      
                          
 
                                          
                          
                                  
                                               

                                                            
                                                   
                                   

                                  
                             
 
                          
                                            

                                                     
                                                  
 
                                                                 
 
                             
                          
                                 
                                              
                                                 
                                         



                                                 


                                                     

                                                         

                                                    
                                 
                                                 

                                                  



                                                 
                                         

                         
                                                                       
                      
                
                                       
         


                                 
 

    
                                 
 
                                  
                              

                                
                                        
 
                                                       
 
                                                  
                            
                       
 
                                         
                                                                
                           
                              
                                                                              
                                         

                       
 
                                                                               

                                          

                                         
                       
         
 

                                          
                                  
                                               
                                               
                                     
                                 
                                   


                                  
 
                                                  
 
                                                                          
 




                                                     
                         
                                      
                                          



                                         
                                 

                                 
 
/*
 * 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.
 *
 * 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 General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Authors:
 *      Michael Zucchi <NotZed@ximian.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#include "mail-send-recv.h"

#include <config.h>
#include <stdio.h>
#include <string.h>
#include <glib/gi18n.h>

#include <shell/e-shell.h>
#include <shell/e-shell-content.h>
#include <shell/e-shell-view.h>
#include <shell/e-shell-window.h>
#include <e-util/e-util.h>

#include "e-mail-account-store.h"
#include "e-mail-ui-session.h"
#include "em-event.h"
#include "em-filter-rule.h"
#include "em-utils.h"

#define d(x)

/* ms between status updates to the gui */
#define STATUS_TIMEOUT (250)

/* pseudo-uri to key the send task on */
#define SEND_URI_KEY "send-task:"

#define SEND_RECV_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR

/* send/receive email */

/* ********************************************************************** */
/*  This stuff below is independent of the stuff above */

/* this stuff is used to keep track of which folders filters have accessed, and
 * what not. the thaw/refreeze thing doesn't really seem to work though */
struct _folder_info {
    gchar *uri;
    CamelFolder *folder;
    time_t update;

    /* How many times updated, to slow it
     * down as we go, if we have lots. */
    gint count;
};

struct _send_data {
    GList *infos;

    GtkDialog *gd;
    gint cancelled;

    /* Since we're never asked to update
     * this one, do it ourselves. */
    CamelFolder *inbox;
    time_t inbox_update;

    GMutex lock;
    GHashTable *folders;

    GHashTable *active; /* send_info's by uri */
};

typedef enum {
    SEND_RECEIVE,       /* receiver */
    SEND_SEND,      /* sender */
    SEND_UPDATE,        /* imap-like 'just update folder info' */
    SEND_INVALID
} send_info_t;

typedef enum {
    SEND_ACTIVE,
    SEND_CANCELLED,
    SEND_COMPLETE
} send_state_t;

struct _send_info {
    send_info_t type;       /* 0 = fetch, 1 = send */
    GCancellable *cancellable;
    CamelSession *session;
    CamelService *service;
    gboolean keep_on_server;
    send_state_t state;
    GtkWidget *progress_bar;
    GtkWidget *cancel_button;

    gint again;     /* need to run send again */

    gint timeout_id;
    gchar *what;
    gint pc;

    GtkWidget *send_account_label;
    gchar *send_url;

    /*time_t update;*/
    struct _send_data *data;
};

static CamelFolder *
        receive_get_folder      (CamelFilterDriver *d,
                         const gchar *uri,
                         gpointer data,
                         GError **error);
static void send_done (gpointer data);

static struct _send_data *send_data = NULL;
static GtkWidget *send_recv_dialog = NULL;

static void
free_folder_info (struct _folder_info *info)
{
    /*camel_folder_thaw (info->folder); */
    mail_sync_folder (info->folder, FALSE, NULL, NULL);
    g_object_unref (info->folder);
    g_free (info->uri);
    g_free (info);
}

static void
free_send_info (struct _send_info *info)
{
    if (info->cancellable != NULL)
        g_object_unref (info->cancellable);
    if (info->session != NULL)
        g_object_unref (info->session);
    if (info->service != NULL)
        g_object_unref (info->service);
    if (info->timeout_id != 0)
        g_source_remove (info->timeout_id);
    g_free (info->what);
    g_free (info->send_url);
    g_free (info);
}

static struct _send_data *
setup_send_data (EMailSession *session)
{
    struct _send_data *data;

    if (send_data == NULL) {
        send_data = data = g_malloc0 (sizeof (*data));
        g_mutex_init (&data->lock);
        data->folders = g_hash_table_new_full (
            g_str_hash, g_str_equal,
            (GDestroyNotify) NULL,
            (GDestroyNotify) free_folder_info);
        data->inbox =
            e_mail_session_get_local_folder (
            session, E_MAIL_LOCAL_FOLDER_LOCAL_INBOX);
        g_object_ref (data->inbox);
        data->active = g_hash_table_new_full (
            g_str_hash, g_str_equal,
            (GDestroyNotify) g_free,
            (GDestroyNotify) free_send_info);
    }

    return send_data;
}

static void
receive_cancel (GtkButton *button,
                struct _send_info *info)
{
    if (info->state == SEND_ACTIVE) {
        g_cancellable_cancel (info->cancellable);
        if (info->progress_bar != NULL)
            gtk_progress_bar_set_text (
                GTK_PROGRESS_BAR (info->progress_bar),
                _("Canceling..."));
        info->state = SEND_CANCELLED;
    }
    if (info->cancel_button)
        gtk_widget_set_sensitive (info->cancel_button, FALSE);
}

static void
free_send_data (void)
{
    struct _send_data *data = send_data;

    g_return_if_fail (g_hash_table_size (data->active) == 0);

    if (data->inbox) {
        mail_sync_folder (data->inbox, FALSE, NULL, NULL);
        /*camel_folder_thaw (data->inbox);      */
        g_object_unref (data->inbox);
    }

    g_list_free (data->infos);
    g_hash_table_destroy (data->active);
    g_hash_table_destroy (data->folders);
    g_mutex_clear (&data->lock);
    g_free (data);
    send_data = NULL;
}

static void
cancel_send_info (gpointer key,
                  struct _send_info *info,
                  gpointer data)
{
    receive_cancel (GTK_BUTTON (info->cancel_button), info);
}

static void
hide_send_info (gpointer key,
                struct _send_info *info,
                gpointer data)
{
    info->cancel_button = NULL;
    info->progress_bar = NULL;

    if (info->timeout_id != 0) {
        g_source_remove (info->timeout_id);
        info->timeout_id = 0;
    }
}

static void
dialog_destroy_cb (struct _send_data *data,
                   GObject *deadbeef)
{
    g_hash_table_foreach (data->active, (GHFunc) hide_send_info, NULL);
    data->gd = NULL;
    send_recv_dialog = NULL;
}

static void
dialog_response (GtkDialog *gd,
                 gint button,
                 struct _send_data *data)
{
    switch (button) {
    case GTK_RESPONSE_CANCEL:
        d (printf ("cancelled whole thing\n"));
        if (!data->cancelled) {
            data->cancelled = TRUE;
            g_hash_table_foreach (data->active, (GHFunc) cancel_send_info, NULL);
        }
        gtk_dialog_set_response_sensitive (gd, GTK_RESPONSE_CANCEL, FALSE);
        break;
    default:
        d (printf ("hiding dialog\n"));
        g_hash_table_foreach (data->active, (GHFunc) hide_send_info, NULL);
        data->gd = NULL;
        /*gtk_widget_destroy((GtkWidget *)gd);*/
        break;
    }
}

static GMutex status_lock;
static gchar *format_service_name (CamelService *service);

static gint
operation_status_timeout (gpointer data)
{
    struct _send_info *info = data;

    if (info->progress_bar) {
        GtkProgressBar *progress_bar;

        g_mutex_lock (&status_lock);

        progress_bar = GTK_PROGRESS_BAR (info->progress_bar);

        gtk_progress_bar_set_fraction (progress_bar, info->pc / 100.0);
        if (info->what != NULL)
            gtk_progress_bar_set_text (progress_bar, info->what);
        if (info->service != NULL && info->send_account_label) {
            gchar *tmp = format_service_name (info->service);

            gtk_label_set_markup (
                GTK_LABEL (info->send_account_label), tmp);

            g_free (tmp);
        }

        g_mutex_unlock (&status_lock);

        return TRUE;
    }

    return FALSE;
}

static void
set_send_status (struct _send_info *info,
                 const gchar *desc,
                 gint pc)
{
    g_mutex_lock (&status_lock);

    g_free (info->what);
    info->what = g_strdup (desc);
    info->pc = pc;

    g_mutex_unlock (&status_lock);
}

static void
set_transport_service (struct _send_info *info,
                       const gchar *transport_uid)
{
    CamelService *service;

    g_mutex_lock (&status_lock);

    service = camel_session_ref_service (info->session, transport_uid);

    if (CAMEL_IS_TRANSPORT (service)) {
        if (info->service != NULL)
            g_object_unref (info->service);
        info->service = g_object_ref (service);
    }

    if (service != NULL)
        g_object_unref (service);

    g_mutex_unlock (&status_lock);
}

/* for camel operation status */
static void
operation_status (CamelOperation *op,
                  const gchar *what,
                  gint pc,
                  struct _send_info *info)
{
    set_send_status (info, what, pc);
}

static gchar *
format_service_name (CamelService *service)
{
    CamelProvider *provider;
    CamelSettings *settings;
    gchar *service_name = NULL;
    const gchar *display_name;
    gchar *pretty_url = NULL;
    gchar *host = NULL;
    gchar *path = NULL;
    gchar *user = NULL;
    gchar *cp;
    gboolean have_host = FALSE;
    gboolean have_path = FALSE;
    gboolean have_user = FALSE;

    provider = camel_service_get_provider (service);
    display_name = camel_service_get_display_name (service);

    settings = camel_service_ref_settings (service);

    if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
        host = camel_network_settings_dup_host (
            CAMEL_NETWORK_SETTINGS (settings));
        have_host = (host != NULL) && (*host != '\0');

        user = camel_network_settings_dup_user (
            CAMEL_NETWORK_SETTINGS (settings));
        have_user = (user != NULL) && (*user != '\0');
    }

    if (CAMEL_IS_LOCAL_SETTINGS (settings)) {
        path = camel_local_settings_dup_path (
            CAMEL_LOCAL_SETTINGS (settings));
        have_path = (path != NULL) && (*path != '\0');
    }

    g_object_unref (settings);

    /* Shorten user names with '@', since multiple '@' in a
     * 'user@host' label look weird.  This is just supposed
     * to be a hint anyway so it doesn't matter if it's not
     * strictly correct. */
    if (have_user && (cp = strchr (user, '@')) != NULL)
        *cp = '\0';

    g_return_val_if_fail (provider != NULL, NULL);

    /* This should never happen, but if the service has no
     * display name, fall back to the generic service name. */
    if (display_name == NULL || *display_name == '\0') {
        service_name = camel_service_get_name (service, TRUE);
        display_name = service_name;
    }

    if (have_host && have_user) {
        pretty_url = g_markup_printf_escaped (
            "<b>%s</b> <small>(%s@%s)</small>",
            display_name, user, host);
    } else if (have_host) {
        pretty_url = g_markup_printf_escaped (
            "<b>%s</b> <small>(%s)</small>",
            display_name, host);
    } else if (have_path) {
        pretty_url = g_markup_printf_escaped (
            "<b>%s</b> <small>(%s)</small>",
            display_name, path);
    } else {
        pretty_url = g_markup_printf_escaped (
            "<b>%s</b>", display_name);
    }

    g_free (service_name);
    g_free (host);
    g_free (path);
    g_free (user);

    return pretty_url;
}

struct ReportErrorToUIData
{
    gchar *display_name;
    gchar *error_ident;
    GError *error;
};

static gboolean
report_error_to_ui_cb (gpointer user_data)
{
    struct ReportErrorToUIData *data = user_data;
    EShellView *shell_view = NULL;

    g_return_val_if_fail (data != NULL, FALSE);
    g_return_val_if_fail (data->display_name != NULL, FALSE);
    g_return_val_if_fail (data->error_ident != NULL, FALSE);
    g_return_val_if_fail (data->error != NULL, FALSE);

    if (send_recv_dialog) {
        GtkWidget *parent;

        parent = gtk_widget_get_parent (send_recv_dialog);
        if (parent && E_IS_SHELL_WINDOW (parent)) {
            EShellWindow *shell_window = E_SHELL_WINDOW (parent);

            shell_view = e_shell_window_get_shell_view (shell_window, "mail");
        }
    }

    if (!shell_view) {
        EShell *shell;
        GtkWindow *active_window;

        shell = e_shell_get_default ();
        active_window = e_shell_get_active_window (shell);

        if (E_IS_SHELL_WINDOW (active_window)) {
            EShellWindow *shell_window = E_SHELL_WINDOW (active_window);

            shell_view = e_shell_window_get_shell_view (shell_window, "mail");
        }
    }

    if (shell_view) {
        EShellContent *shell_content;
        EAlertSink *alert_sink;
        EAlert *alert;

        shell_content = e_shell_view_get_shell_content (shell_view);
        alert_sink = E_ALERT_SINK (shell_content);

        alert = e_alert_new (data->error_ident, data->display_name, data->error->message, NULL);

        e_alert_sink_submit_alert (alert_sink, alert);

        g_object_unref (alert);
    } else {
        /* This may not happen, but just in case... */
        g_warning ("%s: %s '%s': %s\n", G_STRFUNC, data->error_ident, data->display_name, data->error->message);
    }

    g_free (data->display_name);
    g_free (data->error_ident);
    g_error_free (data->error);
    g_free (data);

    return FALSE;
}

static void
report_error_to_ui (CamelService *service,
            const gchar *folder_name,
            const GError *error)
{
    gchar *tmp = NULL;
    const gchar *display_name, *ident;
    struct ReportErrorToUIData *data;

    g_return_if_fail (CAMEL_IS_SERVICE (service));
    g_return_if_fail (error != NULL);

    if (folder_name) {
        tmp = g_strdup_printf ("%s: %s",
            camel_service_get_display_name (service),
            folder_name);
        display_name = tmp;
        ident = "mail:no-refresh-folder";
    } else {
        display_name = camel_service_get_display_name (service);
        ident = "mail:failed-connect";
    }

    data = g_new0 (struct ReportErrorToUIData, 1);
    data->display_name = g_strdup (display_name);
    data->error_ident = g_strdup (ident);
    data->error = g_error_copy (error);

    g_idle_add_full (G_PRIORITY_DEFAULT, report_error_to_ui_cb, data, NULL);

    g_free (tmp);
}

static send_info_t
get_receive_type (CamelService *service)
{
    CamelProvider *provider;
    const gchar *uid;

    /* Disregard CamelNullStores. */
    if (CAMEL_IS_NULL_STORE (service))
        return SEND_INVALID;

    /* mbox pointing to a file is a 'Local delivery'
     * source which requires special processing. */
    if (em_utils_is_local_delivery_mbox_file (service))
        return SEND_RECEIVE;

    provider = camel_service_get_provider (service);

    if (provider == NULL)
        return SEND_INVALID;

    /* skip some well-known services */
    uid = camel_service_get_uid (service);
    if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0)
        return SEND_INVALID;
    if (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0)
        return SEND_INVALID;

    if (provider->object_types[CAMEL_PROVIDER_STORE]) {
        if (provider->flags & CAMEL_PROVIDER_IS_STORAGE)
            return SEND_UPDATE;
        else
            return SEND_RECEIVE;
    }

    if (provider->object_types[CAMEL_PROVIDER_TRANSPORT])
        return SEND_SEND;

    return SEND_INVALID;
}

static gboolean
get_keep_on_server (CamelService *service)
{
    GObjectClass *class;
    CamelSettings *settings;
    gboolean keep_on_server = FALSE;

    settings = camel_service_ref_settings (service);
    class = G_OBJECT_GET_CLASS (settings);

    /* XXX This is a POP3-specific setting. */
    if (g_object_class_find_property (class, "keep-on-server") != NULL)
        g_object_get (
            settings, "keep-on-server",
            &keep_on_server, NULL);

    g_object_unref (settings);

    return keep_on_server;
}

static struct _send_data *
build_dialog (GtkWindow *parent,
              EMailSession *session,
              CamelFolder *outbox,
              CamelService *transport,
              gboolean allow_send)
{
    GtkDialog *gd;
    GtkWidget *wgrid;
    GtkGrid *grid;
    gint row;
    GList *list = NULL;
    struct _send_data *data;
    GtkWidget *container;
    GtkWidget *send_icon;
    GtkWidget *recv_icon;
    GtkWidget *scrolled_window;
    GtkWidget *label;
    GtkWidget *progress_bar;
    GtkWidget *cancel_button;
    EMailAccountStore *account_store;
    struct _send_info *info;
    gchar *pretty_url;
    EMEventTargetSendReceive *target;
    GQueue queue = G_QUEUE_INIT;

    account_store = e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session));

    send_recv_dialog = gtk_dialog_new ();

    gd = GTK_DIALOG (send_recv_dialog);
    gtk_window_set_modal (GTK_WINDOW (send_recv_dialog), FALSE);
    gtk_window_set_icon_name (GTK_WINDOW (gd), "mail-send-receive");
    gtk_window_set_default_size (GTK_WINDOW (gd), 600, 200);
    gtk_window_set_title (GTK_WINDOW (gd), _("Send & Receive Mail"));
    gtk_window_set_transient_for (GTK_WINDOW (gd), parent);

    e_restore_window (
        GTK_WINDOW (gd),
        "/org/gnome/evolution/mail/send-recv-window/",
        E_RESTORE_WINDOW_SIZE);

    container = gtk_dialog_get_action_area (gd);
    gtk_container_set_border_width (GTK_CONTAINER (container), 6);

    container = gtk_dialog_get_content_area (gd);
    gtk_container_set_border_width (GTK_CONTAINER (container), 0);

    cancel_button = e_dialog_button_new_with_icon ("process-stop", _("Cancel _All"));
    gtk_widget_show (cancel_button);
    gtk_dialog_add_action_widget (gd, cancel_button, GTK_RESPONSE_CANCEL);

    wgrid = gtk_grid_new ();
    grid = GTK_GRID (wgrid);
    gtk_container_set_border_width (GTK_CONTAINER (grid), 6);
    gtk_grid_set_column_spacing (grid, 6);

    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    gtk_container_set_border_width (
        GTK_CONTAINER (scrolled_window), 6);
    gtk_scrolled_window_set_policy (
        GTK_SCROLLED_WINDOW (scrolled_window),
        GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    gtk_widget_set_size_request (scrolled_window, 50, 50);

    container = gtk_dialog_get_content_area (gd);
    gtk_scrolled_window_add_with_viewport (
        GTK_SCROLLED_WINDOW (scrolled_window), wgrid);
    gtk_box_pack_start (
        GTK_BOX (container), scrolled_window, TRUE, TRUE, 0);
    gtk_widget_show (scrolled_window);

    /* must bet setup after send_recv_dialog as it may re-trigger send-recv button */
    data = setup_send_data (session);

    row = 0;
    e_mail_account_store_queue_enabled_services (account_store, &queue);
    while (!g_queue_is_empty (&queue)) {
        CamelService *service;
        const gchar *uid;

        service = g_queue_pop_head (&queue);
        uid = camel_service_get_uid (service);

        /* see if we have an outstanding download active */
        info = g_hash_table_lookup (data->active, uid);
        if (info == NULL) {
            send_info_t type = SEND_INVALID;

            type = get_receive_type (service);

            if (type == SEND_INVALID || type == SEND_SEND)
                continue;

            info = g_malloc0 (sizeof (*info));
            info->type = type;
            info->session = g_object_ref (session);
            info->service = g_object_ref (service);
            info->keep_on_server = get_keep_on_server (service);
            info->cancellable = camel_operation_new ();
            info->state = allow_send ? SEND_ACTIVE : SEND_COMPLETE;
            info->timeout_id = e_named_timeout_add (
                STATUS_TIMEOUT, operation_status_timeout, info);

            g_signal_connect (
                info->cancellable, "status",
                G_CALLBACK (operation_status), info);

            g_hash_table_insert (
                data->active, g_strdup (uid), info);
            list = g_list_prepend (list, info);

        } else if (info->progress_bar != NULL) {
            /* incase we get the same source pop up again */
            continue;

        } else if (info->timeout_id == 0) {
            info->timeout_id = e_named_timeout_add (
                STATUS_TIMEOUT, operation_status_timeout, info);
        }

        recv_icon = gtk_image_new_from_icon_name (
            "mail-inbox", SEND_RECV_ICON_SIZE);
        gtk_widget_set_valign (recv_icon, GTK_ALIGN_START);

        pretty_url = format_service_name (service);
        label = gtk_label_new (NULL);
        gtk_label_set_ellipsize (
            GTK_LABEL (label), PANGO_ELLIPSIZE_END);
        gtk_label_set_markup (GTK_LABEL (label), pretty_url);
        g_free (pretty_url);

        progress_bar = gtk_progress_bar_new ();
        gtk_progress_bar_set_show_text (
            GTK_PROGRESS_BAR (progress_bar), TRUE);
        gtk_progress_bar_set_text (
            GTK_PROGRESS_BAR (progress_bar),
            (info->type == SEND_UPDATE) ?
            _("Updating...") : _("Waiting..."));
        gtk_widget_set_margin_bottom (progress_bar, 12);

        cancel_button = e_dialog_button_new_with_icon ("process-stop", _("_Cancel"));
        gtk_widget_set_valign (cancel_button, GTK_ALIGN_END);
        gtk_widget_set_margin_bottom (cancel_button, 12);

        /* g_object_set(data->label, "bold", TRUE, NULL); */
        gtk_misc_set_alignment (GTK_MISC (label), 0, .5);

        gtk_widget_set_hexpand (label, TRUE);
        gtk_widget_set_halign (label, GTK_ALIGN_FILL);

        gtk_grid_attach (grid, recv_icon, 0, row, 1, 2);
        gtk_grid_attach (grid, label, 1, row, 1, 1);
        gtk_grid_attach (grid, progress_bar, 1, row + 1, 1, 1);
        gtk_grid_attach (grid, cancel_button, 2, row, 1, 2);

        info->progress_bar = progress_bar;
        info->cancel_button = cancel_button;
        info->data = data;

        g_signal_connect (
            cancel_button, "clicked",
            G_CALLBACK (receive_cancel), info);

        row = row + 2;
    }

    /* we also need gd during emition to be able to catch Cancel All */
    data->gd = gd;
    target = em_event_target_new_send_receive (
        em_event_peek (), wgrid, data, row, EM_EVENT_SEND_RECEIVE);
    e_event_emit (
        (EEvent *) em_event_peek (), "mail.sendreceive",
        (EEventTarget *) target);

    /* Skip displaying the SMTP row if we've got no outbox,
     * outgoing account or unsent mails. */
    if (allow_send && outbox && CAMEL_IS_TRANSPORT (transport)
     && (camel_folder_get_message_count (outbox) -
        camel_folder_get_deleted_message_count (outbox)) != 0) {

        info = g_hash_table_lookup (data->active, SEND_URI_KEY);
        if (info == NULL) {
            info = g_malloc0 (sizeof (*info));
            info->type = SEND_SEND;
            info->session = g_object_ref (session);
            info->service = g_object_ref (transport);
            info->keep_on_server = FALSE;
            info->cancellable = camel_operation_new ();
            info->state = SEND_ACTIVE;
            info->timeout_id = e_named_timeout_add (
                STATUS_TIMEOUT, operation_status_timeout, info);

            g_signal_connect (
                info->cancellable, "status",
                G_CALLBACK (operation_status), info);

            g_hash_table_insert (
                data->active, g_strdup (SEND_URI_KEY), info);
            list = g_list_prepend (list, info);
        } else if (info->timeout_id == 0) {
            info->timeout_id = e_named_timeout_add (
                STATUS_TIMEOUT, operation_status_timeout, info);
        }

        send_icon = gtk_image_new_from_icon_name (
            "mail-outbox", SEND_RECV_ICON_SIZE);
        gtk_widget_set_valign (send_icon, GTK_ALIGN_START);

        pretty_url = format_service_name (transport);
        label = gtk_label_new (NULL);
        gtk_label_set_ellipsize (
            GTK_LABEL (label), PANGO_ELLIPSIZE_END);
        gtk_label_set_markup (GTK_LABEL (label), pretty_url);
        g_free (pretty_url);

        progress_bar = gtk_progress_bar_new ();
        gtk_progress_bar_set_show_text (
            GTK_PROGRESS_BAR (progress_bar), TRUE);
        gtk_progress_bar_set_text (
            GTK_PROGRESS_BAR (progress_bar), _("Waiting..."));
        gtk_widget_set_margin_bottom (progress_bar, 12);

        cancel_button = e_dialog_button_new_with_icon ("process-stop", _("_Cancel"));
        gtk_widget_set_valign (cancel_button, GTK_ALIGN_END);

        gtk_misc_set_alignment (GTK_MISC (label), 0, .5);

        gtk_widget_set_hexpand (label, TRUE);
        gtk_widget_set_halign (label, GTK_ALIGN_FILL);

        gtk_grid_attach (grid, send_icon, 0, row, 1, 2);
        gtk_grid_attach (grid, label, 1, row, 1, 1);
        gtk_grid_attach (grid, progress_bar, 1, row + 1, 1, 1);
        gtk_grid_attach (grid, cancel_button, 2, row, 1, 2);

        info->progress_bar = progress_bar;
        info->cancel_button = cancel_button;
        info->data = data;
        info->send_account_label = label;

        g_signal_connect (
            cancel_button, "clicked",
            G_CALLBACK (receive_cancel), info);
    }

    gtk_widget_show_all (wgrid);

    if (parent != NULL)
        gtk_widget_show (GTK_WIDGET (gd));

    g_signal_connect (
        gd, "response",
        G_CALLBACK (dialog_response), data);

    g_object_weak_ref ((GObject *) gd, (GWeakNotify) dialog_destroy_cb, data);

    data->infos = list;

    return data;
}

static void
update_folders (gchar *uri,
                struct _folder_info *info,
                gpointer data)
{
    time_t now = *((time_t *) data);

    d (printf ("checking update for folder: %s\n", info->uri));

    /* let it flow through to the folders every 10 seconds */
    /* we back off slowly as we progress */
    if (now > info->update + 10 + info->count *5) {
        d (printf ("upating a folder: %s\n", info->uri));
        /*camel_folder_thaw(info->folder);
          camel_folder_freeze (info->folder);*/
        info->update = now;
        info->count++;
    }
}

static void
receive_status (CamelFilterDriver *driver,
                enum camel_filter_status_t status,
                gint pc,
                const gchar *desc,
                gpointer data)
{
    struct _send_info *info = data;
    time_t now = time (NULL);

    /* let it flow through to the folder, every now and then too? */
    g_hash_table_foreach (info->data->folders, (GHFunc) update_folders, &now);

    if (info->data->inbox && now > info->data->inbox_update + 20) {
        d (printf ("updating inbox too\n"));
        /* this doesn't seem to work right :( */
        /*camel_folder_thaw(info->data->inbox);
          camel_folder_freeze (info->data->inbox);*/
        info->data->inbox_update = now;
    }

    /* we just pile them onto the port, assuming it can handle it.
     * We could also have a receiver port and see if they've been processed
     * yet, so if this is necessary its not too hard to add */
    /* the mail_gui_port receiver will free everything for us */
    switch (status) {
    case CAMEL_FILTER_STATUS_START:
    case CAMEL_FILTER_STATUS_END:
        set_send_status (info, desc, pc);
        break;
    case CAMEL_FILTER_STATUS_ACTION:
        set_transport_service (info, desc);
        break;
    default:
        break;
    }
}

/* when receive/send is complete */
static void
receive_done (gpointer data)
{
    struct _send_info *info = data;
    const gchar *uid;

    uid = camel_service_get_uid (info->service);
    g_return_if_fail (uid != NULL);

    /* if we've been called to run again - run again */
    if (info->type == SEND_SEND && info->state == SEND_ACTIVE && info->again) {
        CamelFolder *local_outbox;

        local_outbox =
            e_mail_session_get_local_folder (
            E_MAIL_SESSION (info->session),
            E_MAIL_LOCAL_FOLDER_OUTBOX);

        g_return_if_fail (CAMEL_IS_TRANSPORT (info->service));

        info->again = 0;
        mail_send_queue (
            E_MAIL_SESSION (info->session),
            local_outbox,
            CAMEL_TRANSPORT (info->service),
            E_FILTER_SOURCE_OUTGOING,
            info->cancellable,
            receive_get_folder, info,
            receive_status, info,
            send_done, info);
        return;
    }

    if (info->progress_bar) {
        const gchar *text;

        gtk_progress_bar_set_fraction (
            GTK_PROGRESS_BAR (info->progress_bar), 1.0);

        if (info->state == SEND_CANCELLED)
            text = _("Canceled");
        else {
            text = _("Complete");
            info->state = SEND_COMPLETE;
        }

        gtk_progress_bar_set_text (
            GTK_PROGRESS_BAR (info->progress_bar), text);
    }

    if (info->cancel_button)
        gtk_widget_set_sensitive (info->cancel_button, FALSE);

    /* remove/free this active download */
    d (printf ("%s: freeing info %p\n", G_STRFUNC, info));
    if (info->type == SEND_SEND) {
        gpointer key = NULL, value = NULL;
        if (!g_hash_table_lookup_extended (info->data->active, SEND_URI_KEY, &key, &value))
            key = NULL;

        g_hash_table_steal (info->data->active, SEND_URI_KEY);
        g_free (key);
    } else {
        gpointer key = NULL, value = NULL;
        if (!g_hash_table_lookup_extended (info->data->active, uid, &key, &value))
            key = NULL;

        g_hash_table_steal (info->data->active, uid);
        g_free (key);
    }
    info->data->infos = g_list_remove (info->data->infos, info);

    if (g_hash_table_size (info->data->active) == 0) {
        if (info->data->gd)
            gtk_widget_destroy ((GtkWidget *) info->data->gd);
        free_send_data ();
    }

    free_send_info (info);
}

static void
send_done (gpointer data)
{
    receive_done (data);
}
/* although we dont do anythign smart here yet, there is no need for this interface to
 * be available to anyone else.
 * This can also be used to hook into which folders are being updated, and occasionally
 * let them refresh */
static CamelFolder *
receive_get_folder (CamelFilterDriver *d,
                    const gchar *uri,
                    gpointer data,
                    GError **error)
{
    struct _send_info *info = data;
    CamelFolder *folder;
    struct _folder_info *oldinfo;
    gpointer oldkey, oldinfoptr;

    g_mutex_lock (&info->data->lock);
    oldinfo = g_hash_table_lookup (info->data->folders, uri);
    g_mutex_unlock (&info->data->lock);

    if (oldinfo) {
        g_object_ref (oldinfo->folder);
        return oldinfo->folder;
    }

    /* FIXME Not passing a GCancellable here. */
    folder = e_mail_session_uri_to_folder_sync (
        E_MAIL_SESSION (info->session), uri, 0, NULL, error);
    if (!folder)
        return NULL;

    /* we recheck that the folder hasn't snuck in while we were loading it... */
    /* and we assume the newer one is the same, but unref the old one anyway */
    g_mutex_lock (&info->data->lock);

    if (g_hash_table_lookup_extended (
            info->data->folders, uri, &oldkey, &oldinfoptr)) {
        oldinfo = (struct _folder_info *) oldinfoptr;
        g_object_unref (oldinfo->folder);
        oldinfo->folder = folder;
    } else {
        oldinfo = g_malloc0 (sizeof (*oldinfo));
        oldinfo->folder = folder;
        oldinfo->uri = g_strdup (uri);
        g_hash_table_insert (info->data->folders, oldinfo->uri, oldinfo);
    }

    g_object_ref (folder);

    g_mutex_unlock (&info->data->lock);

    return folder;
}

/* ********************************************************************** */

static gboolean
delete_junk_sync (CamelStore *store,
          GCancellable *cancellable,
          GError **error)
{
    CamelFolder *folder;
    GPtrArray *uids;
    guint32 flags;
    guint32 mask;
    guint ii;

    g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);

    folder = camel_store_get_junk_folder_sync (store, cancellable, error);
    if (folder == NULL)
        return FALSE;

    uids = camel_folder_get_uids (folder);
    flags = mask = CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN;

    camel_folder_freeze (folder);

    for (ii = 0; ii < uids->len; ii++) {
        const gchar *uid = uids->pdata[ii];
        camel_folder_set_message_flags (folder, uid, flags, mask);
    }

    camel_folder_thaw (folder);

    camel_folder_free_uids (folder, uids);
    g_object_unref (folder);

    return TRUE;
}

struct TestShouldData
{
    gint64 last_delete_junk;
    gint64 last_expunge;
};

static void
test_should_delete_junk_or_expunge (CamelStore *store,
                    gboolean *should_delete_junk,
                    gboolean *should_expunge)
{
    static GMutex mutex;
    static GHashTable *last_expunge = NULL;

    GSettings *settings;
    const gchar *uid;
    gint64 trash_empty_date = 0, junk_empty_date = 0;
    gint trash_empty_days = 0, junk_empty_days = 0;
    gint64 now;

    g_return_if_fail (CAMEL_IS_STORE (store));
    g_return_if_fail (should_delete_junk != NULL);
    g_return_if_fail (should_expunge != NULL);

    *should_delete_junk = FALSE;
    *should_expunge = FALSE;

    uid = camel_service_get_uid (CAMEL_SERVICE (store));
    g_return_if_fail (uid != NULL);

    settings = g_settings_new ("org.gnome.evolution.mail");

    now = time (NULL) / 60 / 60 / 24;

    *should_delete_junk = g_settings_get_boolean (settings, "junk-empty-on-exit");
    *should_expunge = g_settings_get_boolean (settings, "trash-empty-on-exit");

    if (*should_delete_junk || *should_expunge) {
        junk_empty_days = g_settings_get_int (settings, "junk-empty-on-exit-days");
        junk_empty_date = g_settings_get_int (settings, "junk-empty-date");

        trash_empty_days = g_settings_get_int (settings, "trash-empty-on-exit-days");
        trash_empty_date = g_settings_get_int (settings, "trash-empty-date");

        g_mutex_lock (&mutex);
        if (!last_expunge) {
            last_expunge = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
        } else {
            struct TestShouldData *tsd;

            tsd = g_hash_table_lookup (last_expunge, uid);
            if (tsd) {
                junk_empty_date = tsd->last_delete_junk;
                trash_empty_date = tsd->last_expunge;
            }
        }
        g_mutex_unlock (&mutex);
    }

    *should_delete_junk = *should_delete_junk && junk_empty_days > 0 && junk_empty_date + junk_empty_days <= now;
    *should_expunge = *should_expunge && trash_empty_days > 0 && trash_empty_date + trash_empty_days <= now;

    if (*should_delete_junk || *should_expunge) {
        struct TestShouldData *tsd;

        if (*should_delete_junk)
            junk_empty_date = now;
        if (*should_expunge)
            trash_empty_date = now;

        g_mutex_lock (&mutex);
        tsd = g_hash_table_lookup (last_expunge, uid);
        if (!tsd) {
            tsd = g_new0 (struct TestShouldData, 1);
            g_hash_table_insert (last_expunge, g_strdup (uid), tsd);
        }

        tsd->last_delete_junk = junk_empty_date;
        tsd->last_expunge = trash_empty_date;
        g_mutex_unlock (&mutex);
    }

    g_object_unref (settings);
}

static void
get_folders (CamelStore *store,
             GPtrArray *folders,
             CamelFolderInfo *info)
{
    while (info) {
        if (camel_store_can_refresh_folder (store, info, NULL)) {
            if ((info->flags & CAMEL_FOLDER_NOSELECT) == 0) {
                gchar *folder_uri;

                folder_uri = e_mail_folder_uri_build (
                    store, info->full_name);
                g_ptr_array_add (folders, folder_uri);
            }
        }

        get_folders (store, folders, info->child);
        info = info->next;
    }
}

static void
main_op_cancelled_cb (GCancellable *main_op,
                      GCancellable *refresh_op)
{
    g_cancellable_cancel (refresh_op);
}

struct _refresh_folders_msg {
    MailMsg base;

    struct _send_info *info;
    GPtrArray *folders;
    CamelStore *store;
    CamelFolderInfo *finfo;
};

static gchar *
refresh_folders_desc (struct _refresh_folders_msg *m)
{
    return g_strdup_printf (
        _("Checking for new mail at '%s'"),
        camel_service_get_display_name (CAMEL_SERVICE (m->store)));
}

static void
refresh_folders_exec (struct _refresh_folders_msg *m,
                      GCancellable *cancellable,
                      GError **error)
{
    CamelFolder *folder;
    gint i;
    gboolean success;
    gboolean delete_junk = FALSE, expunge = FALSE;
    GError *local_error = NULL;
    gulong handler_id = 0;

    if (cancellable)
        handler_id = g_signal_connect (
            m->info->cancellable, "cancelled",
            G_CALLBACK (main_op_cancelled_cb), cancellable);

    success = camel_service_connect_sync (
        CAMEL_SERVICE (m->store), cancellable, error);
    if (!success)
        goto exit;

    get_folders (m->store, m->folders, m->finfo);

    camel_operation_push_message (m->info->cancellable, _("Updating..."));

    test_should_delete_junk_or_expunge (m->store, &delete_junk, &expunge);

    if (delete_junk && !delete_junk_sync (m->store, cancellable, error)) {
        camel_operation_pop_message (m->info->cancellable);
        goto exit;
    }

    for (i = 0; i < m->folders->len; i++) {
        folder = e_mail_session_uri_to_folder_sync (
            E_MAIL_SESSION (m->info->session),
            m->folders->pdata[i], 0,
            cancellable, &local_error);
        if (folder && camel_folder_synchronize_sync (folder, expunge, cancellable, &local_error))
            camel_folder_refresh_info_sync (folder, cancellable, &local_error);

        if (local_error != NULL) {
            if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
                CamelStore *store = m->store;
                const gchar *full_name;

                if (folder) {
                    store = camel_folder_get_parent_store (folder);
                    full_name = camel_folder_get_full_name (folder);
                } else {
                    store = m->store;
                    full_name = (const gchar *) m->folders->pdata[i];
                }

                report_error_to_ui (CAMEL_SERVICE (store), full_name, local_error);
            }

            g_clear_error (&local_error);
        }

        if (folder)
            g_object_unref (folder);

        if (g_cancellable_is_cancelled (m->info->cancellable) ||
            g_cancellable_is_cancelled (cancellable))
            break;

        if (m->info->state != SEND_CANCELLED)
            camel_operation_progress (
                m->info->cancellable, 100 * i / m->folders->len);
    }

    camel_operation_pop_message (m->info->cancellable);

exit:
    if (handler_id > 0)
        g_signal_handler_disconnect (m->info->cancellable, handler_id);
}

static void
refresh_folders_done (struct _refresh_folders_msg *m)
{
    receive_done (m->info);
}

static void
refresh_folders_free (struct _refresh_folders_msg *m)
{
    gint i;

    for (i = 0; i < m->folders->len; i++)
        g_free (m->folders->pdata[i]);
    g_ptr_array_free (m->folders, TRUE);

    camel_folder_info_free (m->finfo);
    g_object_unref (m->store);
}

static MailMsgInfo refresh_folders_info = {
    sizeof (struct _refresh_folders_msg),
    (MailMsgDescFunc) refresh_folders_desc,
    (MailMsgExecFunc) refresh_folders_exec,
    (MailMsgDoneFunc) refresh_folders_done,
    (MailMsgFreeFunc) refresh_folders_free
};

static void
receive_update_got_folderinfo (GObject *source_object,
                               GAsyncResult *result,
                               gpointer user_data)
{
    CamelFolderInfo *info = NULL;
    struct _send_info *send_info = user_data;
    GError *local_error = NULL;

    mail_folder_cache_note_store_finish (
        MAIL_FOLDER_CACHE (source_object),
        result, &info, &local_error);

    /* Ignore cancellations. */
    if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        g_warn_if_fail (info == NULL);
        g_error_free (local_error);

        receive_done (send_info);

    /* XXX Need to hand this off to an EAlertSink. */
    } else if (local_error != NULL) {
        g_warn_if_fail (info == NULL);
        report_error_to_ui (send_info->service, NULL, local_error);
        g_error_free (local_error);

        receive_done (send_info);

    /* CamelFolderInfo may be NULL even if no error occurred. */
    } else if (info != NULL) {
        GPtrArray *folders = g_ptr_array_new ();
        struct _refresh_folders_msg *m;

        m = mail_msg_new (&refresh_folders_info);
        m->store = g_object_ref (send_info->service);
        m->folders = folders;
        m->info = send_info;
        m->finfo = info;  /* takes ownership */

        mail_msg_unordered_push (m);

    } else {
        receive_done (send_info);
    }
}

static void
receive_update_got_store (CamelStore *store,
                          struct _send_info *info)
{
    MailFolderCache *folder_cache;

    folder_cache = e_mail_session_get_folder_cache (
        E_MAIL_SESSION (info->session));

    if (store != NULL) {
        CamelProvider *provider;

        /* do not update remote stores in offline */
        provider = camel_service_get_provider (CAMEL_SERVICE (store));
        if (provider && (provider->flags & CAMEL_PROVIDER_IS_REMOTE) != 0 &&
            !camel_session_get_online (info->session))
            store = NULL;
    }

    if (store != NULL) {
        mail_folder_cache_note_store (
            folder_cache, store, info->cancellable,
            receive_update_got_folderinfo, info);
    } else {
        receive_done (info);
    }
}


struct _refresh_local_store_msg {
    MailMsg base;

    CamelStore *store;
    gboolean delete_junk;
    gboolean expunge_trash;
};

static gchar *
refresh_local_store_desc (struct _refresh_local_store_msg *m)
{
    const gchar *display_name;

    display_name = camel_service_get_display_name (CAMEL_SERVICE (m->store));

    if (m->delete_junk && m->expunge_trash)
        return g_strdup_printf (_("Deleting junk and expunging trash at '%s'"), display_name);
    else if (m->delete_junk)
        return g_strdup_printf (_("Deleting junk at '%s'"), display_name);
    else
        return g_strdup_printf (_("Expunging trash at '%s'"), display_name);
}

static void
refresh_local_store_exec (struct _refresh_local_store_msg *m,
              GCancellable *cancellable,
              GError **error)
{
    if (m->delete_junk && !delete_junk_sync (m->store, cancellable, error))
        return;

    if (m->expunge_trash) {
        CamelFolder *trash;

        trash = camel_store_get_trash_folder_sync (m->store, cancellable, error);

        if (trash != NULL) {
            e_mail_folder_expunge_sync (trash, cancellable, error);
            g_object_unref (trash);
        }
    }
}

static void
refresh_local_store_free (struct _refresh_local_store_msg *m)
{
    g_object_unref (m->store);
}

static MailMsgInfo refresh_local_store_info = {
    sizeof (struct _refresh_local_store_msg),
    (MailMsgDescFunc) refresh_local_store_desc,
    (MailMsgExecFunc) refresh_local_store_exec,
    (MailMsgDoneFunc) NULL,
    (MailMsgFreeFunc) refresh_local_store_free
};

static void
maybe_delete_junk_or_expunge_local_store (EMailSession *session)
{
    CamelStore *store;
    gboolean delete_junk = FALSE, expunge_trash = FALSE;
    struct _refresh_local_store_msg *m;

    store = e_mail_session_get_local_store (session);
    test_should_delete_junk_or_expunge (store, &delete_junk, &expunge_trash);

    if (!delete_junk && !expunge_trash)
        return;

    m = mail_msg_new (&refresh_local_store_info);
    m->store = g_object_ref (store);
    m->delete_junk = delete_junk;
    m->expunge_trash = expunge_trash;

    mail_msg_unordered_push (m);
}

static CamelService *
ref_default_transport (EMailSession *session)
{
    ESource *source;
    ESourceRegistry *registry;
    CamelService *service;
    const gchar *extension_name;
    const gchar *uid;

    registry = e_mail_session_get_registry (session);
    source = e_source_registry_ref_default_mail_identity (registry);

    if (source == NULL)
        return NULL;

    extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
    if (e_source_has_extension (source, extension_name)) {
        ESourceMailSubmission *extension;
        gchar *uid;

        extension = e_source_get_extension (source, extension_name);
        uid = e_source_mail_submission_dup_transport_uid (extension);

        g_object_unref (source);
        source = e_source_registry_ref_source (registry, uid);

        g_free (uid);
    } else {
        g_object_unref (source);
        source = NULL;
    }

    if (source == NULL)
        return NULL;

    uid = e_source_get_uid (source);
    service = camel_session_ref_service (CAMEL_SESSION (session), uid);

    g_object_unref (source);

    return service;
}

static GtkWidget *
send_receive (GtkWindow *parent,
              EMailSession *session,
              gboolean allow_send)
{
    CamelFolder *local_outbox;
    CamelService *transport;
    struct _send_data *data;
    GList *scan, *siter;

    if (send_recv_dialog != NULL) {
        if (parent != NULL && gtk_widget_get_realized (send_recv_dialog)) {
            gtk_window_present (GTK_WINDOW (send_recv_dialog));
        }
        return send_recv_dialog;
    }

    transport = ref_default_transport (session);

    local_outbox =
        e_mail_session_get_local_folder (
        session, E_MAIL_LOCAL_FOLDER_OUTBOX);

    data = build_dialog (
        parent, session, local_outbox, transport, allow_send);

    if (transport != NULL)
        g_object_unref (transport);

    maybe_delete_junk_or_expunge_local_store (session);

    scan = g_list_copy (data->infos);

    for (siter = scan; siter != NULL; siter = siter->next) {
        struct _send_info *info = siter->data;

        if (!CAMEL_IS_SERVICE (info->service))
            continue;

        switch (info->type) {
        case SEND_RECEIVE:
            mail_fetch_mail (
                CAMEL_STORE (info->service),
                E_FILTER_SOURCE_INCOMING,
                NULL, NULL, NULL,
                info->cancellable,
                receive_get_folder, info,
                receive_status, info,
                receive_done, info);
            break;
        case SEND_SEND:
            /* todo, store the folder in info? */
            mail_send_queue (
                session, local_outbox,
                CAMEL_TRANSPORT (info->service),
                E_FILTER_SOURCE_OUTGOING,
                info->cancellable,
                receive_get_folder, info,
                receive_status, info,
                send_done, info);
            break;
        case SEND_UPDATE:
            receive_update_got_store (
                CAMEL_STORE (info->service), info);
            break;
        default:
            break;
        }
    }

    g_list_free (scan);

    return send_recv_dialog;
}

GtkWidget *
mail_send_receive (GtkWindow *parent,
                   EMailSession *session)
{
    return send_receive (parent, session, TRUE);
}

GtkWidget *
mail_receive (GtkWindow *parent,
              EMailSession *session)
{
    return send_receive (parent, session, FALSE);
}

/* We setup the download info's in a hashtable, if we later
 * need to build the gui, we insert them in to add them. */
void
mail_receive_service (CamelService *service)
{
    struct _send_info *info;
    struct _send_data *data;
    CamelSession *session;
    CamelFolder *local_outbox;
    const gchar *uid;
    send_info_t type = SEND_INVALID;

    g_return_if_fail (CAMEL_IS_SERVICE (service));

    uid = camel_service_get_uid (service);
    session = camel_service_ref_session (service);

    data = setup_send_data (E_MAIL_SESSION (session));
    info = g_hash_table_lookup (data->active, uid);

    if (info != NULL)
        goto exit;

    type = get_receive_type (service);

    if (type == SEND_INVALID || type == SEND_SEND)
        goto exit;

    info = g_malloc0 (sizeof (*info));
    info->type = type;
    info->progress_bar = NULL;
    info->session = g_object_ref (session);
    info->service = g_object_ref (service);
    info->keep_on_server = get_keep_on_server (service);
    info->cancellable = camel_operation_new ();
    info->cancel_button = NULL;
    info->data = data;
    info->state = SEND_ACTIVE;
    info->timeout_id = 0;

    g_signal_connect (
        info->cancellable, "status",
        G_CALLBACK (operation_status), info);

    d (printf ("Adding new info %p\n", info));

    g_hash_table_insert (data->active, g_strdup (uid), info);

    switch (info->type) {
    case SEND_RECEIVE:
        mail_fetch_mail (
            CAMEL_STORE (service),
            E_FILTER_SOURCE_INCOMING,
            NULL, NULL, NULL,
            info->cancellable,
            receive_get_folder, info,
            receive_status, info,
            receive_done, info);
        break;
    case SEND_SEND:
        /* todo, store the folder in info? */
        local_outbox =
            e_mail_session_get_local_folder (
            E_MAIL_SESSION (session),
            E_MAIL_LOCAL_FOLDER_OUTBOX);
        mail_send_queue (
            E_MAIL_SESSION (session),
            local_outbox,
            CAMEL_TRANSPORT (service),
            E_FILTER_SOURCE_OUTGOING,
            info->cancellable,
            receive_get_folder, info,
            receive_status, info,
            send_done, info);
        break;
    case SEND_UPDATE:
        receive_update_got_store (CAMEL_STORE (service), info);
        break;
    default:
        g_return_if_reached ();
    }

exit:
    g_object_unref (session);
}

void
mail_send (EMailSession *session)
{
    CamelFolder *local_outbox;
    CamelService *service;
    struct _send_info *info;
    struct _send_data *data;
    send_info_t type = SEND_INVALID;

    g_return_if_fail (E_IS_MAIL_SESSION (session));

    service = ref_default_transport (session);
    if (service == NULL)
        return;

    data = setup_send_data (session);
    info = g_hash_table_lookup (data->active, SEND_URI_KEY);
    if (info != NULL) {
        info->again++;
        d (printf ("send of %s still in progress\n", transport->url));
        g_object_unref (service);
        return;
    }

    d (printf ("starting non-interactive send of '%s'\n", transport->url));

    type = get_receive_type (service);
    if (type == SEND_INVALID) {
        g_object_unref (service);
        return;
    }

    info = g_malloc0 (sizeof (*info));
    info->type = SEND_SEND;
    info->progress_bar = NULL;
    info->session = g_object_ref (session);
    info->service = g_object_ref (service);
    info->keep_on_server = FALSE;
    info->cancellable = NULL;
    info->cancel_button = NULL;
    info->data = data;
    info->state = SEND_ACTIVE;
    info->timeout_id = 0;

    d (printf ("Adding new info %p\n", info));

    g_hash_table_insert (data->active, g_strdup (SEND_URI_KEY), info);

    /* todo, store the folder in info? */
    local_outbox =
        e_mail_session_get_local_folder (
        session, E_MAIL_LOCAL_FOLDER_OUTBOX);

    mail_send_queue (
        session, local_outbox,
        CAMEL_TRANSPORT (service),
        E_FILTER_SOURCE_OUTGOING,
        info->cancellable,
        receive_get_folder, info,
        receive_status, info,
        send_done, info);

    g_object_unref (service);
}