aboutsummaryrefslogblamecommitdiffstats
path: root/composer/e-msg-composer.c
blob: f73960e3fa6f80da391490160bf78be5ee822d2b (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                                                           
  


                                                                           
  



                                                                             
  

                                                                           
  
  
           





                                                        
  

   
                    


                   
                   
                   
                      
                     
                     
                   

                  
                            
 
                               
 





                                             







                                          
 
                        

                                   
 




                                             




                               

                                
 

                                   









                                                         
                
 
      
               

                    

                           


      
                
             

                       
              


                   

















                                                                        
                                  
 
                                                       



                                                                           
 
                                                

                                                                           
                                                                         
                                                                           


                                                                           
                                                                         
                                                                           


                                                                          
                                                                         
                                                                           


                                                                           
                                                                         
                                                                           
                                                             
 
                         

                       
                        
                                                        
 














                                                          

                                                 












                                                             










                                                                         
              
                                          
                                       
                               
                                           
                                             
 
                              
                              
                    


                                  
                         

                          
                                    


                                                   
 

                                                        
                                                            
 
                                         
                                             



                                                                 




                                                                         
                                           
         
                                      
                                






                                                    
                                                         
 
                                                
                                                                                 
                                       
                          
                                                                  
 
                                      
                                             
                                                                
 

                                   
 





                                                          
 

                                     
 
                                
 


                    





                                                          
                                                                                  
                                                             
                                                                                               
 
                                                  
                                                                                 



                    


                                              

                             
                   
 

                                         
 

                            
 







                                                      
 


                     
                      




                                                           

                   
               

                                                  




















                                                                   
                            

                                    
 


                                          
                   
 

                          
 
                                                 

                               
 
                                 



                                         
                                                                                       
                                                        
                                               

                                        
                                                         
                               
 
                                               
                          
 


                                                     
                                                    
                                          
                                                               
            
                                                      

 
              


                                              
 
                             
 
                                
                                                    
                                                      
                            
 
                                                                  
                                                         
                            
                                                  
 
                                                                     
                                                    

                                                 
                                          
 
                                                  


                                                     
                                                         

                            
 
                                                 
 
                                  

 
                                                                              
                          
   


                                                 



                                                    








                                          

                                                












                                                                                  

                                                                                          












                                                                                  

                                                                                          





                                                                     

                                                                                            


                 




                                                        


                                                                         

                                                                               











                                                                                       


                                  



                                              
                                                 


                                         
                                
                        

                              
                         

                                                        
                                                           


                                                           
                                                               
                                                                 
 

                                                              
                                                          
 
                             
                                    
                                           


                                            
                                         














                                                                                
 


                                                

                                                                 



                                                                            
                                                                               
                                       
                        
                                                                    
                 
                                      
 

                                                     
                                                                   

                                     
                                                
                                                                           

                                           
                                                      
                                                                             

                                        









                                                                                
                                                                        
 
                                      


                            


                                                     





                                                                           
                                                                           





                                          
                   
                                                                             
 
                                 


                                                     
                                    


                                                         
                                                











                                                                       


















                                                                       


                                                  
 


























                                                                              
                                  


                                   




                                       




                                                                          






                                                                               













                                                                
                                                      
                                            
                                                             







                                                                  

                                                                  

                                                          

                                                                            















                                                              

                                 


                                                             

                                                             

                                                    
                                                       

                                                                  

                                                                  

                                                             
                                                                



                                                              
                                                          



























                                                                      
                                

                                   




                                            





                                                                             
 














                                                                             
                                  

                                                

                                     

                                                   




                                                                

                                                         






                                                                     

                                                            





























                                                                                    

                                                              

                                                             

                                                              


                                                          

                                                                            
















                                                              


                                                             

                                                               
                                                         



                                                                    
                                                











                                                             
                                    

























































                                                                                                 
                                                                 




                                                                           
                                                                 





                       


















                                                                  








































                                                            

                                
                                    
                               
                                
                                    
                                          

                                  
                                    

                               
                                
                            
                         
                        

                                     
               
 
                              
                                                           

                                                             
 
                                                                        
                                                                          

                                          



                                                           
                                                            
                                                                 

                                                           

                                           
 

                                              
 

                                             
 

                                                








                                                              

                                                                          




                                                                      
                                             




                                                                         
         
 

                                                     














                                                                                                 

                                                                   

                                                                          
 

                                                                  
                                         


                                                          
         
 



                                                                    

                                                         








                                                                              
         
 

                                                     
                                         


                                                        
                          

                                                            
 

                                                                   

                                                        

                                                              





                                                        


































                                                                           





                                                                           
                        




                                                                                

                                              

                         
 
                                           




                                                                   
                
                            

                                      
 

                                                              
                                           

                                                                           
                              
 
                                                                


                                                                       
                                                                                
                                                                           

                                         
         
 
                  


                                                                 
 


                                                                              
 


                                                                               
 


                                                                       
                                                       
                                                             
                                
 







                                                                               
 
                                        
 















                                                                        

                                                        
                            
                            
                             
                                    


                                      
 

                                                              
                                                                                                   
 
                                           

                                                               
                                                                            
                                                                           

                              


                                                                         
 

                                                                      
 


                                                                               

                                                               
                                        
 






                                                                               
 
                                                     
                                              

                                                                            
                                                          
 
                                              
                                              


                                                                             
                                                      
                                      
 
                                             
                                              
                                                                     

                                                                       
                                                      
                                      
 




                                                                             


                                                      
                                                         
 
                                                                  

                                                                      

                                                                  
                                                                              
 
                                                      


                                                           
                                                                          
                                              
 

                                              





                                                                                          
 

                                                                      
                        

                                                          
                 
         
 

                                                                   
                                                                 
                                                                   
 

                                                               
 
                                              





                                                                        
                                                           
                                      
 
                                                     
                                                         
 

                                                                         
         
 







                                                               
 

                                
 






                                                      
 


                                                                            
 

                                                                     
 

                                                                  
 
                                                     
 



                                                        
 



                                                                
 
                                               

 
                
 
               
                                          
 


                                  
 
                                                     
 



                                                                         


                                  




                                                               
 
                             

 
           


                                        
 

                              
 

                                                        
 
          










                                                                           

           

                                                                   



                                                      
                                                                               
                            
                                           


                                                              
                
                                                              
         
 
                          
                                                       
 

 
                               
 
           
                                                    
 

                              

                                                                 
                               




                                                            

 
           
                                                        
 

                                    
 

                                                              
 



                                                              

 
           
                                                              
 
                                          


                                   

                                    
                        
                        




                                    
                         
 
                                                           
                                                               
 

                                                         
                       
 
                                                                 














                                                                      
                                                      
                                                               


                                                   

                                                       



                                                       
 
                                                         



                                                       
 
                                                            



                                                       


                                                                            
 
                                

 





                                                                 

                              
 

                                                      









                                                                    
                                                      

















                                                                    

                                                                


                                
                                                              



                                                                   

 
           
                                                       

                                                        
                                
 
                                                                
 


                                                                   
 
                                                                 

 








                                                     
 
                                                             
 





                                                                         
























                                                                                        









                                                                

                     
                              


                                               
 


                                                                               
 









































                                                                                             

                                                                  

                                                                           
                                                        

                                                                               
                   

                                                                      


















                                                                                             
 




                                                                                
 

                                                                           
 





















                                                                                             

                                                                      
 
           
                                                      
 

                              
 


                                                      

 
               
                                                     
 
                      

                                    
 
                                                    
 




                                                                            



                                                            
                                                                
                                                              

                                                          
                                                         
                                                






                                                     


                                                         



                                                   


                                                          




                                                          


                                                        

                                                   





                                                                           

                                            

 
           



































                                                                       





                                                               
















                                                                         



                                                         


                                                     
                                                                        


           









                                                                     
                       




                                                                

                                                                






                                                            
                                          




                                                        







































                                                                    
                                          
 
                      
                               
                                   


                                    

                                          
                                 
                                
                            
                        
                        
 
                                           
 
                                                    
 

                                                  


                                                           




                                                                             
                                                                      
 

                                       

                                                                 

                                                              
 

                                        

                                                                       

                                          

                                                                         

                                      



                                                             
 





                                                                    

                                    

                                                               
                                                                   
                                                                               
                                                      
                                  
 


                               
                                                    

                                                                        
                          



                                                                                

                          
                                                
                                                                    
 
                          
                                                       
                                                                           
 



                                                                       
                               
 
                                         

                                                                      
                                         

                                                                      
                                         

                                                                      
                                         

                                                                              
                                         

                                                                      
                                         

                                                                    
                                         

                                                                        
                                         


                                                                      
                                                         












                                                                    
                                                                  






                                                                     

                                            

                                                                

                                                               
 
                                                          

                                                                           


           
                                      
 
                                                         
                      
 



                                                                    
 
                                                       

                                       



                                                                   
 


                                                    
                                                                       
 
 


                                    
                               

                                    

                              
                          
 
                                                
                                                                     
 


                                                           
 




                                                             
                                                                                       


                                                     
 




                                                                
                                                                                       


                                                     
 
                                                  

                                                  

 



                                                 
                               
                                

                              
 
                                           

                                                      
 



                                                           
 

                                           
                                               


                            
 
                                              


                                                     
 
                                                                                 
                                                          


                            




                                                             
 



                                                           



                                                                           
 





                                                                          
 





                                                                         

         
                                                            

                                                               

 






                                                        














                                                                   















                                                                   
           
                                                    
 
                                   
                                     
 


                                                                       
                                                               
                                                               

                                                       
                                                             
 

                                                
                                                                     
 

                                              

                                         










                                                             







                                             










                                                 









                                                             

                                      
                                            
                                  





                                                          
 

                                                
                                            
                                  











                                                                    




                                              





                                            







                                                   
                                            
 
                                                               

                                                                          
 
 

                      
                     





                                                 
                                  
 

                                                        

                                    
















                                                                  

 







                                                                  



                                                        
 


                                                 

 
           
                                                          
 
                          
 
                                                                    
 

                                                        
 
                                                                   

 


                                                           
                                                        

                                                   
 

                                       

                              
 

                       
 
                                                                    
                                                                      

                                                      
 







                                                                     

                                                                            
                                                                                   
                                                                                     



                                                                       



                                                            
 


                                                          
                                                      





                                                                         
 


                                                          
 








                                                                           
 





                                                                            
         
 
 












                                                                           
                                                              

                                  
 
                                                                    
                                          
                       
 

                                                                       

 
           

                                                   
                                                 
                                                   
                                    
 


                                       
                                       
                              
 

                                                                        

                                                                       


                                                                                 
                                                               
                                                                                     



                                                                 
 

                                                           
 
                              
                       
 
                                                                    
                                                                      
 

                                                      
 
                                                                  


                                                                     

                                                          

                                                                     
                                                 
                                                                                         
 
                                                                    

                                                                        
                                                    
                                                                                         
 
                                                                                              


                                                                     
                                                                                         
 
                        
                                                          
                                          
                                                                                         
                 
 


                                                                       
 
                                          
                                                                                   

                                                                                 
 



                                                            
 


                                                     
                                                    
                                                      





                                       
                              
                                   

                                       
 











                                                                                        
 
                                                        
                                                 
                                           

                                                                 
                                
                                 
 

                          
 
                                           
 
                                                                    
 
                                                                      
 

                                                                              
 
                                                                  


                                                                       
 
                                                          


                                                                      
                                                                                                 
 
                                                                    

                                                                          
                                                    
                                                                                         
 
                                                                                              


                                                                     
                                                                                                 
 
                        
                                                          
                                          
                                                                                                 
                 
 


                                                                       
 
                                          
                                                                                   

                                                                                 
 


                                                            
 
                                   

 
           

                                                        
                                                      
                                                        
                                         
 

                                                                              









                                                                    
 

                                 
 
                                                                            
                                                                              
 

                                                   
 
                                                       
 
                                                                  

                                                                             
                                                         
                                                                                              
 
                                                                            


                                                                                
                                                                            
                                                                
 
                                

                                                                     
                                                  
                                                                                              
                         
 





                                                                                       
                                                                                    

                                                      

                                                                                     
                                                           

                                                                                  



                                                                    
 


                              
 
                                          
                                                                                   




                                                                                                    
         

 
           

                                            
                                          
                                            
                             
 
                       
 
                                                        
 



                                               
 
                                                                    
 

                                 
 
                                                                            
                                                                              
 

                                                   
 
                                                       
 
                                                                  

                                                                             
                                                         
                                                                                              
 
                                                                            


                                                                                
                                                                            
                                                                
 

                                                                            
                                                              
                                                                                              
 
                                

                                                                     
                                                  
                                                                                              
                         
 


                                                  
 

                                                                             
























                                                                                                              
 


                                                                              




                                                                            
 




                                                                    


           
                                          
 



                                    
                                    
                                          
                   
                          
 
                                                           
                                                                            
 


                                                                             
 

















                                                                           

                                                                     



                                                                             









































                                                                             
                                                                       
         
 
                                   

 

                                   
                     
                                             
                                                  
                                                        







                                                                        
                                               
                                                           
                                                         
                                                           
 
                                            
                                                                  
                                                      




                                          
                               
                                  
                                    
                               

                              

                                      
                            
                    
 

                                                        









                                                                        
         
 
                                              
                                                     
                                                      
                                                           
                                               
 

                                                                         
                                                              

                                     
         
 


                                                                




                                                                       

                                                                    

                                                             
         
 




                                                 
 




                                                 
 



                                                                       
 


                                                                                    
 


                                                 
 




                                                                       
                 
         
 

                                              
 


                                                 
 



                                                                       
 

                                                                              
 
                                                      
                 
         
 


                                              
 


                                                  
 



                                                                        
 

                                                                              
 
                                                        
                 
         
 



                                                


                                        
                                                           
 
                                                                       



                                                                   
 
                              
 


                                   
 
                                                   

                                                              






                                                                     
                             
                              
 

                                                               
 
                                                     
                                            
                                                                              






                                                                                           
                                                                                      






                                                                                           
                                                                                    













                                                                                         
         
 
                                                                           
                                                       
                                      
 
                                       

                                                                         


                                                                           
 




                                                                             
 


                                                     

                                                                             





                                                                 


                                        
         
 
                                                   
                                                                    
                                           
                                         


                                                      

                                                                            
 
                                                          

                                                                      
                                                 
                                                                                     
 
                                                                    


                                                                          
                                                                                     
 

                                                                    

                                                                     
                                                      
                                                                                     
 
                        
                                                          
                                          
                                                                                     
                 
                
                                         


                              

                                                                            
 



                                                                               

                                                                              


                                                               
                 
 
























                                                                                                      
         
 
                                     
                                                
 







                                                                        
 
 

                               
                     






                                                 

                                                       
                                                       
                                                       
 

                                    

                              
                             
 
                                                        
                                                                     
 
                                                    
                                                   
                                                           
 
                                                           
 
                                           
                               
 
                                                                       
                                                             
 


                                                                     
 
                        

 
   
                              




                                                                      


                                                                      


                                           
                                                   
 
                      

                                     
 

                                                                  
                                                    
                                                                    
 
                                                                





                                                                











                                                                  








                                               
                               

                              

                             

                                                                   

                                                                               
                                                                        


                                                 

                                                           




                                                 
                                
                                   

                                                         
                                             
                                     

                                                           




                                                           
                                                                       


                                                      
 






                                            


   

                              
  
                                 

    
                                            
 
                            
                              

                                          
 
                                                        
 


                                                                          

                                                           
                       
         
 
                                                      
 

                                                                
 
                                                                     







                                                           


                                                       

                                  
                               

                              

                             

                                                                   


                                                           
                                                                        


                                                 





                                                                    




                                                 
                                
                                   

                                                         
                                             
                                     





                                                                    



                                                           
 
                                                                       


                                                      
 
                       

                                                  


                                 
                                                 


                                                                    
 
                                     


   
                                 
                              
  
                                                                         

    
                                                      
 
                            
                              
                                  
 
                                                        
 
                                                      
 

                                                                
 
                                                                     


                                                          









                                                                     
                               

                              

                             

                                                                   

                                                                               
                                                                        







                                                 
                                
                                   

                                                         
                                             













                                                           


                                                      










                                                            
                            
                              










                                                                          
                                                      
 

                                                                
 
                                                                     



                                                                     








                                              
                               

                             

                                                                   


                                                           
                                                                        









                                                 
                                   














                                                                   

 


                              
                                           




                                             
                                                           
 
                            
                              
                                  
 

                                                        

                                                      
                                              
                                                                

                                             
                                                                     




                                                            

 
              

                                    






                                                                 
 




                                                                        
 


                                                          
 

                    
 
               

                                       


                                                   

                                                           

                                    
                                  





                     




                                   





                                                          
                                                           

                                                                       
                 



           



                                                     















                                                                     
                                                       
 
               
                                           
 
                    
                                     
                              
                        

                      








                                                                           





                                                                          
                                                   


                                      
         
 
                          

                                






                                                                          













                                                                               

         
                           
                          
 


                           
           

                                      
 

                                







                                                  
 
                                                           

                                                             
 
                                
 








                                                                       
 


                        
 

                                                
 


                                                         
 
                                             

                                           
 
                                                
 
                                                      
 
                                                                 
                                                           

                                                                        
                                                           

                                                                         
                                                           


                                                                             
                                                           



                                                                          


                                                                      






                                                                                            
                                                           



                                                                          


                                                                      






                                                                                      

                                                        
                                                           
                                                                  
                                                        
                                                                        
                                                                        
                                                               



                                                                                         
                                                                                      


                                                                                  
                                                            





                                                                              
                                                           

                                                                                      
 
                                         
 


                                        
                                                                        



                                               
 
                     
 

                                                       

                                              
                                                
 


                          
 


                                                                   
 


                                   
 

                                                             
 

                                
 
                                                                                      
                                                           

                                  

 
   
                               
                     
                     
  

                                                              
    
              

                                              
 
                               
 
                                                        
                                                                                  
 
                                              
 
                                      
 

                        
 
   


                                                     

                                                                            
  
                                             
    


                                                     
                                                        
 

                                                        
 
                                                           

 
   



                                                  
  
                                                              

    

                                                
                                                
 
                                                   
                                    

                              

                                  
                    
 
                                                        
 

                                                      
                                                           
 


                                       
                                                                        
                                                                          
 

                                        
                                                     
                                                          

                                                
 

                                                                     
 







                                                                              
 

                                                                            
 
                                                                           
                                                
 

                                                                       


                                                                         

                 

                                

 
   
                             


                              
  


                                                                        
    


                                                  
                                              
 
                                  
 


                                                        
 



                                                                   
 

   



                              
  




                                                                     
    
    


                                                  
 
                                                        
                                        
                                         
 
                                                      
                                                          


   


                                
  

                                                                 
    


                                                     
 

                                  
 


                                                        
                              
 





                                                                              


                 
 
   












































































                                                                           

                               
                                           
  

                                                                      
    

                                                
 


                                
 
                                                        



                                                             
 


                                                              


                                                          
                                    

 















                                                                           
                                                                 






                                                
   
                              
                              
  

                                                                           
                                                        
    
    
                                                   
                                             
                                                      

                                                         
 


                                   

                              
 
                                                        
 


                                                      


                                                       
 

                                                                          
                                                    
                                                    
 


                                                                      
 


                                                                      
 


                                                                      
 


                                                                      
 



                                                                      
 



                                                                      
 



                                                    
 
 
                  


                                                          
 

                                   
 



                                                   
 

                                                                     
 
                                                                  
                            
 
                                                                     
 
                                      

 


                                                         
                                                            

                                                               
 

                                   
 
                                                        
 


                                                             
 

                                                                          

                                                
 




                                                    
 






                                                                
 








                                                                     
                            
 
                                                                     
 

                                      
 






                                                               

                              
                                   
                                                       
                          
 
                                                        
 



                                                             

                                                                          






                                                                          
 

























                                                                      












                                                                
 



                                                         
 




                                                                     
 
                                                                     
 
                                      

 

                                                
 

                                                  
                                    




                                    
 
                                                                  
 
                                                           
 
                                                               
                                                                 
                                                    
 

                                                                        
 













                                                                         

 

                                                    
 



                                      
                                                                  
 







                                                                               

                                         
         
 

                       
 
   





































                                                                              
                                       
  

                                                      
            

                                                            

                              

                          

                                    
 
                                                                  
 

                                                      

                                                                             
 
                                    
                                                             
                                                                    
                      
 
                     
 
 










                                                       
                                            



                                                   





                                                                            
        

                                                  

                             

                              


                                    
                                            

                      
                                       

                                                      
 
                                                               
                                                         


                                                                            
                                                   








                                                              


                                                                       


                                                
                             




















                                                                          

                                                        
 
                                                                  
 
                                                                      
 
 


                                                           

                                                                  
                                                                    

 

                                                 
 
                            














                                                                       
 
                                                   
                                                               


                                                           
                                  
 

                                            
 












                                                                  
 
                                                        
 

                                                              
 

                                                
 

                                                              
 
                                                         

         





                                                                       


    
                                                                 
 
                                                          
 
                                                        
 



                                                             
 




                                                                      

         

                                                
 
                                                                                   
 






                                                              
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 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.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Authors:
 *      Ettore Perazzoli (ettore@ximian.com)
 *      Jeffrey Stedfast (fejj@ximian.com)
 *      Miguel de Icaza  (miguel@ximian.com)
 *      Radek Doulik     (rodo@ximian.com)
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

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

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <enchant/enchant.h>

#include "e-composer-private.h"

#include <em-format/e-mail-part.h>
#include <em-format/e-mail-parser.h>
#include <em-format/e-mail-formatter-quote.h>

#include <shell/e-shell.h>

typedef struct _AsyncContext AsyncContext;

struct _AsyncContext {
    EActivity *activity;

    CamelMimeMessage *message;
    CamelDataWrapper *top_level_part;
    CamelDataWrapper *text_plain_part;

    ESource *source;
    CamelSession *session;
    CamelInternetAddress *from;

    CamelTransferEncoding plain_encoding;
    GtkPrintOperationAction print_action;

    GPtrArray *recipients;

    guint skip_content : 1;
    guint need_thread : 1;
    guint pgp_sign : 1;
    guint pgp_encrypt : 1;
    guint smime_sign : 1;
    guint smime_encrypt : 1;
};

/* Flags for building a message. */
typedef enum {
    COMPOSER_FLAG_HTML_CONTENT      = 1 << 0,
    COMPOSER_FLAG_SAVE_OBJECT_DATA      = 1 << 1,
    COMPOSER_FLAG_PRIORITIZE_MESSAGE    = 1 << 2,
    COMPOSER_FLAG_REQUEST_READ_RECEIPT  = 1 << 3,
    COMPOSER_FLAG_PGP_SIGN          = 1 << 4,
    COMPOSER_FLAG_PGP_ENCRYPT       = 1 << 5,
    COMPOSER_FLAG_SMIME_SIGN        = 1 << 6,
    COMPOSER_FLAG_SMIME_ENCRYPT     = 1 << 7,
    COMPOSER_FLAG_HTML_MODE         = 1 << 8,
    COMPOSER_FLAG_SAVE_DRAFT        = 1 << 9
} ComposerFlags;

enum {
    PROP_0,
    PROP_BUSY,
    PROP_EDITOR,
    PROP_FOCUS_TRACKER,
    PROP_SHELL
};

enum {
    PRESEND,
    SEND,
    SAVE_TO_DRAFTS,
    SAVE_TO_OUTBOX,
    PRINT,
    LAST_SIGNAL
};

enum DndTargetType {
    DND_TARGET_TYPE_TEXT_URI_LIST,
    DND_TARGET_TYPE_MOZILLA_URL,
    DND_TARGET_TYPE_TEXT_HTML,
    DND_TARGET_TYPE_UTF8_STRING,
    DND_TARGET_TYPE_TEXT_PLAIN,
    DND_TARGET_TYPE_STRING
};

static GtkTargetEntry drag_dest_targets[] = {
    { (gchar *) "text/uri-list", 0, DND_TARGET_TYPE_TEXT_URI_LIST },
    { (gchar *) "_NETSCAPE_URL", 0, DND_TARGET_TYPE_MOZILLA_URL },
    { (gchar *) "text/html", 0, DND_TARGET_TYPE_TEXT_HTML },
    { (gchar *) "UTF8_STRING", 0, DND_TARGET_TYPE_UTF8_STRING },
    { (gchar *) "text/plain", 0, DND_TARGET_TYPE_TEXT_PLAIN },
    { (gchar *) "STRING", 0, DND_TARGET_TYPE_STRING },
};

static guint signals[LAST_SIGNAL];

/* used by e_msg_composer_add_message_attachments () */
static void add_attachments_from_multipart  (EMsgComposer *composer,
                         CamelMultipart *multipart,
                         gboolean just_inlines,
                         gint depth);

/* used by e_msg_composer_new_with_message () */
static void handle_multipart        (EMsgComposer *composer,
                         CamelMultipart *multipart,
                         gboolean keep_signature,
                         GCancellable *cancellable,
                         gint depth);
static void handle_multipart_alternative    (EMsgComposer *composer,
                         CamelMultipart *multipart,
                         gboolean keep_signature,
                         GCancellable *cancellable,
                         gint depth);
static void handle_multipart_encrypted  (EMsgComposer *composer,
                         CamelMimePart *multipart,
                         gboolean keep_signature,
                         GCancellable *cancellable,
                         gint depth);
static void handle_multipart_signed     (EMsgComposer *composer,
                         CamelMultipart *multipart,
                         gboolean keep_signature,
                         GCancellable *cancellable,
                         gint depth);

G_DEFINE_TYPE_WITH_CODE (
    EMsgComposer,
    e_msg_composer,
    GTK_TYPE_WINDOW,
    G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))

static void
async_context_free (AsyncContext *context)
{
    if (context->activity != NULL)
        g_object_unref (context->activity);

    if (context->message != NULL)
        g_object_unref (context->message);

    if (context->top_level_part != NULL)
        g_object_unref (context->top_level_part);

    if (context->text_plain_part != NULL)
        g_object_unref (context->text_plain_part);

    if (context->source != NULL)
        g_object_unref (context->source);

    if (context->session != NULL)
        g_object_unref (context->session);

    if (context->from != NULL)
        g_object_unref (context->from);

    if (context->recipients != NULL)
        g_ptr_array_free (context->recipients, TRUE);

    g_slice_free (AsyncContext, context);
}

/**
 * emcu_part_to_html:
 * @part:
 *
 * Converts a mime part's contents into html text.  If @credits is given,
 * then it will be used as an attribution string, and the
 * content will be cited.  Otherwise no citation or attribution
 * will be performed.
 *
 * Return Value: The part in displayable html format.
 **/
static gchar *
emcu_part_to_html (EMsgComposer *composer,
                   CamelMimePart *part,
                   gssize *len,
                   gboolean keep_signature,
                   GCancellable *cancellable)
{
    CamelSession *session;
    GOutputStream *stream;
    gchar *text;
    EMailParser *parser;
    EMailFormatter *formatter;
    EMailPartList *part_list;
    GString *part_id;
    EShell *shell;
    GtkWindow *window;
    GQueue queue = G_QUEUE_INIT;

    shell = e_shell_get_default ();
    window = e_shell_get_active_window (shell);

    session = e_msg_composer_ref_session (composer);

    part_list = e_mail_part_list_new (NULL, NULL, NULL);

    part_id = g_string_sized_new (0);
    parser = e_mail_parser_new (session);
    e_mail_parser_parse_part (
        parser, part, part_id, cancellable, &queue);
    while (!g_queue_is_empty (&queue)) {
        EMailPart *mail_part = g_queue_pop_head (&queue);

        if (!e_mail_part_get_is_attachment (mail_part) &&
            !mail_part->is_hidden)
            e_mail_part_list_add_part (part_list, mail_part);

        g_object_unref (mail_part);
    }
    g_string_free (part_id, TRUE);
    g_object_unref (parser);
    g_object_unref (session);

    if (e_mail_part_list_is_empty (part_list)) {
        g_object_unref (part_list);
        return NULL;
    }

    stream = g_memory_output_stream_new_resizable ();

    formatter = e_mail_formatter_quote_new (
        NULL, keep_signature ? E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG : 0);
    e_mail_formatter_update_style (
        formatter,
        gtk_widget_get_state_flags (GTK_WIDGET (window)));

    e_mail_formatter_format_sync (
        formatter, part_list, stream,
        0, E_MAIL_FORMATTER_MODE_PRINTING, cancellable);

    g_object_unref (formatter);
    g_object_unref (part_list);

    g_output_stream_write (stream, "", 1, NULL, NULL);

    g_output_stream_close (stream, NULL, NULL);

    text = g_memory_output_stream_steal_data (
        G_MEMORY_OUTPUT_STREAM (stream));

    if (len != NULL)
        *len = strlen (text);

    g_object_unref (stream);

    return text;
}

/* copy of mail_tool_remove_xevolution_headers */
static struct _camel_header_raw *
emcu_remove_xevolution_headers (CamelMimeMessage *message)
{
    struct _camel_header_raw *scan, *list = NULL;

    for (scan = ((CamelMimePart *) message)->headers; scan; scan = scan->next)
        if (!strncmp (scan->name, "X-Evolution", 11))
            camel_header_raw_append (&list, scan->name, scan->value, scan->offset);

    for (scan = list; scan; scan = scan->next)
        camel_medium_remove_header ((CamelMedium *) message, scan->name);

    return list;
}

static EDestination **
destination_list_to_vector_sized (GList *list,
                                  gint n)
{
    EDestination **destv;
    gint i = 0;

    if (n == -1)
        n = g_list_length (list);

    if (n == 0)
        return NULL;

    destv = g_new (EDestination *, n + 1);
    while (list != NULL && i < n) {
        destv[i] = E_DESTINATION (list->data);
        list->data = NULL;
        i++;
        list = g_list_next (list);
    }
    destv[i] = NULL;

    return destv;
}

static EDestination **
destination_list_to_vector (GList *list)
{
    return destination_list_to_vector_sized (list, -1);
}

#define LINE_LEN 72

static gboolean
text_requires_quoted_printable (const gchar *text,
                                gsize len)
{
    const gchar *p;
    gsize pos;

    if (!text)
        return FALSE;

    if (len == -1)
        len = strlen (text);

    if (len >= 5 && strncmp (text, "From ", 5) == 0)
        return TRUE;

    for (p = text, pos = 0; pos + 6 <= len; pos++, p++) {
        if (*p == '\n' && strncmp (p + 1, "From ", 5) == 0)
            return TRUE;
    }

    return FALSE;
}

static CamelTransferEncoding
best_encoding (GByteArray *buf,
               const gchar *charset)
{
    gchar *in, *out, outbuf[256], *ch;
    gsize inlen, outlen;
    gint status, count = 0;
    iconv_t cd;

    if (!charset)
        return -1;

    cd = camel_iconv_open (charset, "utf-8");
    if (cd == (iconv_t) -1)
        return -1;

    in = (gchar *) buf->data;
    inlen = buf->len;
    do {
        out = outbuf;
        outlen = sizeof (outbuf);
        status = camel_iconv (cd, (const gchar **) &in, &inlen, &out, &outlen);
        for (ch = out - 1; ch >= outbuf; ch--) {
            if ((guchar) *ch > 127)
                count++;
        }
    } while (status == (gsize) -1 && errno == E2BIG);
    camel_iconv_close (cd);

    if (status == (gsize) -1 || status > 0)
        return -1;

    if ((count == 0) && (buf->len < LINE_LEN) &&
        !text_requires_quoted_printable (
        (const gchar *) buf->data, buf->len))
        return CAMEL_TRANSFER_ENCODING_7BIT;
    else if (count <= buf->len * 0.17)
        return CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
    else
        return CAMEL_TRANSFER_ENCODING_BASE64;
}

static gchar *
best_charset (GByteArray *buf,
              const gchar *default_charset,
              CamelTransferEncoding *encoding)
{
    const gchar *charset;

    /* First try US-ASCII */
    *encoding = best_encoding (buf, "US-ASCII");
    if (*encoding == CAMEL_TRANSFER_ENCODING_7BIT)
        return NULL;

    /* Next try the user-specified charset for this message */
    *encoding = best_encoding (buf, default_charset);
    if (*encoding != -1)
        return g_strdup (default_charset);

    /* Now try the user's default charset from the mail config */
    charset = e_composer_get_default_charset ();
    *encoding = best_encoding (buf, charset);
    if (*encoding != -1)
        return g_strdup (charset);

    /* Try to find something that will work */
    charset = camel_charset_best (
        (const gchar *) buf->data, buf->len);
    if (charset == NULL) {
        *encoding = CAMEL_TRANSFER_ENCODING_7BIT;
        return NULL;
    }

    *encoding = best_encoding (buf, charset);

    return g_strdup (charset);
}

/* These functions builds a CamelMimeMessage for the message that the user has
 * composed in 'composer'.
 */

static void
set_recipients_from_destv (CamelMimeMessage *msg,
                           EDestination **to_destv,
                           EDestination **cc_destv,
                           EDestination **bcc_destv,
                           gboolean redirect)
{
    CamelInternetAddress *to_addr;
    CamelInternetAddress *cc_addr;
    CamelInternetAddress *bcc_addr;
    CamelInternetAddress *target;
    const gchar *text_addr, *header;
    gboolean seen_hidden_list = FALSE;
    gint i;

    to_addr = camel_internet_address_new ();
    cc_addr = camel_internet_address_new ();
    bcc_addr = camel_internet_address_new ();

    for (i = 0; to_destv != NULL && to_destv[i] != NULL; ++i) {
        text_addr = e_destination_get_address (to_destv[i]);

        if (text_addr && *text_addr) {
            target = to_addr;
            if (e_destination_is_evolution_list (to_destv[i])
                && !e_destination_list_show_addresses (to_destv[i])) {
                target = bcc_addr;
                seen_hidden_list = TRUE;
            }

            if (camel_address_decode (CAMEL_ADDRESS (target), text_addr) <= 0)
                camel_internet_address_add (target, "", text_addr);
        }
    }

    for (i = 0; cc_destv != NULL && cc_destv[i] != NULL; ++i) {
        text_addr = e_destination_get_address (cc_destv[i]);
        if (text_addr && *text_addr) {
            target = cc_addr;
            if (e_destination_is_evolution_list (cc_destv[i])
                && !e_destination_list_show_addresses (cc_destv[i])) {
                target = bcc_addr;
                seen_hidden_list = TRUE;
            }

            if (camel_address_decode (CAMEL_ADDRESS (target), text_addr) <= 0)
                camel_internet_address_add (target, "", text_addr);
        }
    }

    for (i = 0; bcc_destv != NULL && bcc_destv[i] != NULL; ++i) {
        text_addr = e_destination_get_address (bcc_destv[i]);
        if (text_addr && *text_addr) {
            if (camel_address_decode (CAMEL_ADDRESS (bcc_addr), text_addr) <= 0)
                camel_internet_address_add (bcc_addr, "", text_addr);
        }
    }

    if (redirect)
        header = CAMEL_RECIPIENT_TYPE_RESENT_TO;
    else
        header = CAMEL_RECIPIENT_TYPE_TO;

    if (camel_address_length (CAMEL_ADDRESS (to_addr)) > 0) {
        camel_mime_message_set_recipients (msg, header, to_addr);
    } else if (seen_hidden_list) {
        camel_medium_set_header (
            CAMEL_MEDIUM (msg), header, "Undisclosed-Recipient:;");
    }

    header = redirect ? CAMEL_RECIPIENT_TYPE_RESENT_CC : CAMEL_RECIPIENT_TYPE_CC;
    if (camel_address_length (CAMEL_ADDRESS (cc_addr)) > 0) {
        camel_mime_message_set_recipients (msg, header, cc_addr);
    }

    header = redirect ? CAMEL_RECIPIENT_TYPE_RESENT_BCC : CAMEL_RECIPIENT_TYPE_BCC;
    if (camel_address_length (CAMEL_ADDRESS (bcc_addr)) > 0) {
        camel_mime_message_set_recipients (msg, header, bcc_addr);
    }

    g_object_unref (to_addr);
    g_object_unref (cc_addr);
    g_object_unref (bcc_addr);
}

static void
build_message_headers (EMsgComposer *composer,
                       CamelMimeMessage *message,
                       gboolean redirect)
{
    EComposerHeaderTable *table;
    EComposerHeader *header;
    ESource *source;
    const gchar *subject;
    const gchar *reply_to;
    const gchar *uid;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));

    table = e_msg_composer_get_header_table (composer);

    uid = e_composer_header_table_get_identity_uid (table);
    source = e_composer_header_table_ref_source (table, uid);

    /* Subject: */
    subject = e_composer_header_table_get_subject (table);
    camel_mime_message_set_subject (message, subject);

    if (source != NULL) {
        CamelMedium *medium;
        CamelInternetAddress *addr;
        ESourceMailIdentity *mi;
        ESourceMailSubmission *ms;
        const gchar *extension_name;
        const gchar *header_name;
        const gchar *name, *address;
        const gchar *transport_uid;
        const gchar *sent_folder;

        extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
        mi = e_source_get_extension (source, extension_name);

        name = e_source_mail_identity_get_name (mi);
        address = e_source_mail_identity_get_address (mi);

        extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
        ms = e_source_get_extension (source, extension_name);

        sent_folder = e_source_mail_submission_get_sent_folder (ms);
        transport_uid = e_source_mail_submission_get_transport_uid (ms);

        medium = CAMEL_MEDIUM (message);

        /* From: / Resent-From: */
        addr = camel_internet_address_new ();
        camel_internet_address_add (addr, name, address);
        if (redirect) {
            gchar *value;

            value = camel_address_encode (CAMEL_ADDRESS (addr));
            camel_medium_set_header (medium, "Resent-From", value);
            g_free (value);
        } else {
            camel_mime_message_set_from (message, addr);
        }
        g_object_unref (addr);

        /* X-Evolution-Identity */
        header_name = "X-Evolution-Identity";
        camel_medium_set_header (medium, header_name, uid);

        /* X-Evolution-Fcc */
        header_name = "X-Evolution-Fcc";
        camel_medium_set_header (medium, header_name, sent_folder);

        /* X-Evolution-Transport */
        header_name = "X-Evolution-Transport";
        camel_medium_set_header (medium, header_name, transport_uid);

        g_object_unref (source);
    }

    /* Reply-To: */
    reply_to = e_composer_header_table_get_reply_to (table);
    if (reply_to != NULL && *reply_to != '\0') {
        CamelInternetAddress *addr;

        addr = camel_internet_address_new ();

        if (camel_address_unformat (CAMEL_ADDRESS (addr), reply_to) > 0)
            camel_mime_message_set_reply_to (message, addr);

        g_object_unref (addr);
    }

    /* To:, Cc:, Bcc: */
    header = e_composer_header_table_get_header (
        table, E_COMPOSER_HEADER_TO);
    if (e_composer_header_get_visible (header)) {
        EDestination **to, **cc, **bcc;

        to = e_composer_header_table_get_destinations_to (table);
        cc = e_composer_header_table_get_destinations_cc (table);
        bcc = e_composer_header_table_get_destinations_bcc (table);

        set_recipients_from_destv (message, to, cc, bcc, redirect);

        e_destination_freev (to);
        e_destination_freev (cc);
        e_destination_freev (bcc);
    }

    /* Date: */
    camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0);

    /* X-Evolution-PostTo: */
    header = e_composer_header_table_get_header (
        table, E_COMPOSER_HEADER_POST_TO);
    if (e_composer_header_get_visible (header)) {
        CamelMedium *medium;
        const gchar *name = "X-Evolution-PostTo";
        GList *list, *iter;

        medium = CAMEL_MEDIUM (message);
        camel_medium_remove_header (medium, name);

        list = e_composer_header_table_get_post_to (table);
        for (iter = list; iter != NULL; iter = iter->next) {
            gchar *folder = iter->data;
            camel_medium_add_header (medium, name, folder);
            g_free (folder);
        }
        g_list_free (list);
    }
}

static CamelCipherHash
account_hash_algo_to_camel_hash (const gchar *hash_algo)
{
    CamelCipherHash res = CAMEL_CIPHER_HASH_DEFAULT;

    if (hash_algo && *hash_algo) {
        if (g_ascii_strcasecmp (hash_algo, "sha1") == 0)
            res = CAMEL_CIPHER_HASH_SHA1;
        else if (g_ascii_strcasecmp (hash_algo, "sha256") == 0)
            res = CAMEL_CIPHER_HASH_SHA256;
        else if (g_ascii_strcasecmp (hash_algo, "sha384") == 0)
            res = CAMEL_CIPHER_HASH_SHA384;
        else if (g_ascii_strcasecmp (hash_algo, "sha512") == 0)
            res = CAMEL_CIPHER_HASH_SHA512;
    }

    return res;
}

static void
composer_add_charset_filter (CamelStream *stream,
                             const gchar *charset)
{
    CamelMimeFilter *filter;

    filter = camel_mime_filter_charset_new ("UTF-8", charset);
    camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
    g_object_unref (filter);
}

static void
composer_add_quoted_printable_filter (CamelStream *stream)
{
    CamelMimeFilter *filter;

    filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_ENC);
    camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
    g_object_unref (filter);

    filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_FROM);
    camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
    g_object_unref (filter);
}

/* Helper for composer_build_message_thread() */
static gboolean
composer_build_message_pgp (AsyncContext *context,
                            GCancellable *cancellable,
                            GError **error)
{
    ESourceOpenPGP *extension;
    CamelCipherContext *cipher;
    CamelDataWrapper *content;
    CamelMimePart *mime_part;
    const gchar *extension_name;
    const gchar *pgp_key_id;
    const gchar *signing_algorithm;
    gboolean always_trust;
    gboolean encrypt_to_self;

    /* Return silently if we're not signing or encrypting with PGP. */
    if (!context->pgp_sign && !context->pgp_encrypt)
        return TRUE;

    extension_name = E_SOURCE_EXTENSION_OPENPGP;
    extension = e_source_get_extension (context->source, extension_name);

    always_trust = e_source_openpgp_get_always_trust (extension);
    encrypt_to_self = e_source_openpgp_get_encrypt_to_self (extension);
    pgp_key_id = e_source_openpgp_get_key_id (extension);
    signing_algorithm = e_source_openpgp_get_signing_algorithm (extension);

    mime_part = camel_mime_part_new ();

    camel_medium_set_content (
        CAMEL_MEDIUM (mime_part),
        context->top_level_part);

    if (context->top_level_part == context->text_plain_part)
        camel_mime_part_set_encoding (
            mime_part, context->plain_encoding);

    g_object_unref (context->top_level_part);
    context->top_level_part = NULL;

    if (pgp_key_id == NULL || *pgp_key_id == '\0')
        camel_internet_address_get (
            context->from, 0, NULL, &pgp_key_id);

    if (context->pgp_sign) {
        CamelMimePart *npart;
        gboolean success;

        npart = camel_mime_part_new ();

        cipher = camel_gpg_context_new (context->session);
        camel_gpg_context_set_always_trust (
            CAMEL_GPG_CONTEXT (cipher), always_trust);

        success = camel_cipher_context_sign_sync (
            cipher, pgp_key_id,
            account_hash_algo_to_camel_hash (signing_algorithm),
            mime_part, npart, cancellable, error);

        g_object_unref (cipher);

        g_object_unref (mime_part);

        if (!success) {
            g_object_unref (npart);
            return FALSE;
        }

        mime_part = npart;
    }

    if (context->pgp_encrypt) {
        CamelMimePart *npart;
        gboolean success;

        npart = camel_mime_part_new ();

        /* Check to see if we should encrypt to self.
         * NB: Gets removed immediately after use. */
        if (encrypt_to_self && pgp_key_id != NULL)
            g_ptr_array_add (
                context->recipients,
                g_strdup (pgp_key_id));

        cipher = camel_gpg_context_new (context->session);
        camel_gpg_context_set_always_trust (
            CAMEL_GPG_CONTEXT (cipher), always_trust);

        success = camel_cipher_context_encrypt_sync (
            cipher, pgp_key_id, context->recipients,
            mime_part, npart, cancellable, error);

        g_object_unref (cipher);

        if (encrypt_to_self && pgp_key_id != NULL)
            g_ptr_array_set_size (
                context->recipients,
                context->recipients->len - 1);

        g_object_unref (mime_part);

        if (!success) {
            g_object_unref (npart);
            return FALSE;
        }

        mime_part = npart;
    }

    content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
    context->top_level_part = g_object_ref (content);

    g_object_unref (mime_part);

    return TRUE;
}

#ifdef HAVE_SSL
static gboolean
composer_build_message_smime (AsyncContext *context,
                              GCancellable *cancellable,
                              GError **error)
{
    ESourceSMIME *extension;
    CamelCipherContext *cipher;
    CamelMimePart *mime_part;
    const gchar *extension_name;
    const gchar *signing_algorithm;
    const gchar *signing_certificate;
    const gchar *encryption_certificate;
    gboolean encrypt_to_self;
    gboolean have_signing_certificate;
    gboolean have_encryption_certificate;

    /* Return silently if we're not signing or encrypting with S/MIME. */
    if (!context->smime_sign && !context->smime_encrypt)
        return TRUE;

    extension_name = E_SOURCE_EXTENSION_SMIME;
    extension = e_source_get_extension (context->source, extension_name);

    encrypt_to_self =
        e_source_smime_get_encrypt_to_self (extension);

    signing_algorithm =
        e_source_smime_get_signing_algorithm (extension);

    signing_certificate =
        e_source_smime_get_signing_certificate (extension);

    encryption_certificate =
        e_source_smime_get_encryption_certificate (extension);

    have_signing_certificate =
        (signing_certificate != NULL) &&
        (*signing_certificate != '\0');

    have_encryption_certificate =
        (encryption_certificate != NULL) &&
        (*encryption_certificate != '\0');

    if (context->smime_sign && !have_signing_certificate) {
        g_set_error (
            error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
            _("Cannot sign outgoing message: "
            "No signing certificate set for "
            "this account"));
        return FALSE;
    }

    if (context->smime_encrypt && !have_encryption_certificate) {
        g_set_error (
            error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
            _("Cannot encrypt outgoing message: "
            "No encryption certificate set for "
            "this account"));
        return FALSE;
    }

    mime_part = camel_mime_part_new ();

    camel_medium_set_content (
        CAMEL_MEDIUM (mime_part),
        context->top_level_part);

    if (context->top_level_part == context->text_plain_part)
        camel_mime_part_set_encoding (
            mime_part, context->plain_encoding);

    g_object_unref (context->top_level_part);
    context->top_level_part = NULL;

    if (context->smime_sign) {
        CamelMimePart *npart;
        gboolean success;

        npart = camel_mime_part_new ();

        cipher = camel_smime_context_new (context->session);

        /* if we're also encrypting, envelope-sign rather than clear-sign */
        if (context->smime_encrypt) {
            camel_smime_context_set_sign_mode (
                (CamelSMIMEContext *) cipher,
                CAMEL_SMIME_SIGN_ENVELOPED);
            camel_smime_context_set_encrypt_key (
                (CamelSMIMEContext *) cipher,
                TRUE, encryption_certificate);
        } else if (have_encryption_certificate) {
            camel_smime_context_set_encrypt_key (
                (CamelSMIMEContext *) cipher,
                TRUE, encryption_certificate);
        }

        success = camel_cipher_context_sign_sync (
            cipher, signing_certificate,
            account_hash_algo_to_camel_hash (signing_algorithm),
            mime_part, npart, cancellable, error);

        g_object_unref (cipher);

        g_object_unref (mime_part);

        if (!success) {
            g_object_unref (npart);
            return FALSE;
        }

        mime_part = npart;
    }

    if (context->smime_encrypt) {
        gboolean success;

        /* Check to see if we should encrypt to self.
         * NB: Gets removed immediately after use. */
        if (encrypt_to_self)
            g_ptr_array_add (
                context->recipients, g_strdup (
                encryption_certificate));

        cipher = camel_smime_context_new (context->session);
        camel_smime_context_set_encrypt_key (
            (CamelSMIMEContext *) cipher, TRUE,
            encryption_certificate);

        success = camel_cipher_context_encrypt_sync (
            cipher, NULL,
            context->recipients, mime_part,
            CAMEL_MIME_PART (context->message),
            cancellable, error);

        g_object_unref (cipher);

        if (!success)
            return FALSE;

        if (encrypt_to_self)
            g_ptr_array_set_size (
                context->recipients,
                context->recipients->len - 1);
    }

    /* we replaced the message directly, we don't want to do reparenting foo */
    if (context->smime_encrypt) {
        context->skip_content = TRUE;
    } else {
        CamelDataWrapper *content;

        content = camel_medium_get_content (
            CAMEL_MEDIUM (mime_part));
        context->top_level_part = g_object_ref (content);
    }

    g_object_unref (mime_part);

    return TRUE;
}
#endif

static void
composer_build_message_thread (GSimpleAsyncResult *simple,
                               EMsgComposer *composer,
                               GCancellable *cancellable)
{
    AsyncContext *context;
    GError *error = NULL;

    context = g_simple_async_result_get_op_res_gpointer (simple);

    /* Setup working recipient list if we're encrypting. */
    if (context->pgp_encrypt || context->smime_encrypt) {
        gint ii, jj;

        const gchar *types[] = {
            CAMEL_RECIPIENT_TYPE_TO,
            CAMEL_RECIPIENT_TYPE_CC,
            CAMEL_RECIPIENT_TYPE_BCC
        };

        context->recipients = g_ptr_array_new_with_free_func (
            (GDestroyNotify) g_free);
        for (ii = 0; ii < G_N_ELEMENTS (types); ii++) {
            CamelInternetAddress *addr;
            const gchar *address;

            addr = camel_mime_message_get_recipients (
                context->message, types[ii]);
            for (jj = 0; camel_internet_address_get (addr, jj, NULL, &address); jj++)
                g_ptr_array_add (
                    context->recipients,
                    g_strdup (address));
        }
    }

    if (!composer_build_message_pgp (context, cancellable, &error)) {
        g_simple_async_result_take_error (simple, error);
        return;
    }

#if defined (HAVE_NSS)
    if (!composer_build_message_smime (context, cancellable, &error)) {
        g_simple_async_result_take_error (simple, error);
        return;
    }
#endif /* HAVE_NSS */
}

static void
composer_add_evolution_composer_mode_header (CamelMedium *medium,
                                             ComposerFlags flags)
{
    GString *string;

    string = g_string_sized_new (128);

    if (flags & COMPOSER_FLAG_HTML_MODE)
        g_string_append (string, "text/html");
    else
        g_string_append (string, "text/plain");

    camel_medium_add_header (
        medium, "X-Evolution-Composer-Mode", string->str);

    g_string_free (string, TRUE);
}

static void
composer_add_evolution_format_header (CamelMedium *medium,
                                      ComposerFlags flags)
{
    GString *string;

    string = g_string_sized_new (128);

    if (flags & COMPOSER_FLAG_HTML_CONTENT)
        g_string_append (string, "text/html");
    else
        g_string_append (string, "text/plain");

    if (flags & COMPOSER_FLAG_PGP_SIGN)
        g_string_append (string, ", pgp-sign");

    if (flags & COMPOSER_FLAG_PGP_ENCRYPT)
        g_string_append (string, ", pgp-encrypt");

    if (flags & COMPOSER_FLAG_SMIME_SIGN)
        g_string_append (string, ", smime-sign");

    if (flags & COMPOSER_FLAG_SMIME_ENCRYPT)
        g_string_append (string, ", smime-encrypt");

    camel_medium_add_header (
        medium, "X-Evolution-Format", string->str);

    g_string_free (string, TRUE);
}

static void
composer_build_message (EMsgComposer *composer,
                        ComposerFlags flags,
                        gint io_priority,
                        GCancellable *cancellable,
                        GAsyncReadyCallback callback,
                        gpointer user_data)
{
    EMsgComposerPrivate *priv;
    GSimpleAsyncResult *simple;
    AsyncContext *context;
    EAttachmentView *view;
    EAttachmentStore *store;
    EComposerHeaderTable *table;
    CamelDataWrapper *html;
    ESourceMailIdentity *mi;
    const gchar *extension_name;
    const gchar *iconv_charset = NULL;
    const gchar *identity_uid;
    const gchar *organization;
    CamelMultipart *body = NULL;
    CamelContentType *type;
    CamelStream *stream;
    CamelStream *mem_stream;
    CamelMimePart *part;
    GByteArray *data;
    ESource *source;
    gchar *charset, *message_uid;
    const gchar *from_domain;
    gint i;

    priv = composer->priv;
    table = e_msg_composer_get_header_table (composer);
    view = e_msg_composer_get_attachment_view (composer);
    store = e_attachment_view_get_store (view);

    identity_uid = e_composer_header_table_get_identity_uid (table);
    source = e_composer_header_table_ref_source (table, identity_uid);
    g_return_if_fail (source != NULL);

    /* Do all the non-blocking work here, and defer
     * any blocking operations to a separate thread. */

    context = g_slice_new0 (AsyncContext);
    context->source = source;  /* takes the reference */
    context->session = e_msg_composer_ref_session (composer);
    context->from = e_msg_composer_get_from (composer);

    if (flags & COMPOSER_FLAG_PGP_SIGN)
        context->pgp_sign = TRUE;

    if (flags & COMPOSER_FLAG_PGP_ENCRYPT)
        context->pgp_encrypt = TRUE;

    if (flags & COMPOSER_FLAG_SMIME_SIGN)
        context->smime_sign = TRUE;

    if (flags & COMPOSER_FLAG_SMIME_ENCRYPT)
        context->smime_encrypt = TRUE;

    context->need_thread =
        context->pgp_sign || context->pgp_encrypt ||
        context->smime_sign || context->smime_encrypt;

    simple = g_simple_async_result_new (
        G_OBJECT (composer), callback,
        user_data, composer_build_message);

    g_simple_async_result_set_check_cancellable (simple, cancellable);

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

    /* If this is a redirected message, just tweak the headers. */
    if (priv->redirect) {
        context->skip_content = TRUE;
        context->message = g_object_ref (priv->redirect);
        build_message_headers (composer, context->message, TRUE);
        g_simple_async_result_complete (simple);
        g_object_unref (simple);
        return;
    }

    context->message = camel_mime_message_new ();

    if (context->from && camel_internet_address_get (context->from, 0, NULL, &from_domain)) {
        const gchar *at = strchr (from_domain, '@');
        if (at)
            from_domain = at + 1;
        else
            from_domain = NULL;
    } else {
        from_domain = NULL;
    }

    if (!from_domain || !*from_domain)
        from_domain = "localhost";

    message_uid = camel_header_msgid_generate (from_domain);

    /* Explicitly generate a Message-ID header here so it's
     * consistent for all outbound streams (SMTP, Fcc, etc). */
    camel_mime_message_set_message_id (context->message, message_uid);
    g_free (message_uid);

    build_message_headers (composer, context->message, FALSE);
    for (i = 0; i < priv->extra_hdr_names->len; i++) {
        camel_medium_add_header (
            CAMEL_MEDIUM (context->message),
            priv->extra_hdr_names->pdata[i],
            priv->extra_hdr_values->pdata[i]);
    }

    extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
    mi = e_source_get_extension (source, extension_name);
    organization = e_source_mail_identity_get_organization (mi);

    /* Disposition-Notification-To */
    if (flags & COMPOSER_FLAG_REQUEST_READ_RECEIPT) {
        const gchar *mdn_address;

        mdn_address = e_source_mail_identity_get_reply_to (mi);
        if (mdn_address == NULL)
            mdn_address = e_source_mail_identity_get_address (mi);
        if (mdn_address != NULL)
            camel_medium_add_header (
                CAMEL_MEDIUM (context->message),
                "Disposition-Notification-To", mdn_address);
    }

    /* X-Priority */
    if (flags & COMPOSER_FLAG_PRIORITIZE_MESSAGE)
        camel_medium_add_header (
            CAMEL_MEDIUM (context->message),
            "X-Priority", "1");

    /* Organization */
    if (organization != NULL && *organization != '\0') {
        gchar *encoded_organization;

        encoded_organization = camel_header_encode_string (
            (const guchar *) organization);
        camel_medium_set_header (
            CAMEL_MEDIUM (context->message),
            "Organization", encoded_organization);
        g_free (encoded_organization);
    }

    /* X-Evolution-Format */
    composer_add_evolution_format_header (
        CAMEL_MEDIUM (context->message), flags);

    /* X-Evolution-Composer-Mode */
    composer_add_evolution_composer_mode_header (
        CAMEL_MEDIUM (context->message), flags);

    if (flags & COMPOSER_FLAG_SAVE_DRAFT) {
        gchar *text;
        EHTMLEditor *editor;
        EHTMLEditorView *view;
        EHTMLEditorSelection *selection;

        editor = e_msg_composer_get_editor (composer);
        view = e_html_editor_get_view (editor);
        selection = e_html_editor_view_get_selection (view);

        data = g_byte_array_new ();

        e_html_editor_view_embed_styles (view);
        e_html_editor_selection_save_caret_position (selection);

        text = e_html_editor_view_get_text_html_for_drafts (view);

        e_html_editor_view_remove_embed_styles (view);
        e_html_editor_selection_restore_caret_position (selection);

        g_byte_array_append (data, (guint8 *) text, strlen (text));

        g_free (text);

        type = camel_content_type_new ("text", "html");
        camel_content_type_set_param (type, "charset", "utf-8");
        iconv_charset = camel_iconv_charset_name ("utf-8");

        goto wrap_drafts_html;
    }

    /* Build the text/plain part. */

    if (priv->mime_body) {
        if (text_requires_quoted_printable (priv->mime_body, -1)) {
            context->plain_encoding =
                CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
        } else {
            context->plain_encoding = CAMEL_TRANSFER_ENCODING_7BIT;
            for (i = 0; priv->mime_body[i]; i++) {
                if ((guchar) priv->mime_body[i] > 127) {
                    context->plain_encoding =
                    CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
                    break;
                }
            }
        }

        data = g_byte_array_new ();
        g_byte_array_append (
            data, (const guint8 *) priv->mime_body,
            strlen (priv->mime_body));
        type = camel_content_type_decode (priv->mime_type);

    } else {
        gchar *text;
        EHTMLEditor *editor;
        EHTMLEditorView *view;

        editor = e_msg_composer_get_editor (composer);
        view = e_html_editor_get_view (editor);
        data = g_byte_array_new ();
        text = e_html_editor_view_get_text_plain (view);
        g_byte_array_append (data, (guint8 *) text, strlen (text));
        g_free (text);

        type = camel_content_type_new ("text", "plain");
        charset = best_charset (
            data, priv->charset, &context->plain_encoding);
        if (charset != NULL) {
            camel_content_type_set_param (type, "charset", charset);
            iconv_charset = camel_iconv_charset_name (charset);
            g_free (charset);
        }
    }

 wrap_drafts_html:
    mem_stream = camel_stream_mem_new_with_byte_array (data);
    stream = camel_stream_filter_new (mem_stream);
    g_object_unref (mem_stream);

    /* Convert the stream to the appropriate charset. */
    if (iconv_charset && g_ascii_strcasecmp (iconv_charset, "UTF-8") != 0)
        composer_add_charset_filter (stream, iconv_charset);

    /* Encode the stream to quoted-printable if necessary. */
    if (context->plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
        composer_add_quoted_printable_filter (stream);

    /* Construct the content object.  This does not block since
     * we're constructing the data wrapper from a memory stream. */
    context->top_level_part = camel_data_wrapper_new ();
    camel_data_wrapper_construct_from_stream_sync (
        context->top_level_part, stream, NULL, NULL);
    g_object_unref (stream);

    context->text_plain_part = g_object_ref (context->top_level_part);

    /* Avoid re-encoding the data when adding it to a MIME part. */
    if (context->plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
        context->top_level_part->encoding = context->plain_encoding;

    camel_data_wrapper_set_mime_type_field (
        context->top_level_part, type);

    camel_content_type_unref (type);

    /* Build the text/html part, and wrap it and the text/plain part
     * in a multipart/alternative part.  Additionally, if there are
     * inline images then wrap the multipart/alternative part along
     * with the images in a multipart/related part.
     *
     * So the structure of all this will be:
     *
     *    multipart/related
     *        multipart/alternative
     *            text/plain
     *            text/html
     *        image/<<whatever>>
     *        image/<<whatever>>
     *        ...
     */

    if ((flags & COMPOSER_FLAG_HTML_CONTENT) != 0 &&
        !(flags & COMPOSER_FLAG_SAVE_DRAFT)) {
        gchar *text;
        guint count;
        gsize length;
        gboolean pre_encode;
        EHTMLEditor *editor;
        EHTMLEditorView *view;
        GList *inline_images;

        editor = e_msg_composer_get_editor (composer);
        view = e_html_editor_get_view (editor);
        inline_images = e_html_editor_view_get_parts_for_inline_images (view, from_domain);

        data = g_byte_array_new ();
        text = e_html_editor_view_get_text_html (view);
        length = strlen (text);
        g_byte_array_append (data, (guint8 *) text, (guint) length);
        pre_encode = text_requires_quoted_printable (text, length);
        g_free (text);

        mem_stream = camel_stream_mem_new_with_byte_array (data);
        stream = camel_stream_filter_new (mem_stream);
        g_object_unref (mem_stream);

        if (pre_encode)
            composer_add_quoted_printable_filter (stream);

        /* Construct the content object.  This does not block since
         * we're constructing the data wrapper from a memory stream. */
        html = camel_data_wrapper_new ();
        camel_data_wrapper_construct_from_stream_sync (
            html, stream, NULL, NULL);
        g_object_unref (stream);

        camel_data_wrapper_set_mime_type (
            html, "text/html; charset=utf-8");

        /* Avoid re-encoding the data when adding it to a MIME part. */
        if (pre_encode)
            html->encoding =
                CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;

        /* Build the multipart/alternative */
        body = camel_multipart_new ();
        camel_data_wrapper_set_mime_type (
            CAMEL_DATA_WRAPPER (body), "multipart/alternative");
        camel_multipart_set_boundary (body, NULL);

        /* Add the text/plain part. */
        part = camel_mime_part_new ();
        camel_medium_set_content (
            CAMEL_MEDIUM (part), context->top_level_part);
        camel_mime_part_set_encoding (part, context->plain_encoding);
        camel_multipart_add_part (body, part);
        g_object_unref (part);

        /* Add the text/html part. */
        part = camel_mime_part_new ();
        camel_medium_set_content (CAMEL_MEDIUM (part), html);
        camel_mime_part_set_encoding (
            part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
        camel_multipart_add_part (body, part);
        g_object_unref (part);

        g_object_unref (context->top_level_part);
        g_object_unref (html);

        /* If there are inlined images, construct a multipart/related
         * containing the multipart/alternative and the images. */
        count = g_list_length (inline_images);
        if (count > 0) {
            guint ii;
            CamelMultipart *html_with_images;

            html_with_images = camel_multipart_new ();
            camel_data_wrapper_set_mime_type (
                CAMEL_DATA_WRAPPER (html_with_images),
                "multipart/related; "
                "type=\"multipart/alternative\"");
            camel_multipart_set_boundary (html_with_images, NULL);

            part = camel_mime_part_new ();
            camel_medium_set_content (
                CAMEL_MEDIUM (part),
                CAMEL_DATA_WRAPPER (body));
            camel_multipart_add_part (html_with_images, part);
            g_object_unref (part);

            g_object_unref (body);

            for (ii = 0; ii < count; ii++) {
                CamelMimePart *part = g_list_nth_data (inline_images, ii);
                camel_multipart_add_part (
                    html_with_images, part);
                g_object_unref (part);
            }

            context->top_level_part =
                CAMEL_DATA_WRAPPER (html_with_images);
        } else {
            context->top_level_part =
                CAMEL_DATA_WRAPPER (body);
        }
    }

    /* If there are attachments, wrap what we've built so far
     * along with the attachments in a multipart/mixed part. */
    if (e_attachment_store_get_num_attachments (store) > 0) {
        CamelMultipart *multipart = camel_multipart_new ();

        /* Generate a random boundary. */
        camel_multipart_set_boundary (multipart, NULL);

        part = camel_mime_part_new ();
        camel_medium_set_content (
            CAMEL_MEDIUM (part),
            context->top_level_part);
        if (context->top_level_part == context->text_plain_part)
            camel_mime_part_set_encoding (
                part, context->plain_encoding);
        camel_multipart_add_part (multipart, part);
        g_object_unref (part);

        e_attachment_store_add_to_multipart (
            store, multipart, priv->charset);

        g_object_unref (context->top_level_part);
        context->top_level_part = CAMEL_DATA_WRAPPER (multipart);
    }

    /* Run any blocking operations in a separate thread. */
    if (context->need_thread)
        g_simple_async_result_run_in_thread (
            simple, (GSimpleAsyncThreadFunc)
            composer_build_message_thread,
            io_priority, cancellable);
    else
        g_simple_async_result_complete (simple);

    g_object_unref (simple);
}

static CamelMimeMessage *
composer_build_message_finish (EMsgComposer *composer,
                               GAsyncResult *result,
                               GError **error)
{
    GSimpleAsyncResult *simple;
    AsyncContext *context;

    g_return_val_if_fail (
        g_simple_async_result_is_valid (
        result, G_OBJECT (composer), composer_build_message), NULL);

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

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

    /* Finalize some details before returning. */

    if (!context->skip_content)
        camel_medium_set_content (
            CAMEL_MEDIUM (context->message),
            context->top_level_part);

    if (context->top_level_part == context->text_plain_part)
        camel_mime_part_set_encoding (
            CAMEL_MIME_PART (context->message),
            context->plain_encoding);

    return g_object_ref (context->message);
}

/* Signatures */

static gboolean
use_top_signature (EMsgComposer *composer)
{
    EMsgComposerPrivate *priv;
    GSettings *settings;
    gboolean top_signature;

    priv = E_MSG_COMPOSER_GET_PRIVATE (composer);

    /* The composer had been created from a stored message, thus the
     * signature placement is either there already, or pt it at the
     * bottom regardless of a preferences (which is for reply anyway,
     * not for Edit as new) */
    if (priv->is_from_message)
        return FALSE;

    /* FIXME This should be an EMsgComposer property. */
    settings = g_settings_new ("org.gnome.evolution.mail");
    top_signature = g_settings_get_boolean (
        settings, "composer-top-signature");
    g_object_unref (settings);

    return top_signature;
}

static void
set_editor_text (EMsgComposer *composer,
                 const gchar *text,
                 gboolean set_signature)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (text != NULL);

    /*
     *
     * Keeping Signatures in the beginning of composer
     * ------------------------------------------------
     *
     * Purists are gonna blast me for this.
     * But there are so many people (read Outlook users) who want this.
     * And Evo is an exchange-client, Outlook-replacement etc.
     * So Here it goes :(
     *
     * -- Sankar
     *
     */

    /* "Edit as New Message" sets "priv->is_from_message".
     * Always put the signature at the bottom for that case. */

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);

    if (!composer->priv->is_from_message && use_top_signature (composer)) {
        gchar *body;
        /* put marker to the top */
        body = g_strdup_printf ("<BR>%s", text);
        e_html_editor_view_set_text_html (view, body);
        g_free (body);
    } else {
        e_html_editor_view_set_text_html (view, text);
    }

    if (set_signature)
        e_composer_update_signature (composer);

}

/* Miscellaneous callbacks.  */

static void
attachment_store_changed_cb (EMsgComposer *composer)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;

    /* Mark the editor as changed so it prompts about unsaved
     * changes on close. */
    editor = e_msg_composer_get_editor (composer);
    if (editor) {
        view = e_html_editor_get_view (editor);
        e_html_editor_view_set_changed (view, TRUE);
    }
}

static void
msg_composer_subject_changed_cb (EMsgComposer *composer)
{
    EComposerHeaderTable *table;
    const gchar *subject;

    table = e_msg_composer_get_header_table (composer);
    subject = e_composer_header_table_get_subject (table);

    if (subject == NULL || *subject == '\0')
        subject = _("Compose Message");

    gtk_window_set_title (GTK_WINDOW (composer), subject);
}

static void
msg_composer_mail_identity_changed_cb (EMsgComposer *composer)
{
    EMailSignatureComboBox *combo_box;
    ESourceMailComposition *mc;
    ESourceOpenPGP *pgp;
    ESourceSMIME *smime;
    EComposerHeaderTable *table;
    GtkToggleAction *action;
    ESource *source;
    gboolean active;
    gboolean can_sign;
    gboolean pgp_sign;
    gboolean smime_sign;
    gboolean smime_encrypt;
    const gchar *extension_name;
    const gchar *uid;

    table = e_msg_composer_get_header_table (composer);
    uid = e_composer_header_table_get_identity_uid (table);

    /* Silently return if no identity is selected. */
    if (uid == NULL)
        return;

    source = e_composer_header_table_ref_source (table, uid);
    g_return_if_fail (source != NULL);

    extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
    mc = e_source_get_extension (source, extension_name);

    extension_name = E_SOURCE_EXTENSION_OPENPGP;
    pgp = e_source_get_extension (source, extension_name);
    pgp_sign = e_source_openpgp_get_sign_by_default (pgp);

    extension_name = E_SOURCE_EXTENSION_SMIME;
    smime = e_source_get_extension (source, extension_name);
    smime_sign = e_source_smime_get_sign_by_default (smime);
    smime_encrypt = e_source_smime_get_encrypt_by_default (smime);

    can_sign =
        (composer->priv->mime_type == NULL) ||
        e_source_mail_composition_get_sign_imip (mc) ||
        (g_ascii_strncasecmp (
            composer->priv->mime_type,
            "text/calendar", 13) != 0);

    action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
    active = gtk_toggle_action_get_active (action);
    active &= composer->priv->is_from_message;
    active |= (can_sign && pgp_sign);
    gtk_toggle_action_set_active (action, active);

    action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
    active = gtk_toggle_action_get_active (action);
    active &= composer->priv->is_from_message;
    active |= (can_sign && smime_sign);
    gtk_toggle_action_set_active (action, active);

    action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
    active = gtk_toggle_action_get_active (action);
    active &= composer->priv->is_from_message;
    active |= smime_encrypt;
    gtk_toggle_action_set_active (action, active);

    combo_box = e_composer_header_table_get_signature_combo_box (table);
    e_mail_signature_combo_box_set_identity_uid (combo_box, uid);

    g_object_unref (source);
}

static void
msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
                                         GdkAtom *targets,
                                         gint n_targets,
                                         EMsgComposer *composer)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);

    /* Order is important here to ensure common use cases are
     * handled correctly.  See GNOME bug #603715 for details. */

    if (gtk_targets_include_uri (targets, n_targets)) {
        e_composer_paste_uris (composer, clipboard);
        return;
    }

    /* Only paste HTML content in HTML mode. */
    if (e_html_editor_view_get_html_mode (view)) {
        if (e_targets_include_html (targets, n_targets)) {
            e_composer_paste_html (composer, clipboard);
            return;
        }
    }

    if (gtk_targets_include_text (targets, n_targets)) {
        e_composer_paste_text (composer, clipboard);
        return;
    }

    if (gtk_targets_include_image (targets, n_targets, TRUE)) {
        e_composer_paste_image (composer, clipboard);
        return;
    }
}

static void
msg_composer_paste_primary_clipboard_cb (EHTMLEditorView *view,
                                         EMsgComposer *composer)
{
    GtkClipboard *clipboard;

    clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);

    gtk_clipboard_request_targets (
        clipboard, (GtkClipboardTargetsReceivedFunc)
        msg_composer_paste_clipboard_targets_cb, composer);
}

static void
msg_composer_paste_clipboard_cb (EHTMLEditorView *view,
                                 EMsgComposer *composer)
{
    GtkClipboard *clipboard;

    clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);

    gtk_clipboard_request_targets (
        clipboard, (GtkClipboardTargetsReceivedFunc)
        msg_composer_paste_clipboard_targets_cb, composer);

    g_signal_stop_emission_by_name (view, "paste-clipboard");
}

static gboolean
msg_composer_drag_motion_cb (GtkWidget *widget,
                             GdkDragContext *context,
                             gint x,
                             gint y,
                             guint time,
                             EMsgComposer *composer)
{
    EAttachmentView *view;

    view = e_msg_composer_get_attachment_view (composer);

    /* Stop the signal from propagating to GtkHtml. */
    g_signal_stop_emission_by_name (widget, "drag-motion");

    return e_attachment_view_drag_motion (view, context, x, y, time);
}

static gchar *
next_uri (guchar **uri_list,
          gint *len,
          gint *list_len)
{
    guchar *uri, *begin;

    begin = *uri_list;
    *len = 0;
    while (**uri_list && **uri_list != '\n' && **uri_list != '\r' && *list_len) {
        (*uri_list) ++;
        (*len) ++;
        (*list_len) --;
    }

    uri = (guchar *) g_strndup ((gchar *) begin, *len);

    while ((!**uri_list || **uri_list == '\n' || **uri_list == '\r') && *list_len) {
        (*uri_list) ++;
        (*list_len) --;
    }

    return (gchar *) uri;
}

static void
msg_composer_drag_data_received_cb (GtkWidget *widget,
                                    GdkDragContext *context,
                                    gint x,
                                    gint y,
                                    GtkSelectionData *selection,
                                    guint info,
                                    guint time,
                                    EMsgComposer *composer)
{
    GdkAtom atom;
    gchar *name;
    EAttachmentView *view;
    EHTMLEditor *editor;
    EHTMLEditorView *html_editor_view;
    EHTMLEditorSelection *editor_selection;

    editor = e_msg_composer_get_editor (composer);
    html_editor_view = e_html_editor_get_view (editor);
    editor_selection = e_html_editor_view_get_selection (html_editor_view);

    atom = gtk_selection_data_get_target (selection);
    name = gdk_atom_name (atom);

    if (g_strcmp0 (name, "UTF8_STRING") == 0 || g_strcmp0 (name, "text/html") == 0) {
        gboolean is_text;
        const guchar *data;
        gint length;
        gint list_len, len;
        gchar *text;

        is_text = g_strcmp0 (name, "UTF8_STRING") == 0;

        data = gtk_selection_data_get_data (selection);
        length = gtk_selection_data_get_length (selection);

        if (!data || length < 0) {
            g_free (name);
            return;
        }

        list_len = length;
        do {
            text = next_uri ((guchar **) &data, &len, &list_len);
            if (is_text)
                e_html_editor_selection_insert_text (editor_selection, text);
            else
                e_html_editor_selection_insert_html (editor_selection, text);
        } while (list_len);

        e_html_editor_view_check_magic_links (html_editor_view, FALSE);
        e_html_editor_view_force_spell_check (html_editor_view);

        e_html_editor_selection_scroll_to_caret (editor_selection);

        /* Stop the signal from propagating */
        g_signal_stop_emission_by_name (widget, "drag-data-received");
        g_free (name);
        return;
    }

    g_free (name);

    /* HTML mode has a few special cases for drops... */
    if (e_html_editor_view_get_html_mode (html_editor_view)) {
        /* If we're receiving an image, we want the image to be
         * inserted in the message body.  Let GtkHtml handle it. */
        /* FIXME WebKit - how to reproduce this?
        if (gtk_selection_data_targets_include_image (selection, TRUE))
            return;
         */
        /* If we're receiving URIs and -all- the URIs point to
         * image files, we want the image(s) to be inserted in
         * the message body. */
        if (e_composer_selection_is_image_uris (composer, selection)) {
            const guchar *data;
            gint length;
            gint list_len, len;
            gchar *uri;

            data = gtk_selection_data_get_data (selection);
            length = gtk_selection_data_get_length (selection);

            if (!data || length < 0)
                return;

            list_len = length;
            do {
                uri = next_uri ((guchar **) &data, &len, &list_len);
                e_html_editor_selection_insert_image (editor_selection, uri);
            } while (list_len);
        }

        if (e_composer_selection_is_base64_uris (composer, selection)) {
            const guchar *data;
            gint length;
            gint list_len, len;
            gchar *uri;

            data = gtk_selection_data_get_data (selection);
            length = gtk_selection_data_get_length (selection);

            if (!data || length < 0)
                return;

            list_len = length;
            do {
                uri = next_uri ((guchar **) &data, &len, &list_len);

                e_html_editor_selection_insert_image (editor_selection, uri);
            } while (list_len);
        }
    } else {
        view = e_msg_composer_get_attachment_view (composer);

        /* Forward the data to the attachment view.  Note that calling
         * e_attachment_view_drag_data_received() will not work because
         * that function only handles the case where all the other drag
         * handlers have failed. */
        e_attachment_paned_drag_data_received (
            E_ATTACHMENT_PANED (view),
            context, x, y, selection, info, time);
    }
    /* Stop the signal from propagating */
    g_signal_stop_emission_by_name (widget, "drag-data-received");
}

static void
msg_composer_notify_header_cb (EMsgComposer *composer)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    e_html_editor_view_set_changed (view, TRUE);
}

static gboolean
msg_composer_delete_event_cb (EMsgComposer *composer)
{
    EShell *shell;
    GtkApplication *application;
    GList *windows;

    shell = e_msg_composer_get_shell (composer);

    /* If the "async" action group is insensitive, it means an
     * asynchronous operation is in progress.  Block the event. */
    if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
        return TRUE;

    application = GTK_APPLICATION (shell);
    windows = gtk_application_get_windows (application);

    if (g_list_length (windows) == 1) {
        /* This is the last watched window, use the quit
         * mechanism to have a draft saved properly */
        e_shell_quit (shell, E_SHELL_QUIT_ACTION);
    } else {
        /* There are more watched windows opened,
         * invoke only a close action */
        gtk_action_activate (ACTION (CLOSE));
    }

    return TRUE;
}

static void
msg_composer_prepare_for_quit_cb (EShell *shell,
                                  EActivity *activity,
                                  EMsgComposer *composer)
{
    if (e_msg_composer_is_exiting (composer)) {
        /* needs save draft first */
        g_object_ref (activity);
        g_object_weak_ref (
            G_OBJECT (composer), (GWeakNotify)
            g_object_unref, activity);
        gtk_action_activate (ACTION (SAVE_DRAFT));
    }
}

static void
msg_composer_quit_requested_cb (EShell *shell,
                                EShellQuitReason reason,
                                EMsgComposer *composer)
{
    if (e_msg_composer_is_exiting (composer)) {
        g_signal_handlers_disconnect_by_func (
            shell, msg_composer_quit_requested_cb, composer);
        g_signal_handlers_disconnect_by_func (
            shell, msg_composer_prepare_for_quit_cb, composer);
    } else if (!e_msg_composer_can_close (composer, FALSE) &&
            !e_msg_composer_is_exiting (composer)) {
        e_shell_cancel_quit (shell);
    }
}

static void
msg_composer_set_shell (EMsgComposer *composer,
                        EShell *shell)
{
    g_return_if_fail (E_IS_SHELL (shell));
    g_return_if_fail (composer->priv->shell == NULL);

    composer->priv->shell = shell;

    g_object_add_weak_pointer (
        G_OBJECT (shell), &composer->priv->shell);
}

static void
msg_composer_set_property (GObject *object,
                           guint property_id,
                           const GValue *value,
                           GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_SHELL:
            msg_composer_set_shell (
                E_MSG_COMPOSER (object),
                g_value_get_object (value));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
msg_composer_get_property (GObject *object,
                           guint property_id,
                           GValue *value,
                           GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_BUSY:
            g_value_set_boolean (
                value, e_msg_composer_is_busy (
                E_MSG_COMPOSER (object)));
            return;

        case PROP_FOCUS_TRACKER:
            g_value_set_object (
                value, e_msg_composer_get_focus_tracker (
                E_MSG_COMPOSER (object)));
            return;

        case PROP_SHELL:
            g_value_set_object (
                value, e_msg_composer_get_shell (
                E_MSG_COMPOSER (object)));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
msg_composer_finalize (GObject *object)
{
    EMsgComposer *composer = E_MSG_COMPOSER (object);

    e_composer_private_finalize (composer);

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

static void
msg_composer_gallery_drag_data_get (GtkIconView *icon_view,
                                    GdkDragContext *context,
                                    GtkSelectionData *selection_data,
                                    guint target_type,
                                    guint time)
{
    GtkTreePath *path;
    GtkCellRenderer *cell;
    GtkTreeModel *model;
    GtkTreeIter iter;
    GdkAtom target;
    gchar *str_data;

    if (!gtk_icon_view_get_cursor (icon_view, &path, &cell))
        return;

    target = gtk_selection_data_get_target (selection_data);

    model = gtk_icon_view_get_model (icon_view);
    gtk_tree_model_get_iter (model, &iter, path);
    gtk_tree_model_get (model, &iter, 1, &str_data, -1);
    gtk_tree_path_free (path);

    /* only supports "text/uri-list" */
    gtk_selection_data_set (
        selection_data, target, 8,
        (guchar *) str_data, strlen (str_data));
    g_free (str_data);
}

static void
composer_notify_activity_cb (EActivityBar *activity_bar,
                             GParamSpec *pspec,
                             EMsgComposer *composer)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    WebKitWebView *web_view;
    gboolean editable;
    gboolean busy;

    busy = (e_activity_bar_get_activity (activity_bar) != NULL);

    if (busy == composer->priv->busy)
        return;

    composer->priv->busy = busy;

    if (busy)
        e_msg_composer_save_focused_widget (composer);

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    web_view = WEBKIT_WEB_VIEW (view);

    if (busy) {
        editable = webkit_web_view_get_editable (web_view);
        webkit_web_view_set_editable (web_view, FALSE);
        composer->priv->saved_editable = editable;
    } else {
        editable = composer->priv->saved_editable;
        webkit_web_view_set_editable (web_view, editable);
    }

    g_object_notify (G_OBJECT (composer), "busy");

    if (!busy)
        e_msg_composer_restore_focus_on_composer (composer);
}

static void
msg_composer_constructed (GObject *object)
{
    EShell *shell;
    EMsgComposer *composer;
    EActivityBar *activity_bar;
    EAttachmentView *view;
    EAttachmentStore *store;
    EComposerHeaderTable *table;
    EHTMLEditor *editor;
    EHTMLEditorView *html_editor_view;
    GtkUIManager *ui_manager;
    GtkToggleAction *action;
    GSettings *settings;
    const gchar *id;
    gboolean active;

    composer = E_MSG_COMPOSER (object);

    shell = e_msg_composer_get_shell (composer);

    e_composer_private_constructed (composer);

    editor = e_msg_composer_get_editor (composer);
    html_editor_view = e_html_editor_get_view (editor);
    ui_manager = e_html_editor_get_ui_manager (editor);
    view = e_msg_composer_get_attachment_view (composer);
    table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table);

    gtk_window_set_title (GTK_WINDOW (composer), _("Compose Message"));
    gtk_window_set_icon_name (GTK_WINDOW (composer), "mail-message-new");
    gtk_window_set_default_size (GTK_WINDOW (composer), 600, 500);

    g_signal_connect (
        object, "delete-event",
        G_CALLBACK (msg_composer_delete_event_cb), NULL);

    gtk_application_add_window (
        GTK_APPLICATION (shell), GTK_WINDOW (object));

    g_signal_connect (
        shell, "quit-requested",
        G_CALLBACK (msg_composer_quit_requested_cb), composer);

    g_signal_connect (
        shell, "prepare-for-quit",
        G_CALLBACK (msg_composer_prepare_for_quit_cb), composer);

    /* Restore Persistent State */

    e_restore_window (
        GTK_WINDOW (composer),
        "/org/gnome/evolution/mail/composer-window/",
        E_RESTORE_WINDOW_SIZE);

    activity_bar = e_html_editor_get_activity_bar (editor);
    g_signal_connect (
        activity_bar, "notify::activity",
        G_CALLBACK (composer_notify_activity_cb), composer);


    /* Honor User Preferences */

    /* FIXME This should be an EMsgComposer property. */
    settings = g_settings_new ("org.gnome.evolution.mail");
    action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT));
    active = g_settings_get_boolean (settings, "composer-request-receipt");
    gtk_toggle_action_set_active (action, active);
    g_object_unref (settings);

    /* Clipboard Support */

    g_signal_connect (
        html_editor_view, "paste-clipboard",
        G_CALLBACK (msg_composer_paste_clipboard_cb), composer);

    g_signal_connect (
        html_editor_view, "paste-primary-clipboard",
        G_CALLBACK (msg_composer_paste_primary_clipboard_cb), composer);

    /* Drag-and-Drop Support */

    g_signal_connect (
        html_editor_view, "drag-motion",
        G_CALLBACK (msg_composer_drag_motion_cb), composer);

    g_signal_connect (
        html_editor_view, "drag-data-received",
        G_CALLBACK (msg_composer_drag_data_received_cb), composer);

    g_signal_connect (
        composer->priv->gallery_icon_view, "drag-data-get",
        G_CALLBACK (msg_composer_gallery_drag_data_get), NULL);

    /* Configure Headers */

    e_signal_connect_notify_swapped (
        table, "notify::destinations-bcc",
        G_CALLBACK (msg_composer_notify_header_cb), composer);
    e_signal_connect_notify_swapped (
        table, "notify::destinations-cc",
        G_CALLBACK (msg_composer_notify_header_cb), composer);
    e_signal_connect_notify_swapped (
        table, "notify::destinations-to",
        G_CALLBACK (msg_composer_notify_header_cb), composer);
    e_signal_connect_notify_swapped (
        table, "notify::identity-uid",
        G_CALLBACK (msg_composer_mail_identity_changed_cb), composer);
    e_signal_connect_notify_swapped (
        table, "notify::reply-to",
        G_CALLBACK (msg_composer_notify_header_cb), composer);
    e_signal_connect_notify_swapped (
        table, "notify::signature-uid",
        G_CALLBACK (e_composer_update_signature), composer);
    e_signal_connect_notify_swapped (
        table, "notify::subject",
        G_CALLBACK (msg_composer_subject_changed_cb), composer);
    e_signal_connect_notify_swapped (
        table, "notify::subject",
        G_CALLBACK (msg_composer_notify_header_cb), composer);

    msg_composer_mail_identity_changed_cb (composer);

    /* Attachments */

    store = e_attachment_view_get_store (view);

    g_signal_connect_swapped (
        store, "row-deleted",
        G_CALLBACK (attachment_store_changed_cb), composer);

    g_signal_connect_swapped (
        store, "row-inserted",
        G_CALLBACK (attachment_store_changed_cb), composer);

    /* Initialization may have tripped the "changed" state. */
    e_html_editor_view_set_changed (html_editor_view, FALSE);

    gtk_drag_dest_set (
        GTK_WIDGET (html_editor_view),
        GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
        drag_dest_targets, G_N_ELEMENTS (drag_dest_targets),
        GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);

    id = "org.gnome.evolution.composer";
    e_plugin_ui_register_manager (ui_manager, id, composer);
    e_plugin_ui_enable_manager (ui_manager, id);

    e_extensible_load_extensions (E_EXTENSIBLE (composer));

    e_msg_composer_set_body_text (composer, "", TRUE);
    /* Chain up to parent's constructed() method. */
    G_OBJECT_CLASS (e_msg_composer_parent_class)->constructed (object);
}

static void
msg_composer_dispose (GObject *object)
{
    EMsgComposer *composer = E_MSG_COMPOSER (object);
    EShell *shell;

    if (composer->priv->address_dialog != NULL) {
        gtk_widget_destroy (composer->priv->address_dialog);
        composer->priv->address_dialog = NULL;
    }

    /* FIXME Our EShell is already unreferenced. */
    shell = e_shell_get_default ();

    g_signal_handlers_disconnect_by_func (
        shell, msg_composer_quit_requested_cb, composer);
    g_signal_handlers_disconnect_by_func (
        shell, msg_composer_prepare_for_quit_cb, composer);

    e_composer_private_dispose (composer);

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

static void
msg_composer_map (GtkWidget *widget)
{
    EMsgComposer *composer;
    EComposerHeaderTable *table;
    GtkWidget *input_widget;
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    const gchar *text;

    /* Chain up to parent's map() method. */
    GTK_WIDGET_CLASS (e_msg_composer_parent_class)->map (widget);

    composer = E_MSG_COMPOSER (widget);
    editor = e_msg_composer_get_editor (composer);
    table = e_msg_composer_get_header_table (composer);

    /* If the 'To' field is empty, focus it. */
    input_widget =
        e_composer_header_table_get_header (
        table, E_COMPOSER_HEADER_TO)->input_widget;
    text = gtk_entry_get_text (GTK_ENTRY (input_widget));
    if (gtk_widget_get_visible (input_widget) && (text == NULL || *text == '\0')) {
        gtk_widget_grab_focus (input_widget);
        return;
    }

    /* If not, check the 'Subject' field. */
    input_widget =
        e_composer_header_table_get_header (
        table, E_COMPOSER_HEADER_SUBJECT)->input_widget;
    text = gtk_entry_get_text (GTK_ENTRY (input_widget));
    if (gtk_widget_get_visible (input_widget) && (text == NULL || *text == '\0')) {
        gtk_widget_grab_focus (input_widget);
        return;
    }

    /* Jump to the editor as a last resort. */
    view = e_html_editor_get_view (editor);
    gtk_widget_grab_focus (GTK_WIDGET (view));
}

static gboolean
msg_composer_key_press_event (GtkWidget *widget,
                              GdkEventKey *event)
{
    EMsgComposer *composer;
    GtkWidget *input_widget;
    EHTMLEditor *editor;
    EHTMLEditorView *view;

    composer = E_MSG_COMPOSER (widget);
    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);

    input_widget =
        e_composer_header_table_get_header (
        e_msg_composer_get_header_table (composer),
        E_COMPOSER_HEADER_SUBJECT)->input_widget;

#ifdef HAVE_XFREE
    if (event->keyval == XF86XK_Send) {
        e_msg_composer_send (composer);
        return TRUE;
    }
#endif /* HAVE_XFREE */

    if (event->keyval == GDK_KEY_Escape) {
        gtk_action_activate (ACTION (CLOSE));
        return TRUE;
    }

    if (event->keyval == GDK_KEY_Tab && gtk_widget_is_focus (input_widget)) {
        gtk_widget_grab_focus (GTK_WIDGET (view));
        return TRUE;
    }

    if (gtk_widget_is_focus (GTK_WIDGET (view))) {
        if (event->keyval == GDK_KEY_ISO_Left_Tab) {
            gtk_widget_grab_focus (input_widget);
            return TRUE;
        }

        if ((((event)->state & GDK_SHIFT_MASK) &&
            ((event)->keyval == GDK_KEY_Insert)) ||
            (((event)->state & GDK_CONTROL_MASK) &&
            ((event)->keyval == GDK_KEY_v))) {
            g_signal_emit_by_name (
                WEBKIT_WEB_VIEW (view), "paste-clipboard");
            return TRUE;
        }

        if (((event)->state & GDK_CONTROL_MASK) &&
            ((event)->keyval == GDK_KEY_Insert)) {
            g_signal_emit_by_name (
                WEBKIT_WEB_VIEW (view), "copy-clipboard");
            return TRUE;
        }

        if (((event)->state & GDK_SHIFT_MASK) &&
            ((event)->keyval == GDK_KEY_Delete)) {
            g_signal_emit_by_name (
                WEBKIT_WEB_VIEW (view), "cut-clipboard");
            return TRUE;
        }
    }

    /* Chain up to parent's key_press_event() method. */
    return GTK_WIDGET_CLASS (e_msg_composer_parent_class)->
        key_press_event (widget, event);
}

static gboolean
msg_composer_presend (EMsgComposer *composer)
{
    /* This keeps the signal accumulator at TRUE. */
    return TRUE;
}

static gboolean
msg_composer_accumulator_false_abort (GSignalInvocationHint *ihint,
                                      GValue *return_accu,
                                      const GValue *handler_return,
                                      gpointer dummy)
{
    gboolean v_boolean;

    v_boolean = g_value_get_boolean (handler_return);
    g_value_set_boolean (return_accu, v_boolean);

    /* FALSE means abort the signal emission. */
    return v_boolean;
}

/**
 * e_msg_composer_is_busy:
 * @composer: an #EMsgComposer
 *
 * Returns %TRUE only while an #EActivity is in progress.
 *
 * Returns: whether @composer is busy
 **/
gboolean
e_msg_composer_is_busy (EMsgComposer *composer)
{
    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);

    return composer->priv->busy;
}

static void
e_msg_composer_class_init (EMsgComposerClass *class)
{
    GObjectClass *object_class;
    GtkWidgetClass *widget_class;

    g_type_class_add_private (class, sizeof (EMsgComposerPrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->set_property = msg_composer_set_property;
    object_class->get_property = msg_composer_get_property;
    object_class->dispose = msg_composer_dispose;
    object_class->finalize = msg_composer_finalize;
    object_class->constructed = msg_composer_constructed;

    widget_class = GTK_WIDGET_CLASS (class);
    widget_class->map = msg_composer_map;
    widget_class->key_press_event = msg_composer_key_press_event;

    class->presend = msg_composer_presend;

    g_object_class_install_property (
        object_class,
        PROP_BUSY,
        g_param_spec_boolean (
            "busy",
            "Busy",
            "Whether an activity is in progress",
            FALSE,
            G_PARAM_READABLE |
            G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        object_class,
        PROP_FOCUS_TRACKER,
        g_param_spec_object (
            "focus-tracker",
            NULL,
            NULL,
            E_TYPE_FOCUS_TRACKER,
            G_PARAM_READABLE));

    g_object_class_install_property (
        object_class,
        PROP_SHELL,
        g_param_spec_object (
            "shell",
            "Shell",
            "The EShell singleton",
            E_TYPE_SHELL,
            G_PARAM_READWRITE |
            G_PARAM_CONSTRUCT_ONLY));

    signals[PRESEND] = g_signal_new (
        "presend",
        G_OBJECT_CLASS_TYPE (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (EMsgComposerClass, presend),
        msg_composer_accumulator_false_abort,
        NULL,
        e_marshal_BOOLEAN__VOID,
        G_TYPE_BOOLEAN, 0);

    signals[SEND] = g_signal_new (
        "send",
        G_OBJECT_CLASS_TYPE (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (EMsgComposerClass, send),
        NULL, NULL,
        e_marshal_VOID__OBJECT_OBJECT,
        G_TYPE_NONE, 2,
        CAMEL_TYPE_MIME_MESSAGE,
        E_TYPE_ACTIVITY);

    signals[SAVE_TO_DRAFTS] = g_signal_new (
        "save-to-drafts",
        G_OBJECT_CLASS_TYPE (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (EMsgComposerClass, save_to_drafts),
        NULL, NULL,
        e_marshal_VOID__OBJECT_OBJECT,
        G_TYPE_NONE, 2,
        CAMEL_TYPE_MIME_MESSAGE,
        E_TYPE_ACTIVITY);

    signals[SAVE_TO_OUTBOX] = g_signal_new (
        "save-to-outbox",
        G_OBJECT_CLASS_TYPE (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (EMsgComposerClass, save_to_outbox),
        NULL, NULL,
        e_marshal_VOID__OBJECT_OBJECT,
        G_TYPE_NONE, 2,
        CAMEL_TYPE_MIME_MESSAGE,
        E_TYPE_ACTIVITY);

    signals[PRINT] = g_signal_new (
        "print",
        G_OBJECT_CLASS_TYPE (class),
        G_SIGNAL_RUN_LAST,
        0, NULL, NULL,
        e_marshal_VOID__ENUM_OBJECT_OBJECT,
        G_TYPE_NONE, 3,
        GTK_TYPE_PRINT_OPERATION_ACTION,
        CAMEL_TYPE_MIME_MESSAGE,
        E_TYPE_ACTIVITY);
}

static void
e_msg_composer_init (EMsgComposer *composer)
{
    composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer);

    composer->priv->editor = g_object_ref_sink (e_html_editor_new ());
}

/**
 * e_msg_composer_new:
 * @shell: an #EShell
 *
 * Create a new message composer widget.
 *
 * Returns: A pointer to the newly created widget
 **/
EMsgComposer *
e_msg_composer_new (EShell *shell)
{
    g_return_val_if_fail (E_IS_SHELL (shell), NULL);

    return g_object_new (
        E_TYPE_MSG_COMPOSER,
        "shell", shell, NULL);
}

/**
 * e_msg_composer_get_editor:
 * @composer: an #EMsgComposer
 *
 * Returns @composer's internal #EHTMLEditor instance.
 *
 * Returns: an #EHTMLEditor
 **/
EHTMLEditor *
e_msg_composer_get_editor (EMsgComposer *composer)
{
    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    return composer->priv->editor;
}

EFocusTracker *
e_msg_composer_get_focus_tracker (EMsgComposer *composer)
{
    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    return composer->priv->focus_tracker;
}

static void
e_msg_composer_set_pending_body (EMsgComposer *composer,
                                 gchar *text,
                                 gssize length)
{
    g_object_set_data_full (
        G_OBJECT (composer), "body:text",
        text, (GDestroyNotify) g_free);
}

static void
e_msg_composer_flush_pending_body (EMsgComposer *composer)
{
    const gchar *body;

    body = g_object_get_data (G_OBJECT (composer), "body:text");

    if (body != NULL)
        set_editor_text (composer, body, FALSE);

    g_object_set_data (G_OBJECT (composer), "body:text", NULL);
}

static void
add_attachments_handle_mime_part (EMsgComposer *composer,
                                  CamelMimePart *mime_part,
                                  gboolean just_inlines,
                                  gboolean related,
                                  gint depth)
{
    CamelContentType *content_type;
    CamelDataWrapper *wrapper;
    EHTMLEditor *editor;
    EHTMLEditorView *view;

    if (!mime_part)
        return;

    content_type = camel_mime_part_get_content_type (mime_part);
    wrapper = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);

    if (CAMEL_IS_MULTIPART (wrapper)) {
        /* another layer of multipartness... */
        add_attachments_from_multipart (
            composer, (CamelMultipart *) wrapper,
            just_inlines, depth + 1);
    } else if (just_inlines) {
        if (camel_mime_part_get_content_id (mime_part) ||
            camel_mime_part_get_content_location (mime_part))
            e_html_editor_view_add_inline_image_from_mime_part (
                view, mime_part);
    } else if (related && camel_content_type_is (content_type, "image", "*")) {
        e_html_editor_view_add_inline_image_from_mime_part (view, mime_part);
    } else if (camel_content_type_is (content_type, "text", "*") &&
        camel_mime_part_get_filename (mime_part) == NULL) {
        /* Do nothing if this is a text/anything without a
         * filename, otherwise attach it too. */
    } else {
        e_msg_composer_attach (composer, mime_part);
    }
}

static void
add_attachments_from_multipart (EMsgComposer *composer,
                                CamelMultipart *multipart,
                                gboolean just_inlines,
                                gint depth)
{
    /* find appropriate message attachments to add to the composer */
    CamelMimePart *mime_part;
    gboolean related;
    gint i, nparts;

    related = camel_content_type_is (
        CAMEL_DATA_WRAPPER (multipart)->mime_type,
        "multipart", "related");

    if (CAMEL_IS_MULTIPART_SIGNED (multipart)) {
        mime_part = camel_multipart_get_part (
            multipart, CAMEL_MULTIPART_SIGNED_CONTENT);
        add_attachments_handle_mime_part (
            composer, mime_part, just_inlines, related, depth);
    } else if (CAMEL_IS_MULTIPART_ENCRYPTED (multipart)) {
        /* XXX What should we do in this case? */
    } else {
        nparts = camel_multipart_get_number (multipart);

        for (i = 0; i < nparts; i++) {
            mime_part = camel_multipart_get_part (multipart, i);
            add_attachments_handle_mime_part (
                composer, mime_part, just_inlines,
                related, depth);
        }
    }
}

/**
 * e_msg_composer_add_message_attachments:
 * @composer: the composer to add the attachments to.
 * @message: the source message to copy the attachments from.
 * @just_inlines: whether to attach all attachments or just add
 * inline images.
 *
 * Walk through all the mime parts in @message and add them to the composer
 * specified in @composer.
 */
void
e_msg_composer_add_message_attachments (EMsgComposer *composer,
                                        CamelMimeMessage *message,
                                        gboolean just_inlines)
{
    CamelDataWrapper *wrapper;

    wrapper = camel_medium_get_content (CAMEL_MEDIUM (message));
    if (!CAMEL_IS_MULTIPART (wrapper))
        return;

    add_attachments_from_multipart (
        composer, (CamelMultipart *) wrapper, just_inlines, 0);
}

static void
handle_multipart_signed (EMsgComposer *composer,
                         CamelMultipart *multipart,
                         gboolean keep_signature,
                         GCancellable *cancellable,
                         gint depth)
{
    CamelContentType *content_type;
    CamelDataWrapper *content;
    CamelMimePart *mime_part;
    GtkToggleAction *action = NULL;
    const gchar *protocol;

    content = CAMEL_DATA_WRAPPER (multipart);
    content_type = camel_data_wrapper_get_mime_type_field (content);
    protocol = camel_content_type_param (content_type, "protocol");

    if (protocol == NULL)
        action = NULL;
    else if (g_ascii_strcasecmp (protocol, "application/pgp-signature") == 0)
        action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
    else if (g_ascii_strcasecmp (protocol, "application/x-pkcs7-signature") == 0)
        action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));

    if (action)
        gtk_toggle_action_set_active (action, TRUE);

    mime_part = camel_multipart_get_part (
        multipart, CAMEL_MULTIPART_SIGNED_CONTENT);

    if (mime_part == NULL)
        return;

    content_type = camel_mime_part_get_content_type (mime_part);
    content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));

    if (CAMEL_IS_MULTIPART (content)) {
        multipart = CAMEL_MULTIPART (content);

        /* Note: depth is preserved here because we're not
         * counting multipart/signed as a multipart, instead
         * we want to treat the content part as our mime part
         * here. */

        if (CAMEL_IS_MULTIPART_SIGNED (content)) {
            /* Handle the signed content and configure
             * the composer to sign outgoing messages. */
            handle_multipart_signed (
                composer, multipart, keep_signature, cancellable, depth);

        } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) {
            /* Decrypt the encrypted content and configure
             * the composer to encrypt outgoing messages. */
            handle_multipart_encrypted (
                composer, mime_part, keep_signature, cancellable, depth);

        } else if (camel_content_type_is (content_type, "multipart", "alternative")) {
            /* This contains the text/plain and text/html
             * versions of the message body. */
            handle_multipart_alternative (
                composer, multipart, keep_signature, cancellable, depth);

        } else {
            /* There must be attachments... */
            handle_multipart (
                composer, multipart, keep_signature, cancellable, depth);
        }

    } else if (camel_content_type_is (content_type, "text", "*")) {
        gchar *html;
        gssize length;

        html = emcu_part_to_html (
            composer, mime_part, &length, keep_signature, cancellable);
        if (html)
            e_msg_composer_set_pending_body (composer, html, length);

    } else {
        e_msg_composer_attach (composer, mime_part);
    }
}

static void
handle_multipart_encrypted (EMsgComposer *composer,
                            CamelMimePart *multipart,
                            gboolean keep_signature,
                            GCancellable *cancellable,
                            gint depth)
{
    CamelContentType *content_type;
    CamelCipherContext *cipher;
    CamelDataWrapper *content;
    CamelMimePart *mime_part;
    CamelSession *session;
    CamelCipherValidity *valid;
    GtkToggleAction *action = NULL;
    const gchar *protocol;

    content_type = camel_mime_part_get_content_type (multipart);
    protocol = camel_content_type_param (content_type, "protocol");

    if (protocol && g_ascii_strcasecmp (protocol, "application/pgp-encrypted") == 0)
        action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
    else if (content_type && (
            camel_content_type_is (content_type, "application", "x-pkcs7-mime")
         || camel_content_type_is (content_type, "application", "pkcs7-mime")))
        action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));

    if (action)
        gtk_toggle_action_set_active (action, TRUE);

    session = e_msg_composer_ref_session (composer);
    cipher = camel_gpg_context_new (session);
    mime_part = camel_mime_part_new ();
    valid = camel_cipher_context_decrypt_sync (
        cipher, multipart, mime_part, cancellable, NULL);
    g_object_unref (cipher);
    g_object_unref (session);

    if (valid == NULL)
        return;

    camel_cipher_validity_free (valid);

    content_type = camel_mime_part_get_content_type (mime_part);

    content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));

    if (CAMEL_IS_MULTIPART (content)) {
        CamelMultipart *content_multipart = CAMEL_MULTIPART (content);

        /* Note: depth is preserved here because we're not
         * counting multipart/encrypted as a multipart, instead
         * we want to treat the content part as our mime part
         * here. */

        if (CAMEL_IS_MULTIPART_SIGNED (content)) {
            /* Handle the signed content and configure the
             * composer to sign outgoing messages. */
            handle_multipart_signed (
                composer, content_multipart, keep_signature, cancellable, depth);

        } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) {
            /* Decrypt the encrypted content and configure the
             * composer to encrypt outgoing messages. */
            handle_multipart_encrypted (
                composer, mime_part, keep_signature, cancellable, depth);

        } else if (camel_content_type_is (content_type, "multipart", "alternative")) {
            /* This contains the text/plain and text/html
             * versions of the message body. */
            handle_multipart_alternative (
                composer, content_multipart, keep_signature, cancellable, depth);

        } else {
            /* There must be attachments... */
            handle_multipart (
                composer, content_multipart, keep_signature, cancellable, depth);
        }

    } else if (camel_content_type_is (content_type, "text", "*")) {
        gchar *html;
        gssize length;

        html = emcu_part_to_html (
            composer, mime_part, &length, keep_signature, cancellable);
        if (html)
            e_msg_composer_set_pending_body (composer, html, length);

    } else {
        e_msg_composer_attach (composer, mime_part);
    }

    g_object_unref (mime_part);
}

static void
handle_multipart_alternative (EMsgComposer *composer,
                              CamelMultipart *multipart,
                              gboolean keep_signature,
                              GCancellable *cancellable,
                              gint depth)
{
    /* Find the text/html part and set the composer body to its content */
    CamelMimePart *text_part = NULL, *fallback_text_part = NULL;
    gint i, nparts;

    nparts = camel_multipart_get_number (multipart);

    for (i = 0; i < nparts; i++) {
        CamelContentType *content_type;
        CamelDataWrapper *content;
        CamelMimePart *mime_part;

        mime_part = camel_multipart_get_part (multipart, i);

        if (!mime_part)
            continue;

        content_type = camel_mime_part_get_content_type (mime_part);
        content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));

        if (CAMEL_IS_MULTIPART (content)) {
            CamelMultipart *mp;

            mp = CAMEL_MULTIPART (content);

            if (CAMEL_IS_MULTIPART_SIGNED (content)) {
                /* Handle the signed content and configure
                 * the composer to sign outgoing messages. */
                handle_multipart_signed (
                    composer, mp, keep_signature, cancellable, depth + 1);

            } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) {
                /* Decrypt the encrypted content and configure
                 * the composer to encrypt outgoing messages. */
                handle_multipart_encrypted (
                    composer, mime_part, keep_signature,
                    cancellable, depth + 1);

            } else {
                /* Depth doesn't matter so long as we
                 * don't pass 0. */
                handle_multipart (
                    composer, mp, keep_signature, cancellable, depth + 1);
            }

        } else if (camel_content_type_is (content_type, "text", "html")) {
            /* text/html is preferable, so once we find it we're done... */
            text_part = mime_part;
            break;
        } else if (camel_content_type_is (content_type, "text", "*")) {
            /* anyt text part not text/html is second rate so the first
             * text part we find isn't necessarily the one we'll use. */
            if (!text_part)
                text_part = mime_part;

            /* this is when prefer-plain filters out text/html part, then
             * the text/plain should be used */
            if (camel_content_type_is (content_type, "text", "plain"))
                fallback_text_part = mime_part;
        } else {
            e_msg_composer_attach (composer, mime_part);
        }
    }

    if (text_part) {
        gchar *html;
        gssize length;

        html = emcu_part_to_html (
            composer, text_part, &length, keep_signature, cancellable);
        if (!html && fallback_text_part)
            html = emcu_part_to_html (
                composer, fallback_text_part, &length, keep_signature, cancellable);
        if (html)
            e_msg_composer_set_pending_body (composer, html, length);
    }
}

static void
handle_multipart (EMsgComposer *composer,
                  CamelMultipart *multipart,
                  gboolean keep_signature,
                  GCancellable *cancellable,
                  gint depth)
{
    gint i, nparts;

    nparts = camel_multipart_get_number (multipart);

    for (i = 0; i < nparts; i++) {
        CamelContentType *content_type;
        CamelDataWrapper *content;
        CamelMimePart *mime_part;

        mime_part = camel_multipart_get_part (multipart, i);

        if (!mime_part)
            continue;

        content_type = camel_mime_part_get_content_type (mime_part);
        content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));

        if (CAMEL_IS_MULTIPART (content)) {
            CamelMultipart *mp;

            mp = CAMEL_MULTIPART (content);

            if (CAMEL_IS_MULTIPART_SIGNED (content)) {
                /* Handle the signed content and configure
                 * the composer to sign outgoing messages. */
                handle_multipart_signed (
                    composer, mp, keep_signature, cancellable, depth + 1);

            } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) {
                /* Decrypt the encrypted content and configure
                 * the composer to encrypt outgoing messages. */
                handle_multipart_encrypted (
                    composer, mime_part, keep_signature,
                    cancellable, depth + 1);

            } else if (camel_content_type_is (
                content_type, "multipart", "alternative")) {
                handle_multipart_alternative (
                    composer, mp, keep_signature, cancellable, depth + 1);

            } else {
                /* Depth doesn't matter so long as we
                 * don't pass 0. */
                handle_multipart (
                    composer, mp, keep_signature, cancellable, depth + 1);
            }

        } else if (depth == 0 && i == 0) {
            gchar *html;
            gssize length;

            /* Since the first part is not multipart/alternative,
             * this must be the body. */

            /* If we are opening message from Drafts */
            if (composer->priv->is_from_draft) {
                /* Extract the body */
                CamelDataWrapper *dw;

                dw = camel_medium_get_content ((CamelMedium *) mime_part);
                if (dw) {
                    CamelStream *mem = camel_stream_mem_new ();
                    GByteArray *bytes;

                    camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL);
                    camel_stream_close (mem, cancellable, NULL);

                    bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
                    if (bytes && bytes->len)
                        html = g_strndup ((const gchar *) bytes->data, bytes->len);

                    g_object_unref (mem);
                }
            } else {
                html = emcu_part_to_html (
                    composer, mime_part, &length, keep_signature, cancellable);
            }
            e_msg_composer_set_pending_body (composer, html, length);

        } else if (camel_mime_part_get_content_id (mime_part) ||
               camel_mime_part_get_content_location (mime_part)) {
            /* special in-line attachment */
            EHTMLEditor *editor;

            editor = e_msg_composer_get_editor (composer);
            e_html_editor_view_add_inline_image_from_mime_part (
                e_html_editor_get_view (editor), mime_part);

        } else {
            /* normal attachment */
            e_msg_composer_attach (composer, mime_part);
        }
    }
}

static void
set_signature_gui (EMsgComposer *composer)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    WebKitDOMDocument *document;
    WebKitDOMNodeList *nodes;
    EComposerHeaderTable *table;
    EMailSignatureComboBox *combo_box;
    gchar *uid;
    gulong ii, length;

    table = e_msg_composer_get_header_table (composer);
    combo_box = e_composer_header_table_get_signature_combo_box (table);

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));

    uid = NULL;
    nodes = webkit_dom_document_get_elements_by_class_name (
        document, "-x-evo-signature");
    length = webkit_dom_node_list_get_length (nodes);
    for (ii = 0; ii < length; ii++) {
        WebKitDOMNode *node;
        gchar *id;

        node = webkit_dom_node_list_item (nodes, ii);
        id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (node));
        if (id && (strlen (id) == 1) && (*id == '1')) {
            uid = webkit_dom_element_get_attribute (
                WEBKIT_DOM_ELEMENT (node), "name");
            g_free (id);
            break;
        }
        g_free (id);
    }

    /* The combo box active ID is the signature's ESource UID. */
    if (uid != NULL) {
        gtk_combo_box_set_active_id (GTK_COMBO_BOX (combo_box), uid);
        g_free (uid);
    }
}

static void
composer_add_auto_recipients (ESource *source,
                              const gchar *property_name,
                              GHashTable *hash_table)
{
    ESourceMailComposition *extension;
    CamelInternetAddress *inet_addr;
    const gchar *extension_name;
    gchar *comma_separated_addrs;
    gchar **addr_array = NULL;
    gint length, ii;
    gint retval;

    extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
    extension = e_source_get_extension (source, extension_name);

    g_object_get (extension, property_name, &addr_array, NULL);

    if (addr_array == NULL)
        return;

    inet_addr = camel_internet_address_new ();
    comma_separated_addrs = g_strjoinv (", ", addr_array);

    retval = camel_address_decode (
        CAMEL_ADDRESS (inet_addr), comma_separated_addrs);

    g_free (comma_separated_addrs);
    g_strfreev (addr_array);

    if (retval == -1)
        return;

    length = camel_address_length (CAMEL_ADDRESS (inet_addr));

    for (ii = 0; ii < length; ii++) {
        const gchar *name;
        const gchar *addr;

        if (camel_internet_address_get (inet_addr, ii, &name, &addr))
            g_hash_table_add (hash_table, g_strdup (addr));
    }

    g_object_unref (inet_addr);
}

/**
 * e_msg_composer_new_with_message:
 * @shell: an #EShell
 * @message: The message to use as the source
 * @keep_signature: Keep message signature, if any
 * @cancellable: optional #GCancellable object, or %NULL
 *
 * Create a new message composer widget.
 *
 * Note: Designed to work only for messages constructed using Evolution.
 *
 * Returns: A pointer to the newly created widget
 **/
EMsgComposer *
e_msg_composer_new_with_message (EShell *shell,
                                 CamelMimeMessage *message,
                                 gboolean keep_signature,
                                 GCancellable *cancellable)
{
    CamelInternetAddress *to, *cc, *bcc;
    GList *To = NULL, *Cc = NULL, *Bcc = NULL, *postto = NULL;
    const gchar *format, *subject, *composer_mode;
    EDestination **Tov, **Ccv, **Bccv;
    GHashTable *auto_cc, *auto_bcc;
    CamelContentType *content_type;
    struct _camel_header_raw *headers;
    CamelDataWrapper *content;
    EMsgComposer *composer;
    EMsgComposerPrivate *priv;
    EComposerHeaderTable *table;
    ESource *source = NULL;
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    GtkToggleAction *action;
    struct _camel_header_raw *xev;
    gchar *identity_uid;
    gint len, i;

    g_return_val_if_fail (E_IS_SHELL (shell), NULL);

    headers = CAMEL_MIME_PART (message)->headers;
    while (headers != NULL) {
        gchar *value;

        if (strcmp (headers->name, "X-Evolution-PostTo") == 0) {
            value = g_strstrip (g_strdup (headers->value));
            postto = g_list_append (postto, value);
        }

        headers = headers->next;
    }

    composer = e_msg_composer_new (shell);
    priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
    editor = e_msg_composer_get_editor (composer);
    table = e_msg_composer_get_header_table (composer);
    view = e_html_editor_get_view (editor);

    if (postto) {
        e_composer_header_table_set_post_to_list (table, postto);
        g_list_foreach (postto, (GFunc) g_free, NULL);
        g_list_free (postto);
        postto = NULL;
    }

    /* Restore the mail identity preference. */
    identity_uid = (gchar *) camel_medium_get_header (
        CAMEL_MEDIUM (message), "X-Evolution-Identity");
    if (!identity_uid) {
        /* for backward compatibility */
        identity_uid = (gchar *) camel_medium_get_header (
            CAMEL_MEDIUM (message), "X-Evolution-Account");
    }
    if (identity_uid != NULL) {
        identity_uid = g_strstrip (g_strdup (identity_uid));
        source = e_composer_header_table_ref_source (
            table, identity_uid);
    }

    auto_cc = g_hash_table_new_full (
        (GHashFunc) camel_strcase_hash,
        (GEqualFunc) camel_strcase_equal,
        (GDestroyNotify) g_free,
        (GDestroyNotify) NULL);

    auto_bcc = g_hash_table_new_full (
        (GHashFunc) camel_strcase_hash,
        (GEqualFunc) camel_strcase_equal,
        (GDestroyNotify) g_free,
        (GDestroyNotify) NULL);

    if (source != NULL) {
        composer_add_auto_recipients (source, "cc", auto_cc);
        composer_add_auto_recipients (source, "bcc", auto_bcc);
    }

    to = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
    cc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
    bcc = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC);

    len = CAMEL_ADDRESS (to)->addresses->len;
    for (i = 0; i < len; i++) {
        const gchar *name, *addr;

        if (camel_internet_address_get (to, i, &name, &addr)) {
            EDestination *dest = e_destination_new ();
            e_destination_set_name (dest, name);
            e_destination_set_email (dest, addr);
            To = g_list_append (To, dest);
        }
    }

    Tov = destination_list_to_vector (To);
    g_list_free (To);

    len = CAMEL_ADDRESS (cc)->addresses->len;
    for (i = 0; i < len; i++) {
        const gchar *name, *addr;

        if (camel_internet_address_get (cc, i, &name, &addr)) {
            EDestination *dest = e_destination_new ();
            e_destination_set_name (dest, name);
            e_destination_set_email (dest, addr);

            if (g_hash_table_contains (auto_cc, addr))
                e_destination_set_auto_recipient (dest, TRUE);

            Cc = g_list_append (Cc, dest);
        }
    }

    Ccv = destination_list_to_vector (Cc);
    g_hash_table_destroy (auto_cc);
    g_list_free (Cc);

    len = CAMEL_ADDRESS (bcc)->addresses->len;
    for (i = 0; i < len; i++) {
        const gchar *name, *addr;

        if (camel_internet_address_get (bcc, i, &name, &addr)) {
            EDestination *dest = e_destination_new ();
            e_destination_set_name (dest, name);
            e_destination_set_email (dest, addr);

            if (g_hash_table_contains (auto_bcc, addr))
                e_destination_set_auto_recipient (dest, TRUE);

            Bcc = g_list_append (Bcc, dest);
        }
    }

    Bccv = destination_list_to_vector (Bcc);
    g_hash_table_destroy (auto_bcc);
    g_list_free (Bcc);

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

    subject = camel_mime_message_get_subject (message);

    e_composer_header_table_set_identity_uid (table, identity_uid);
    e_composer_header_table_set_destinations_to (table, Tov);
    e_composer_header_table_set_destinations_cc (table, Ccv);
    e_composer_header_table_set_destinations_bcc (table, Bccv);
    e_composer_header_table_set_subject (table, subject);

    g_free (identity_uid);

    e_destination_freev (Tov);
    e_destination_freev (Ccv);
    e_destination_freev (Bccv);

    /* Restore the format editing preference */
    format = camel_medium_get_header (
        CAMEL_MEDIUM (message), "X-Evolution-Format");

    composer_mode = camel_medium_get_header (
        CAMEL_MEDIUM (message), "X-Evolution-Composer-Mode");

    if (composer_mode && *composer_mode)
        composer->priv->is_from_draft = TRUE;

    if (format != NULL) {
        gchar **flags;

        while (*format && camel_mime_is_lwsp (*format))
            format++;

        flags = g_strsplit (format, ", ", 0);
        for (i = 0; flags[i]; i++) {
            if (g_ascii_strcasecmp (flags[i], "text/html") == 0) {
                if (g_ascii_strcasecmp (composer_mode, "text/html") == 0) {
                    e_html_editor_view_set_html_mode (
                        view, TRUE);
                } else {
                    e_html_editor_view_set_html_mode (
                        view, FALSE);
                }
            } else if (g_ascii_strcasecmp (flags[i], "text/plain") == 0) {
                if (g_ascii_strcasecmp (composer_mode, "text/html") == 0) {
                    e_html_editor_view_set_html_mode (
                        view, TRUE);
                } else {
                    e_html_editor_view_set_html_mode (
                        view, FALSE);
                }
            } else if (g_ascii_strcasecmp (flags[i], "pgp-sign") == 0) {
                action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
                gtk_toggle_action_set_active (action, TRUE);
            } else if (g_ascii_strcasecmp (flags[i], "pgp-encrypt") == 0) {
                action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
                gtk_toggle_action_set_active (action, TRUE);
            } else if (g_ascii_strcasecmp (flags[i], "smime-sign") == 0) {
                action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
                gtk_toggle_action_set_active (action, TRUE);
            } else if (g_ascii_strcasecmp (flags[i], "smime-encrypt") == 0) {
                action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
                gtk_toggle_action_set_active (action, TRUE);
            }
        }
        g_strfreev (flags);
    }

    /* Remove any other X-Evolution-* headers that may have been set */
    xev = emcu_remove_xevolution_headers (message);
    camel_header_raw_clear (&xev);

    /* Check for receipt request */
    if (camel_medium_get_header (
        CAMEL_MEDIUM (message), "Disposition-Notification-To")) {
        action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT));
        gtk_toggle_action_set_active (action, TRUE);
    }

    /* Check for mail priority */
    if (camel_medium_get_header (CAMEL_MEDIUM (message), "X-Priority")) {
        action = GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE));
        gtk_toggle_action_set_active (action, TRUE);
    }

    /* set extra headers */
    headers = CAMEL_MIME_PART (message)->headers;
    while (headers) {
        if (g_ascii_strcasecmp (headers->name, "References") == 0 ||
            g_ascii_strcasecmp (headers->name, "In-Reply-To") == 0) {
            g_ptr_array_add (
                composer->priv->extra_hdr_names,
                g_strdup (headers->name));
            g_ptr_array_add (
                composer->priv->extra_hdr_values,
                g_strdup (headers->value));
        }

        headers = headers->next;
    }

    /* Restore the attachments and body text */
    content = camel_medium_get_content (CAMEL_MEDIUM (message));
    if (CAMEL_IS_MULTIPART (content)) {
        CamelMimePart *mime_part;
        CamelMultipart *multipart;

        multipart = CAMEL_MULTIPART (content);
        mime_part = CAMEL_MIME_PART (message);
        content_type = camel_mime_part_get_content_type (mime_part);

        if (CAMEL_IS_MULTIPART_SIGNED (content)) {
            /* Handle the signed content and configure the
             * composer to sign outgoing messages. */
            handle_multipart_signed (
                composer, multipart, keep_signature, cancellable, 0);

        } else if (CAMEL_IS_MULTIPART_ENCRYPTED (content)) {
            /* Decrypt the encrypted content and configure the
             * composer to encrypt outgoing messages. */
            handle_multipart_encrypted (
                composer, mime_part, keep_signature, cancellable, 0);

        } else if (camel_content_type_is (
            content_type, "multipart", "alternative")) {
            /* This contains the text/plain and text/html
             * versions of the message body. */
            handle_multipart_alternative (
                composer, multipart, keep_signature, cancellable, 0);

        } else {
            /* There must be attachments... */
            handle_multipart (
                composer, multipart, keep_signature, cancellable, 0);
        }
    } else {
        CamelMimePart *mime_part;
        gchar *html;
        gssize length;

        mime_part = CAMEL_MIME_PART (message);
        content_type = camel_mime_part_get_content_type (mime_part);

        if (content_type != NULL && (
            camel_content_type_is (
                content_type, "application", "x-pkcs7-mime") ||
            camel_content_type_is (
                content_type, "application", "pkcs7-mime"))) {

            gtk_toggle_action_set_active (
                GTK_TOGGLE_ACTION (
                ACTION (SMIME_ENCRYPT)), TRUE);
        }

        /* If we are opening message from Drafts */
        if (composer->priv->is_from_draft) {
            /* Extract the body */
            CamelDataWrapper *dw;

            dw = camel_medium_get_content ((CamelMedium *) mime_part);
            if (dw) {
                CamelStream *mem = camel_stream_mem_new ();
                GByteArray *bytes;

                camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL);
                camel_stream_close (mem, cancellable, NULL);

                bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
                if (bytes && bytes->len)
                    html = g_strndup ((const gchar *) bytes->data, bytes->len);

                g_object_unref (mem);
            }
        } else {
            html = emcu_part_to_html (
                composer, CAMEL_MIME_PART (message),
                &length, keep_signature, cancellable);
        }
        e_msg_composer_set_pending_body (composer, html, length);
    }

    priv->is_from_message = TRUE;
    priv->set_signature_from_message = TRUE;

    /* We wait until now to set the body text because we need to
     * ensure that the attachment bar has all the attachments before
     * we request them. */
    e_msg_composer_flush_pending_body (composer);

    set_signature_gui (composer);

    return composer;
}

/**
 * e_msg_composer_new_redirect:
 * @shell: an #EShell
 * @message: The message to use as the source
 *
 * Create a new message composer widget.
 *
 * Returns: A pointer to the newly created widget
 **/
EMsgComposer *
e_msg_composer_new_redirect (EShell *shell,
                             CamelMimeMessage *message,
                             const gchar *identity_uid,
                             GCancellable *cancellable)
{
    EMsgComposer *composer;
    EComposerHeaderTable *table;
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    const gchar *subject;

    g_return_val_if_fail (E_IS_SHELL (shell), NULL);
    g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);

    composer = e_msg_composer_new_with_message (
        shell, message, TRUE, cancellable);
    table = e_msg_composer_get_header_table (composer);

    subject = camel_mime_message_get_subject (message);

    composer->priv->redirect = message;
    g_object_ref (message);

    e_composer_header_table_set_identity_uid (table, identity_uid);
    e_composer_header_table_set_subject (table, subject);

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE);

    return composer;
}

/**
 * e_msg_composer_ref_session:
 * @composer: an #EMsgComposer
 *
 * Returns the mail module's global #CamelSession instance.  Calling
 * this function will load the mail module if it isn't already loaded.
 *
 * The returned #CamelSession is referenced for thread-safety and must
 * be unreferenced with g_object_unref() when finished with it.
 *
 * Returns: the mail module's #CamelSession
 **/
CamelSession *
e_msg_composer_ref_session (EMsgComposer *composer)
{
    EShell *shell;
    EShellBackend *shell_backend;
    CamelSession *session = NULL;

    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    shell = e_msg_composer_get_shell (composer);
    shell_backend = e_shell_get_backend_by_name (shell, "mail");

    g_object_get (shell_backend, "session", &session, NULL);
    g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);

    return session;
}

/**
 * e_msg_composer_get_shell:
 * @composer: an #EMsgComposer
 *
 * Returns the #EShell that was passed to e_msg_composer_new().
 *
 * Returns: the #EShell
 **/
EShell *
e_msg_composer_get_shell (EMsgComposer *composer)
{
    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    return E_SHELL (composer->priv->shell);
}

static void
msg_composer_send_cb (EMsgComposer *composer,
                      GAsyncResult *result,
                      AsyncContext *context)
{
    CamelMimeMessage *message;
    EAlertSink *alert_sink;
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    GError *error = NULL;

    alert_sink = e_activity_get_alert_sink (context->activity);

    message = e_msg_composer_get_message_finish (composer, result, &error);

    if (e_activity_handle_cancellation (context->activity, error)) {
        g_warn_if_fail (message == NULL);
        async_context_free (context);
        g_error_free (error);

        gtk_window_present (GTK_WINDOW (composer));
        return;
    }

    if (error != NULL) {
        g_warn_if_fail (message == NULL);
        e_alert_submit (
            alert_sink,
            "mail-composer:no-build-message",
            error->message, NULL);
        async_context_free (context);
        g_error_free (error);

        gtk_window_present (GTK_WINDOW (composer));
        return;
    }

    g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));

    /* The callback can set editor 'changed' if anything failed. */
    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    e_html_editor_view_set_changed (view, TRUE);

    g_signal_emit (
        composer, signals[SEND], 0,
        message, context->activity);

    g_object_unref (message);

    async_context_free (context);
}

/**
 * e_msg_composer_send:
 * @composer: an #EMsgComposer
 *
 * Send the message in @composer.
 **/
void
e_msg_composer_send (EMsgComposer *composer)
{
    EHTMLEditor *editor;
    AsyncContext *context;
    GCancellable *cancellable;
    gboolean proceed_with_send = TRUE;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    /* This gives the user a chance to abort the send. */
    g_signal_emit (composer, signals[PRESEND], 0, &proceed_with_send);

    if (!proceed_with_send) {
        gtk_window_present (GTK_WINDOW (composer));
        return;
    }

    editor = e_msg_composer_get_editor (composer);

    context = g_slice_new0 (AsyncContext);
    context->activity = e_html_editor_new_activity (editor);

    cancellable = e_activity_get_cancellable (context->activity);

    e_msg_composer_get_message (
        composer, G_PRIORITY_DEFAULT, cancellable,
        (GAsyncReadyCallback) msg_composer_send_cb,
        context);
}

static void
msg_composer_save_to_drafts_cb (EMsgComposer *composer,
                                GAsyncResult *result,
                                AsyncContext *context)
{
    CamelMimeMessage *message;
    EAlertSink *alert_sink;
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    GError *error = NULL;

    alert_sink = e_activity_get_alert_sink (context->activity);

    message = e_msg_composer_get_message_draft_finish (
        composer, result, &error);

    if (e_activity_handle_cancellation (context->activity, error)) {
        g_warn_if_fail (message == NULL);
        async_context_free (context);
        g_error_free (error);

        if (e_msg_composer_is_exiting (composer)) {
            gtk_window_present (GTK_WINDOW (composer));
            composer->priv->application_exiting = FALSE;
        }

        return;
    }

    if (error != NULL) {
        g_warn_if_fail (message == NULL);
        e_alert_submit (
            alert_sink,
            "mail-composer:no-build-message",
            error->message, NULL);
        async_context_free (context);
        g_error_free (error);

        if (e_msg_composer_is_exiting (composer)) {
            gtk_window_present (GTK_WINDOW (composer));
            composer->priv->application_exiting = FALSE;
        }

        return;
    }

    g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));

    /* The callback can set editor 'changed' if anything failed. */
    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    e_html_editor_view_set_changed (view, FALSE);

    g_signal_emit (
        composer, signals[SAVE_TO_DRAFTS],
        0, message, context->activity);

    g_object_unref (message);

    if (e_msg_composer_is_exiting (composer))
        g_object_weak_ref (
            G_OBJECT (context->activity),
            (GWeakNotify) gtk_widget_destroy, composer);

    async_context_free (context);
}

/**
 * e_msg_composer_save_to_drafts:
 * @composer: an #EMsgComposer
 *
 * Save the message in @composer to the selected account's Drafts folder.
 **/
void
e_msg_composer_save_to_drafts (EMsgComposer *composer)
{
    EHTMLEditor *editor;
    AsyncContext *context;
    GCancellable *cancellable;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    editor = e_msg_composer_get_editor (composer);

    context = g_slice_new0 (AsyncContext);
    context->activity = e_html_editor_new_activity (editor);

    cancellable = e_activity_get_cancellable (context->activity);

    e_msg_composer_get_message_draft (
        composer, G_PRIORITY_DEFAULT, cancellable,
        (GAsyncReadyCallback) msg_composer_save_to_drafts_cb,
        context);
}

static void
msg_composer_save_to_outbox_cb (EMsgComposer *composer,
                                GAsyncResult *result,
                                AsyncContext *context)
{
    CamelMimeMessage *message;
    EAlertSink *alert_sink;
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    GError *error = NULL;

    alert_sink = e_activity_get_alert_sink (context->activity);

    message = e_msg_composer_get_message_finish (composer, result, &error);

    if (e_activity_handle_cancellation (context->activity, error)) {
        g_warn_if_fail (message == NULL);
        async_context_free (context);
        g_error_free (error);
        return;
    }

    if (error != NULL) {
        g_warn_if_fail (message == NULL);
        e_alert_submit (
            alert_sink,
            "mail-composer:no-build-message",
            error->message, NULL);
        async_context_free (context);
        g_error_free (error);
        return;
    }

    g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));

    g_signal_emit (
        composer, signals[SAVE_TO_OUTBOX],
        0, message, context->activity);

    g_object_unref (message);

    async_context_free (context);

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    e_html_editor_view_set_changed (view, FALSE);
}

/**
 * e_msg_composer_save_to_outbox:
 * @composer: an #EMsgComposer
 *
 * Save the message in @composer to the local Outbox folder.
 **/
void
e_msg_composer_save_to_outbox (EMsgComposer *composer)
{
    EHTMLEditor *editor;
    AsyncContext *context;
    GCancellable *cancellable;
    gboolean proceed_with_save = TRUE;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    /* This gives the user a chance to abort the save. */
    g_signal_emit (composer, signals[PRESEND], 0, &proceed_with_save);

    if (!proceed_with_save)
        return;

    editor = e_msg_composer_get_editor (composer);

    context = g_slice_new0 (AsyncContext);
    context->activity = e_html_editor_new_activity (editor);

    cancellable = e_activity_get_cancellable (context->activity);

    e_msg_composer_get_message (
        composer, G_PRIORITY_DEFAULT, cancellable,
        (GAsyncReadyCallback) msg_composer_save_to_outbox_cb,
        context);
}

static void
msg_composer_print_cb (EMsgComposer *composer,
                       GAsyncResult *result,
                       AsyncContext *context)
{
    CamelMimeMessage *message;
    EAlertSink *alert_sink;
    GError *error = NULL;

    alert_sink = e_activity_get_alert_sink (context->activity);

    message = e_msg_composer_get_message_print_finish (
        composer, result, &error);

    if (e_activity_handle_cancellation (context->activity, error)) {
        g_warn_if_fail (message == NULL);
        async_context_free (context);
        g_error_free (error);
        return;
    }

    if (error != NULL) {
        g_warn_if_fail (message == NULL);
        async_context_free (context);
        e_alert_submit (
            alert_sink,
            "mail-composer:no-build-message",
            error->message, NULL);
        g_error_free (error);
        return;
    }

    g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));

    g_signal_emit (
        composer, signals[PRINT], 0,
        context->print_action, message, context->activity);

    g_object_unref (message);

    async_context_free (context);
}

/**
 * e_msg_composer_print:
 * @composer: an #EMsgComposer
 * @print_action: the print action to start
 *
 * Print the message in @composer.
 **/
void
e_msg_composer_print (EMsgComposer *composer,
                      GtkPrintOperationAction print_action)
{
    EHTMLEditor *editor;
    AsyncContext *context;
    GCancellable *cancellable;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    editor = e_msg_composer_get_editor (composer);

    context = g_slice_new0 (AsyncContext);
    context->activity = e_html_editor_new_activity (editor);
    context->print_action = print_action;

    cancellable = e_activity_get_cancellable (context->activity);

    e_msg_composer_get_message_print (
        composer, G_PRIORITY_DEFAULT, cancellable,
        (GAsyncReadyCallback) msg_composer_print_cb,
        context);
}

static GList *
add_recipients (GList *list,
                const gchar *recips)
{
    CamelInternetAddress *cia;
    const gchar *name, *addr;
    gint num, i;

    cia = camel_internet_address_new ();
    num = camel_address_decode (CAMEL_ADDRESS (cia), recips);

    for (i = 0; i < num; i++) {
        if (camel_internet_address_get (cia, i, &name, &addr)) {
            EDestination *dest = e_destination_new ();
            e_destination_set_name (dest, name);
            e_destination_set_email (dest, addr);

            list = g_list_append (list, dest);
        }
    }

    return list;
}

static gboolean
list_contains_addr (const GList *list,
                    EDestination *dest)
{
    g_return_val_if_fail (dest != NULL, FALSE);

    while (list != NULL) {
        if (e_destination_equal (dest, list->data))
            return TRUE;

        list = list->next;
    }

    return FALSE;
}

static void
merge_cc_bcc (EDestination **addrv,
              GList **merge_into,
              const GList *to,
              const GList *cc,
              const GList *bcc)
{
    gint ii;

    for (ii = 0; addrv && addrv[ii]; ii++) {
        if (!list_contains_addr (to, addrv[ii]) &&
            !list_contains_addr (cc, addrv[ii]) &&
            !list_contains_addr (bcc, addrv[ii])) {
            *merge_into = g_list_append (
                *merge_into, g_object_ref (addrv[ii]));
        }
    }
}

static void
merge_always_cc_and_bcc (EComposerHeaderTable *table,
                         const GList *to,
                         GList **cc,
                         GList **bcc)
{
    EDestination **addrv;

    g_return_if_fail (table != NULL);
    g_return_if_fail (cc != NULL);
    g_return_if_fail (bcc != NULL);

    addrv = e_composer_header_table_get_destinations_cc (table);
    merge_cc_bcc (addrv, cc, to, *cc, *bcc);
    e_destination_freev (addrv);

    addrv = e_composer_header_table_get_destinations_bcc (table);
    merge_cc_bcc (addrv, bcc, to, *cc, *bcc);
    e_destination_freev (addrv);
}

static const gchar *blacklist[] = { ".", "etc", ".." };

static gboolean
file_is_blacklisted (const gchar *argument)
{
    GFile *file;
    gboolean blacklisted = FALSE;
    guint ii, jj, n_parts;
    gchar *filename;
    gchar **parts;

    /* The "attach" argument may be a URI or local path.  Normalize
     * it to a local path if we can.  We only blacklist local files. */
    file = g_file_new_for_commandline_arg (argument);
    filename = g_file_get_path (file);
    g_object_unref (file);

    if (filename == NULL)
        return FALSE;

    parts = g_strsplit (filename, G_DIR_SEPARATOR_S, -1);
    n_parts = g_strv_length (parts);

    for (ii = 0; ii < G_N_ELEMENTS (blacklist); ii++) {
        for (jj = 0; jj < n_parts; jj++) {
            if (g_str_has_prefix (parts[jj], blacklist[ii])) {
                blacklisted = TRUE;
                break;
            }
        }
    }

    if (blacklisted) {
        gchar *base_dir;

        /* Don't blacklist files in trusted base directories. */
        if (g_str_has_prefix (filename, g_get_user_data_dir ()))
            blacklisted = FALSE;
        if (g_str_has_prefix (filename, g_get_user_cache_dir ()))
            blacklisted = FALSE;
        if (g_str_has_prefix (filename, g_get_user_config_dir ()))
            blacklisted = FALSE;

        /* Apparently KDE still uses ~/.kde heavily, and some
         * distributions use ~/.kde4 to distinguish KDE4 data
         * from KDE3 data.  Trust these directories as well. */

        base_dir = g_build_filename (g_get_home_dir (), ".kde", NULL);
        if (g_str_has_prefix (filename, base_dir))
            blacklisted = FALSE;
        g_free (base_dir);

        base_dir = g_build_filename (g_get_home_dir (), ".kde4", NULL);
        if (g_str_has_prefix (filename, base_dir))
            blacklisted = FALSE;
        g_free (base_dir);
    }

    g_strfreev (parts);
    g_free (filename);

    return blacklisted;
}

static void
handle_mailto (EMsgComposer *composer,
               const gchar *mailto)
{
    EAttachmentView *view;
    EAttachmentStore *store;
    EComposerHeaderTable *table;
    GList *to = NULL, *cc = NULL, *bcc = NULL;
    EDestination **tov, **ccv, **bccv;
    gchar *subject = NULL, *body = NULL;
    gchar *header, *content, *buf;
    gsize nread, nwritten;
    const gchar *p;
    gint len, clen;

    table = e_msg_composer_get_header_table (composer);
    view = e_msg_composer_get_attachment_view (composer);
    store = e_attachment_view_get_store (view);

    buf = g_strdup (mailto);

    /* Parse recipients (everything after ':' until '?' or eos). */
    p = buf + 7;
    len = strcspn (p, "?");
    if (len) {
        content = g_strndup (p, len);
        camel_url_decode (content);
        to = add_recipients (to, content);
        g_free (content);
    }

    p += len;
    if (*p == '?') {
        p++;

        while (*p) {
            len = strcspn (p, "=&");

            /* If it's malformed, give up. */
            if (p[len] != '=')
                break;

            header = (gchar *) p;
            header[len] = '\0';
            p += len + 1;

            clen = strcspn (p, "&");

            content = g_strndup (p, clen);

            if (!g_ascii_strcasecmp (header, "to")) {
                camel_url_decode (content);
                to = add_recipients (to, content);
            } else if (!g_ascii_strcasecmp (header, "cc")) {
                camel_url_decode (content);
                cc = add_recipients (cc, content);
            } else if (!g_ascii_strcasecmp (header, "bcc")) {
                camel_url_decode (content);
                bcc = add_recipients (bcc, content);
            } else if (!g_ascii_strcasecmp (header, "subject")) {
                g_free (subject);
                camel_url_decode (content);
                if (g_utf8_validate (content, -1, NULL)) {
                    subject = content;
                    content = NULL;
                } else {
                    subject = g_locale_to_utf8 (
                        content, clen, &nread,
                        &nwritten, NULL);
                    if (subject) {
                        subject = g_realloc (subject, nwritten + 1);
                        subject[nwritten] = '\0';
                    }
                }
            } else if (!g_ascii_strcasecmp (header, "body")) {
                g_free (body);
                camel_url_decode (content);
                if (g_utf8_validate (content, -1, NULL)) {
                    body = content;
                    content = NULL;
                } else {
                    body = g_locale_to_utf8 (
                        content, clen, &nread,
                        &nwritten, NULL);
                    if (body) {
                        body = g_realloc (body, nwritten + 1);
                        body[nwritten] = '\0';
                    }
                }
            } else if (!g_ascii_strcasecmp (header, "attach") ||
                   !g_ascii_strcasecmp (header, "attachment")) {
                EAttachment *attachment;

                camel_url_decode (content);
                if (file_is_blacklisted (content))
                    e_alert_submit (
                        E_ALERT_SINK (composer),
                        "mail:blacklisted-file",
                        content, NULL);
                if (g_ascii_strncasecmp (content, "file:", 5) == 0)
                    attachment = e_attachment_new_for_uri (content);
                else
                    attachment = e_attachment_new_for_path (content);
                e_attachment_store_add_attachment (store, attachment);
                e_attachment_load_async (
                    attachment, (GAsyncReadyCallback)
                    e_attachment_load_handle_error, composer);
                g_object_unref (attachment);
            } else if (!g_ascii_strcasecmp (header, "from")) {
                /* Ignore */
            } else if (!g_ascii_strcasecmp (header, "reply-to")) {
                /* ignore */
            } else {
                /* add an arbitrary header? */
                camel_url_decode (content);
                e_msg_composer_add_header (composer, header, content);
            }

            g_free (content);

            p += clen;
            if (*p == '&') {
                p++;
                if (!g_ascii_strncasecmp (p, "amp;", 4))
                    p += 4;
            }
        }
    }

    g_free (buf);

    merge_always_cc_and_bcc (table, to, &cc, &bcc);

    tov = destination_list_to_vector (to);
    ccv = destination_list_to_vector (cc);
    bccv = destination_list_to_vector (bcc);

    g_list_free (to);
    g_list_free (cc);
    g_list_free (bcc);

    e_composer_header_table_set_destinations_to (table, tov);
    e_composer_header_table_set_destinations_cc (table, ccv);
    e_composer_header_table_set_destinations_bcc (table, bccv);

    e_destination_freev (tov);
    e_destination_freev (ccv);
    e_destination_freev (bccv);

    e_composer_header_table_set_subject (table, subject);
    g_free (subject);

    if (body) {
        gchar *htmlbody;

        htmlbody = camel_text_to_html (body, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
        set_editor_text (composer, htmlbody, TRUE);
        g_free (htmlbody);
    }
}

/**
 * e_msg_composer_new_from_url:
 * @shell: an #EShell
 * @url: a mailto URL
 *
 * Create a new message composer widget, and fill in fields as
 * defined by the provided URL.
 **/
EMsgComposer *
e_msg_composer_new_from_url (EShell *shell,
                             const gchar *url)
{
    EMsgComposer *composer;

    g_return_val_if_fail (E_IS_SHELL (shell), NULL);
    g_return_val_if_fail (g_ascii_strncasecmp (url, "mailto:", 7) == 0, NULL);

    composer = e_msg_composer_new (shell);

    handle_mailto (composer, url);

    return composer;
}

/**
 * e_msg_composer_set_body_text:
 * @composer: a composer object
 * @text: the HTML text to initialize the editor with
 * @update_signature: whether update signature in the text after setting it;
 *    Might be usually called with TRUE.
 *
 * Loads the given HTML text into the editor.
 **/
void
e_msg_composer_set_body_text (EMsgComposer *composer,
                              const gchar *text,
                              gboolean update_signature)
{
    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (text != NULL);

    set_editor_text (composer, text, update_signature);
}

/**
 * e_msg_composer_set_body:
 * @composer: a composer object
 * @body: the data to initialize the composer with
 * @mime_type: the MIME type of data
 *
 * Loads the given data into the composer as the message body.
 **/
void
e_msg_composer_set_body (EMsgComposer *composer,
                         const gchar *body,
                         const gchar *mime_type)
{
    EMsgComposerPrivate *priv = composer->priv;
    EComposerHeaderTable *table;
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    ESource *source;
    const gchar *identity_uid;
    gchar *buff;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    table = e_msg_composer_get_header_table (composer);

    /* Disable signature */
    priv->disable_signature = TRUE;

    identity_uid = e_composer_header_table_get_identity_uid (table);
    source = e_composer_header_table_ref_source (table, identity_uid);

    buff = g_markup_printf_escaped (
        "<b>%s</b>",
        _("The composer contains a non-text "
        "message body, which cannot be edited."));
    set_editor_text (composer, buff, FALSE);
    g_free (buff);

    e_html_editor_view_set_html_mode (view, FALSE);
    webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), FALSE);

    g_free (priv->mime_body);
    priv->mime_body = g_strdup (body);
    g_free (priv->mime_type);
    priv->mime_type = g_strdup (mime_type);

    if (g_ascii_strncasecmp (priv->mime_type, "text/calendar", 13) == 0) {
        ESourceMailComposition *extension;
        const gchar *extension_name;

        extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
        extension = e_source_get_extension (source, extension_name);

        if (!e_source_mail_composition_get_sign_imip (extension)) {
            GtkToggleAction *action;

            action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
            gtk_toggle_action_set_active (action, FALSE);

            action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
            gtk_toggle_action_set_active (action, FALSE);
        }
    }

    g_object_unref (source);
}

/**
 * e_msg_composer_add_header:
 * @composer: an #EMsgComposer
 * @name: the header's name
 * @value: the header's value
 *
 * Adds a new custom header created from @name and @value.  The header
 * is not shown in the user interface but will be added to the resulting
 * MIME message when sending or saving.
 **/
void
e_msg_composer_add_header (EMsgComposer *composer,
                           const gchar *name,
                           const gchar *value)
{
    EMsgComposerPrivate *priv;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (name != NULL);
    g_return_if_fail (value != NULL);

    priv = composer->priv;

    g_ptr_array_add (priv->extra_hdr_names, g_strdup (name));
    g_ptr_array_add (priv->extra_hdr_values, g_strdup (value));
}

/**
 * e_msg_composer_set_header:
 * @composer: an #EMsgComposer
 * @name: the header's name
 * @value: the header's value
 *
 * Replaces all custom headers matching @name that were added with
 * e_msg_composer_add_header() or e_msg_composer_set_header(), with
 * a new custom header created from @name and @value.  The header is
 * not shown in the user interface but will be added to the resulting
 * MIME message when sending or saving.
 **/
void
e_msg_composer_set_header (EMsgComposer *composer,
                           const gchar *name,
                           const gchar *value)
{
    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (name != NULL);
    g_return_if_fail (value != NULL);

    e_msg_composer_remove_header (composer, name);
    e_msg_composer_add_header (composer, name, value);
}

/**
 * e_msg_composer_remove_header:
 * @composer: an #EMsgComposer
 * @name: the header's name
 *
 * Removes all custom headers matching @name that were added with
 * e_msg_composer_add_header() or e_msg_composer_set_header().
 **/
void
e_msg_composer_remove_header (EMsgComposer *composer,
                              const gchar *name)
{
    EMsgComposerPrivate *priv;
    guint ii;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (name != NULL);

    priv = composer->priv;

    for (ii = 0; ii < priv->extra_hdr_names->len; ii++) {
        if (g_strcmp0 (priv->extra_hdr_names->pdata[ii], name) == 0) {
            g_free (priv->extra_hdr_names->pdata[ii]);
            g_free (priv->extra_hdr_values->pdata[ii]);
            g_ptr_array_remove_index (priv->extra_hdr_names, ii);
            g_ptr_array_remove_index (priv->extra_hdr_values, ii);
        }
    }
}

/**
 * e_msg_composer_set_draft_headers:
 * @composer: an #EMsgComposer
 * @folder_uri: folder URI of the last saved draft
 * @message_uid: message UID of the last saved draft
 *
 * Add special X-Evolution-Draft headers to remember the most recently
 * saved draft message, even across Evolution sessions.  These headers
 * can be used to mark the draft message for deletion after saving a
 * newer draft or sending the composed message.
 **/
void
e_msg_composer_set_draft_headers (EMsgComposer *composer,
                                  const gchar *folder_uri,
                                  const gchar *message_uid)
{
    const gchar *header_name;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (folder_uri != NULL);
    g_return_if_fail (message_uid != NULL);

    header_name = "X-Evolution-Draft-Folder";
    e_msg_composer_set_header (composer, header_name, folder_uri);

    header_name = "X-Evolution-Draft-Message";
    e_msg_composer_set_header (composer, header_name, message_uid);
}

/**
 * e_msg_composer_set_source_headers:
 * @composer: an #EMsgComposer
 * @folder_uri: folder URI of the source message
 * @message_uid: message UID of the source message
 * @flags: flags to set on the source message after sending
 *
 * Add special X-Evolution-Source headers to remember the message being
 * forwarded or replied to, even across Evolution sessions.  These headers
 * can be used to set appropriate flags on the source message after sending
 * the composed message.
 **/
void
e_msg_composer_set_source_headers (EMsgComposer *composer,
                                   const gchar *folder_uri,
                                   const gchar *message_uid,
                                   CamelMessageFlags flags)
{
    GString *buffer;
    const gchar *header_name;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (folder_uri != NULL);
    g_return_if_fail (message_uid != NULL);

    buffer = g_string_sized_new (32);

    if (flags & CAMEL_MESSAGE_ANSWERED)
        g_string_append (buffer, "ANSWERED ");
    if (flags & CAMEL_MESSAGE_ANSWERED_ALL)
        g_string_append (buffer, "ANSWERED_ALL ");
    if (flags & CAMEL_MESSAGE_FORWARDED)
        g_string_append (buffer, "FORWARDED ");
    if (flags & CAMEL_MESSAGE_SEEN)
        g_string_append (buffer, "SEEN ");

    header_name = "X-Evolution-Source-Folder";
    e_msg_composer_set_header (composer, header_name, folder_uri);

    header_name = "X-Evolution-Source-Message";
    e_msg_composer_set_header (composer, header_name, message_uid);

    header_name = "X-Evolution-Source-Flags";
    e_msg_composer_set_header (composer, header_name, buffer->str);

    g_string_free (buffer, TRUE);
}

/**
 * e_msg_composer_attach:
 * @composer: a composer object
 * @mime_part: the #CamelMimePart to attach
 *
 * Attaches @attachment to the message being composed in the composer.
 **/
void
e_msg_composer_attach (EMsgComposer *composer,
                       CamelMimePart *mime_part)
{
    EAttachmentView *view;
    EAttachmentStore *store;
    EAttachment *attachment;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));
    g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));

    view = e_msg_composer_get_attachment_view (composer);
    store = e_attachment_view_get_store (view);

    attachment = e_attachment_new ();
    e_attachment_set_mime_part (attachment, mime_part);
    e_attachment_store_add_attachment (store, attachment);
    e_attachment_load_async (
        attachment, (GAsyncReadyCallback)
        e_attachment_load_handle_error, composer);
    g_object_unref (attachment);
}

static void
composer_get_message_ready (EMsgComposer *composer,
                            GAsyncResult *result,
                            GSimpleAsyncResult *simple)
{
    CamelMimeMessage *message;
    GError *error = NULL;

    message = composer_build_message_finish (composer, result, &error);

    if (message != NULL)
        g_simple_async_result_set_op_res_gpointer (
            simple, message, (GDestroyNotify) g_object_unref);

    if (error != NULL) {
        g_warn_if_fail (message == NULL);
        g_simple_async_result_take_error (simple, error);
    }

    g_simple_async_result_complete (simple);

    g_object_unref (simple);
}

/**
 * e_msg_composer_get_message:
 * @composer: an #EMsgComposer
 *
 * Retrieve the message edited by the user as a #CamelMimeMessage.  The
 * #CamelMimeMessage object is created on the fly; subsequent calls to this
 * function will always create new objects from scratch.
 **/
void
e_msg_composer_get_message (EMsgComposer *composer,
                            gint io_priority,
                            GCancellable *cancellable,
                            GAsyncReadyCallback callback,
                            gpointer user_data)
{
    GSimpleAsyncResult *simple;
    GtkAction *action;
    ComposerFlags flags = 0;
    EHTMLEditor *editor;
    EHTMLEditorView *view;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);

    simple = g_simple_async_result_new (
        G_OBJECT (composer), callback,
        user_data, e_msg_composer_get_message);

    g_simple_async_result_set_check_cancellable (simple, cancellable);

    if (e_html_editor_view_get_html_mode (view))
        flags |= COMPOSER_FLAG_HTML_CONTENT;

    action = ACTION (PRIORITIZE_MESSAGE);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_PRIORITIZE_MESSAGE;

    action = ACTION (REQUEST_READ_RECEIPT);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_REQUEST_READ_RECEIPT;

    action = ACTION (PGP_SIGN);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_PGP_SIGN;

    action = ACTION (PGP_ENCRYPT);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_PGP_ENCRYPT;

#ifdef HAVE_NSS
    action = ACTION (SMIME_SIGN);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_SMIME_SIGN;

    action = ACTION (SMIME_ENCRYPT);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_SMIME_ENCRYPT;
#endif

    composer_build_message (
        composer, flags, io_priority,
        cancellable, (GAsyncReadyCallback)
        composer_get_message_ready, simple);
}

CamelMimeMessage *
e_msg_composer_get_message_finish (EMsgComposer *composer,
                                   GAsyncResult *result,
                                   GError **error)
{
    GSimpleAsyncResult *simple;
    CamelMimeMessage *message;

    g_return_val_if_fail (
        g_simple_async_result_is_valid (
        result, G_OBJECT (composer),
        e_msg_composer_get_message), NULL);

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

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

    g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);

    return g_object_ref (message);
}

void
e_msg_composer_get_message_print (EMsgComposer *composer,
                                  gint io_priority,
                                  GCancellable *cancellable,
                                  GAsyncReadyCallback callback,
                                  gpointer user_data)
{
    GSimpleAsyncResult *simple;
    ComposerFlags flags = 0;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    simple = g_simple_async_result_new (
        G_OBJECT (composer), callback,
        user_data, e_msg_composer_get_message_print);

    g_simple_async_result_set_check_cancellable (simple, cancellable);

    flags |= COMPOSER_FLAG_HTML_CONTENT;
    flags |= COMPOSER_FLAG_SAVE_OBJECT_DATA;

    composer_build_message (
        composer, flags, io_priority,
        cancellable, (GAsyncReadyCallback)
        composer_get_message_ready, simple);
}

CamelMimeMessage *
e_msg_composer_get_message_print_finish (EMsgComposer *composer,
                                         GAsyncResult *result,
                                         GError **error)
{
    GSimpleAsyncResult *simple;
    CamelMimeMessage *message;

    g_return_val_if_fail (
        g_simple_async_result_is_valid (
        result, G_OBJECT (composer),
        e_msg_composer_get_message_print), NULL);

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

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

    g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);

    return g_object_ref (message);
}

void
e_msg_composer_get_message_draft (EMsgComposer *composer,
                                  gint io_priority,
                                  GCancellable *cancellable,
                                  GAsyncReadyCallback callback,
                                  gpointer user_data)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    GSimpleAsyncResult *simple;
    ComposerFlags flags = COMPOSER_FLAG_SAVE_DRAFT;
    GtkAction *action;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    simple = g_simple_async_result_new (
        G_OBJECT (composer), callback,
        user_data, e_msg_composer_get_message_draft);

    g_simple_async_result_set_check_cancellable (simple, cancellable);

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    /* We need to remember composer mode */
    if (e_html_editor_view_get_html_mode (view))
        flags |= COMPOSER_FLAG_HTML_MODE;
    /* We want to save HTML content everytime when we save as draft */
    flags |= COMPOSER_FLAG_SAVE_DRAFT;

    action = ACTION (PRIORITIZE_MESSAGE);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_PRIORITIZE_MESSAGE;

    action = ACTION (REQUEST_READ_RECEIPT);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_REQUEST_READ_RECEIPT;

    action = ACTION (PGP_SIGN);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_PGP_SIGN;

    action = ACTION (PGP_ENCRYPT);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_PGP_ENCRYPT;

#ifdef HAVE_NSS
    action = ACTION (SMIME_SIGN);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_SMIME_SIGN;

    action = ACTION (SMIME_ENCRYPT);
    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
        flags |= COMPOSER_FLAG_SMIME_ENCRYPT;
#endif

    composer_build_message (
        composer, flags, io_priority,
        cancellable, (GAsyncReadyCallback)
        composer_get_message_ready, simple);
}

CamelMimeMessage *
e_msg_composer_get_message_draft_finish (EMsgComposer *composer,
                                         GAsyncResult *result,
                                         GError **error)
{
    GSimpleAsyncResult *simple;
    CamelMimeMessage *message;

    g_return_val_if_fail (
        g_simple_async_result_is_valid (
        result, G_OBJECT (composer),
        e_msg_composer_get_message_draft), NULL);

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

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

    g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);

    return g_object_ref (message);
}

CamelInternetAddress *
e_msg_composer_get_from (EMsgComposer *composer)
{
    CamelInternetAddress *inet_address = NULL;
    ESourceMailIdentity *mail_identity;
    EComposerHeaderTable *table;
    ESource *source;
    const gchar *extension_name;
    const gchar *uid;
    gchar *name;
    gchar *address;

    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    table = e_msg_composer_get_header_table (composer);

    uid = e_composer_header_table_get_identity_uid (table);
    source = e_composer_header_table_ref_source (table, uid);
    g_return_val_if_fail (source != NULL, NULL);

    extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
    mail_identity = e_source_get_extension (source, extension_name);

    name = e_source_mail_identity_dup_name (mail_identity);
    address = e_source_mail_identity_dup_address (mail_identity);

    g_object_unref (source);

    if (name != NULL && address != NULL) {
        inet_address = camel_internet_address_new ();
        camel_internet_address_add (inet_address, name, address);
    }

    g_free (name);
    g_free (address);

    return inet_address;
}

CamelInternetAddress *
e_msg_composer_get_reply_to (EMsgComposer *composer)
{
    CamelInternetAddress *address;
    EComposerHeaderTable *table;
    const gchar *reply_to;

    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    table = e_msg_composer_get_header_table (composer);

    reply_to = e_composer_header_table_get_reply_to (table);
    if (reply_to == NULL || *reply_to == '\0')
        return NULL;

    address = camel_internet_address_new ();
    if (camel_address_unformat (CAMEL_ADDRESS (address), reply_to) == -1) {
        g_object_unref (address);
        address = NULL;
    }

    return address;
}

/**
 * e_msg_composer_get_raw_message_text_without_signature:
 *
 * Returns the text/plain of the message from composer without signature
 **/
GByteArray *
e_msg_composer_get_raw_message_text_without_signature (EMsgComposer *composer)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    GByteArray *array;
    gint ii, length;
    WebKitDOMDocument *document;
    WebKitDOMNodeList *list;

    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
    array = g_byte_array_new ();

    list = webkit_dom_document_query_selector_all (
        document, "body > *:not(.-x-evo-signature-wrapper)", NULL);
    length = webkit_dom_node_list_get_length (list);
    for (ii = 0; ii < length; ii++) {
        gchar *text;
        WebKitDOMNode *node = webkit_dom_node_list_item (list, ii);

        text = webkit_dom_html_element_get_inner_text (
            WEBKIT_DOM_HTML_ELEMENT (node));
        g_byte_array_append (array, (guint8 *) text, strlen (text));
        g_free (text);
    }

    return array;
}

/**
 * e_msg_composer_get_raw_message_text:
 *
 * Returns the text/plain of the message from composer
 **/
GByteArray *
e_msg_composer_get_raw_message_text (EMsgComposer *composer)
{
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    GByteArray *array;
    gchar *text;
    WebKitDOMDocument *document;
    WebKitDOMHTMLElement *body;

    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);
    document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (view));
    body = webkit_dom_document_get_body (document);

    array = g_byte_array_new ();
    text = webkit_dom_html_element_get_inner_text (body);
    g_byte_array_append (array, (guint8 *) text, strlen (text));
    g_free (text);

    return array;
}

gboolean
e_msg_composer_is_exiting (EMsgComposer *composer)
{
    g_return_val_if_fail (composer != NULL, FALSE);

    return composer->priv->application_exiting;
}

void
e_msg_composer_request_close (EMsgComposer *composer)
{
    g_return_if_fail (composer != NULL);

    composer->priv->application_exiting = TRUE;
}

/* Returns whether can close the composer immediately. It will return FALSE
 * also when saving to drafts, but the e_msg_composer_is_exiting will return
 * TRUE for this case.  can_save_draft means whether can save draft
 * immediately, or rather keep it on the caller (when FALSE). If kept on the
 * folder, then returns FALSE and sets interval variable to return TRUE in
 * e_msg_composer_is_exiting. */
gboolean
e_msg_composer_can_close (EMsgComposer *composer,
                          gboolean can_save_draft)
{
    gboolean res = FALSE;
    EHTMLEditor *editor;
    EHTMLEditorView *view;
    EComposerHeaderTable *table;
    GdkWindow *window;
    GtkWidget *widget;
    const gchar *subject, *message_name;
    gint response;

    widget = GTK_WIDGET (composer);
    editor = e_msg_composer_get_editor (composer);
    view = e_html_editor_get_view (editor);

    /* this means that there is an async operation running,
     * in which case the composer cannot be closed */
    if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
        return FALSE;

    if (!e_html_editor_view_get_changed (view))
        return TRUE;

    window = gtk_widget_get_window (widget);
    gdk_window_raise (window);

    table = e_msg_composer_get_header_table (composer);
    subject = e_composer_header_table_get_subject (table);

    if (subject == NULL || *subject == '\0')
        message_name = "mail-composer:exit-unsaved-no-subject";
    else
        message_name = "mail-composer:exit-unsaved";

    response = e_alert_run_dialog_for_args (
        GTK_WINDOW (composer),
        message_name,
        subject, NULL);

    switch (response) {
        case GTK_RESPONSE_YES:
            gtk_widget_hide (widget);
            e_msg_composer_request_close (composer);
            if (can_save_draft)
                gtk_action_activate (ACTION (SAVE_DRAFT));
            break;

        case GTK_RESPONSE_NO:
            res = TRUE;
            break;

        case GTK_RESPONSE_CANCEL:
            break;
    }

    return res;
}

EComposerHeaderTable *
e_msg_composer_get_header_table (EMsgComposer *composer)
{
    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    return E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
}

EAttachmentView *
e_msg_composer_get_attachment_view (EMsgComposer *composer)
{
    g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);

    return E_ATTACHMENT_VIEW (composer->priv->attachment_paned);
}

void
e_save_spell_languages (const GList *spell_dicts)
{
    GSettings *settings;
    GPtrArray *lang_array;

    /* Build a list of spell check language codes. */
    lang_array = g_ptr_array_new ();
    while (spell_dicts != NULL) {
        ESpellDictionary *dict = spell_dicts->data;
        const gchar *language_code;

        language_code = e_spell_dictionary_get_code (dict);
        g_ptr_array_add (lang_array, (gpointer) language_code);

        spell_dicts = g_list_next (spell_dicts);
    }

    g_ptr_array_add (lang_array, NULL);

    /* Save the language codes to GSettings. */
    settings = g_settings_new ("org.gnome.evolution.mail");
    g_settings_set_strv (
        settings, "composer-spell-languages",
        (const gchar * const *) lang_array->pdata);
    g_object_unref (settings);

    g_ptr_array_free (lang_array, TRUE);
}

void
e_msg_composer_is_from_new_message (EMsgComposer *composer,
                                    gboolean is_from_new_message)
{
    g_return_if_fail (composer != NULL);

    composer->priv->is_from_new_message = is_from_new_message;
}

void
e_msg_composer_save_focused_widget (EMsgComposer *composer)
{
    GtkWidget *widget;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    widget = gtk_window_get_focus (GTK_WINDOW (composer));
    composer->priv->focused_entry = widget;

    if (E_IS_HTML_EDITOR_VIEW (widget)) {
        EHTMLEditorSelection *selection;

        selection = e_html_editor_view_get_selection (
            E_HTML_EDITOR_VIEW (widget));

        e_html_editor_selection_save (selection);
    }

    if (GTK_IS_EDITABLE (widget)) {
        gtk_editable_get_selection_bounds (
            GTK_EDITABLE (widget),
            &composer->priv->focused_entry_selection_start,
            &composer->priv->focused_entry_selection_end);
    }
}

void
e_msg_composer_restore_focus_on_composer (EMsgComposer *composer)
{
    GtkWidget *widget = composer->priv->focused_entry;

    g_return_if_fail (E_IS_MSG_COMPOSER (composer));

    if (!widget)
        return;

    gtk_window_set_focus (GTK_WINDOW (composer), widget);

    if (GTK_IS_EDITABLE (widget)) {
        gtk_editable_select_region (
            GTK_EDITABLE (widget),
            composer->priv->focused_entry_selection_start,
            composer->priv->focused_entry_selection_end);
    }

    if (E_IS_HTML_EDITOR_VIEW (widget)) {
        EHTMLEditorSelection *selection;

        e_html_editor_view_force_spell_check (E_HTML_EDITOR_VIEW (widget));

        selection = e_html_editor_view_get_selection (
            E_HTML_EDITOR_VIEW (widget));

        e_html_editor_selection_restore (selection);
    }

    composer->priv->focused_entry = NULL;
}