/* * * 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 * * * Authors: * Chris Toshok * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #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 #include #include #include #include #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; }