aboutsummaryrefslogblamecommitdiffstats
path: root/smime/gui/certificate-manager.c
blob: 624059c9025564c3f7c535e041b6779632b1b57e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
  
  



                                                                
  



                                                                    
  
                                                                   
                                                                             
  




                                                        


   



                    
                    
 
                       
 
                            
                              
                                
                               

                   
                         
                      







                     
                          
                                  
                          

                                              
 
                
                            


                                          
                                           
                                        




                                          

                                         
                                              
                                           



                                         

                                           
                                                
                                             




                                    

                         









                                                                                         











                                                                                                   
                                                      
                                           
































                                                                        

                                                            
                           
 

                              
                                                                                      





                                                                                     
 
                                        
                                                                 
                                                                               






                                                                         

                                                                       
                                                                                             
                                     


                                                                
 
                                                                    
                                                                      



                                                                                                 
                                                                                           

                                                                                                     
                 
 


                                             


           



                                                            
                                                                                                                   








                                                                               

                                                                         
                                               













                                                                                                                                                
 






                                                          
                                                                                                                   








                                                                               





                                                                                 





                                                            
                              




                                                                
                              

 






























































                                                                                                            
           

                                                                                      
                                            

                                                        
                                       

                                                           
 










                                                                   



                                                              
                                    
                                  
 



                                                                                 
                                                          
                                                                             

                                                            
 



                                                                         
                                                          
                                                                             

                                                            
 



                                                                              
                                                                             

                                                            
 



                                                                        
                                                                             

                                                            
 


                                                                                               
                                    
                                                                                                     
 

                                                                                                     
 

                                                                                                 
 

                                                                                                     
 

                                                                                                             


           



                                                             
                                                                                                                      



                                                      
                                                                                  



                                             





                                                                                 



           



                                                             
                                                                                                                      


















                                                                                  

                                                               
                           
 

                              
                                                                                      





                                                                                     
 
                                        

                                                                                
                                                                             





                                                                         

                                                                       
                                                                                             

                                              


                                                                



                                                                        

                                                                      




                                                                                                 
                                                                                              




                                                                                                                           
                 
 
                                  

                                                                               

                                             






                                                               
                                                                                                                      



                                                      
                                                                                  



                                             

                                                                         
                                               




                                                                                                                                                   








                                                                     
 


           

                                                                                         
                                            





                                                              
                    

                                    




                                                                   





                                                              
                                    
                                  
 



                                                                                 
                                                          
                                                                                

                                                            
 



                                                                               
                                                          
                                                                                

                                                            
 



                                                                         
                                                          
                                                                                

                                                            

                                                                                 


                                                                                                  
 


                                                                                                       


                                                                                                       





                                                                                                           

 
           


                                                        
 
                                                                                                                        



                                                      
                                                                                    



                                             












                                                                                 
 
                                                                                                                        










                                                                                    





                                                                                                                   
 












                                                                                                     
 
                                                                            
                         


                                                    



           

                                                          
                           
 

                              
                                                                                      





                                                                                     
 
                                        







                                                                             

                                                                       
                                                                                             

                                              


                                                                



                                                                        

                                                                      




                                                                                                 




                                                                                                                             
                 
 

                                                                               


                                             






                                                          
                                                                                                                        



                                                      
                                                                                    



                                             

                                                                         
                                               




                                                                                                                                                     








                                                                     
 




                                                                                           
                                            





                                                         
                    

                                      


                                                                   



           


                                                              
                                    
                                  
 



                                                                                 
                                                                                  

                                                            
 

                                                                                                    
 


                                                                                             


                                                                                             

                                                                                                 
 

                                                                                                 

 




                                                        
                                                          
                                                                                                              




                                                                                           
                                                                                    
 
                                                                          








                                                                                                             
                                                                           
 
                                 
                                                                  
                                                            


                                                                       

                                            
            
                                                                  
                                                                  


                                                                       

                                            




                                                           

                                        
                                                          
                                                                                                                 




                                                                                              
                                                                                    
 
                                                                          








                                                                                                                
                                                                           

                                 
                                                                  

                                                               
                                                               


                                            
                                                                  

                                                                  
                                                               

                                            






                                                      
                                                          
                                                                                                                   




                                                                                                

                                                                      
 
                                                                          








                                                                                                                  
                                                                           
 
                                 
                                                                  


                                                            
            
                                                                  




















                                                                  

                                

                         

















                                                                                                            

                            





                                                                                                               
                                                                                    




                                                                                                       





                                                                                                 



                         





                                                                                                                 
                                                                                      




                                                                                                         






                                                                                                   
                      


                                        
         



                                        
                           






                                                             


                                              
                                                                                              



                                                                                                     
                                             

                                              
                 
         

                                        

 
               

                                         

                                       







                                                           



                                                                              

                     

 

                                                           
 
                      
                          
                          


                                                        
 
                                                        
 
                                                                                          
                          
 
                                                      
 

                                                                        
 


                                                                                                                
 




                                                                                                              
 



                                                                                                            
 



                                                                                                         
 


                                                
 




                                                                    
 
                                                                                   
                              
 

                                                              
 
                                            

                                                                           
 
                      
 
/*
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Authors:
 *      Chris Toshok <toshok@ximian.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

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

#include <gtk/gtk.h>

#include <glib/gi18n.h>

#include "ca-trust-dialog.h"
#include "cert-trust-dialog.h"
#include "certificate-manager.h"
#include "certificate-viewer.h"

#include "e-cert.h"
#include "e-cert-trust.h"
#include "e-cert-db.h"

#include "nss.h"
#include <cms.h>
#include <cert.h>
#include <certdb.h>
#include <pkcs11.h>
#include <pk11func.h>

#include "shell/e-shell.h"
#include "e-util/e-dialog-utils.h"
#include "e-util/e-util.h"
#include "e-util/e-util-private.h"
#include "widgets/misc/e-preferences-window.h"

typedef struct {
    GtkBuilder *builder;

    GtkWidget *yourcerts_treeview;
    GtkTreeStore *yourcerts_treemodel;
    GtkTreeModel *yourcerts_streemodel;
    GHashTable *yourcerts_root_hash;
    GtkWidget *view_your_button;
    GtkWidget *backup_your_button;
    GtkWidget *backup_all_your_button;
    GtkWidget *import_your_button;
    GtkWidget *delete_your_button;

    GtkWidget *contactcerts_treeview;
    GtkTreeModel *contactcerts_streemodel;
    GHashTable *contactcerts_root_hash;
    GtkWidget *view_contact_button;
    GtkWidget *edit_contact_button;
    GtkWidget *import_contact_button;
    GtkWidget *delete_contact_button;

    GtkWidget *authoritycerts_treeview;
    GtkTreeModel *authoritycerts_streemodel;
    GHashTable *authoritycerts_root_hash;
    GtkWidget *view_ca_button;
    GtkWidget *edit_ca_button;
    GtkWidget *import_ca_button;
    GtkWidget *delete_ca_button;

} CertificateManagerData;

typedef void (*AddCertCb)(CertificateManagerData *cfm, ECert *cert);

static void unload_certs (CertificateManagerData *cfm, ECertType type);
static void load_certs (CertificateManagerData *cfm, ECertType type, AddCertCb add_cert);

static void add_user_cert (CertificateManagerData *cfm, ECert *cert);
static void add_contact_cert (CertificateManagerData *cfm, ECert *cert);
static void add_ca_cert (CertificateManagerData *cfm, ECert *cert);

static void
report_and_free_error (CertificateManagerData *cfm, const gchar *where, GError *error)
{
    g_return_if_fail (cfm != NULL);

    e_notice (gtk_widget_get_toplevel (cfm->yourcerts_treeview),
          GTK_MESSAGE_ERROR, "%s: %s", where, error ? error->message : _("Unknown error"));

    if (error)
        g_error_free (error);
}

static void
handle_selection_changed (GtkTreeSelection *selection,
              gint cert_column,
              GtkWidget *view_button,
              GtkWidget *edit_button,
              GtkWidget *delete_button)
{
    GtkTreeIter iter;
    gboolean cert_selected = FALSE;
    GtkTreeModel *model;

    if (gtk_tree_selection_get_selected (selection,
                         &model,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (model,
                    &iter,
                    cert_column, &cert,
                    -1);

        if (cert) {
            cert_selected = TRUE;
            g_object_unref (cert);
        }
    }

    if (delete_button)
        gtk_widget_set_sensitive (delete_button, cert_selected);
    if (edit_button)
        gtk_widget_set_sensitive (edit_button, cert_selected);
    if (view_button)
        gtk_widget_set_sensitive (view_button, cert_selected);
}

static void
import_your (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkWidget *filesel;

    GtkFileFilter* filter;

    filesel = gtk_file_chooser_dialog_new (_("Select a certificate to import..."),
                           NULL,
                           GTK_FILE_CHOOSER_ACTION_OPEN,
                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                           GTK_STOCK_OPEN, GTK_RESPONSE_OK,
                           NULL);
    gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK);

    filter = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter, _("All PKCS12 files"));
    gtk_file_filter_add_mime_type (filter, "application/x-x509-user-cert");
    gtk_file_filter_add_mime_type (filter, "application/x-pkcs12");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);

    filter = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter, _("All files"));
    gtk_file_filter_add_pattern (filter, "*");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);

    if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (filesel))) {
        gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel));
        GError *error = NULL;

        /* destroy dialog to get rid of it in the GUI */
        gtk_widget_destroy (filesel);

        if (e_cert_db_import_pkcs12_file (e_cert_db_peek (),
                          filename, &error)) {
            /* there's no telling how many certificates were added during the import,
               so we blow away the contact cert display and regenerate it. */
            unload_certs (cfm, E_CERT_USER);
            load_certs (cfm, E_CERT_USER, add_user_cert);
            gtk_tree_view_expand_all (GTK_TREE_VIEW (cfm->yourcerts_treeview));
        } else {
            report_and_free_error (cfm, _("Failed to import user's certificate"), error);
        }

        g_free (filename);
    } else
        gtk_widget_destroy (filesel);
}

static void
delete_your (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkTreeIter iter;

    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->yourcerts_treeview)),
                         NULL,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (GTK_TREE_MODEL (cfm->yourcerts_streemodel),
                    &iter,
                    4, &cert,
                    -1);

        if (cert
            && e_cert_db_delete_cert (e_cert_db_peek (), cert)) {
            GtkTreeIter child_iter;
            gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (cfm->yourcerts_streemodel),
                                    &child_iter,
                                    &iter);
            gtk_tree_store_remove (GTK_TREE_STORE (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cfm->yourcerts_streemodel))),
                           &child_iter);

            /* we need two unrefs here, one to unref the
               gtk_tree_model_get above, and one to unref
               the initial ref when we created the cert
               and added it to the tree */
            g_object_unref (cert);
            g_object_unref (cert);
        }
    }

}

static void
view_your (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkTreeIter iter;

    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->yourcerts_treeview)),
                         NULL,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (GTK_TREE_MODEL (cfm->yourcerts_streemodel),
                    &iter,
                    4, &cert,
                    -1);

        if (cert) {
            GtkWidget *dialog = certificate_viewer_show (cert);
            g_signal_connect (dialog, "response",
                      G_CALLBACK (gtk_widget_destroy), NULL);
            gtk_widget_show (dialog);
        }
    }
}

static void
backup_your (GtkWidget *widget, CertificateManagerData *cfm)
{
    /* FIXME: implement */
}

static void
backup_all_your (GtkWidget *widget, CertificateManagerData *cfm)
{
    /* FIXME: implement */
}

struct find_cert_data {
    ECert *cert;
    GtkTreePath *path;
    gint cert_index;
};

static gboolean
find_cert_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
    struct find_cert_data *fcd = data;
    ECert *cert = NULL;

    g_return_val_if_fail (model != NULL, TRUE);
    g_return_val_if_fail (iter != NULL, TRUE);
    g_return_val_if_fail (data != NULL, TRUE);

    gtk_tree_model_get (model, iter, fcd->cert_index, &cert, -1);

    if (cert && g_strcmp0 (e_cert_get_serial_number (cert), e_cert_get_serial_number (fcd->cert)) == 0
        && g_strcmp0 (e_cert_get_subject_name (cert), e_cert_get_subject_name (fcd->cert)) == 0
        && g_strcmp0 (e_cert_get_sha1_fingerprint (cert), e_cert_get_sha1_fingerprint (fcd->cert)) == 0
        && g_strcmp0 (e_cert_get_md5_fingerprint (cert), e_cert_get_md5_fingerprint (fcd->cert)) == 0) {
        fcd->path = gtk_tree_path_copy (path);
    }

    return fcd->path != NULL;
}

static void
select_certificate (CertificateManagerData *cfm, GtkTreeView *treeview, ECert *cert)
{
    GtkTreeModel *model;
    GtkTreeSelection *selection;
    struct find_cert_data fcd;

    g_return_if_fail (treeview != NULL);
    g_return_if_fail (GTK_IS_TREE_VIEW (treeview));
    g_return_if_fail (cert != NULL);
    g_return_if_fail (E_IS_CERT (cert));

    model = gtk_tree_view_get_model (treeview);
    g_return_if_fail (model != NULL);

    fcd.cert = cert;
    fcd.path = NULL;
    fcd.cert_index = cfm->yourcerts_treeview == GTK_WIDGET (treeview) ? 4
               : cfm->authoritycerts_treeview == GTK_WIDGET (treeview) ? 1
               : 3;

    gtk_tree_model_foreach (model, find_cert_cb, &fcd);

    if (fcd.path) {
        gtk_tree_view_expand_to_path (treeview, fcd.path);

        selection = gtk_tree_view_get_selection (treeview);
        gtk_tree_selection_select_path (selection, fcd.path);

        gtk_tree_view_scroll_to_cell (treeview, fcd.path, NULL, FALSE, 0.0, 0.0);

        gtk_tree_path_free (fcd.path);
    }
}

static void
yourcerts_selection_changed (GtkTreeSelection *selection, CertificateManagerData *cfm)
{
    handle_selection_changed (selection,
                  4,
                  cfm->view_your_button,
                  NULL,
                  cfm->delete_your_button);
}

static GtkTreeModel*
create_yourcerts_treemodel (void)
{
    return GTK_TREE_MODEL (gtk_tree_store_new (5,
                           G_TYPE_STRING,
                           G_TYPE_STRING,
                           G_TYPE_STRING,
                           G_TYPE_STRING,
                           G_TYPE_OBJECT));
}

static void
initialize_yourcerts_ui (CertificateManagerData *cfm)
{
    GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
    GtkTreeSelection *selection;
    GtkTreeViewColumn *column;

    column = gtk_tree_view_column_new_with_attributes (_("Certificate Name"),
                               cell,
                               "text", 0,
                               NULL);
    gtk_tree_view_column_set_resizable (column, TRUE);
    gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->yourcerts_treeview),
                     column);
    gtk_tree_view_column_set_sort_column_id (column, 0);

    column = gtk_tree_view_column_new_with_attributes (_("Purposes"),
                               cell,
                               "text", 1,
                               NULL);
    gtk_tree_view_column_set_resizable (column, TRUE);
    gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->yourcerts_treeview),
                     column);
    gtk_tree_view_column_set_sort_column_id (column, 1);

    column = gtk_tree_view_column_new_with_attributes (_("Serial Number"),
                               cell,
                               "text", 2,
                               NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->yourcerts_treeview),
                     column);
    gtk_tree_view_column_set_sort_column_id (column, 2);

    column = gtk_tree_view_column_new_with_attributes (_("Expires"),
                               cell,
                               "text", 3,
                               NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->yourcerts_treeview),
                     column);
    gtk_tree_view_column_set_sort_column_id (column, 3);

    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->yourcerts_treeview));
    g_signal_connect (selection, "changed", G_CALLBACK (yourcerts_selection_changed), cfm);

    if (cfm->import_your_button)
        g_signal_connect (cfm->import_your_button, "clicked", G_CALLBACK (import_your), cfm);

    if (cfm->delete_your_button)
        g_signal_connect (cfm->delete_your_button, "clicked", G_CALLBACK (delete_your), cfm);

    if (cfm->view_your_button)
        g_signal_connect (cfm->view_your_button, "clicked", G_CALLBACK (view_your), cfm);

    if (cfm->backup_your_button)
        g_signal_connect (cfm->backup_your_button, "clicked", G_CALLBACK (backup_your), cfm);

    if (cfm->backup_all_your_button)
        g_signal_connect (cfm->backup_all_your_button, "clicked", G_CALLBACK (backup_all_your), cfm);
}

static void
view_contact (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkTreeIter iter;

    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->contactcerts_treeview)),
                         NULL,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (GTK_TREE_MODEL (cfm->contactcerts_streemodel),
                    &iter,
                    3, &cert,
                    -1);

        if (cert) {
            GtkWidget *dialog = certificate_viewer_show (cert);
            g_signal_connect (dialog, "response",
                      G_CALLBACK (gtk_widget_destroy), NULL);
            gtk_widget_show (dialog);
        }
    }
}

static void
edit_contact (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkTreeIter iter;

    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->contactcerts_treeview)),
                         NULL,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (GTK_TREE_MODEL (cfm->contactcerts_streemodel),
                    &iter,
                    3, &cert,
                    -1);

        if (cert) {
            GtkWidget *dialog = cert_trust_dialog_show (cert);
            g_signal_connect (dialog, "response",
                      G_CALLBACK (gtk_widget_destroy), NULL);
            gtk_widget_show (dialog);
        }
    }
}

static void
import_contact (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkWidget *filesel;

    GtkFileFilter *filter;

    filesel = gtk_file_chooser_dialog_new (_("Select a certificate to import..."),
                           NULL,
                           GTK_FILE_CHOOSER_ACTION_OPEN,
                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                           GTK_STOCK_OPEN, GTK_RESPONSE_OK,
                           NULL);
    gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK);

    filter = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter, _("All email certificate files"));
    gtk_file_filter_add_mime_type (filter, "application/x-x509-email-cert");
    gtk_file_filter_add_mime_type (filter, "application/x-x509-ca-cert");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);

    filter = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter, _("All files"));
    gtk_file_filter_add_pattern (filter, "*");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);

    if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (filesel))) {
        gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel));
        GError *error = NULL;
        GSList *imported_certs = NULL;

        /* destroy dialog to get rid of it in the GUI */
        gtk_widget_destroy (filesel);

        if (e_cert_db_import_certs_from_file (e_cert_db_peek (),
                              filename,
                              E_CERT_CONTACT,
                              &imported_certs,
                              &error)) {

            /* there's no telling how many certificates were added during the import,
               so we blow away the contact cert display and regenerate it. */
            unload_certs (cfm, E_CERT_CONTACT);
            load_certs (cfm, E_CERT_CONTACT, add_contact_cert);
            gtk_tree_view_expand_all (GTK_TREE_VIEW (cfm->contactcerts_treeview));

            if (imported_certs)
                select_certificate (cfm, GTK_TREE_VIEW (cfm->contactcerts_treeview), imported_certs->data);
        } else {
            report_and_free_error (cfm, _("Failed to import contact's certificate"), error);
        }

        g_free (filename);
        g_slist_foreach (imported_certs, (GFunc) g_object_unref, NULL);
        g_slist_free (imported_certs);
    } else
        gtk_widget_destroy (filesel);
}

static void
delete_contact (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkTreeIter iter;

    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->contactcerts_treeview)),
                         NULL,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (GTK_TREE_MODEL (cfm->contactcerts_streemodel),
                    &iter,
                    3, &cert,
                    -1);

        if (cert
            && e_cert_db_delete_cert (e_cert_db_peek (), cert)) {
            GtkTreeIter child_iter;
            gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (cfm->contactcerts_streemodel),
                                    &child_iter,
                                    &iter);
            gtk_tree_store_remove (GTK_TREE_STORE (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cfm->contactcerts_streemodel))),
                           &child_iter);

            /* we need two unrefs here, one to unref the
               gtk_tree_model_get above, and one to unref
               the initial ref when we created the cert
               and added it to the tree */
            g_object_unref (cert);
            g_object_unref (cert);
        }
    }

}

static void
contactcerts_selection_changed (GtkTreeSelection *selection, CertificateManagerData *cfm)
{
    handle_selection_changed (selection,
                  3,
                  cfm->view_contact_button,
                  cfm->edit_contact_button,
                  cfm->delete_contact_button);
}

static GtkTreeModel*
create_contactcerts_treemodel (void)
{
    return GTK_TREE_MODEL (gtk_tree_store_new (4,
                           G_TYPE_STRING,
                           G_TYPE_STRING,
                           G_TYPE_STRING,
                           G_TYPE_OBJECT));
}

static void
initialize_contactcerts_ui (CertificateManagerData *cfm)
{
    GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
    GtkTreeSelection *selection;
    GtkTreeViewColumn *column;

    column = gtk_tree_view_column_new_with_attributes (_("Certificate Name"),
                               cell,
                               "text", 0,
                               NULL);
    gtk_tree_view_column_set_resizable (column, TRUE);
    gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->contactcerts_treeview),
                     column);
    gtk_tree_view_column_set_sort_column_id (column, 0);

    column = gtk_tree_view_column_new_with_attributes (_("E-Mail Address"),
                               cell,
                               "text", 1,
                               NULL);
    gtk_tree_view_column_set_resizable (column, TRUE);
    gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->contactcerts_treeview),
                     column);
    gtk_tree_view_column_set_sort_column_id (column, 1);

    column = gtk_tree_view_column_new_with_attributes (_("Purposes"),
                               cell,
                               "text", 2,
                               NULL);
    gtk_tree_view_column_set_resizable (column, TRUE);
    gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->contactcerts_treeview),
                     column);
    gtk_tree_view_column_set_sort_column_id (column, 2);

    cfm->contactcerts_root_hash = g_hash_table_new (g_str_hash, g_str_equal);

    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->contactcerts_treeview));
    g_signal_connect (selection, "changed", G_CALLBACK (contactcerts_selection_changed), cfm);

    if (cfm->view_contact_button)
        g_signal_connect (cfm->view_contact_button, "clicked", G_CALLBACK (view_contact), cfm);

    if (cfm->edit_contact_button)
        g_signal_connect (cfm->edit_contact_button, "clicked", G_CALLBACK (edit_contact), cfm);

    if (cfm->import_contact_button)
        g_signal_connect (cfm->import_contact_button, "clicked", G_CALLBACK (import_contact), cfm);

    if (cfm->delete_contact_button)
        g_signal_connect (cfm->delete_contact_button, "clicked", G_CALLBACK (delete_contact), cfm);

}

static void
view_ca (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkTreeIter iter;

    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->authoritycerts_treeview)),
                         NULL,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (GTK_TREE_MODEL (cfm->authoritycerts_streemodel),
                    &iter,
                    1, &cert,
                    -1);

        if (cert) {
            GtkWidget *dialog = certificate_viewer_show (cert);
            g_signal_connect (dialog, "response",
                      G_CALLBACK (gtk_widget_destroy), NULL);
            gtk_widget_show (dialog);
        }
    }
}

static void
edit_ca (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkTreeIter iter;

    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->authoritycerts_treeview)),
                         NULL,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (GTK_TREE_MODEL (cfm->authoritycerts_streemodel),
                    &iter,
                    1, &cert,
                    -1);

        if (cert) {
            GtkWidget *dialog = ca_trust_dialog_show (cert, FALSE);
            CERTCertificate *icert = e_cert_get_internal_cert (cert);

            ca_trust_dialog_set_trust (dialog,
                           e_cert_trust_has_trusted_ca (icert->trust, TRUE,  FALSE, FALSE),
                           e_cert_trust_has_trusted_ca (icert->trust, FALSE, TRUE,  FALSE),
                           e_cert_trust_has_trusted_ca (icert->trust, FALSE, FALSE, TRUE));

            if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
                gboolean trust_ssl, trust_email, trust_objsign;
                CERTCertTrust trust;

                ca_trust_dialog_get_trust (dialog,
                               &trust_ssl, &trust_email, &trust_objsign);

                e_cert_trust_init (&trust);
                e_cert_trust_set_valid_ca (&trust);
                e_cert_trust_add_ca_trust (&trust,
                               trust_ssl,
                               trust_email,
                               trust_objsign);

                e_cert_db_change_cert_trust (icert, &trust);
            }

            gtk_widget_destroy (dialog);
        }
    }
}

static void
import_ca (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkWidget *filesel;

    GtkFileFilter *filter;

    filesel = gtk_file_chooser_dialog_new (_("Select a certificate to import..."),
                           NULL,
                           GTK_FILE_CHOOSER_ACTION_OPEN,
                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                           GTK_STOCK_OPEN, GTK_RESPONSE_OK,
                           NULL);
    gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK);

    filter = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter, _("All CA certificate files"));
    gtk_file_filter_add_mime_type (filter, "application/x-x509-ca-cert");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);

    filter = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter, _("All files"));
    gtk_file_filter_add_pattern (filter, "*");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter);

    if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (filesel))) {
        gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel));
        GSList *imported_certs = NULL;
        GError *error = NULL;

        /* destroy dialog to get rid of it in the GUI */
        gtk_widget_destroy (filesel);

        if (e_cert_db_import_certs_from_file (e_cert_db_peek (),
                              filename,
                              E_CERT_CA,
                              &imported_certs,
                              &error)) {

            /* there's no telling how many certificates were added during the import,
               so we blow away the CA cert display and regenerate it. */
            unload_certs (cfm, E_CERT_CA);
            load_certs (cfm, E_CERT_CA, add_ca_cert);

            if (imported_certs)
                select_certificate (cfm, GTK_TREE_VIEW (cfm->authoritycerts_treeview), imported_certs->data);
        } else {
            report_and_free_error (cfm, _("Failed to import certificate authority's certificate"), error);
        }

        g_slist_foreach (imported_certs, (GFunc) g_object_unref, NULL);
        g_slist_free (imported_certs);
        g_free (filename);
    } else
        gtk_widget_destroy (filesel);
}

static void
delete_ca (GtkWidget *widget, CertificateManagerData *cfm)
{
    GtkTreeIter iter;

    if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->authoritycerts_treeview)),
                         NULL,
                         &iter)) {
        ECert *cert;

        gtk_tree_model_get (GTK_TREE_MODEL (cfm->authoritycerts_streemodel),
                    &iter,
                    1, &cert,
                    -1);

        if (cert
            && e_cert_db_delete_cert (e_cert_db_peek (), cert)) {
            GtkTreeIter child_iter;
            gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (cfm->authoritycerts_streemodel),
                                    &child_iter,
                                    &iter);
            gtk_tree_store_remove (GTK_TREE_STORE (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cfm->authoritycerts_streemodel))),
                           &child_iter);

            /* we need two unrefs here, one to unref the
               gtk_tree_model_get above, and one to unref
               the initial ref when we created the cert
               and added it to the tree */
            g_object_unref (cert);
            g_object_unref (cert);
        }
    }

}

static void
authoritycerts_selection_changed (GtkTreeSelection *selection, CertificateManagerData *cfm)
{
    handle_selection_changed (selection,
                  1,
                  cfm->view_ca_button,
                  cfm->edit_ca_button,
                  cfm->delete_ca_button);
}

static GtkTreeModel*
create_authoritycerts_treemodel (void)
{
    return GTK_TREE_MODEL (gtk_tree_store_new (2,
                           G_TYPE_STRING,
                           G_TYPE_OBJECT));

}

static void
initialize_authoritycerts_ui (CertificateManagerData *cfm)
{
    GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
    GtkTreeSelection *selection;
    GtkTreeViewColumn *column;

    column = gtk_tree_view_column_new_with_attributes (_("Certificate Name"),
                               cell,
                               "text", 0,
                               NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (cfm->authoritycerts_treeview),
                     column);
    gtk_tree_view_column_set_sort_column_id (column, 0);

    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (cfm->authoritycerts_treeview));
    g_signal_connect (selection, "changed", G_CALLBACK (authoritycerts_selection_changed), cfm);

    if (cfm->view_ca_button)
        g_signal_connect (cfm->view_ca_button, "clicked", G_CALLBACK (view_ca), cfm);

    if (cfm->edit_ca_button)
        g_signal_connect (cfm->edit_ca_button, "clicked", G_CALLBACK (edit_ca), cfm);

    if (cfm->import_ca_button)
        g_signal_connect (cfm->import_ca_button, "clicked", G_CALLBACK (import_ca), cfm);

    if (cfm->delete_ca_button)
        g_signal_connect (cfm->delete_ca_button, "clicked", G_CALLBACK (delete_ca), cfm);
}

static void
add_user_cert (CertificateManagerData *cfm, ECert *cert)
{
    GtkTreeIter iter;
    GtkTreeIter *parent_iter = NULL;
    const gchar *organization = e_cert_get_org (cert);
    GtkTreeModel *model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cfm->yourcerts_streemodel));

    if (organization) {
        parent_iter = g_hash_table_lookup (cfm->yourcerts_root_hash, organization);
        if (!parent_iter) {
            /* create a new toplevel node */
            gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);

            gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                        0, organization, -1);

            /* now copy it off into parent_iter and insert it into
               the hashtable */
            parent_iter = gtk_tree_iter_copy (&iter);
            g_hash_table_insert (cfm->yourcerts_root_hash, g_strdup (organization), parent_iter);
        }
    }

    gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent_iter);

    if (e_cert_get_cn (cert))
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                    0, e_cert_get_cn (cert),
                    1, e_cert_get_usage (cert),
                    2, e_cert_get_serial_number (cert),
                    3, e_cert_get_expires_on (cert),
                    4, cert,
                    -1);
    else
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                    0, e_cert_get_nickname (cert),
                    1, e_cert_get_usage (cert),
                    2, e_cert_get_serial_number (cert),
                    3, e_cert_get_expires_on (cert),
                    4, cert,
                    -1);
}

static void
add_contact_cert (CertificateManagerData *cfm, ECert *cert)
{
    GtkTreeIter iter;
    GtkTreeIter *parent_iter = NULL;
    const gchar *organization = e_cert_get_org (cert);
    GtkTreeModel *model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cfm->contactcerts_streemodel));

    if (organization) {
        parent_iter = g_hash_table_lookup (cfm->contactcerts_root_hash, organization);
        if (!parent_iter) {
            /* create a new toplevel node */
            gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);

            gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                        0, organization, -1);

            /* now copy it off into parent_iter and insert it into
               the hashtable */
            parent_iter = gtk_tree_iter_copy (&iter);
            g_hash_table_insert (cfm->contactcerts_root_hash, g_strdup (organization), parent_iter);
        }
    }

    gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent_iter);

    if (e_cert_get_cn (cert))
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                    0, e_cert_get_cn (cert),
                    1, e_cert_get_email (cert),
                    2, e_cert_get_usage (cert),
                    3, cert,
                    -1);
    else
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                    0, e_cert_get_nickname (cert),
                    1, e_cert_get_email (cert),
                    2, e_cert_get_usage (cert),
                    3, cert,
                    -1);
}

static void
add_ca_cert (CertificateManagerData *cfm, ECert *cert)
{
    GtkTreeIter iter;
    GtkTreeIter *parent_iter = NULL;
    const gchar *organization = e_cert_get_org (cert);
    GtkTreeModel *model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cfm->authoritycerts_streemodel));

    if (organization) {
        parent_iter = g_hash_table_lookup (cfm->authoritycerts_root_hash, organization);
        if (!parent_iter) {
            /* create a new toplevel node */
            gtk_tree_store_append (GTK_TREE_STORE (model),
                           &iter, NULL);

            gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                        0, organization, -1);

            /* now copy it off into parent_iter and insert it into
               the hashtable */
            parent_iter = gtk_tree_iter_copy (&iter);
            g_hash_table_insert (cfm->authoritycerts_root_hash, g_strdup (organization), parent_iter);
        }
    }

    gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent_iter);

    if (e_cert_get_cn (cert))
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                    0, e_cert_get_cn (cert),
                    1, cert,
                    -1);
    else
        gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                    0, e_cert_get_nickname (cert),
                    1, cert,
                    -1);
}

static void
destroy_key (gpointer data)
{
    g_free (data);
}

static void
destroy_value (gpointer data)
{
    gtk_tree_iter_free (data);
}

static void
unload_certs (CertificateManagerData *cfm,
          ECertType type)
{
    GtkTreeModel *treemodel;

    switch (type) {
    case E_CERT_USER:
        treemodel = create_yourcerts_treemodel ();

        cfm->yourcerts_streemodel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (treemodel));

        g_object_unref (treemodel);

        gtk_tree_view_set_model (GTK_TREE_VIEW (cfm->yourcerts_treeview),
                     cfm->yourcerts_streemodel);

        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (cfm->yourcerts_streemodel),
                              0,
                              GTK_SORT_ASCENDING);

        if (cfm->yourcerts_root_hash)
            g_hash_table_destroy (cfm->yourcerts_root_hash);

        cfm->yourcerts_root_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                  destroy_key, destroy_value);
        break;
    case E_CERT_CONTACT:
        treemodel = create_contactcerts_treemodel ();

        cfm->contactcerts_streemodel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (treemodel));

        g_object_unref (treemodel);

        gtk_tree_view_set_model (GTK_TREE_VIEW (cfm->contactcerts_treeview),
                     cfm->contactcerts_streemodel);

        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (cfm->contactcerts_streemodel),
                              0,
                              GTK_SORT_ASCENDING);

        if (cfm->contactcerts_root_hash)
            g_hash_table_destroy (cfm->contactcerts_root_hash);

        cfm->contactcerts_root_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                     destroy_key, destroy_value);
        break;
    case E_CERT_SITE:
        break;
    case E_CERT_CA:
        treemodel = create_authoritycerts_treemodel ();

        cfm->authoritycerts_streemodel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (treemodel));

        g_object_unref (treemodel);

        gtk_tree_view_set_model (GTK_TREE_VIEW (cfm->authoritycerts_treeview),
                     cfm->authoritycerts_streemodel);

        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (cfm->authoritycerts_streemodel),
                              0,
                              GTK_SORT_ASCENDING);

        if (cfm->authoritycerts_root_hash)
            g_hash_table_destroy (cfm->authoritycerts_root_hash);

        cfm->authoritycerts_root_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
                                       destroy_key, destroy_value);

        break;
    case E_CERT_UNKNOWN:
        /* nothing to do here */
        break;
    }
}

static void
load_certs (CertificateManagerData *cfm,
        ECertType type,
        AddCertCb add_cert)
{
    CERTCertList *certList;
    CERTCertListNode *node;

    certList = PK11_ListCerts (PK11CertListUnique, NULL);

    for (node = CERT_LIST_HEAD (certList);
         !CERT_LIST_END (node, certList);
         node = CERT_LIST_NEXT (node)) {
        ECert *cert = e_cert_new (CERT_DupCertificate ((CERTCertificate*)node->cert));
        ECertType ct = e_cert_get_cert_type (cert);

        /* show everything else in a contact tab */
        if (ct == type || (type == E_CERT_CONTACT && ct != E_CERT_CA && ct != E_CERT_USER)) {
            add_cert (cfm, cert);
        } else {
            g_object_unref (cert);
        }
    }

    CERT_DestroyCertList (certList);
}

static gboolean
populate_ui (CertificateManagerData *cfm)
{
    /* This is an idle callback. */

    unload_certs (cfm, E_CERT_USER);
    load_certs (cfm, E_CERT_USER, add_user_cert);

    unload_certs (cfm, E_CERT_CONTACT);
    load_certs (cfm, E_CERT_CONTACT, add_contact_cert);

    unload_certs (cfm, E_CERT_CA);
    load_certs (cfm, E_CERT_CA, add_ca_cert);

    /* expand all three trees */
    gtk_tree_view_expand_all (GTK_TREE_VIEW (cfm->yourcerts_treeview));
    gtk_tree_view_expand_all (GTK_TREE_VIEW (cfm->contactcerts_treeview));

    return FALSE;
}

GtkWidget *
certificate_manager_config_new (EPreferencesWindow *window)
{
    EShell *shell;
    GtkWidget *parent;
    GtkWidget *widget;
    CertificateManagerData *cfm_data;

    shell = e_preferences_window_get_shell (window);

    g_return_val_if_fail (E_IS_SHELL (shell), NULL);

    /* We need to peek the db here to make sure it (and NSS) are fully initialized. */
    e_cert_db_peek ();

    cfm_data = g_new0 (CertificateManagerData, 1);

    cfm_data->builder = gtk_builder_new ();
    e_load_ui_builder_definition (cfm_data->builder, "smime-ui.ui");

    cfm_data->yourcerts_treeview = e_builder_get_widget (cfm_data->builder, "yourcerts-treeview");
    cfm_data->contactcerts_treeview = e_builder_get_widget (cfm_data->builder, "contactcerts-treeview");
    cfm_data->authoritycerts_treeview = e_builder_get_widget (cfm_data->builder, "authoritycerts-treeview");

    cfm_data->view_your_button = e_builder_get_widget (cfm_data->builder, "your-view-button");
    cfm_data->backup_your_button = e_builder_get_widget (cfm_data->builder, "your-backup-button");
    cfm_data->backup_all_your_button = e_builder_get_widget (cfm_data->builder, "your-backup-all-button");
    cfm_data->import_your_button = e_builder_get_widget (cfm_data->builder, "your-import-button");
    cfm_data->delete_your_button = e_builder_get_widget (cfm_data->builder, "your-delete-button");

    cfm_data->view_contact_button = e_builder_get_widget (cfm_data->builder, "contact-view-button");
    cfm_data->edit_contact_button = e_builder_get_widget (cfm_data->builder, "contact-edit-button");
    cfm_data->import_contact_button = e_builder_get_widget (cfm_data->builder, "contact-import-button");
    cfm_data->delete_contact_button = e_builder_get_widget (cfm_data->builder, "contact-delete-button");

    cfm_data->view_ca_button = e_builder_get_widget (cfm_data->builder, "authority-view-button");
    cfm_data->edit_ca_button = e_builder_get_widget (cfm_data->builder, "authority-edit-button");
    cfm_data->import_ca_button = e_builder_get_widget (cfm_data->builder, "authority-import-button");
    cfm_data->delete_ca_button = e_builder_get_widget (cfm_data->builder, "authority-delete-button");

    initialize_yourcerts_ui (cfm_data);
    initialize_contactcerts_ui (cfm_data);
    initialize_authoritycerts_ui (cfm_data);

    /* Run this in an idle callback so Evolution has a chance to
     * fully initialize itself and start its main loop before we
     * load certificates, since doing so may trigger a password
     * dialog, and dialogs require a main loop. */
    g_idle_add ((GSourceFunc) populate_ui, cfm_data);

    widget = e_builder_get_widget (cfm_data->builder, "cert-manager-notebook");
    g_object_ref (widget);

    parent = gtk_widget_get_parent (widget);
    gtk_container_remove (GTK_CONTAINER (parent), widget);

    /* FIXME: remove when implemented */
    gtk_widget_set_sensitive (cfm_data->backup_your_button, FALSE);
    gtk_widget_set_sensitive (cfm_data->backup_all_your_button, FALSE);

    return widget;
}