aboutsummaryrefslogblamecommitdiffstats
path: root/libemail-engine/mail-folder-cache.c
blob: a48a92c61f830b1f09e672a2e04628be0c2255e4 (plain) (tree)
1
2
3
4
5
6
7
8
  
                                                                


                                                               


                                                                  



                                                                    
                                                                             


           


                                                        

                                                        
                                       
  

   




                                                            
                    
                   

      
                   
                 
 
                       

                        
                                          
 
                                    
 
                              
                         
                                
                           
                               
 
            
            
 



                                                                
                                    
                                      
                                          
                                            
 
                                

                                   
                                                     
                      
 
                                                     


                                  


                                                           


                                  
  
 
      




                          



                           
                              
                       




                                  

                                
 
                    
 













                                                                      
 

                                             
  
 

                                
 
                    
 
                          

                                   
 

                                         

  




                              

                       
 
                          
 




                                                      









                                         
  
 




















                                                                         
                                                                 
 




























                                                                
           
                                                  
 



















                                                                

 
           
                                           
 












                                                                  




                                  





                                                 
 
                                                            


                                         
                                                    
 
                                         



                                                                  
                                                                      

                                              
                                                                        

                                           









                                                               
 
                          


           
                                        
 



                                                                 
 


                                                          

































































































































                                                                            












                                                         






































                                                         
 




































































































                                                                          

         


















                                                                     
 

                           
 













                                                                     
 
                           

 
               
                                                     
 

                               
 
                                              



                                                                 
                                                 
 
                            






















                                                                        
 






                                                                    
                 
 
                                          
                               




                                                          
 





                                                                     
                                       



                                                           
                                                      


                                                      

                 


                                                                      
                                                                       
                                                                           




                                                                             

                                                                            


                                                   
 
                                             
                                                                              
                                                        


                         
                                       
         

                     


           
                                                        
 


                                   
 



                                                 
 













                                                                  

 







                                                                      
                                                               




                                                                      






                                                                     
 
           
                                       
                                        
                                  



                                         
 
                                  

                              
                            

                         
 



                                                                  
                                                     


                                                                          
 

                                                       
                             





                                          


                                                                                


                                                                   

                                                                         


                                                             
                                                      
                                                                         
                                                                   

                                                   

                                                                                          
                                                          
 
                                                                                               

                                                         
                         
                        
                                                       
                                 
                                                      


                                                                                        

                                        
         
 


                                                    
 
                          
                                  
 

                                                                    
                                    
                                                


                                                         

                                                     
         


           

                                                  
                                          
 
                                                          
                                                    


                                  
                              
                                 
                               
                                
                               

                     
                      
                                                           
 

                                                              
                                                                           
 



                                                                                           

                                                                       
                                              
 





                                                                      
 
                                         


                                     

                                                                  
                                                                 
                                                               

                                                                      



                                                                          
                                                                             
                                                                                                  

                                                                                                              
                                              













                                                                                                       
                                                                              
                         

                 
 
                    

                                                        
                                                               
 






                                                         
         



                         

                                 
 
 
           
                                          
                                           
                               
 
                                                                 
 
                                               
 

                                                                
 
                                                                    
                                                                  
 




                                                                    
                                                     
         


           
                                     
                                  
                                    
 
                                
 



                                                                             
                





                                               
 
                                                                        
 

                                                                   
                                        

                                                             
                                                                  
 


                                                     
         
 
 
           
                                       
                                    
                                      
 
                    
                                                     
 
                              
                                                                      
 

                              


           

                                                  
                                                   
 
                              
 




                                                                     

 
           


                                                
 

                                                      
                                           
                                                                
 
 
           

                                            
                                               
 
                                                      


           

                                                    
                                                     
 
                                
 




                                                             
         
 
 
           

                                               
                                                
 

                                                      
                                           
                                                                  

 
           
                                       
                                      


                                     
 
                                                         


                                    
                                
 
                                                           
                                                  

                                                                
                                                                                  

                                                                         
                                                                    
                                                        

                                                    
 



                                           
 
                                                                    
 
                                            
 
                                                 
                                                     
 


                                                      
 
                                             

                                                        
                                                    
                                                                  
                                      
                                                                            
                                      

                                                                                


                                    

                                                                                 




                                    
 
                     

 
           

                                 
 
                    
                                              
 
                              
                                                         


                              

 
           

                             
 

                                                                
 
                                                   
 

           


                                               
                                                
 
                              
 

                                                                     
                                                        
                                     
                        
 




                                                                             
                                            
                                                                      
 
                                        



                                                                    

                 
                                                 
 
                                              
         
 
 
                        
                     


                          

                                           
 
                            
                   
 
                                                                               

                                                              
 



                   


                                           
 

                                            
                                
 



                                                               
                                                      

                                                                                  
                                      
                                                             

                                                        


                                      
                                                                     


           
                                           
 
                                  

 





                                          

  

                                
 
                           
 
                                                     
 



                                                                    
 

                                                                       
 

                                                      
 

                                                    
 

                                               
 
                                                                 
 
                    

 









                                                                      






                                                         

 















                                                                              
           

















                                                                       












                                                                          

                                            


                                                      
 

                                                  

                                                   
 


                                                

         

                                                                     
 

                                                                      
 
                                                     



                                                                           
























                                                                    
                                                      

                                                        

                                                                












                                                                  
                                                          

                                 




























                                                                    
                                                      

                                                        

                                                                















                                                                  
                                                          

                                 



























                                                                    
                                                      
 

                                                                


















                                                                  
                                                          

                                 


           
                                                          
 
                                   
 
                                                                          
 
                                              
                                                                    
                                                          
                                                            
 



                                                                         











                                                        


                                                        
                                               


                                                  



                                                   
                                                                         
                                 


                                 



                                                        
                                               




                                                                          



                                                    
                                                                           
                                 


                                 



                                                        
                                               


                                           



                                                   
                                                                       
                                             
                     


                                 



                                                        

                                                       


                                           



                                                   
                                                                       
                                 



                                 



                                                        
                                               



                                                                               




                                                                              
                                 



                                 



                                                        
                                               




                                                                   

                                                                           
            



                                                   
                                                                       
                                 






                                 


           
                                               
 
                                  


                         





                                                   
                                                            
                                                                         
 

                                                        
 

                                                                            


                                                      

                                                       


                                                        

 
                 
                            
 
                                                           
 

   

















                                                                        































































































                                                                             








                                                                   






                                                                
                                  
                                                             



































                                                                               
   






                                                                               
                                                     
                                                
                                                        

                                                           
 
                              

                                    
 
                                                        
                                                  
 


                                                                             
 

                                                                
 


                                                      
 



                                                                            
 
                                         













                                                                      
                                           
 

                                
                                      
 
 




























                                                                           


   






                                                                               
                                                      


                                                   

                                   

                               


                                                        


                                                              

                                                         
 



                                                                         
                       








                                                                

         
                                                      
 
                                                                       
 



                                                               
 
                                            
 
                                        


   

















                                                                       
                                





                                                                   





                                                         




                        



                                
  


                                                                          
  








                                                                        
 
                                




                                                                  
 




                                                               
         
 
                      

 












                                                                          
        
                                                                

                                                                  
                                                                     
 
                                





                                                                   
 





                                                         
         
 
                         

 
    
                                                                



                                                           
                                                        

                                             

                                                                   
 
                                                                        



                                                                     
                                                          


    
                                                                 



                                                            
                                                        

                                             

                                                                   
 
                                                                         



                                                                     
                                                          
 

    


                                                          
                              



                                                        

                                      
 



                                                         
 
                                                                
 

                                                                          
 



                                                                            








                                                          



                                                                    















                                                                  
/*
 * 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/>
 *
 *
 * Authors:
 *   Peter Williams <peterw@ximian.com>
 *   Michael Zucchi <notzed@ximian.com>
 *   Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 * Copyright (C) 2009 Intel Corporation
 *
 */

/**
 * SECTION: mail-folder-cache
 * @short_description: Stores information about open folders
 **/

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

#include <string.h>
#include <time.h>

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

#include <libedataserver/libedataserver.h>

#include <libemail-engine/mail-mt.h>

#include "mail-folder-cache.h"
#include "e-mail-utils.h"
#include "e-mail-folder-utils.h"
#include "e-mail-session.h"
#include "e-mail-store-utils.h"

#define w(x)
#define d(x)

#define MAIL_FOLDER_CACHE_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE \
    ((obj), MAIL_TYPE_FOLDER_CACHE, MailFolderCachePrivate))

typedef struct _StoreInfo StoreInfo;
typedef struct _FolderInfo FolderInfo;
typedef struct _AsyncContext AsyncContext;
typedef struct _UpdateClosure UpdateClosure;

struct _MailFolderCachePrivate {
    GMainContext *main_context;

    /* source id for the ping timeout callback */
    guint ping_id;

    /* Store to storeinfo table, active stores */
    GHashTable *store_info_ht;
    GMutex store_info_ht_lock;

    /* hack for people who LIKE to have unsent count */
    gint count_sent;
    gint count_trash;

    GQueue local_folder_uris;
    GQueue remote_folder_uris;
};

enum {
    PROP_0,
    PROP_MAIN_CONTEXT,
};

enum {
    FOLDER_AVAILABLE,
    FOLDER_UNAVAILABLE,
    FOLDER_DELETED,
    FOLDER_RENAMED,
    FOLDER_UNREAD_UPDATED,
    FOLDER_CHANGED,
    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

struct _StoreInfo {
    volatile gint ref_count;

    GMutex lock;

    CamelStore *store;
    gulong folder_opened_handler_id;
    gulong folder_created_handler_id;
    gulong folder_deleted_handler_id;
    gulong folder_renamed_handler_id;
    gulong folder_subscribed_handler_id;
    gulong folder_unsubscribed_handler_id;

    GHashTable *folder_info_ht; /* by full_name */
    gboolean first_update;      /* TRUE, then FALSE forever */

    /* Hold a reference to keep them alive. */
    CamelFolder *vjunk;
    CamelFolder *vtrash;

    /* Outstanding folderinfo requests */
    GQueue folderinfo_updates;
};

struct _FolderInfo {
    volatile gint ref_count;

    GMutex lock;

    CamelStore *store;
    gchar *full_name;
    CamelFolderInfoFlags flags;

    GWeakRef folder;
    gulong folder_changed_handler_id;
};

struct _AsyncContext {
    StoreInfo *store_info;
    CamelFolderInfo *info;
};

struct _UpdateClosure {
    GWeakRef cache;

    CamelStore *store;

    /* Signal ID for one of:
     * AVAILABLE, DELETED, RENAMED, UNAVAILABLE */
    guint signal_id;

    gboolean new_messages;

    gchar *full_name;
    gchar *oldfull;

    gint unread;

    /* for only one new message... */
    gchar *msg_uid;
    gchar *msg_sender;
    gchar *msg_subject;
};

/* Forward Declarations */
static void store_folder_created_cb     (CamelStore *store,
                         CamelFolderInfo *info,
                         MailFolderCache *cache);
static void store_folder_deleted_cb     (CamelStore *store,
                         CamelFolderInfo *info,
                         MailFolderCache *cache);
static void store_folder_opened_cb      (CamelStore *store,
                         CamelFolder *folder,
                         MailFolderCache *cache);
static void store_folder_renamed_cb     (CamelStore *store,
                         const gchar *old_name,
                         CamelFolderInfo *info,
                         MailFolderCache *cache);
static void store_folder_subscribed_cb  (CamelStore *store,
                         CamelFolderInfo *info,
                         MailFolderCache *cache);
static void store_folder_unsubscribed_cb    (CamelStore *store,
                         CamelFolderInfo *info,
                         MailFolderCache *cache);

G_DEFINE_TYPE (MailFolderCache, mail_folder_cache, G_TYPE_OBJECT)

static FolderInfo *
folder_info_new (CamelStore *store,
                 const gchar *full_name,
                 CamelFolderInfoFlags flags)
{
    FolderInfo *folder_info;

    folder_info = g_slice_new0 (FolderInfo);
    folder_info->ref_count = 1;
    folder_info->store = g_object_ref (store);
    folder_info->full_name = g_strdup (full_name);
    folder_info->flags = flags;

    g_mutex_init (&folder_info->lock);

    return folder_info;
}

static FolderInfo *
folder_info_ref (FolderInfo *folder_info)
{
    g_return_val_if_fail (folder_info != NULL, NULL);
    g_return_val_if_fail (folder_info->ref_count > 0, NULL);

    g_atomic_int_inc (&folder_info->ref_count);

    return folder_info;
}

static void
folder_info_clear_folder (FolderInfo *folder_info)
{
    CamelFolder *folder;

    g_return_if_fail (folder_info != NULL);

    g_mutex_lock (&folder_info->lock);

    folder = g_weak_ref_get (&folder_info->folder);

    if (folder != NULL) {
        g_signal_handler_disconnect (
            folder,
            folder_info->folder_changed_handler_id);

        g_weak_ref_set (&folder_info->folder, NULL);
        folder_info->folder_changed_handler_id = 0;

        g_object_unref (folder);
    }

    g_mutex_unlock (&folder_info->lock);
}

static void
folder_info_unref (FolderInfo *folder_info)
{
    g_return_if_fail (folder_info != NULL);
    g_return_if_fail (folder_info->ref_count > 0);

    if (g_atomic_int_dec_and_test (&folder_info->ref_count)) {
        folder_info_clear_folder (folder_info);

        g_clear_object (&folder_info->store);
        g_free (folder_info->full_name);

        g_mutex_clear (&folder_info->lock);

        g_slice_free (FolderInfo, folder_info);
    }
}

static StoreInfo *
store_info_new (CamelStore *store)
{
    StoreInfo *store_info;

    store_info = g_slice_new0 (StoreInfo);
    store_info->ref_count = 1;
    store_info->store = g_object_ref (store);
    store_info->first_update = TRUE;

    store_info->folder_info_ht = g_hash_table_new_full (
        (GHashFunc) g_str_hash,
        (GEqualFunc) g_str_equal,
        (GDestroyNotify) NULL,
        (GDestroyNotify) folder_info_unref);

    g_mutex_init (&store_info->lock);

    /* If these are vfolders then they need to be opened
     * now, otherwise they won't keep track of all folders. */
    if (store->flags & CAMEL_STORE_VJUNK)
        store_info->vjunk = camel_store_get_junk_folder_sync (
            store, NULL, NULL);
    if (store->flags & CAMEL_STORE_VTRASH)
        store_info->vtrash = camel_store_get_trash_folder_sync (
            store, NULL, NULL);

    return store_info;
}

static StoreInfo *
store_info_ref (StoreInfo *store_info)
{
    g_return_val_if_fail (store_info != NULL, NULL);
    g_return_val_if_fail (store_info->ref_count > 0, NULL);

    g_atomic_int_inc (&store_info->ref_count);

    return store_info;
}

static void
store_info_unref (StoreInfo *store_info)
{
    g_return_if_fail (store_info != NULL);
    g_return_if_fail (store_info->ref_count > 0);

    if (g_atomic_int_dec_and_test (&store_info->ref_count)) {

        g_warn_if_fail (
            g_queue_is_empty (
            &store_info->folderinfo_updates));

        if (store_info->folder_opened_handler_id > 0) {
            g_signal_handler_disconnect (
                store_info->store,
                store_info->folder_opened_handler_id);
        }

        if (store_info->folder_created_handler_id > 0) {
            g_signal_handler_disconnect (
                store_info->store,
                store_info->folder_created_handler_id);
        }

        if (store_info->folder_deleted_handler_id > 0) {
            g_signal_handler_disconnect (
                store_info->store,
                store_info->folder_deleted_handler_id);
        }

        if (store_info->folder_subscribed_handler_id > 0) {
            g_signal_handler_disconnect (
                store_info->store,
                store_info->folder_subscribed_handler_id);
        }

        if (store_info->folder_unsubscribed_handler_id > 0) {
            g_signal_handler_disconnect (
                store_info->store,
                store_info->folder_unsubscribed_handler_id);
        }

        g_hash_table_destroy (store_info->folder_info_ht);

        g_clear_object (&store_info->store);
        g_clear_object (&store_info->vjunk);
        g_clear_object (&store_info->vtrash);

        g_mutex_clear (&store_info->lock);

        g_slice_free (StoreInfo, store_info);
    }
}

static FolderInfo *
store_info_ref_folder_info (StoreInfo *store_info,
                            const gchar *folder_name)
{
    GHashTable *folder_info_ht;
    FolderInfo *folder_info;

    g_return_val_if_fail (store_info != NULL, NULL);
    g_return_val_if_fail (folder_name != NULL, NULL);

    g_mutex_lock (&store_info->lock);

    folder_info_ht = store_info->folder_info_ht;

    folder_info = g_hash_table_lookup (folder_info_ht, folder_name);
    if (folder_info != NULL)
        folder_info_ref (folder_info);

    g_mutex_unlock (&store_info->lock);

    return folder_info;
}

static void
store_info_insert_folder_info (StoreInfo *store_info,
                               FolderInfo *folder_info)
{
    GHashTable *folder_info_ht;

    g_return_if_fail (store_info != NULL);
    g_return_if_fail (folder_info != NULL);
    g_return_if_fail (folder_info->full_name != NULL);

    g_mutex_lock (&store_info->lock);

    folder_info_ht = store_info->folder_info_ht;

    g_hash_table_insert (
        folder_info_ht,
        folder_info->full_name,
        folder_info_ref (folder_info));

    g_mutex_unlock (&store_info->lock);
}

static GList *
store_info_list_folder_info (StoreInfo *store_info)
{
    GList *list;

    g_return_val_if_fail (store_info != NULL, NULL);

    g_mutex_lock (&store_info->lock);

    list = g_hash_table_get_values (store_info->folder_info_ht);
    g_list_foreach (list, (GFunc) folder_info_ref, NULL);

    g_mutex_unlock (&store_info->lock);

    return list;
}

static FolderInfo *
store_info_steal_folder_info (StoreInfo *store_info,
                              const gchar *folder_name)
{
    GHashTable *folder_info_ht;
    FolderInfo *folder_info;

    g_return_val_if_fail (store_info != NULL, NULL);
    g_return_val_if_fail (folder_name != NULL, NULL);

    g_mutex_lock (&store_info->lock);

    folder_info_ht = store_info->folder_info_ht;

    folder_info = g_hash_table_lookup (folder_info_ht, folder_name);
    if (folder_info != NULL) {
        folder_info_ref (folder_info);
        g_hash_table_remove (folder_info_ht, folder_name);
    }

    g_mutex_unlock (&store_info->lock);

    return folder_info;
}

static void
async_context_free (AsyncContext *async_context)
{
    if (async_context->info != NULL)
        camel_store_free_folder_info (
            async_context->store_info->store,
            async_context->info);

    store_info_unref (async_context->store_info);

    g_slice_free (AsyncContext, async_context);
}

static UpdateClosure *
update_closure_new (MailFolderCache *cache,
                    CamelStore *store)
{
    UpdateClosure *closure;

    closure = g_slice_new0 (UpdateClosure);
    g_weak_ref_set (&closure->cache, cache);
    closure->store = g_object_ref (store);

    return closure;
}

static void
update_closure_free (UpdateClosure *closure)
{
    g_weak_ref_set (&closure->cache, NULL);

    g_clear_object (&closure->store);

    g_free (closure->full_name);
    g_free (closure->oldfull);
    g_free (closure->msg_uid);
    g_free (closure->msg_sender);
    g_free (closure->msg_subject);

    g_slice_free (UpdateClosure, closure);
}

static StoreInfo *
mail_folder_cache_new_store_info (MailFolderCache *cache,
                                  CamelStore *store)
{
    StoreInfo *store_info;
    gulong handler_id;

    g_return_val_if_fail (store != NULL, NULL);

    store_info = store_info_new (store);

    handler_id = g_signal_connect (
        store, "folder-opened",
        G_CALLBACK (store_folder_opened_cb), cache);
    store_info->folder_opened_handler_id = handler_id;

    handler_id = g_signal_connect (
        store, "folder-created",
        G_CALLBACK (store_folder_created_cb), cache);
    store_info->folder_created_handler_id = handler_id;

    handler_id = g_signal_connect (
        store, "folder-deleted",
        G_CALLBACK (store_folder_deleted_cb), cache);
    store_info->folder_deleted_handler_id = handler_id;

    handler_id = g_signal_connect (
        store, "folder-renamed",
        G_CALLBACK (store_folder_renamed_cb), cache);
    store_info->folder_renamed_handler_id = handler_id;

    if (CAMEL_IS_SUBSCRIBABLE (store)) {
        handler_id = g_signal_connect (
            store, "folder-subscribed",
            G_CALLBACK (store_folder_subscribed_cb), cache);
        store_info->folder_subscribed_handler_id = handler_id;

        handler_id = g_signal_connect (
            store, "folder-unsubscribed",
            G_CALLBACK (store_folder_unsubscribed_cb), cache);
        store_info->folder_unsubscribed_handler_id = handler_id;
    }

    g_mutex_lock (&cache->priv->store_info_ht_lock);

    g_hash_table_insert (
        cache->priv->store_info_ht,
        g_object_ref (store),
        store_info_ref (store_info));

    g_mutex_unlock (&cache->priv->store_info_ht_lock);

    return store_info;
}

static StoreInfo *
mail_folder_cache_ref_store_info (MailFolderCache *cache,
                                  CamelStore *store)
{
    GHashTable *store_info_ht;
    StoreInfo *store_info;

    g_return_val_if_fail (store != NULL, NULL);

    g_mutex_lock (&cache->priv->store_info_ht_lock);

    store_info_ht = cache->priv->store_info_ht;

    store_info = g_hash_table_lookup (store_info_ht, store);
    if (store_info != NULL)
        store_info_ref (store_info);

    g_mutex_unlock (&cache->priv->store_info_ht_lock);

    return store_info;
}

static GList *
mail_folder_cache_list_stores (MailFolderCache *cache)
{
    GHashTable *store_info_ht;
    GList *list;

    g_mutex_lock (&cache->priv->store_info_ht_lock);

    store_info_ht = cache->priv->store_info_ht;

    list = g_hash_table_get_keys (store_info_ht);
    g_list_foreach (list, (GFunc) g_object_ref, NULL);

    g_mutex_unlock (&cache->priv->store_info_ht_lock);

    return list;
}

static StoreInfo *
mail_folder_cache_steal_store_info (MailFolderCache *cache,
                                    CamelStore *store)
{
    GHashTable *store_info_ht;
    StoreInfo *store_info;

    g_return_val_if_fail (store != NULL, NULL);

    g_mutex_lock (&cache->priv->store_info_ht_lock);

    store_info_ht = cache->priv->store_info_ht;

    store_info = g_hash_table_lookup (store_info_ht, store);
    if (store_info != NULL) {
        store_info_ref (store_info);
        g_hash_table_remove (store_info_ht, store);
    }

    g_mutex_unlock (&cache->priv->store_info_ht_lock);

    return store_info;
}

static FolderInfo *
mail_folder_cache_ref_folder_info (MailFolderCache *cache,
                                   CamelStore *store,
                                   const gchar *folder_name)
{
    StoreInfo *store_info;
    FolderInfo *folder_info = NULL;

    store_info = mail_folder_cache_ref_store_info (cache, store);
    if (store_info != NULL) {
        folder_info = store_info_ref_folder_info (
            store_info, folder_name);
        store_info_unref (store_info);
    }

    return folder_info;
}

static FolderInfo *
mail_folder_cache_steal_folder_info (MailFolderCache *cache,
                                     CamelStore *store,
                                     const gchar *folder_name)
{
    StoreInfo *store_info;
    FolderInfo *folder_info = NULL;

    store_info = mail_folder_cache_ref_store_info (cache, store);
    if (store_info != NULL) {
        folder_info = store_info_steal_folder_info (
            store_info, folder_name);
        store_info_unref (store_info);
    }

    return folder_info;
}

static gboolean
mail_folder_cache_update_idle_cb (gpointer user_data)
{
    MailFolderCache *cache;
    UpdateClosure *closure;

    closure = (UpdateClosure *) user_data;

    /* Sanity checks. */
    g_return_val_if_fail (closure->full_name != NULL, FALSE);

    cache = g_weak_ref_get (&closure->cache);

    if (cache != NULL) {
        if (closure->signal_id == signals[FOLDER_DELETED]) {
            g_signal_emit (
                cache,
                closure->signal_id, 0,
                closure->store,
                closure->full_name);
        }

        if (closure->signal_id == signals[FOLDER_UNAVAILABLE]) {
            g_signal_emit (
                cache,
                closure->signal_id, 0,
                closure->store,
                closure->full_name);
        }

        if (closure->signal_id == signals[FOLDER_AVAILABLE]) {
            g_signal_emit (
                cache,
                closure->signal_id, 0,
                closure->store,
                closure->full_name);
        }

        if (closure->signal_id == signals[FOLDER_RENAMED]) {
            g_signal_emit (
                cache,
                closure->signal_id, 0,
                closure->store,
                closure->oldfull,
                closure->full_name);
        }

        /* update unread counts */
        g_signal_emit (
            cache,
            signals[FOLDER_UNREAD_UPDATED], 0,
            closure->store,
            closure->full_name,
            closure->unread);

        /* XXX The old code excluded this on FOLDER_RENAMED.
         *     Not sure if that was intentional (if so it was
         *     very subtle!) but we'll preserve the behavior.
         *     If it turns out to be a bug then just remove
         *     the signal_id check. */
        if (closure->signal_id != signals[FOLDER_RENAMED]) {
            g_signal_emit (
                cache,
                signals[FOLDER_CHANGED], 0,
                closure->store,
                closure->full_name,
                closure->new_messages,
                closure->msg_uid,
                closure->msg_sender,
                closure->msg_subject);
        }

        if (CAMEL_IS_VEE_STORE (closure->store) &&
           (closure->signal_id == signals[FOLDER_AVAILABLE] ||
            closure->signal_id == signals[FOLDER_RENAMED])) {
            /* Normally the vfolder store takes care of the
             * folder_opened event itself, but we add folder to
             * the noting system later, thus we do not know about
             * search folders to update them in a tree, thus
             * ensure their changes will be tracked correctly. */
            CamelFolder *folder;

            /* FIXME camel_store_get_folder_sync() may block. */
            folder = camel_store_get_folder_sync (
                closure->store,
                closure->full_name,
                0, NULL, NULL);

            if (folder != NULL) {
                mail_folder_cache_note_folder (cache, folder);
                g_object_unref (folder);
            }
        }

        g_object_unref (cache);
    }

    return FALSE;
}

static void
mail_folder_cache_submit_update (UpdateClosure *closure)
{
    GMainContext *main_context;
    MailFolderCache *cache;
    GSource *idle_source;

    g_return_if_fail (closure != NULL);

    cache = g_weak_ref_get (&closure->cache);
    g_return_if_fail (cache != NULL);

    main_context = mail_folder_cache_ref_main_context (cache);

    idle_source = g_idle_source_new ();
    g_source_set_callback (
        idle_source,
        mail_folder_cache_update_idle_cb,
        closure,
        (GDestroyNotify) update_closure_free);
    g_source_attach (idle_source, main_context);
    g_source_unref (idle_source);

    g_main_context_unref (main_context);

    g_object_unref (cache);
}

/* This is how unread counts work (and don't work):
 *
 * camel_folder_unread_message_count() only gives a correct answer if
 * the store is paying attention to the folder. (Some stores always
 * pay attention to all folders, but IMAP can only pay attention to
 * one folder at a time.) But it doesn't have any way to know when
 * it's lying, so it's only safe to call it when you know for sure
 * that the store is paying attention to the folder, such as when it's
 * just been created, or you get a folder_changed signal on it.
 *
 * camel_store_get_folder_info() always gives correct answers for the
 * folders it checks, but it can also return -1 for a folder, meaning
 * it didn't check, and so you should stick with your previous answer.
 *
 * update_1folder is called from three places: with info != NULL when
 * the folder is created (or get_folder_info), with info == NULL when
 * a folder changed event is emitted.
 *
 * So if info is NULL, camel_folder_unread_message_count is correct,
 * and if it's not NULL and its unread_message_count isn't -1, then
 * it's correct.  */

static void
update_1folder (MailFolderCache *cache,
                FolderInfo *folder_info,
                gint new_messages,
                const gchar *msg_uid,
                const gchar *msg_sender,
                const gchar *msg_subject,
                CamelFolderInfo *info)
{
    ESourceRegistry *registry;
    CamelService *service;
    CamelSession *session;
    CamelFolder *folder;
    gint unread = -1;
    gint deleted;

    /* XXX This is a dirty way to obtain the ESourceRegistry,
     *     but it avoids MailFolderCache requiring it up front
     *     in mail_folder_cache_new(), which just complicates
     *     application startup even more. */
    service = CAMEL_SERVICE (folder_info->store);
    session = camel_service_get_session (service);
    registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
    g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));

    folder = g_weak_ref_get (&folder_info->folder);

    if (folder != NULL) {
        gboolean folder_is_sent;
        gboolean folder_is_drafts;
        gboolean folder_is_outbox;
        gboolean folder_is_vtrash;
        gboolean special_case;

        folder_is_sent = em_utils_folder_is_sent (registry, folder);
        folder_is_drafts = em_utils_folder_is_drafts (registry, folder);
        folder_is_outbox = em_utils_folder_is_outbox (registry, folder);
        folder_is_vtrash = CAMEL_IS_VTRASH_FOLDER (folder);

        special_case =
            (cache->priv->count_trash && folder_is_vtrash) ||
            (cache->priv->count_sent && folder_is_sent) ||
            folder_is_drafts || folder_is_outbox;

        if (special_case) {
            d (printf (" total count\n"));
            unread = camel_folder_get_message_count (folder);
            if (folder_is_drafts || folder_is_outbox) {
                guint32 junked = 0;

                deleted = camel_folder_get_deleted_message_count (folder);
                if (deleted > 0)
                    unread -= deleted;

                junked = camel_folder_summary_get_junk_count (folder->summary);
                if (junked > 0)
                    unread -= junked;
            }
        } else {
            d (printf (" unread count\n"));
            if (info)
                unread = info->unread;
            else
                unread = camel_folder_get_unread_message_count (folder);
        }

        g_object_unref (folder);
    }

    d (printf (
        "folder updated: unread %d: '%s'\n",
        unread, folder_info->full_name));

    if (unread >= 0) {
        UpdateClosure *up;

        up = update_closure_new (cache, folder_info->store);
        up->full_name = g_strdup (folder_info->full_name);
        up->unread = unread;
        up->new_messages = new_messages;
        up->msg_uid = g_strdup (msg_uid);
        up->msg_sender = g_strdup (msg_sender);
        up->msg_subject = g_strdup (msg_subject);

        mail_folder_cache_submit_update (up);
    }
}

static void
folder_changed_cb (CamelFolder *folder,
                   CamelFolderChangeInfo *changes,
                   MailFolderCache *cache)
{
    static GHashTable *last_newmail_per_folder = NULL;
    time_t latest_received, new_latest_received;
    CamelFolder *local_drafts;
    CamelFolder *local_outbox;
    CamelFolder *local_sent;
    CamelSession *session;
    CamelStore *parent_store;
    CamelMessageInfo *info;
    FolderInfo *folder_info;
    const gchar *full_name;
    gint new = 0;
    gint i;
    guint32 flags;
    gchar *uid = NULL, *sender = NULL, *subject = NULL;

    full_name = camel_folder_get_full_name (folder);
    parent_store = camel_folder_get_parent_store (folder);
    session = camel_service_ref_session (CAMEL_SERVICE (parent_store));

    if (!last_newmail_per_folder)
        last_newmail_per_folder = g_hash_table_new (g_direct_hash, g_direct_equal);

    /* it's fine to hash them by folder pointer here */
    latest_received = GPOINTER_TO_INT (
        g_hash_table_lookup (last_newmail_per_folder, folder));
    new_latest_received = latest_received;

    local_drafts = e_mail_session_get_local_folder (
        E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_DRAFTS);
    local_outbox = e_mail_session_get_local_folder (
        E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_OUTBOX);
    local_sent = e_mail_session_get_local_folder (
        E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_SENT);

    if (!CAMEL_IS_VEE_FOLDER (folder)
        && folder != local_drafts
        && folder != local_outbox
        && folder != local_sent
        && changes && (changes->uid_added->len > 0)) {
        /* for each added message, check to see that it is
         * brand new, not junk and not already deleted */
        for (i = 0; i < changes->uid_added->len; i++) {
            info = camel_folder_get_message_info (
                folder, changes->uid_added->pdata[i]);
            if (info) {
                flags = camel_message_info_flags (info);
                if (((flags & CAMEL_MESSAGE_SEEN) == 0) &&
                    ((flags & CAMEL_MESSAGE_JUNK) == 0) &&
                    ((flags & CAMEL_MESSAGE_DELETED) == 0) &&
                    (camel_message_info_date_received (info) > latest_received)) {
                    if (camel_message_info_date_received (info) > new_latest_received)
                        new_latest_received = camel_message_info_date_received (info);
                    new++;
                    if (new == 1) {
                        uid = g_strdup (camel_message_info_uid (info));
                        sender = g_strdup (camel_message_info_from (info));
                        subject = g_strdup (camel_message_info_subject (info));
                    } else {
                        g_free (uid);
                        g_free (sender);
                        g_free (subject);

                        uid = NULL;
                        sender = NULL;
                        subject = NULL;
                    }
                }
                camel_folder_free_message_info (folder, info);
            }
        }
    }

    if (new > 0)
        g_hash_table_insert (
            last_newmail_per_folder, folder,
            GINT_TO_POINTER (new_latest_received));

    folder_info = mail_folder_cache_ref_folder_info (
        cache, parent_store, full_name);
    if (folder_info != NULL) {
        update_1folder (
            cache, folder_info, new,
            uid, sender, subject, NULL);
        folder_info_unref (folder_info);
    }

    g_free (uid);
    g_free (sender);
    g_free (subject);

    g_object_unref (session);
}

static void
unset_folder_info (MailFolderCache *cache,
                   FolderInfo *folder_info,
                   gint delete)
{
    d (printf ("unset folderinfo '%s'\n", folder_info->uri));

    folder_info_clear_folder (folder_info);

    if ((folder_info->flags & CAMEL_FOLDER_NOSELECT) == 0) {
        UpdateClosure *up;

        up = update_closure_new (cache, folder_info->store);
        up->full_name = g_strdup (folder_info->full_name);

        if (delete)
            up->signal_id = signals[FOLDER_DELETED];
        else
            up->signal_id = signals[FOLDER_UNAVAILABLE];

        mail_folder_cache_submit_update (up);
    }
}

static void
setup_folder (MailFolderCache *cache,
              CamelFolderInfo *fi,
              StoreInfo *store_info)
{
    FolderInfo *folder_info;

    folder_info = store_info_ref_folder_info (store_info, fi->full_name);
    if (folder_info != NULL) {
        update_1folder (cache, folder_info, 0, NULL, NULL, NULL, fi);
        folder_info_unref (folder_info);
    } else {
        UpdateClosure *up;

        folder_info = folder_info_new (
            store_info->store,
            fi->full_name,
            fi->flags);

        store_info_insert_folder_info (store_info, folder_info);

        up = update_closure_new (cache, store_info->store);
        up->full_name = g_strdup (fi->full_name);
        up->unread = fi->unread;

        if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0)
            up->signal_id = signals[FOLDER_AVAILABLE];

        mail_folder_cache_submit_update (up);

        folder_info_unref (folder_info);
    }
}

static void
create_folders (MailFolderCache *cache,
                CamelFolderInfo *fi,
                StoreInfo *store_info)
{
    while (fi) {
        setup_folder (cache, fi, store_info);

        if (fi->child)
            create_folders (cache, fi->child, store_info);

        fi = fi->next;
    }
}

static void
store_folder_subscribed_cb (CamelStore *store,
                            CamelFolderInfo *info,
                            MailFolderCache *cache)
{
    StoreInfo *store_info;

    store_info = mail_folder_cache_ref_store_info (cache, store);
    if (store_info != NULL) {
        setup_folder (cache, info, store_info);
        store_info_unref (store_info);
    }
}

static void
store_folder_created_cb (CamelStore *store,
                         CamelFolderInfo *info,
                         MailFolderCache *cache)
{
    /* We only want created events to do more work
     * if we dont support subscriptions. */
    if (!CAMEL_IS_SUBSCRIBABLE (store))
        store_folder_subscribed_cb (store, info, cache);
}

static void
store_folder_opened_cb (CamelStore *store,
                        CamelFolder *folder,
                        MailFolderCache *cache)
{
    mail_folder_cache_note_folder (cache, folder);
}

static void
store_folder_unsubscribed_cb (CamelStore *store,
                              CamelFolderInfo *info,
                              MailFolderCache *cache)
{
    FolderInfo *folder_info;

    folder_info = mail_folder_cache_steal_folder_info (
        cache, store, info->full_name);
    if (folder_info != NULL) {
        unset_folder_info (cache, folder_info, TRUE);
        folder_info_unref (folder_info);
    }
}

static void
store_folder_deleted_cb (CamelStore *store,
                         CamelFolderInfo *info,
                         MailFolderCache *cache)
{
    /* We only want deleted events to do more work
     * if we dont support subscriptions. */
    if (!CAMEL_IS_SUBSCRIBABLE (store))
        store_folder_unsubscribed_cb (store, info, cache);
}

static void
rename_folders (MailFolderCache *cache,
                StoreInfo *store_info,
                const gchar *oldbase,
                const gchar *newbase,
                CamelFolderInfo *fi)
{
    gchar *old, *olduri, *oldfile, *newuri, *newfile;
    FolderInfo *old_folder_info;
    FolderInfo *new_folder_info;
    UpdateClosure *up;
    const gchar *config_dir;

    up = update_closure_new (cache, store_info->store);
    up->signal_id = signals[FOLDER_AVAILABLE];

    /* Form what was the old name, and try and look it up */
    old = g_strdup_printf ("%s%s", oldbase, fi->full_name + strlen (newbase));
    old_folder_info = store_info_steal_folder_info (store_info, old);
    if (old_folder_info != NULL) {
        up->oldfull = g_strdup (old_folder_info->full_name);
        up->signal_id = signals[FOLDER_RENAMED];
        folder_info_unref (old_folder_info);
    }

    new_folder_info = folder_info_new (
        store_info->store,
        fi->full_name,
        fi->flags);

    store_info_insert_folder_info (store_info, new_folder_info);

    folder_info_unref (new_folder_info);

    up->full_name = g_strdup (fi->full_name);
    up->unread = fi->unread==-1 ? 0 : fi->unread;

    /* No signal emission for NOSELECT folders. */
    if ((fi->flags & CAMEL_FOLDER_NOSELECT) != 0)
        up->signal_id = 0;

    mail_folder_cache_submit_update (up);

    /* rename the meta-data we maintain ourselves */
    config_dir = mail_session_get_config_dir ();
    olduri = e_mail_folder_uri_build (store_info->store, old);
    e_filename_make_safe (olduri);
    newuri = e_mail_folder_uri_build (store_info->store, fi->full_name);
    e_filename_make_safe (newuri);
    oldfile = g_strdup_printf ("%s/custom_view-%s.xml", config_dir, olduri);
    newfile = g_strdup_printf ("%s/custom_view-%s.xml", config_dir, newuri);
    g_rename (oldfile, newfile);
    g_free (oldfile);
    g_free (newfile);
    oldfile = g_strdup_printf ("%s/current_view-%s.xml", config_dir, olduri);
    newfile = g_strdup_printf ("%s/current_view-%s.xml", config_dir, newuri);
    g_rename (oldfile, newfile);
    g_free (oldfile);
    g_free (newfile);
    g_free (olduri);
    g_free (newuri);

    g_free (old);
}

static void
get_folders (CamelFolderInfo *fi,
             GPtrArray *folders)
{
    while (fi) {
        g_ptr_array_add (folders, fi);

        if (fi->child)
            get_folders (fi->child, folders);

        fi = fi->next;
    }
}

static gint
folder_cmp (gconstpointer ap,
            gconstpointer bp)
{
    const CamelFolderInfo *a = ((CamelFolderInfo **) ap)[0];
    const CamelFolderInfo *b = ((CamelFolderInfo **) bp)[0];

    return strcmp (a->full_name, b->full_name);
}

static void
store_folder_renamed_cb (CamelStore *store,
                         const gchar *old_name,
                         CamelFolderInfo *info,
                         MailFolderCache *cache)
{
    StoreInfo *store_info;

    store_info = mail_folder_cache_ref_store_info (cache, store);
    if (store_info != NULL) {
        GPtrArray *folders = g_ptr_array_new ();
        CamelFolderInfo *top;
        gint ii;

        /* Ok, so for some reason the folderinfo we have comes in all
         * messed up from imap, should find out why ... this makes it
         * workable.
         * XXX This refers to the old IMAP backend, not IMAPX, and so
         *     this may not be needed anymore. */
        get_folders (info, folders);
        g_ptr_array_sort (folders, (GCompareFunc) folder_cmp);

        top = folders->pdata[0];
        for (ii = 0; ii < folders->len; ii++) {
            rename_folders (
                cache, store_info, old_name,
                top->full_name, folders->pdata[ii]);
        }

        g_ptr_array_free (folders, TRUE);

        store_info_unref (store_info);
    }
}

struct _ping_store_msg {
    MailMsg base;
    CamelStore *store;
};

static gchar *
ping_store_desc (struct _ping_store_msg *m)
{
    gchar *service_name;
    gchar *msg;

    service_name = camel_service_get_name (CAMEL_SERVICE (m->store), TRUE);
    msg = g_strdup_printf (_("Pinging %s"), service_name);
    g_free (service_name);

    return msg;
}

static void
ping_store_exec (struct _ping_store_msg *m,
                 GCancellable *cancellable,
                 GError **error)
{
    CamelServiceConnectionStatus status;
    CamelService *service;
    gboolean online = FALSE;

    service = CAMEL_SERVICE (m->store);
    status = camel_service_get_connection_status (service);

    if (status == CAMEL_SERVICE_CONNECTED) {
        if (CAMEL_IS_DISCO_STORE (m->store) &&
            camel_disco_store_status (
            CAMEL_DISCO_STORE (m->store)) !=CAMEL_DISCO_STORE_OFFLINE)
            online = TRUE;
        else if (CAMEL_IS_OFFLINE_STORE (m->store) &&
            camel_offline_store_get_online (
            CAMEL_OFFLINE_STORE (m->store)))
            online = TRUE;
    }
    if (online)
        camel_store_noop_sync (m->store, cancellable, error);
}

static void
ping_store_free (struct _ping_store_msg *m)
{
    g_object_unref (m->store);
}

static MailMsgInfo ping_store_info = {
    sizeof (struct _ping_store_msg),
    (MailMsgDescFunc) ping_store_desc,
    (MailMsgExecFunc) ping_store_exec,
    (MailMsgDoneFunc) NULL,
    (MailMsgFreeFunc) ping_store_free
};

static gboolean
ping_cb (MailFolderCache *cache)
{
    GList *list, *link;

    list = mail_folder_cache_list_stores (cache);

    for (link = list; link != NULL; link = g_list_next (link)) {
        CamelService *service;
        CamelServiceConnectionStatus status;
        struct _ping_store_msg *m;

        service = CAMEL_SERVICE (link->data);
        status = camel_service_get_connection_status (service);

        if (status != CAMEL_SERVICE_CONNECTED)
            continue;

        m = mail_msg_new (&ping_store_info);
        m->store = g_object_ref (service);

        mail_msg_slow_ordered_push (m);
    }

    g_list_free_full (list, (GDestroyNotify) g_object_unref);

    return TRUE;
}

static gboolean
store_has_folder_hierarchy (CamelStore *store)
{
    CamelProvider *provider;

    g_return_val_if_fail (store != NULL, FALSE);

    provider = camel_service_get_provider (CAMEL_SERVICE (store));
    g_return_val_if_fail (provider != NULL, FALSE);

    if (provider->flags & CAMEL_PROVIDER_IS_STORAGE)
        return TRUE;

    if (provider->flags & CAMEL_PROVIDER_IS_EXTERNAL)
        return TRUE;

    return FALSE;
}

static GList *
find_folder_uri (GQueue *queue,
                 CamelSession *session,
                 const gchar *folder_uri)
{
    GList *head, *link;

    head = g_queue_peek_head_link (queue);

    for (link = head; link != NULL; link = g_list_next (link))
        if (e_mail_folder_uri_equal (session, link->data, folder_uri))
            break;

    return link;
}

static void
mail_folder_cache_get_property (GObject *object,
                                guint property_id,
                                GValue *value,
                                GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_MAIN_CONTEXT:
            g_value_take_boxed (
                value,
                mail_folder_cache_ref_main_context (
                MAIL_FOLDER_CACHE (object)));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
mail_folder_cache_dispose (GObject *object)
{
    MailFolderCachePrivate *priv;

    priv = MAIL_FOLDER_CACHE_GET_PRIVATE (object);

    g_hash_table_remove_all (priv->store_info_ht);

    /* Chain up to parent's dispose() method. */
    G_OBJECT_CLASS (mail_folder_cache_parent_class)->dispose (object);
}

static void
mail_folder_cache_finalize (GObject *object)
{
    MailFolderCachePrivate *priv;

    priv = MAIL_FOLDER_CACHE_GET_PRIVATE (object);

    g_main_context_unref (priv->main_context);

    g_hash_table_destroy (priv->store_info_ht);
    g_mutex_clear (&priv->store_info_ht_lock);

    if (priv->ping_id > 0) {
        g_source_remove (priv->ping_id);
        priv->ping_id = 0;
    }

    while (!g_queue_is_empty (&priv->local_folder_uris))
        g_free (g_queue_pop_head (&priv->local_folder_uris));

    while (!g_queue_is_empty (&priv->remote_folder_uris))
        g_free (g_queue_pop_head (&priv->remote_folder_uris));

    /* Chain up to parent's finalize() method. */
    G_OBJECT_CLASS (mail_folder_cache_parent_class)->finalize (object);
}

static void
mail_folder_cache_folder_available (MailFolderCache *cache,
                                    CamelStore *store,
                                    const gchar *folder_name)
{
    CamelService *service;
    CamelSession *session;
    CamelProvider *provider;
    GQueue *queue;
    gchar *folder_uri;

    /* Disregard virtual stores. */
    if (CAMEL_IS_VEE_STORE (store))
        return;

    /* Disregard virtual Junk folders. */
    if (store->flags & CAMEL_STORE_VJUNK)
        if (g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)
            return;

    /* Disregard virtual Trash folders. */
    if (store->flags & CAMEL_STORE_VTRASH)
        if (g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0)
            return;

    service = CAMEL_SERVICE (store);
    session = camel_service_ref_session (service);
    provider = camel_service_get_provider (service);

    /* Reuse the store info lock just because it's handy. */
    g_mutex_lock (&cache->priv->store_info_ht_lock);

    folder_uri = e_mail_folder_uri_build (store, folder_name);

    if (provider->flags & CAMEL_PROVIDER_IS_REMOTE)
        queue = &cache->priv->remote_folder_uris;
    else
        queue = &cache->priv->local_folder_uris;

    if (find_folder_uri (queue, session, folder_uri) == NULL)
        g_queue_push_tail (queue, folder_uri);
    else
        g_free (folder_uri);

    g_mutex_unlock (&cache->priv->store_info_ht_lock);

    g_object_unref (session);
}

static void
mail_folder_cache_folder_unavailable (MailFolderCache *cache,
                                      CamelStore *store,
                                      const gchar *folder_name)
{
    CamelService *service;
    CamelSession *session;
    CamelProvider *provider;
    GQueue *queue;
    GList *link;
    gchar *folder_uri;

    /* Disregard virtual stores. */
    if (CAMEL_IS_VEE_STORE (store))
        return;

    /* Disregard virtual Junk folders. */
    if (store->flags & CAMEL_STORE_VJUNK)
        if (g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)
            return;

    /* Disregard virtual Trash folders. */
    if (store->flags & CAMEL_STORE_VTRASH)
        if (g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0)
            return;

    service = CAMEL_SERVICE (store);
    session = camel_service_ref_session (service);
    provider = camel_service_get_provider (service);

    /* Reuse the store info lock just because it's handy. */
    g_mutex_lock (&cache->priv->store_info_ht_lock);

    folder_uri = e_mail_folder_uri_build (store, folder_name);

    if (provider->flags & CAMEL_PROVIDER_IS_REMOTE)
        queue = &cache->priv->remote_folder_uris;
    else
        queue = &cache->priv->local_folder_uris;

    link = find_folder_uri (queue, session, folder_uri);
    if (link != NULL) {
        g_free (link->data);
        g_queue_delete_link (queue, link);
    }

    g_free (folder_uri);

    g_mutex_unlock (&cache->priv->store_info_ht_lock);

    g_object_unref (session);
}

static void
mail_folder_cache_folder_deleted (MailFolderCache *cache,
                                  CamelStore *store,
                                  const gchar *folder_name)
{
    CamelService *service;
    CamelSession *session;
    GQueue *queue;
    GList *link;
    gchar *folder_uri;

    /* Disregard virtual stores. */
    if (CAMEL_IS_VEE_STORE (store))
        return;

    /* Disregard virtual Junk folders. */
    if (store->flags & CAMEL_STORE_VJUNK)
        if (g_strcmp0 (folder_name, CAMEL_VJUNK_NAME) == 0)
            return;

    /* Disregard virtual Trash folders. */
    if (store->flags & CAMEL_STORE_VTRASH)
        if (g_strcmp0 (folder_name, CAMEL_VTRASH_NAME) == 0)
            return;

    service = CAMEL_SERVICE (store);
    session = camel_service_ref_session (service);

    /* Reuse the store info lock just because it's handy. */
    g_mutex_lock (&cache->priv->store_info_ht_lock);

    folder_uri = e_mail_folder_uri_build (store, folder_name);

    queue = &cache->priv->local_folder_uris;
    link = find_folder_uri (queue, session, folder_uri);
    if (link != NULL) {
        g_free (link->data);
        g_queue_delete_link (queue, link);
    }

    queue = &cache->priv->remote_folder_uris;
    link = find_folder_uri (queue, session, folder_uri);
    if (link != NULL) {
        g_free (link->data);
        g_queue_delete_link (queue, link);
    }

    g_free (folder_uri);

    g_mutex_unlock (&cache->priv->store_info_ht_lock);

    g_object_unref (session);
}

static void
mail_folder_cache_class_init (MailFolderCacheClass *class)
{
    GObjectClass *object_class;

    g_type_class_add_private (class, sizeof (MailFolderCachePrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->get_property = mail_folder_cache_get_property;
    object_class->dispose = mail_folder_cache_dispose;
    object_class->finalize = mail_folder_cache_finalize;

    class->folder_available = mail_folder_cache_folder_available;
    class->folder_unavailable = mail_folder_cache_folder_unavailable;
    class->folder_deleted = mail_folder_cache_folder_deleted;

    g_object_class_install_property (
        object_class,
        PROP_MAIN_CONTEXT,
        g_param_spec_boxed (
            "main-context",
            "Main Context",
            "The main loop context on "
            "which to attach event sources",
            G_TYPE_MAIN_CONTEXT,
            G_PARAM_READABLE |
            G_PARAM_STATIC_STRINGS));

    /**
     * MailFolderCache::folder-available
     * @store: the #CamelStore containing the folder
     * @folder_name: the name of the folder
     *
     * Emitted when a folder becomes available
     **/
    signals[FOLDER_AVAILABLE] = g_signal_new (
        "folder-available",
        G_OBJECT_CLASS_TYPE (object_class),
        G_SIGNAL_RUN_FIRST,
        G_STRUCT_OFFSET (MailFolderCacheClass, folder_available),
        NULL, NULL, NULL,
        G_TYPE_NONE, 2,
        CAMEL_TYPE_STORE,
        G_TYPE_STRING);

    /**
     * MailFolderCache::folder-unavailable
     * @store: the #CamelStore containing the folder
     * @folder_name: the name of the folder
     *
     * Emitted when a folder becomes unavailable.  This represents a
     * transient condition.  See MailFolderCache::folder-deleted to be
     * notified when a folder is permanently removed.
     **/
    signals[FOLDER_UNAVAILABLE] = g_signal_new (
        "folder-unavailable",
        G_OBJECT_CLASS_TYPE (object_class),
        G_SIGNAL_RUN_FIRST,
        G_STRUCT_OFFSET (MailFolderCacheClass, folder_unavailable),
        NULL, NULL, NULL,
        G_TYPE_NONE, 2,
        CAMEL_TYPE_STORE,
        G_TYPE_STRING);

    /**
     * MailFolderCache::folder-deleted
     * @store: the #CamelStore containing the folder
     * @folder_name: the name of the folder
     *
     * Emitted when a folder is deleted
     **/
    signals[FOLDER_DELETED] = g_signal_new (
        "folder-deleted",
        G_OBJECT_CLASS_TYPE (object_class),
        G_SIGNAL_RUN_FIRST,
        G_STRUCT_OFFSET (MailFolderCacheClass, folder_deleted),
        NULL, NULL, /* accumulator */
        NULL,
        G_TYPE_NONE, 2,
        CAMEL_TYPE_STORE,
        G_TYPE_STRING);

    /**
     * MailFolderCache::folder-renamed
     * @store: the #CamelStore containing the folder
     * @old_folder_name: the old name of the folder
     * @new_folder_name: the new name of the folder
     *
     * Emitted when a folder is renamed
     **/
    signals[FOLDER_RENAMED] = g_signal_new (
        "folder-renamed",
        G_OBJECT_CLASS_TYPE (object_class),
        G_SIGNAL_RUN_FIRST,
        G_STRUCT_OFFSET (MailFolderCacheClass, folder_renamed),
        NULL, NULL, NULL,
        G_TYPE_NONE, 3,
        CAMEL_TYPE_STORE,
        G_TYPE_STRING,
        G_TYPE_STRING);

    /**
     * MailFolderCache::folder-unread-updated
     * @store: the #CamelStore containing the folder
     * @folder_name: the name of the folder
     * @unread: the number of unread mails in the folder
     *
     * Emitted when a we receive an update to the unread count for a folder
     **/
    signals[FOLDER_UNREAD_UPDATED] = g_signal_new (
        "folder-unread-updated",
        G_OBJECT_CLASS_TYPE (object_class),
        G_SIGNAL_RUN_FIRST,
        G_STRUCT_OFFSET (MailFolderCacheClass, folder_unread_updated),
        NULL, NULL, NULL,
        G_TYPE_NONE, 3,
        CAMEL_TYPE_STORE,
        G_TYPE_STRING,
        G_TYPE_INT);

    /**
     * MailFolderCache::folder-changed
     * @store: the #CamelStore containing the folder
     * @folder_name: the name of the folder
     * @new_messages: the number of new messages for the folder
     * @msg_uid: uid of the new message, or NULL
     * @msg_sender: sender of the new message, or NULL
     * @msg_subject: subject of the new message, or NULL
     *
     * Emitted when a folder has changed.  If @new_messages is not
     * exactly 1, @msg_uid, @msg_sender, and @msg_subject will be NULL.
     **/
    signals[FOLDER_CHANGED] = g_signal_new (
        "folder-changed",
        G_OBJECT_CLASS_TYPE (object_class),
        G_SIGNAL_RUN_FIRST,
        G_STRUCT_OFFSET (MailFolderCacheClass, folder_changed),
        NULL, NULL, NULL,
        G_TYPE_NONE, 6,
        CAMEL_TYPE_STORE,
        G_TYPE_STRING,
        G_TYPE_INT,
        G_TYPE_STRING,
        G_TYPE_STRING,
        G_TYPE_STRING);
}

static void
mail_folder_cache_init (MailFolderCache *cache)
{
    GHashTable *store_info_ht;
    const gchar *buf;
    guint timeout;

    store_info_ht = g_hash_table_new_full (
        (GHashFunc) g_direct_hash,
        (GEqualFunc) g_direct_equal,
        (GDestroyNotify) g_object_unref,
        (GDestroyNotify) store_info_unref);

    cache->priv = MAIL_FOLDER_CACHE_GET_PRIVATE (cache);
    cache->priv->main_context = g_main_context_ref_thread_default ();

    cache->priv->store_info_ht = store_info_ht;
    g_mutex_init (&cache->priv->store_info_ht_lock);

    cache->priv->count_sent = getenv ("EVOLUTION_COUNT_SENT") != NULL;
    cache->priv->count_trash = getenv ("EVOLUTION_COUNT_TRASH") != NULL;

    buf = getenv ("EVOLUTION_PING_TIMEOUT");
    timeout = buf ? strtoul (buf, NULL, 10) : 600;
    cache->priv->ping_id = g_timeout_add_seconds (
        timeout, (GSourceFunc) ping_cb, cache);

    g_queue_init (&cache->priv->local_folder_uris);
    g_queue_init (&cache->priv->remote_folder_uris);
}

MailFolderCache *
mail_folder_cache_new (void)
{
    return g_object_new (MAIL_TYPE_FOLDER_CACHE, NULL);
}

/**
 * mail_folder_cache_ref_main_context:
 *
 * Returns the #GMainContext on which event sources for @cache are to be
 * attached.
 *
 * The returned #GMainContext is referenced for thread-safety and should
 * be unreferenced with g_main_context_unref() when finished with it.
 *
 * Returns: a #GMainContext
 **/
GMainContext *
mail_folder_cache_ref_main_context (MailFolderCache *cache)
{
    g_return_val_if_fail (MAIL_IS_FOLDER_CACHE (cache), NULL);

    return g_main_context_ref (cache->priv->main_context);
}

/* Helper for mail_folder_cache_note_store() */
static void
mail_folder_cache_first_update (MailFolderCache *cache,
                                StoreInfo *store_info)
{
    CamelService *service;
    CamelSession *session;
    const gchar *uid;

    service = CAMEL_SERVICE (store_info->store);
    session = camel_service_ref_session (service);
    uid = camel_service_get_uid (service);

    if (store_info->vjunk != NULL)
        mail_folder_cache_note_folder (cache, store_info->vjunk);

    if (store_info->vtrash != NULL)
        mail_folder_cache_note_folder (cache, store_info->vtrash);

    /* Some extra work for the "On This Computer" store. */
    if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) {
        CamelFolder *folder;
        gint ii;

        for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
            folder = e_mail_session_get_local_folder (
                E_MAIL_SESSION (session), ii);
            mail_folder_cache_note_folder (cache, folder);
        }
    }

    g_object_unref (session);
}

/* Helper for mail_folder_cache_note_store() */
static void
mail_folder_cache_note_store_thread (GSimpleAsyncResult *simple,
                                     GObject *source_object,
                                     GCancellable *cancellable)
{
    MailFolderCache *cache;
    CamelService *service;
    CamelSession *session;
    StoreInfo *store_info;
    GQueue result_queue = G_QUEUE_INIT;
    AsyncContext *async_context;
    GError *local_error = NULL;

    cache = MAIL_FOLDER_CACHE (source_object);
    async_context = g_simple_async_result_get_op_res_gpointer (simple);
    store_info = async_context->store_info;

    service = CAMEL_SERVICE (store_info->store);
    session = camel_service_ref_session (service);

    /* We might get a race when setting up a store, such that it is
     * still left in offline mode, after we've gone online.  This
     * catches and fixes it up when the shell opens us.
     *
     * XXX This is a Bonobo-era artifact.  Do we really still need
     *     to do this?  Also, CamelDiscoStore needs to die already!
     */
    if (camel_session_get_online (session)) {
        gboolean store_online = TRUE;

        if (CAMEL_IS_DISCO_STORE (service)) {
            CamelDiscoStore *disco_store;
            CamelDiscoStoreStatus status;

            disco_store = CAMEL_DISCO_STORE (service);
            status = camel_disco_store_status (disco_store);
            store_online = (status != CAMEL_DISCO_STORE_OFFLINE);
        }

        if (CAMEL_IS_OFFLINE_STORE (service)) {
            store_online = camel_offline_store_get_online (
                CAMEL_OFFLINE_STORE (service));
        }

        if (!store_online) {
            e_mail_store_go_online_sync (
                CAMEL_STORE (service),
                cancellable, &local_error);

            if (local_error != NULL) {
                g_simple_async_result_take_error (
                    simple, local_error);
                goto exit;
            }
        }
    }

    /* No folder hierarchy means we're done. */
    if (!store_has_folder_hierarchy (store_info->store))
        goto exit;

    /* XXX This can return NULL without setting a GError if no
     *     folders match the search criteria or the store does
     *     not support folders.
     *
     *     The function signature should be changed to return a
     *     boolean with the CamelFolderInfo returned through an
     *     "out" parameter so it's easier to distinguish errors
     *     from empty results.
     */
    async_context->info = camel_store_get_folder_info_sync (
        store_info->store, NULL,
        CAMEL_STORE_FOLDER_INFO_FAST |
        CAMEL_STORE_FOLDER_INFO_RECURSIVE |
        CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
        cancellable, &local_error);

    if (local_error != NULL) {
        g_warn_if_fail (async_context->info == NULL);
        g_simple_async_result_take_error (simple, local_error);
        goto exit;
    }

    create_folders (cache, async_context->info, store_info);

    /* Do some extra work for the first update. */
    if (store_info->first_update) {
        mail_folder_cache_first_update (cache, store_info);
        store_info->first_update = FALSE;
    }

exit:
    /* We don't want finish() functions being invoked while holding a
     * locked mutex, so flush the StoreInfo's queue to a local queue. */
    g_mutex_lock (&store_info->lock);
    e_queue_transfer (&store_info->folderinfo_updates, &result_queue);
    g_mutex_unlock (&store_info->lock);

    while (!g_queue_is_empty (&result_queue)) {
        GSimpleAsyncResult *queued_result;

        queued_result = g_queue_pop_head (&result_queue);

        /* Skip the GSimpleAsyncResult passed into this function.
         * g_simple_async_result_run_in_thread() will complete it
         * for us, and we don't want to complete it twice. */
        if (queued_result != simple)
            g_simple_async_result_complete_in_idle (queued_result);

        g_clear_object (&queued_result);
    }

    g_object_unref (session);
}

/**
 * mail_folder_cache_note_store:
 *
 * Add a store whose folders should appear in the shell The folders are scanned
 * from the store, and/or added at runtime via the folder_created event.  The
 * @done function returns if we can free folder info.
 */
void
mail_folder_cache_note_store (MailFolderCache *cache,
                              CamelStore *store,
                              GCancellable *cancellable,
                              GAsyncReadyCallback callback,
                              gpointer user_data)
{
    StoreInfo *store_info;
    GSimpleAsyncResult *simple;
    AsyncContext *async_context;

    g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
    g_return_if_fail (CAMEL_IS_STORE (store));

    store_info = mail_folder_cache_ref_store_info (cache, store);
    if (store_info == NULL)
        store_info = mail_folder_cache_new_store_info (cache, store);

    async_context = g_slice_new0 (AsyncContext);
    async_context->store_info = store_info_ref (store_info);

    simple = g_simple_async_result_new (
        G_OBJECT (cache), callback, user_data,
        mail_folder_cache_note_store);

    g_simple_async_result_set_check_cancellable (simple, cancellable);

    g_simple_async_result_set_op_res_gpointer (
        simple, async_context, (GDestroyNotify) async_context_free);

    g_mutex_lock (&store_info->lock);

    g_queue_push_tail (
        &store_info->folderinfo_updates,
        g_object_ref (simple));

    /* Queue length > 1 means there's already an operation for
     * this store in progress so we'll just pick up the result
     * when it finishes. */
    if (g_queue_get_length (&store_info->folderinfo_updates) == 1)
        g_simple_async_result_run_in_thread (
            simple,
            mail_folder_cache_note_store_thread,
            G_PRIORITY_DEFAULT, cancellable);

    g_mutex_unlock (&store_info->lock);

    g_object_unref (simple);

    store_info_unref (store_info);
}

gboolean
mail_folder_cache_note_store_finish (MailFolderCache *cache,
                                     GAsyncResult *result,
                                     CamelFolderInfo **out_info,
                                     GError **error)
{
    GSimpleAsyncResult *simple;
    AsyncContext *async_context;

    g_return_val_if_fail (
        g_simple_async_result_is_valid (
        result, G_OBJECT (cache),
        mail_folder_cache_note_store), FALSE);

    simple = G_SIMPLE_ASYNC_RESULT (result);
    async_context = g_simple_async_result_get_op_res_gpointer (simple);

    if (g_simple_async_result_propagate_error (simple, error))
        return FALSE;

    if (out_info != NULL) {
        if (async_context->info != NULL)
            *out_info = camel_folder_info_clone (
                async_context->info);
        else
            *out_info = NULL;
    }

    return TRUE;
}

/**
 * mail_folder_cache_note_folder:
 *
 * When a folder has been opened, notify it for watching.  The folder must have
 * already been created on the store (which has already been noted) before the
 * folder can be opened
 */
void
mail_folder_cache_note_folder (MailFolderCache *cache,
                               CamelFolder *folder)
{
    CamelStore *parent_store;
    CamelFolder *cached_folder;
    FolderInfo *folder_info;
    const gchar *full_name;

    g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
    g_return_if_fail (CAMEL_IS_FOLDER (folder));

    full_name = camel_folder_get_full_name (folder);
    parent_store = camel_folder_get_parent_store (folder);

    folder_info = mail_folder_cache_ref_folder_info (
        cache, parent_store, full_name);

    /* XXX Not sure we should just be returning quietly here, but
     *     the old code did.  Using g_return_if_fail() causes a few
     *     warnings on startup which might be worth tracking down. */
    if (folder_info == NULL)
        return;

    g_mutex_lock (&folder_info->lock);

    cached_folder = g_weak_ref_get (&folder_info->folder);
    if (cached_folder != NULL) {
        g_signal_handler_disconnect (
            cached_folder,
            folder_info->folder_changed_handler_id);
        g_object_unref (cached_folder);
    }

    g_weak_ref_set (&folder_info->folder, folder);

    update_1folder (cache, folder_info, 0, NULL, NULL, NULL, NULL);

    folder_info->folder_changed_handler_id =
        g_signal_connect (
            folder, "changed",
            G_CALLBACK (folder_changed_cb), cache);

    g_mutex_unlock (&folder_info->lock);

    folder_info_unref (folder_info);
}

/**
 * mail_folder_cache_has_folder_info:
 * @cache: a #MailFolderCache
 * @store: a #CamelStore
 * @folder_name: a folder name
 *
 * Returns whether @cache has information about the folder described by
 * @store and @folder_name.  This does not necessarily mean it has the
 * #CamelFolder instance, but it at least has some meta-data about it.
 *
 * You can use this function as a folder existence test.
 *
 * Returns: %TRUE if @cache has folder info, %FALSE otherwise
 **/
gboolean
mail_folder_cache_has_folder_info (MailFolderCache *cache,
                                   CamelStore *store,
                                   const gchar *folder_name)
{
    FolderInfo *folder_info;
    gboolean has_info = FALSE;

    g_return_val_if_fail (MAIL_IS_FOLDER_CACHE (cache), FALSE);
    g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
    g_return_val_if_fail (folder_name != NULL, FALSE);

    folder_info = mail_folder_cache_ref_folder_info (
        cache, store, folder_name);
    if (folder_info != NULL) {
        folder_info_unref (folder_info);
        has_info = TRUE;
    }

    return has_info;
}

/**
 * mail_folder_cache_ref_folder:
 * @cache: a #MailFolderCache
 * @store: a #CamelStore
 * @folder_name: a folder name
 *
 * Returns the #CamelFolder for @store and @folder_name if available, or
 * else %NULL if a #CamelFolder instance is not yet cached.  This function
 * does not block.
 *
 * The returned #CamelFolder is referenced for thread-safety and must be
 * unreferenced with g_object_unref() when finished with it.
 *
 * Returns: a #CamelFolder, or %NULL
 **/
CamelFolder *
mail_folder_cache_ref_folder (MailFolderCache *cache,
                              CamelStore *store,
                              const gchar *folder_name)
{
    FolderInfo *folder_info;
    CamelFolder *folder = NULL;

    g_return_val_if_fail (MAIL_IS_FOLDER_CACHE (cache), NULL);
    g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
    g_return_val_if_fail (folder_name != NULL, NULL);

    folder_info = mail_folder_cache_ref_folder_info (
        cache, store, folder_name);
    if (folder_info != NULL) {
        folder = g_weak_ref_get (&folder_info->folder);
        folder_info_unref (folder_info);
    }

    return folder;
}

/**
 * mail_folder_cache_get_folder_info_flags:
 * @cache: a #MailFolderCache
 * @store: a #CamelStore
 * @folder_name: a folder name
 * @flags: return location for #CamelFolderInfoFlags
 *
 * Obtains #CamelFolderInfoFlags for @store and @folder_name if available,
 * and returns %TRUE to indicate @flags was set.  If no folder information
 * is available for @store and @folder_name, the function returns %FALSE.
 *
 * Returns: whether @flags was set
 **/
gboolean
mail_folder_cache_get_folder_info_flags (MailFolderCache *cache,
                                         CamelStore *store,
                                         const gchar *folder_name,
                                         CamelFolderInfoFlags *flags)
{
    FolderInfo *folder_info;
    gboolean flags_set = FALSE;

    g_return_val_if_fail (MAIL_IS_FOLDER_CACHE (cache), FALSE);
    g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
    g_return_val_if_fail (folder_name != NULL, FALSE);
    g_return_val_if_fail (flags != NULL, FALSE);

    folder_info = mail_folder_cache_ref_folder_info (
        cache, store, folder_name);
    if (folder_info != NULL) {
        *flags = folder_info->flags;
        folder_info_unref (folder_info);
        flags_set = TRUE;
    }

    return flags_set;
}

void
mail_folder_cache_get_local_folder_uris (MailFolderCache *cache,
                                         GQueue *out_queue)
{
    GList *head, *link;

    g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
    g_return_if_fail (out_queue != NULL);

    /* Reuse the store_info_ht_lock just because it's handy. */
    g_mutex_lock (&cache->priv->store_info_ht_lock);

    head = g_queue_peek_head_link (&cache->priv->local_folder_uris);

    for (link = head; link != NULL; link = g_list_next (link))
        g_queue_push_tail (out_queue, g_strdup (link->data));

    g_mutex_unlock (&cache->priv->store_info_ht_lock);
}

void
mail_folder_cache_get_remote_folder_uris (MailFolderCache *cache,
                                          GQueue *out_queue)
{
    GList *head, *link;

    g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
    g_return_if_fail (out_queue != NULL);

    /* Reuse the store_info_ht_lock just because it's handy. */
    g_mutex_lock (&cache->priv->store_info_ht_lock);

    head = g_queue_peek_head_link (&cache->priv->remote_folder_uris);

    for (link = head; link != NULL; link = g_list_next (link))
        g_queue_push_tail (out_queue, g_strdup (link->data));

    g_mutex_unlock (&cache->priv->store_info_ht_lock);
}

void
mail_folder_cache_service_removed (MailFolderCache *cache,
                                   CamelService *service)
{
    StoreInfo *store_info;

    g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
    g_return_if_fail (CAMEL_IS_SERVICE (service));

    if (!CAMEL_IS_STORE (service))
        return;

    store_info = mail_folder_cache_steal_store_info (
        cache, CAMEL_STORE (service));
    if (store_info != NULL) {
        GList *list, *link;

        list = store_info_list_folder_info (store_info);

        for (link = list; link != NULL; link = g_list_next (link))
            unset_folder_info (cache, link->data, FALSE);

        g_list_free_full (list, (GDestroyNotify) folder_info_unref);

        store_info_unref (store_info);
    }
}

void
mail_folder_cache_service_enabled (MailFolderCache *cache,
                                   CamelService *service)
{
    g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
    g_return_if_fail (CAMEL_IS_SERVICE (service));

    /* XXX This has no callback and it swallows errors.  Maybe
     *     we don't want a service_enabled() function after all?
     *     Call mail_folder_cache_note_store() directly instead
     *     and handle errors appropriately. */
    mail_folder_cache_note_store (
        cache, CAMEL_STORE (service), NULL, NULL, NULL);
}

void
mail_folder_cache_service_disabled (MailFolderCache *cache,
                                    CamelService *service)
{
    g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
    g_return_if_fail (CAMEL_IS_SERVICE (service));

    /* To the folder cache, disabling a service is the same as
     * removing it.  We keep a separate callback function only
     * to use as a breakpoint target in a debugger. */
    mail_folder_cache_service_removed (cache, service);
}