summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk/certificate_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/gtk/certificate_manager.cc')
-rw-r--r--chrome/browser/gtk/certificate_manager.cc513
1 files changed, 513 insertions, 0 deletions
diff --git a/chrome/browser/gtk/certificate_manager.cc b/chrome/browser/gtk/certificate_manager.cc
new file mode 100644
index 0000000..51d1c7b
--- /dev/null
+++ b/chrome/browser/gtk/certificate_manager.cc
@@ -0,0 +1,513 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/gtk/certificate_manager.h"
+
+#include <cert.h>
+#include <gtk/gtk.h>
+#include <pk11pub.h>
+
+#include <map>
+#include <string>
+
+#include "app/gtk_signal.h"
+#include "app/l10n_util.h"
+#include "app/l10n_util_collator.h"
+#include "base/gtk_util.h"
+#include "base/i18n/time_formatting.h"
+#include "base/nss_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/gtk/certificate_viewer.h"
+#include "chrome/browser/gtk/gtk_util.h"
+#include "chrome/browser/pref_member.h"
+#include "chrome/browser/pref_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h"
+#include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h"
+#include "grit/generated_resources.h"
+
+// PSM = Mozilla's Personal Security Manager.
+namespace psm = mozilla_security_manager;
+
+namespace {
+
+// Convert a char* return value from NSS into a std::string and free the NSS
+// memory. If the arg is NULL, an empty string will be returned instead.
+std::string Stringize(char* nss_text) {
+ std::string s;
+ if (nss_text) {
+ s = nss_text;
+ PORT_Free(nss_text);
+ }
+ return s;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CertificatePage class definition.
+
+class CertificatePage {
+ public:
+ explicit CertificatePage(psm::CertType type);
+
+ void PopulateTree(CERTCertList* cert_list);
+
+ // Get the top-level widget of this page.
+ GtkWidget* widget() { return vbox_; }
+
+ private:
+ // Columns of the tree store.
+ enum {
+ CERT_NAME,
+ CERT_SECURITY_DEVICE,
+ CERT_SERIAL_NUMBER,
+ CERT_EXPIRES_ON,
+ CERT_EXPIRES_ON_INT,
+ CERT_ADDRESS,
+ CERT_POINTER,
+ CERT_STORE_NUM_COLUMNS
+ };
+
+ gint LocaleSortFunc(GtkTreeModel* model, GtkTreeIter* a, GtkTreeIter* b,
+ int col);
+
+ // Gtk event callbacks.
+ CHROMEG_CALLBACK_2(CertificatePage, gint, SortNameFunc, GtkTreeModel*,
+ GtkTreeIter*, GtkTreeIter*);
+ CHROMEG_CALLBACK_2(CertificatePage, gint, SortDeviceFunc, GtkTreeModel*,
+ GtkTreeIter*, GtkTreeIter*);
+ CHROMEG_CALLBACK_0(CertificatePage, void, OnSelectionChanged,
+ GtkTreeSelection*);
+ CHROMEGTK_CALLBACK_0(CertificatePage, void, OnViewClicked);
+
+ psm::CertType type_;
+
+ // The top-level widget of this page.
+ GtkWidget* vbox_;
+
+ GtkWidget* tree_;
+ GtkTreeStore* store_;
+ GtkTreeSelection* selection_;
+ scoped_ptr<icu::Collator> collator_;
+
+ GtkWidget* view_button_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// CertificatePage implementation.
+
+CertificatePage::CertificatePage(psm::CertType type) : type_(type) {
+ vbox_ = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox_),
+ gtk_util::kContentAreaBorder);
+
+ static const int kDescriptionIds[] = {
+ IDS_CERT_MANAGER_USER_TREE_DESCRIPTION,
+ IDS_CERT_MANAGER_OTHER_PEOPLE_TREE_DESCRIPTION,
+ IDS_CERT_MANAGER_SERVER_TREE_DESCRIPTION,
+ IDS_CERT_MANAGER_AUTHORITIES_TREE_DESCRIPTION,
+ IDS_CERT_MANAGER_UNKNOWN_TREE_DESCRIPTION,
+ };
+ DCHECK_EQ(arraysize(kDescriptionIds),
+ static_cast<size_t>(psm::NUM_CERT_TYPES));
+ GtkWidget* description_label = gtk_label_new(l10n_util::GetStringUTF8(
+ kDescriptionIds[type]).c_str());
+ gtk_util::LeftAlignMisc(description_label);
+ gtk_box_pack_start(GTK_BOX(vbox_), description_label, FALSE, FALSE, 0);
+
+ store_ = gtk_tree_store_new(CERT_STORE_NUM_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INT64,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+ tree_ = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store_));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_), TRUE);
+ selection_ = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_));
+ gtk_tree_selection_set_mode(selection_, GTK_SELECTION_SINGLE);
+ g_signal_connect(selection_, "changed", G_CALLBACK(OnSelectionChangedThunk),
+ this);
+
+ GtkTreeViewColumn* name_col = gtk_tree_view_column_new_with_attributes(
+ l10n_util::GetStringUTF8(IDS_CERT_MANAGER_NAME_COLUMN_LABEL).c_str(),
+ gtk_cell_renderer_text_new(),
+ "text", CERT_NAME,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(name_col, CERT_NAME);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), name_col);
+
+ if (type == psm::USER_CERT || type == psm::CA_CERT ||
+ type == psm::UNKNOWN_CERT) {
+ GtkTreeViewColumn* device_col = gtk_tree_view_column_new_with_attributes(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_DEVICE_COLUMN_LABEL).c_str(),
+ gtk_cell_renderer_text_new(),
+ "text", CERT_SECURITY_DEVICE,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(device_col, CERT_SECURITY_DEVICE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), device_col);
+ }
+
+ if (type == psm::USER_CERT) {
+ GtkTreeViewColumn* serial_col = gtk_tree_view_column_new_with_attributes(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_SERIAL_NUMBER_COLUMN_LABEL).c_str(),
+ gtk_cell_renderer_text_new(),
+ "text", CERT_SERIAL_NUMBER,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(serial_col, CERT_SERIAL_NUMBER);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), serial_col);
+ }
+
+ if (type == psm::USER_CERT || type == psm::EMAIL_CERT ||
+ type == psm::SERVER_CERT) {
+ GtkTreeViewColumn* expires_col = gtk_tree_view_column_new_with_attributes(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_EXPIRES_COLUMN_LABEL).c_str(),
+ gtk_cell_renderer_text_new(),
+ "text", CERT_EXPIRES_ON,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(expires_col, CERT_EXPIRES_ON_INT);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), expires_col);
+ }
+
+ if (type == psm::EMAIL_CERT) {
+ GtkTreeViewColumn* addr_col = gtk_tree_view_column_new_with_attributes(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_EMAIL_ADDRESS_COLUMN_LABEL).c_str(),
+ gtk_cell_renderer_text_new(),
+ "text", CERT_ADDRESS,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(addr_col, CERT_ADDRESS);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), addr_col);
+ }
+
+ UErrorCode error = U_ZERO_ERROR;
+ collator_.reset(
+ icu::Collator::createInstance(
+ icu::Locale(g_browser_process->GetApplicationLocale().c_str()),
+ error));
+ if (U_FAILURE(error))
+ collator_.reset(NULL);
+ if (collator_ != NULL) {
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store_), CERT_NAME,
+ SortNameFuncThunk, this, NULL);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store_),
+ CERT_SECURITY_DEVICE, SortDeviceFuncThunk,
+ this, NULL);
+ }
+
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store_), CERT_NAME,
+ GTK_SORT_ASCENDING);
+
+ GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(
+ GTK_SCROLLED_WINDOW(scroll_window), GTK_SHADOW_ETCHED_IN);
+ gtk_container_add(GTK_CONTAINER(scroll_window), tree_);
+ gtk_box_pack_start(GTK_BOX(vbox_), scroll_window, TRUE, TRUE, 0);
+
+ GtkWidget* button_box = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
+ gtk_box_pack_start(GTK_BOX(vbox_), button_box, FALSE, FALSE, 0);
+
+ view_button_ = gtk_button_new_with_mnemonic(
+ gtk_util::ConvertAcceleratorsFromWindowsStyle(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_VIEW_CERT_BUTTON)).c_str());
+ gtk_widget_set_sensitive(view_button_, FALSE);
+ g_signal_connect(view_button_, "clicked",
+ G_CALLBACK(OnViewClickedThunk), this);
+ gtk_box_pack_start(GTK_BOX(button_box), view_button_, FALSE, FALSE, 0);
+
+ // TODO(mattm): Add buttons for import, export, delete, etc
+}
+
+void CertificatePage::PopulateTree(CERTCertList* cert_list) {
+ DCHECK(gtk_tree_model_get_flags(GTK_TREE_MODEL(store_)) &
+ GTK_TREE_MODEL_ITERS_PERSIST);
+
+ typedef std::map<std::string, GtkTreeIter> OrgTreeMap;
+ OrgTreeMap org_tree_map;
+
+ CERTCertListNode* node;
+ for (node = CERT_LIST_HEAD(cert_list);
+ !CERT_LIST_END(node, cert_list);
+ node = CERT_LIST_NEXT(node)) {
+ CERTCertificate* cert = node->cert;
+ psm::CertType type = psm::GetCertType(cert);
+ if (type == type_) {
+ std::string org = Stringize(CERT_GetOrgName(&cert->subject));
+ if (org.empty())
+ org = Stringize(CERT_GetCommonName(&cert->subject));
+ OrgTreeMap::iterator org_tree_map_iter = org_tree_map.find(org);
+ if (org_tree_map_iter == org_tree_map.end()) {
+ GtkTreeIter iter;
+ gtk_tree_store_append(store_, &iter, NULL);
+ gtk_tree_store_set(store_, &iter, CERT_NAME, org.c_str(), -1);
+ org_tree_map_iter = org_tree_map.insert(std::make_pair(org,
+ iter)).first;
+ }
+ std::string name = psm::ProcessIDN(
+ Stringize(CERT_GetCommonName(&cert->subject)));
+ if (name.empty() && cert->nickname) {
+ name = cert->nickname;
+ // Hack copied from mozilla: Cut off text before first :, which seems to
+ // just be the token name.
+ size_t colon_pos = name.find(':');
+ if (colon_pos != std::string::npos)
+ name = name.substr(colon_pos + 1);
+ }
+ GtkTreeIter iter;
+ gtk_tree_store_append(store_, &iter, &org_tree_map_iter->second);
+ gtk_tree_store_set(store_, &iter,
+ CERT_NAME, name.c_str(),
+ CERT_SECURITY_DEVICE,
+ psm::GetCertTokenName(cert).c_str(),
+ CERT_SERIAL_NUMBER,
+ Stringize(CERT_Hexify(
+ &cert->serialNumber, TRUE)).c_str(),
+ CERT_ADDRESS, cert->emailAddr,
+ CERT_POINTER, cert,
+ -1);
+
+ PRTime issued, expires;
+ if (CERT_GetCertTimes(cert, &issued, &expires) == SECSuccess) {
+ gtk_tree_store_set(store_, &iter,
+ CERT_EXPIRES_ON,
+ WideToUTF8(base::TimeFormatShortDateNumeric(
+ base::PRTimeToBaseTime(expires))).c_str(),
+ CERT_EXPIRES_ON_INT, expires,
+ -1);
+ }
+ }
+ }
+
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_));
+}
+
+gint CertificatePage::LocaleSortFunc(GtkTreeModel* model,
+ GtkTreeIter* a,
+ GtkTreeIter* b,
+ int col) {
+ gchar* value1 = NULL;
+ gchar* value2 = NULL;
+ gtk_tree_model_get(model, a, col, &value1, -1);
+ gtk_tree_model_get(model, b, col, &value2, -1);
+ if (!value1 || !value2) {
+ if (value1)
+ return 1;
+ if (value2)
+ return -1;
+ return 0;
+ }
+
+ return l10n_util::CompareStringWithCollator(collator_.get(),
+ UTF8ToWide(value1),
+ UTF8ToWide(value2));
+}
+
+gint CertificatePage::SortNameFunc(GtkTreeModel* model, GtkTreeIter* a,
+ GtkTreeIter* b) {
+ return LocaleSortFunc(model, a, b, CERT_NAME);
+}
+
+gint CertificatePage::SortDeviceFunc(GtkTreeModel* model, GtkTreeIter* a,
+ GtkTreeIter* b) {
+ return LocaleSortFunc(model, a, b, CERT_SECURITY_DEVICE);
+}
+
+void CertificatePage::OnSelectionChanged(GtkTreeSelection* selection) {
+ CERTCertificate* cert = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel* model;
+ if (gtk_tree_selection_get_selected(selection_, &model, &iter))
+ gtk_tree_model_get(model, &iter, CERT_POINTER, &cert, -1);
+
+ gtk_widget_set_sensitive(view_button_, cert ? TRUE : FALSE);
+}
+
+void CertificatePage::OnViewClicked(GtkWidget* button) {
+ GtkTreeIter iter;
+ GtkTreeModel* model;
+ if (!gtk_tree_selection_get_selected(selection_, &model, &iter))
+ return;
+
+ CERTCertificate* cert = NULL;
+ gtk_tree_model_get(model, &iter, CERT_POINTER, &cert, -1);
+ if (cert)
+ ShowCertificateViewer(GTK_WINDOW(gtk_widget_get_toplevel(widget())), cert);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// CertificateManager class definition.
+
+class CertificateManager {
+ public:
+ explicit CertificateManager(gfx::NativeWindow parent, Profile* profile);
+ ~CertificateManager();
+
+ // Shows the Tab corresponding to the specified |page|.
+ void ShowCertificatePage(CertificateManagerPage page);
+
+ private:
+ CHROMEGTK_CALLBACK_2(CertificateManager, void, OnSwitchPage,
+ GtkNotebookPage*, guint);
+
+ CERTCertList* cert_list_;
+
+ CertificatePage user_page_;
+ CertificatePage email_page_;
+ CertificatePage server_page_;
+ CertificatePage ca_page_;
+ CertificatePage unknown_page_;
+
+ GtkWidget* dialog_;
+
+ GtkWidget* notebook_;
+
+ // The last page the user was on when they opened the CertificateManager
+ // window.
+ IntegerPrefMember last_selected_page_;
+
+ DISALLOW_COPY_AND_ASSIGN(CertificateManager);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// CertificateManager implementation.
+
+void OnDestroy(GtkDialog* dialog, CertificateManager* cert_manager) {
+ delete cert_manager;
+}
+
+CertificateManager::CertificateManager(gfx::NativeWindow parent,
+ Profile* profile)
+ : user_page_(psm::USER_CERT),
+ email_page_(psm::EMAIL_CERT),
+ server_page_(psm::SERVER_CERT),
+ ca_page_(psm::CA_CERT),
+ unknown_page_(psm::UNKNOWN_CERT) {
+ // We don't need to observe changes in this value.
+ last_selected_page_.Init(prefs::kCertificateManagerWindowLastTabIndex,
+ profile->GetPrefs(), NULL);
+
+ dialog_ = gtk_dialog_new_with_buttons(
+ l10n_util::GetStringUTF8(IDS_CERTIFICATE_MANAGER_TITLE).c_str(),
+ parent,
+ // Non-modal.
+ GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ NULL);
+ gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
+ gtk_util::kContentAreaSpacing);
+ gtk_window_set_default_size(GTK_WINDOW(dialog_), 600, 440);
+
+ notebook_ = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), notebook_);
+
+ gtk_notebook_append_page(
+ GTK_NOTEBOOK(notebook_),
+ user_page_.widget(),
+ gtk_label_new_with_mnemonic(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_PERSONAL_CERTS_TAB_LABEL).c_str()));
+
+ gtk_notebook_append_page(
+ GTK_NOTEBOOK(notebook_),
+ email_page_.widget(),
+ gtk_label_new_with_mnemonic(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_OTHER_PEOPLES_CERTS_TAB_LABEL).c_str()));
+
+ gtk_notebook_append_page(
+ GTK_NOTEBOOK(notebook_),
+ server_page_.widget(),
+ gtk_label_new_with_mnemonic(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_SERVER_CERTS_TAB_LABEL).c_str()));
+
+ gtk_notebook_append_page(
+ GTK_NOTEBOOK(notebook_),
+ ca_page_.widget(),
+ gtk_label_new_with_mnemonic(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_CERT_AUTHORITIES_TAB_LABEL).c_str()));
+
+ gtk_notebook_append_page(
+ GTK_NOTEBOOK(notebook_),
+ unknown_page_.widget(),
+ gtk_label_new_with_mnemonic(
+ l10n_util::GetStringUTF8(
+ IDS_CERT_MANAGER_UNKNOWN_TAB_LABEL).c_str()));
+
+ cert_list_ = PK11_ListCerts(PK11CertListUnique, NULL);
+ user_page_.PopulateTree(cert_list_);
+ email_page_.PopulateTree(cert_list_);
+ server_page_.PopulateTree(cert_list_);
+ ca_page_.PopulateTree(cert_list_);
+ unknown_page_.PopulateTree(cert_list_);
+
+ // Need to show the notebook before connecting switch-page signal, otherwise
+ // we'll immediately get a signal switching to page 0 and overwrite our
+ // last_selected_page_ value.
+ gtk_util::ShowDialogWithLocalizedSize(dialog_, -1, -1, true);
+
+ g_signal_connect(notebook_, "switch-page",
+ G_CALLBACK(OnSwitchPageThunk), this);
+
+ g_signal_connect(dialog_, "response", G_CALLBACK(gtk_widget_destroy), NULL);
+ g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroy), this);
+}
+
+CertificateManager::~CertificateManager() {
+ CERT_DestroyCertList(cert_list_);
+}
+
+void CertificateManager::OnSwitchPage(GtkWidget* notebook,
+ GtkNotebookPage* page,
+ guint page_num) {
+ int index = static_cast<int>(page_num);
+ DCHECK(index > CERT_MANAGER_PAGE_DEFAULT && index < CERT_MANAGER_PAGE_COUNT);
+ last_selected_page_.SetValue(index);
+}
+
+void CertificateManager::ShowCertificatePage(CertificateManagerPage page) {
+ // Bring options window to front if it already existed and isn't already
+ // in front
+ gtk_window_present_with_time(GTK_WINDOW(dialog_),
+ gtk_get_current_event_time());
+
+ if (page == CERT_MANAGER_PAGE_DEFAULT) {
+ // Remember the last visited page from local state.
+ page = static_cast<CertificateManagerPage>(last_selected_page_.GetValue());
+ if (page == CERT_MANAGER_PAGE_DEFAULT)
+ page = CERT_MANAGER_PAGE_USER;
+ }
+ // If the page number is out of bounds, reset to the first tab.
+ if (page < 0 || page >= gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook_)))
+ page = CERT_MANAGER_PAGE_USER;
+
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook_), page);
+}
+
+} // namespace
+
+namespace certificate_manager_util {
+
+void RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterIntegerPref(prefs::kCertificateManagerWindowLastTabIndex, 0);
+}
+
+} // namespace certificate_manager_util
+
+void ShowCertificateManager(gfx::NativeWindow parent, Profile* profile,
+ CertificateManagerPage page) {
+ base::EnsureNSSInit();
+ CertificateManager* manager = new CertificateManager(parent, profile);
+ manager->ShowCertificatePage(page);
+}