[evolution-data-server] [trust-prompt] Add certificate viewer and "issuer*" parameters



commit 3ba0319ee232d0be1508d18f9f5a132dd0b8c683
Author: Milan Crha <mcrha redhat com>
Date:   Tue Dec 11 18:38:49 2012 +0100

    [trust-prompt] Add certificate viewer and "issuer*" parameters

 modules/trust-prompt/Makefile.am            |    4 +
 modules/trust-prompt/certificate-viewer.c   |  678 +++++++++++++++++++
 modules/trust-prompt/certificate-viewer.h   |   32 +
 modules/trust-prompt/e-asn1-object.c        |  964 +++++++++++++++++++++++++++
 modules/trust-prompt/e-asn1-object.h        |  109 +++
 modules/trust-prompt/module-trust-prompt.c  |   96 +++-
 po/POTFILES.in                              |    2 +
 tests/libedataserver/e-user-prompter-test.c |   41 ++-
 8 files changed, 1920 insertions(+), 6 deletions(-)
---
diff --git a/modules/trust-prompt/Makefile.am b/modules/trust-prompt/Makefile.am
index 53b193a..859cdd9 100644
--- a/modules/trust-prompt/Makefile.am
+++ b/modules/trust-prompt/Makefile.am
@@ -13,6 +13,10 @@ module_trust_prompt_la_CPPFLAGS = \
 	$(NULL)
 
 module_trust_prompt_la_SOURCES = \
+	e-asn1-object.c \
+	e-asn1-object.h \
+	certificate-viewer.c \
+	certificate-viewer.h \
 	module-trust-prompt.c \
 	$(NULL)
 
diff --git a/modules/trust-prompt/certificate-viewer.c b/modules/trust-prompt/certificate-viewer.c
new file mode 100644
index 0000000..ce96e50
--- /dev/null
+++ b/modules/trust-prompt/certificate-viewer.c
@@ -0,0 +1,678 @@
+/*
+ * 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 <glib/gi18n.h>
+
+#include "pk11pub.h"
+#include "hasht.h"
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-asn1-object.h"
+#include "certificate-viewer.h"
+
+#define CERTIFICATE_VIEWER_PRIV_KEY "CertificateViewerPriv-key"
+
+typedef struct _CertificateViewerPriv
+{
+	GtkWidget *issued_to_cn;
+	GtkWidget *issued_to_o;
+	GtkWidget *issued_to_ou;
+	GtkWidget *issued_to_serial;
+	GtkWidget *issued_by_cn;
+	GtkWidget *issued_by_o;
+	GtkWidget *issued_by_ou;
+	GtkWidget *validity_issued_on;
+	GtkWidget *validity_expires_on;
+	GtkWidget *fingerprints_sha1;
+	GtkWidget *fingerprints_md5;
+	GtkWidget *cert_hierarchy_treeview;
+	GtkWidget *cert_fields_treeview;
+	GtkWidget *cert_field_value_textview;
+
+	CERTCertificate *cert;
+	GSList *issuers;
+	GtkTextTag *monospace_tag;
+} CertificateViewerPriv;
+
+static void
+free_priv_struct (gpointer ptr)
+{
+	CertificateViewerPriv *priv = ptr;
+	GSList *iter;
+
+	if (!priv)
+		return;
+
+	if (priv->cert)
+		CERT_DestroyCertificate (priv->cert);
+
+	for (iter = priv->issuers; iter; iter = iter->next) {
+		CERTCertificate *cert = iter->data;
+
+		if (cert)
+			CERT_DestroyCertificate (cert);
+	}
+
+	g_slist_free (priv->issuers);
+
+	g_free (priv);
+}
+
+static void
+begin_section (GtkGrid *add_to,
+	       const gchar *caption,
+	       gint *from_row,
+	       gint for_rows)
+{
+	GtkWidget *widget;
+	PangoAttribute *attr;
+	PangoAttrList *bold;
+
+	g_return_if_fail (add_to != NULL);
+	g_return_if_fail (caption != NULL);
+	g_return_if_fail (from_row != NULL);
+
+	bold = pango_attr_list_new ();
+	attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+	pango_attr_list_insert (bold, attr);
+
+	widget = gtk_label_new (caption);
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"halign", GTK_ALIGN_START,
+		"justify", GTK_JUSTIFY_LEFT,
+		"attributes", bold,
+		"ellipsize", PANGO_ELLIPSIZE_NONE,
+		NULL);
+
+	pango_attr_list_unref (bold);
+
+	gtk_grid_attach (add_to, widget, 0, *from_row, 3, 1);
+	(*from_row)++;
+
+	widget = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
+
+	gtk_grid_attach (add_to, widget, 0, *from_row, 1, for_rows);
+}
+
+static GtkWidget *
+add_info_label (GtkGrid *add_to,
+		const gchar *caption,
+		gint *at_row)
+{
+	GtkWidget *widget;
+
+	g_return_val_if_fail (add_to != NULL, NULL);
+	g_return_val_if_fail (at_row != NULL, NULL);
+
+	if (caption) {
+		widget = gtk_label_new (caption);
+		g_object_set (G_OBJECT (widget),
+			"hexpand", FALSE,
+			"halign", GTK_ALIGN_START,
+			"justify", GTK_JUSTIFY_LEFT,
+			"ellipsize", PANGO_ELLIPSIZE_NONE,
+			NULL);
+
+		gtk_grid_attach (add_to, widget, 1, *at_row, 1, 1);
+	}
+
+	widget = gtk_label_new ("");
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"halign", GTK_ALIGN_START,
+		"justify", GTK_JUSTIFY_LEFT,
+		"ellipsize", PANGO_ELLIPSIZE_NONE,
+		"selectable", caption != NULL,
+		NULL);
+
+	gtk_grid_attach (add_to, widget, caption ? 2 : 1, *at_row, caption ? 1 : 2, 1);
+
+	(*at_row)++;
+
+	return widget;
+}
+
+static GtkWidget *
+add_scrolled_window (GtkGrid *add_to,
+		     const gchar *caption,
+		     gint *at_row,
+		     GtkWidget *add_widget)
+{
+	GtkWidget *widget;
+	PangoAttribute *attr;
+	PangoAttrList *bold;
+
+	g_return_val_if_fail (add_to != NULL, NULL);
+	g_return_val_if_fail (caption != NULL, NULL);
+	g_return_val_if_fail (at_row != NULL, NULL);
+
+	bold = pango_attr_list_new ();
+	attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+	pango_attr_list_insert (bold, attr);
+
+	widget = gtk_label_new (caption);
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"halign", GTK_ALIGN_START,
+		"justify", GTK_JUSTIFY_LEFT,
+		"attributes", bold,
+		"ellipsize", PANGO_ELLIPSIZE_NONE,
+		NULL);
+
+	pango_attr_list_unref (bold);
+
+	gtk_grid_attach (add_to, widget, 0, *at_row, 1, 1);
+	(*at_row)++;
+
+	widget = gtk_scrolled_window_new (NULL, NULL);
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"halign", GTK_ALIGN_FILL,
+		"vexpand", TRUE,
+		"valign", GTK_ALIGN_FILL,
+		"hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+		"vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+		"shadow-type", GTK_SHADOW_ETCHED_IN,
+		NULL);
+
+	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (widget), add_widget);
+
+	gtk_grid_attach (add_to, widget, 0, *at_row, 1, 1);
+	(*at_row)++;
+
+	return add_widget;
+}
+
+#define FLAG_NONE	 (0)
+#define FLAG_PORT_MEMORY (1 << 0)
+#define FLAG_MARKUP	 (1 << 1)
+
+static void
+set_label_text (GtkWidget *label,
+		const gchar *not_part_markup,
+		gchar *text,
+		guint32 flags)
+{
+	if (text) {
+		if ((flags & FLAG_MARKUP) != 0)
+			gtk_label_set_markup (GTK_LABEL (label), text);
+		else
+			gtk_label_set_text (GTK_LABEL (label), text);
+
+		if ((flags & FLAG_PORT_MEMORY) != 0)
+			PORT_Free (text);
+		else
+			g_free (text);
+	} else {
+		gtk_label_set_markup (GTK_LABEL (label), not_part_markup);
+	}
+}
+
+static void
+get_cert_times (CERTCertificate *cert,
+		gchar **issued_on,
+		gchar **expires_on)
+{
+	PRTime time_issued_on;
+	PRTime time_expires_on;
+	PRExplodedTime explodedTime;
+	struct tm exploded_tm;
+	gchar buf[128];
+
+	g_return_if_fail (cert != NULL);
+	g_return_if_fail (issued_on != NULL);
+	g_return_if_fail (expires_on != NULL);
+
+	if (SECSuccess != CERT_GetCertTimes (cert, &time_issued_on, &time_expires_on))
+		return;
+
+	PR_ExplodeTime (time_issued_on, PR_LocalTimeParameters, &explodedTime);
+	exploded_tm.tm_sec = explodedTime.tm_sec;
+	exploded_tm.tm_min = explodedTime.tm_min;
+	exploded_tm.tm_hour = explodedTime.tm_hour;
+	exploded_tm.tm_mday = explodedTime.tm_mday;
+	exploded_tm.tm_mon = explodedTime.tm_month;
+	exploded_tm.tm_year = explodedTime.tm_year - 1900;
+	e_utf8_strftime (buf, sizeof (buf), "%x", &exploded_tm);
+	*issued_on = g_strdup (buf);
+
+	PR_ExplodeTime (time_expires_on, PR_LocalTimeParameters, &explodedTime);
+	exploded_tm.tm_sec = explodedTime.tm_sec;
+	exploded_tm.tm_min = explodedTime.tm_min;
+	exploded_tm.tm_hour = explodedTime.tm_hour;
+	exploded_tm.tm_mday = explodedTime.tm_mday;
+	exploded_tm.tm_mon = explodedTime.tm_month;
+	exploded_tm.tm_year = explodedTime.tm_year - 1900;
+	e_utf8_strftime (buf, sizeof (buf), "%x", &exploded_tm);
+	*expires_on = g_strdup (buf);
+}
+
+static void
+fill_general_page (CertificateViewerPriv *priv)
+{
+	gchar *not_part_markup;
+	gchar *issued_on = NULL;
+	gchar *expires_on = NULL;
+	gchar *port_str;
+	guchar fingerprint[128];
+	SECItem fpItem;
+
+	g_return_if_fail (priv != NULL);
+
+	not_part_markup = g_strconcat ("<i>&lt;", _("Not part of certificate"), "&gt;</i>", NULL);
+
+	set_label_text (priv->issued_to_cn, not_part_markup, CERT_GetCommonName (&priv->cert->subject), FLAG_PORT_MEMORY);
+	set_label_text (priv->issued_to_o, not_part_markup, CERT_GetOrgName (&priv->cert->subject), FLAG_PORT_MEMORY);
+	set_label_text (priv->issued_to_ou, not_part_markup, CERT_GetOrgUnitName (&priv->cert->subject), FLAG_PORT_MEMORY);
+	set_label_text (priv->issued_to_serial, not_part_markup, CERT_Hexify (&priv->cert->serialNumber, TRUE), FLAG_PORT_MEMORY);
+
+	set_label_text (priv->issued_by_cn, not_part_markup, CERT_GetCommonName (&priv->cert->issuer), FLAG_PORT_MEMORY);
+	set_label_text (priv->issued_by_o, not_part_markup, CERT_GetOrgName (&priv->cert->issuer), FLAG_PORT_MEMORY);
+	set_label_text (priv->issued_by_ou, not_part_markup, CERT_GetOrgUnitName (&priv->cert->issuer), FLAG_PORT_MEMORY);
+
+	get_cert_times (priv->cert, &issued_on, &expires_on);
+	set_label_text (priv->validity_issued_on, not_part_markup, issued_on, FLAG_NONE);
+	set_label_text (priv->validity_expires_on, not_part_markup, expires_on, FLAG_NONE);
+
+	memset (fingerprint, 0, sizeof fingerprint);
+	PK11_HashBuf (
+		SEC_OID_SHA1, fingerprint,
+		priv->cert->derCert.data,
+		priv->cert->derCert.len);
+	fpItem.data = fingerprint;
+	fpItem.len = SHA1_LENGTH;
+	port_str = CERT_Hexify (&fpItem, TRUE);
+	set_label_text (priv->fingerprints_sha1, not_part_markup, g_strconcat ("<tt>", port_str, "</tt>", NULL), FLAG_MARKUP);
+	PORT_Free (port_str);
+
+	memset (fingerprint, 0, sizeof fingerprint);
+	PK11_HashBuf (
+		SEC_OID_MD5, fingerprint,
+		priv->cert->derCert.data,
+		priv->cert->derCert.len);
+	fpItem.data = fingerprint;
+	fpItem.len = MD5_LENGTH;
+	port_str = CERT_Hexify (&fpItem, TRUE);
+	set_label_text (priv->fingerprints_md5, not_part_markup,  g_strconcat ("<tt>", port_str, "</tt>", NULL), FLAG_MARKUP);
+	PORT_Free (port_str);
+
+	g_free (not_part_markup);
+}
+
+static void
+populate_fields_tree (CertificateViewerPriv *priv,
+                      EASN1Object *asn1,
+                      GtkTreeIter *root)
+{
+	GtkTreeStore *fields_store;
+	GtkTreeIter new_iter;
+
+	if (!asn1)
+		return;
+
+	fields_store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->cert_fields_treeview)));
+
+	/* first insert a node for the current asn1 */
+	gtk_tree_store_insert (fields_store, &new_iter, root, -1);
+	gtk_tree_store_set (
+		fields_store, &new_iter,
+		0, e_asn1_object_get_display_name (asn1),
+		1, asn1,
+		-1);
+
+	if (e_asn1_object_is_valid_container (asn1)) {
+		GList *children = e_asn1_object_get_children (asn1);
+
+		if (children) {
+			GList *iter;
+			for (iter = children; iter; iter = iter->next) {
+				EASN1Object *subasn1 = iter->data;
+
+				populate_fields_tree (priv, subasn1, &new_iter);
+			}
+		}
+
+		g_list_free_full (children, g_object_unref);
+	}
+}
+
+static void
+hierarchy_selection_changed_cb (GtkTreeSelection *selection,
+				CertificateViewerPriv *priv)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		CERTCertificate *cert;
+		EASN1Object *asn1;
+		GtkTreeStore *fields_store;
+
+		gtk_tree_model_get (model, &iter, 1, &cert, -1);
+
+		if (!cert)
+			return;
+
+		/* display the cert's ASN1 structure */
+		asn1 = e_asn1_object_new_from_cert (cert);
+
+		/* wipe out the old model */
+		fields_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_OBJECT);
+		gtk_tree_view_set_model (
+			GTK_TREE_VIEW (priv->cert_fields_treeview),
+			GTK_TREE_MODEL (fields_store));
+
+		/* populate the fields from the newly selected cert */
+		populate_fields_tree (priv, asn1, NULL);
+		gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->cert_fields_treeview));
+		if (asn1)
+			g_object_unref (asn1);
+
+		/* and blow away the field value */
+		gtk_text_buffer_set_text (
+			gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->cert_field_value_textview)),
+			"", 0);
+	}
+}
+
+static void
+fields_selection_changed_cb (GtkTreeSelection *selection,
+			     CertificateViewerPriv *priv)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model;
+
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		EASN1Object *asn1 = NULL;
+		const gchar *value = NULL;
+		GtkTextView *textview;
+		GtkTextBuffer *textbuffer;
+
+		gtk_tree_model_get (model, &iter, 1, &asn1, -1);
+
+		if (asn1)
+			value = e_asn1_object_get_display_value (asn1);
+
+		textview = GTK_TEXT_VIEW (priv->cert_field_value_textview);
+		textbuffer = gtk_text_view_get_buffer (textview);
+
+		gtk_text_buffer_set_text (textbuffer, "", 0);
+
+		if (value) {
+			GtkTextIter text_iter;
+
+			gtk_text_buffer_get_start_iter (textbuffer, &text_iter);
+
+			gtk_text_buffer_insert_with_tags (textbuffer, &text_iter,
+				value, strlen (value),
+				priv->monospace_tag, NULL);
+		}
+
+		if (asn1)
+			g_object_unref (asn1);
+	}
+}
+
+static void
+fill_details_page (CertificateViewerPriv *priv)
+{
+	GSList *iter;
+	GtkTreeIter root;
+	GtkTreeSelection *selection;
+	gboolean root_set = FALSE;
+	GtkTreeStore *hierarchy_store;
+
+	g_return_if_fail (priv != NULL);
+
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->cert_hierarchy_treeview), FALSE);
+
+	hierarchy_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
+	gtk_tree_view_set_model (
+		GTK_TREE_VIEW (priv->cert_hierarchy_treeview),
+		GTK_TREE_MODEL (hierarchy_store));
+
+	gtk_tree_view_insert_column_with_attributes (
+		GTK_TREE_VIEW (priv->cert_hierarchy_treeview),
+		-1, "Cert", gtk_cell_renderer_text_new (),
+		"text", 0, NULL);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->cert_hierarchy_treeview));
+	g_signal_connect (
+		selection, "changed",
+		G_CALLBACK (hierarchy_selection_changed_cb), priv);
+
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->cert_fields_treeview), FALSE);
+
+	gtk_tree_view_insert_column_with_attributes (
+		GTK_TREE_VIEW (priv->cert_fields_treeview),
+		-1, "Field", gtk_cell_renderer_text_new (),
+		"text", 0, NULL);
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->cert_fields_treeview));
+	g_signal_connect (
+		selection, "changed",
+		G_CALLBACK (fields_selection_changed_cb), priv);
+
+	/* set the font of the field value viewer to be some fixed
+	 * width font to the hex display looks nice. */
+	priv->monospace_tag = gtk_text_buffer_create_tag (
+		gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->cert_field_value_textview)),
+		"mono", "font", "Mono", NULL);
+
+	/* initially populate the hierarchy from the issuers' chain */
+	for (iter = priv->issuers; iter; iter = g_slist_next (iter)) {
+		CERTCertificate *cert = iter->data;
+		gchar *str;
+		GtkTreeIter new_iter;
+
+		if (!cert)
+			continue;
+
+		str = CERT_GetCommonName (&cert->subject);
+
+		gtk_tree_store_insert (hierarchy_store, &new_iter, root_set ? &root : NULL, -1);
+		gtk_tree_store_set (
+			hierarchy_store, &new_iter,
+			0, str ? str : cert->subjectName,
+			1, cert,
+			-1);
+
+		root = new_iter;
+		root_set = TRUE;
+
+		if (str)
+			PORT_Free (str);
+	}
+
+	gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->cert_hierarchy_treeview));
+}
+
+static gchar *
+get_window_title (CERTCertificate *cert)
+{
+	gchar *str;
+
+	g_return_val_if_fail (cert != NULL, NULL);
+
+	if (cert->nickname)
+		return g_strdup (cert->nickname);
+
+	str = CERT_GetCommonName (&cert->subject);
+	if (str) {
+		gchar *title;
+
+		title = g_strdup (str);
+		PORT_Free (str);
+
+		return title;
+	}
+
+	return cert->subjectName;
+}
+
+GtkWidget *
+certificate_viewer_new (GtkWindow *parent,
+			const CERTCertificate *cert,
+			const GSList *issuers_chain_certs)
+{
+	CertificateViewerPriv *priv;
+	GtkWidget *dialog, *notebook, *widget;
+	GtkGrid *grid;
+	gint row;
+	GSList *iter;
+	gchar *title;
+
+	g_return_val_if_fail (cert != NULL, NULL);
+
+	priv = g_new0 (CertificateViewerPriv, 1);
+	priv->cert = CERT_DupCertificate ((CERTCertificate *) cert);
+	priv->issuers = g_slist_copy ((GSList *) issuers_chain_certs);
+
+	/* root issuer first, then bottom down to certificate itself */
+	priv->issuers = g_slist_reverse (priv->issuers);
+	priv->issuers = g_slist_append (priv->issuers, priv->cert);
+
+	for (iter = priv->issuers; iter; iter = g_slist_next (iter)) {
+		iter->data = CERT_DupCertificate (iter->data);
+	}
+
+	title = get_window_title (priv->cert);
+
+	dialog = gtk_dialog_new_with_buttons (title, parent,
+		GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+		NULL);
+
+	g_free (title);
+
+	g_object_set_data_full (G_OBJECT (dialog), CERTIFICATE_VIEWER_PRIV_KEY, priv, free_priv_struct);
+
+	notebook = gtk_notebook_new ();
+	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), notebook);
+	gtk_container_set_border_width (GTK_CONTAINER (notebook), 12);
+
+	/* General page */
+	row = 0;
+	grid = GTK_GRID (gtk_grid_new ());
+	g_object_set (G_OBJECT (grid),
+		"hexpand", TRUE,
+		"halign", GTK_ALIGN_FILL,
+		"vexpand", FALSE,
+		"valign", GTK_ALIGN_START,
+		"border-width", 12,
+		"row-spacing", 6,
+		"column-spacing", 6,
+		NULL);
+
+	begin_section (grid, _("This certificate has been verified for the following uses:"), &row, 4);
+
+	if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageSSLClient) != 0) {
+		widget = add_info_label (grid, NULL, &row);
+		gtk_label_set_text (GTK_LABEL (widget), _("SSL Client Certificate"));
+	}
+
+	if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & (certificateUsageSSLServer | certificateUsageSSLCA)) != 0) {
+		widget = add_info_label (grid, NULL, &row);
+		gtk_label_set_text (GTK_LABEL (widget), _("SSL Server Certificate"));
+	}
+
+	if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageEmailSigner) != 0) {
+		widget = add_info_label (grid, NULL, &row);
+		gtk_label_set_text (GTK_LABEL (widget), _("Email Signer Certificate"));
+	}
+
+	if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageEmailRecipient) != 0) {
+		widget = add_info_label (grid, NULL, &row);
+		gtk_label_set_text (GTK_LABEL (widget), _("Email Recipient Certificate"));
+	}
+
+	widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+	g_object_set (G_OBJECT (widget),
+		"hexpand", TRUE,
+		"halign", GTK_ALIGN_FILL,
+		"vexpand", FALSE,
+		"valign", GTK_ALIGN_START,
+		NULL);
+
+	gtk_grid_attach (grid, widget, 0, row, 3, 1);
+	row++;
+
+	begin_section (grid, _("Issued To"), &row, 4);
+	priv->issued_to_cn = add_info_label (grid, _("Common Name (CN)"), &row);
+	priv->issued_to_o = add_info_label (grid, _("Organization (O)"), &row);
+	priv->issued_to_ou = add_info_label (grid, _("Organizational Unit (OU)"), &row);
+	priv->issued_to_serial = add_info_label (grid, _("Serial Number"), &row);
+
+	begin_section (grid, _("Issued By"), &row, 3);
+	priv->issued_by_cn = add_info_label (grid, _("Common Name (CN)"), &row);
+	priv->issued_by_o = add_info_label (grid, _("Organization (O)"), &row);
+	priv->issued_by_ou = add_info_label (grid, _("Organizational Unit (OU)"), &row);
+
+	begin_section (grid, _("Validity"), &row, 2);
+	priv->validity_issued_on = add_info_label (grid, _("Issued On"), &row);
+	priv->validity_expires_on = add_info_label (grid, _("Expires On"), &row);
+
+	begin_section (grid, _("Fingerprints"), &row, 2);
+	priv->fingerprints_sha1 = add_info_label (grid, _("SHA1 Fingerprint"), &row);
+	priv->fingerprints_md5 = add_info_label (grid, _("MD5 Fingerprint"), &row);
+
+	widget = gtk_label_new (_("General"));
+	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (grid), widget);
+
+	/* Details page */
+	row = 0;
+	grid = GTK_GRID (gtk_grid_new ());
+	g_object_set (G_OBJECT (grid),
+		"hexpand", TRUE,
+		"halign", GTK_ALIGN_FILL,
+		"vexpand", TRUE,
+		"valign", GTK_ALIGN_FILL,
+		"border-width", 12,
+		"row-spacing", 6,
+		"column-spacing", 6,
+		NULL);
+
+	priv->cert_hierarchy_treeview = add_scrolled_window (grid,
+		_("Certificate Hierarchy"), &row, gtk_tree_view_new ());
+
+	priv->cert_fields_treeview = add_scrolled_window (grid,
+		_("Certificate Fields"), &row, gtk_tree_view_new ());
+
+	priv->cert_field_value_textview = add_scrolled_window (grid,
+		_("Field Value"), &row, gtk_text_view_new ());
+
+	widget = gtk_label_new (_("Details"));
+	gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (grid), widget);
+
+	gtk_widget_show_all (notebook);
+
+	fill_general_page (priv);
+	fill_details_page (priv);
+
+	return dialog;
+}
diff --git a/modules/trust-prompt/certificate-viewer.h b/modules/trust-prompt/certificate-viewer.h
new file mode 100644
index 0000000..41f2c83
--- /dev/null
+++ b/modules/trust-prompt/certificate-viewer.h
@@ -0,0 +1,32 @@
+/*
+ * 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)
+ */
+
+#ifndef CERTIFICATE_VIEWER_H
+#define CERTIFICATE_VIEWER_H
+
+#include <gtk/gtk.h>
+#include <cert.h>
+
+GtkWidget *	certificate_viewer_new	(GtkWindow *parent,
+					 const CERTCertificate *cert,
+					 const GSList *issuers_chain_certs);
+
+#endif /* CERTIFICATE_VIEWER_H */
diff --git a/modules/trust-prompt/e-asn1-object.c b/modules/trust-prompt/e-asn1-object.c
new file mode 100644
index 0000000..7e5f27c
--- /dev/null
+++ b/modules/trust-prompt/e-asn1-object.c
@@ -0,0 +1,964 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* The following is the mozilla license blurb, as the bodies some of
+ * these functions were derived from the mozilla source. */
+/*
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ */
+
+/*
+ * Author: Chris Toshok (toshok ximian com)
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "e-asn1-object.h"
+
+#include "pk11func.h"
+#include "certdb.h"
+#include "hasht.h"
+
+#define E_ASN1_OBJECT_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_ASN1_OBJECT, EASN1ObjectPrivate))
+
+struct _EASN1ObjectPrivate {
+	PRUint32 tag;
+	PRUint32 type;
+	gboolean valid_container;
+
+	GList *children;
+
+	gchar *display_name;
+	gchar *value;
+
+	gchar *data;
+	guint data_len;
+};
+
+G_DEFINE_TYPE (EASN1Object, e_asn1_object, G_TYPE_OBJECT)
+
+static gboolean
+get_int_value (SECItem *versionItem,
+               gulong *version)
+{
+	SECStatus srv;
+	srv = SEC_ASN1DecodeInteger (versionItem,version);
+	if (srv != SECSuccess) {
+		g_warning ("could not decode version of cert");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
+process_version (SECItem *versionItem,
+                 EASN1Object **retItem)
+{
+	EASN1Object *item = e_asn1_object_new ();
+	gulong version;
+
+	e_asn1_object_set_display_name (item, _("Version"));
+
+	/* Now to figure out what version this certificate is. */
+
+	if (versionItem->data) {
+		if (!get_int_value (versionItem, &version))
+			return FALSE;
+	} else {
+		/* If there is no version present in the cert, then rfc2459
+		 * says we default to v1 (0) */
+		version = 0;
+	}
+
+	switch (version) {
+	case 0:
+		e_asn1_object_set_display_value (item, _("Version 1"));
+		break;
+	case 1:
+		e_asn1_object_set_display_value (item, _("Version 2"));
+		break;
+	case 2:
+		e_asn1_object_set_display_value (item, _("Version 3"));
+		break;
+	default:
+		g_warning ("Bad value for cert version");
+		return FALSE;
+	}
+
+	*retItem = item;
+	return TRUE;
+}
+
+static gboolean
+process_serial_number_der (SECItem *serialItem,
+                           EASN1Object **retItem)
+{
+	gchar *serialNumber;
+	EASN1Object *item = e_asn1_object_new ();
+
+	e_asn1_object_set_display_name (item, _("Serial Number"));
+
+	serialNumber = CERT_Hexify (serialItem, 1);
+
+	e_asn1_object_set_display_value (item, serialNumber);
+	PORT_Free (serialNumber); /* XXX the right free to use? */
+
+	*retItem = item;
+	return TRUE;
+}
+
+static gboolean
+get_default_oid_format (SECItem *oid,
+                        gchar **text)
+{
+	GString *str;
+	gulong val = oid->data[0];
+	guint ii = val % 40;
+
+	val /= 40;
+
+	str = g_string_new ("");
+	g_string_append_printf (str, "%lu %u ", val, ii);
+
+	val = 0;
+	for (ii = 1; ii < oid->len; ii++) {
+		/* In this loop, we have to parse a DER formatted
+		 * If the first bit is a 1, then the integer is
+		 * represented by more than one byte.  If the
+		 * first bit is set then we continue on and add
+		 * the values of the later bytes until we get
+		 * a byte without the first bit set.
+		*/
+		gulong jj;
+
+		jj = oid->data[ii];
+		val = (val << 7) | (jj & 0x7f);
+		if (jj & 0x80)
+			continue;
+		g_string_append_printf (str, "%lu ", val);
+
+		val = 0;
+  }
+
+  *text = g_string_free (str, FALSE);
+
+  return TRUE;
+}
+
+static gboolean
+get_oid_text (SECItem *oid,
+              gchar **text)
+{
+	SECOidTag oidTag = SECOID_FindOIDTag (oid);
+	gchar *temp;
+
+	switch (oidTag) {
+	case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION:
+		*text = g_strdup (_("PKCS #1 MD2 With RSA Encryption"));
+		break;
+	case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
+		*text = g_strdup (_("PKCS #1 MD5 With RSA Encryption"));
+		break;
+	case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
+		*text = g_strdup (_("PKCS #1 SHA-1 With RSA Encryption"));
+		break;
+	case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
+		*text = g_strdup (_("PKCS #1 SHA-256 With RSA Encryption"));
+		break;
+	case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
+		*text = g_strdup (_("PKCS #1 SHA-384 With RSA Encryption"));
+		break;
+	case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
+		*text = g_strdup (_("PKCS #1 SHA-512 With RSA Encryption"));
+		break;
+	case SEC_OID_AVA_COUNTRY_NAME:
+		*text = g_strdup ("C");
+		break;
+	case SEC_OID_AVA_COMMON_NAME:
+		*text = g_strdup ("CN");
+		break;
+	case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME:
+		*text = g_strdup ("OU");
+		break;
+	case SEC_OID_AVA_ORGANIZATION_NAME:
+		*text = g_strdup ("O");
+		break;
+	case SEC_OID_AVA_LOCALITY:
+		*text = g_strdup ("L");
+		break;
+	case SEC_OID_AVA_DN_QUALIFIER:
+		*text = g_strdup ("DN");
+		break;
+	case SEC_OID_AVA_DC:
+		*text = g_strdup ("DC");
+		break;
+	case SEC_OID_AVA_STATE_OR_PROVINCE:
+		*text = g_strdup ("ST");
+		break;
+	case SEC_OID_PKCS1_RSA_ENCRYPTION:
+		*text = g_strdup (_("PKCS #1 RSA Encryption"));
+		break;
+	case SEC_OID_X509_KEY_USAGE:
+		*text = g_strdup (_("Certificate Key Usage"));
+		break;
+	case SEC_OID_NS_CERT_EXT_CERT_TYPE:
+		*text = g_strdup (_("Netscape Certificate Type"));
+		break;
+	case SEC_OID_X509_AUTH_KEY_ID:
+		*text = g_strdup (_("Certificate Authority Key Identifier"));
+		break;
+	case SEC_OID_RFC1274_UID:
+		*text = g_strdup ("UID");
+		break;
+	case SEC_OID_PKCS9_EMAIL_ADDRESS:
+		*text = g_strdup ("E");
+		break;
+	default:
+		if (!get_default_oid_format (oid, &temp))
+			return FALSE;
+
+		*text = g_strdup_printf (_("Object Identifier (%s)"), temp);
+		g_free (temp);
+
+		break;
+	}
+	return TRUE;
+}
+
+static gboolean
+process_raw_bytes (SECItem *data,
+                   gchar **text)
+{
+	/* This function is used to display some DER bytes
+	 * that we have not added support for decoding.
+	 * It prints the value of the byte out into a
+	 * string that can later be displayed as a byte
+	 * string.  We place a new line after 24 bytes
+	 * to break up extermaly long sequence of bytes.
+	*/
+	GString *str = g_string_new ("");
+	PRUint32 i;
+
+	for (i = 0; i < data->len; i++) {
+		g_string_append_printf (str, "%02x ", data->data[i]);
+		if ((i + 1) % 16 == 0) {
+			g_string_append (str, "\n");
+		}
+	}
+	*text = g_string_free (str, FALSE);
+	return TRUE;
+}
+
+static gboolean
+process_sec_algorithm_id (SECAlgorithmID *algID,
+                          EASN1Object **retSequence)
+{
+	EASN1Object *sequence = e_asn1_object_new ();
+	gchar *text = NULL;
+
+	*retSequence = NULL;
+
+	get_oid_text (&algID->algorithm, &text);
+
+	if (!algID->parameters.len ||
+		algID->parameters.data[0] == E_ASN1_OBJECT_TYPE_NULL) {
+		e_asn1_object_set_display_value (sequence, text);
+		e_asn1_object_set_valid_container (sequence, FALSE);
+	} else {
+		EASN1Object *subitem;
+
+		subitem = e_asn1_object_new ();
+		e_asn1_object_set_display_name (subitem, _("Algorithm Identifier"));
+		e_asn1_object_set_display_value (subitem, text);
+		e_asn1_object_append_child (sequence, subitem);
+		g_object_unref (subitem);
+
+		g_free (text);
+
+		subitem = e_asn1_object_new ();
+		e_asn1_object_set_display_name (subitem, _("Algorithm Parameters"));
+		process_raw_bytes (&algID->parameters, &text);
+		e_asn1_object_set_display_value (subitem, text);
+		e_asn1_object_append_child (sequence, subitem);
+		g_object_unref (subitem);
+	}
+
+	g_free (text);
+	*retSequence = sequence;
+	return TRUE;
+}
+
+static gboolean
+process_subject_public_key_info (CERTSubjectPublicKeyInfo *spki,
+                                 EASN1Object *parentSequence)
+{
+	EASN1Object *spkiSequence = e_asn1_object_new ();
+	EASN1Object *sequenceItem;
+	EASN1Object *printableItem;
+	SECItem data;
+	gchar *text = NULL;
+
+	e_asn1_object_set_display_name (spkiSequence, _("Subject Public Key Info"));
+
+	if (!process_sec_algorithm_id (&spki->algorithm, &sequenceItem))
+		return FALSE;
+
+	e_asn1_object_set_display_name (sequenceItem, _("Subject Public Key Algorithm"));
+
+	e_asn1_object_append_child (spkiSequence, sequenceItem);
+
+	/* The subjectPublicKey field is encoded as a bit string.
+	 * ProcessRawBytes expects the lenght to be in bytes, so
+	 * let's convert the lenght into a temporary SECItem.
+	*/
+	data.data = spki->subjectPublicKey.data;
+	data.len  = spki->subjectPublicKey.len / 8;
+
+	process_raw_bytes (&data, &text);
+	printableItem = e_asn1_object_new ();
+
+	e_asn1_object_set_display_value (printableItem, text);
+	e_asn1_object_set_display_name (printableItem, _("Subject's Public Key"));
+	e_asn1_object_append_child (spkiSequence, printableItem);
+	g_object_unref (printableItem);
+	g_free (text);
+
+	e_asn1_object_append_child (parentSequence, spkiSequence);
+	g_object_unref (spkiSequence);
+
+	return TRUE;
+}
+
+static gboolean
+process_ns_cert_type_extensions (SECItem *extData,
+                                 GString *text)
+{
+	SECItem decoded;
+	guchar nsCertType;
+
+	decoded.data = NULL;
+	decoded.len  = 0;
+	if (SECSuccess != SEC_ASN1DecodeItem (NULL, &decoded,
+					     SEC_ASN1_GET (SEC_BitStringTemplate), extData)) {
+		g_string_append (text, _("Error: Unable to process extension"));
+		return TRUE;
+	}
+
+	nsCertType = decoded.data[0];
+
+	PORT_Free (decoded.data); /* XXX right free? */
+
+	if (nsCertType & NS_CERT_TYPE_SSL_CLIENT) {
+		g_string_append (text, _("SSL Client Certificate"));
+		g_string_append (text, "\n");
+	}
+	if (nsCertType & NS_CERT_TYPE_SSL_SERVER) {
+		g_string_append (text, _("SSL Server Certificate"));
+		g_string_append (text, "\n");
+	}
+	if (nsCertType & NS_CERT_TYPE_EMAIL) {
+		g_string_append (text, _("Email"));
+		g_string_append (text, "\n");
+	}
+	if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING) {
+		g_string_append (text, _("Object Signer"));
+		g_string_append (text, "\n");
+	}
+	if (nsCertType & NS_CERT_TYPE_SSL_CA) {
+		g_string_append (text, _("SSL Certificate Authority"));
+		g_string_append (text, "\n");
+	}
+	if (nsCertType & NS_CERT_TYPE_EMAIL_CA) {
+		g_string_append (text, _("Email Certificate Authority"));
+		g_string_append (text, "\n");
+	}
+	if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING_CA) {
+		g_string_append (text, _("Object Signer"));
+		g_string_append (text, "\n");
+	}
+	return TRUE;
+}
+
+static gboolean
+process_key_usage_extensions (SECItem *extData,
+                              GString *text)
+{
+	SECItem decoded;
+	guchar keyUsage;
+
+	decoded.data = NULL;
+	decoded.len  = 0;
+	if (SECSuccess != SEC_ASN1DecodeItem (NULL, &decoded,
+					     SEC_ASN1_GET (SEC_BitStringTemplate), extData)) {
+		g_string_append (text, _("Error: Unable to process extension"));
+		return TRUE;
+	}
+
+	keyUsage = decoded.data[0];
+	PORT_Free (decoded.data); /* XXX right free? */
+
+	if (keyUsage & KU_DIGITAL_SIGNATURE) {
+		g_string_append (text, _("Signing"));
+		g_string_append (text, "\n");
+	}
+	if (keyUsage & KU_NON_REPUDIATION) {
+		g_string_append (text, _("Non-repudiation"));
+		g_string_append (text, "\n");
+	}
+	if (keyUsage & KU_KEY_ENCIPHERMENT) {
+		g_string_append (text, _("Key Encipherment"));
+		g_string_append (text, "\n");
+	}
+	if (keyUsage & KU_DATA_ENCIPHERMENT) {
+		g_string_append (text, _("Data Encipherment"));
+		g_string_append (text, "\n");
+	}
+	if (keyUsage & KU_KEY_AGREEMENT) {
+		g_string_append (text, _("Key Agreement"));
+		g_string_append (text, "\n");
+	}
+	if (keyUsage & KU_KEY_CERT_SIGN) {
+		g_string_append (text, _("Certificate Signer"));
+		g_string_append (text, "\n");
+	}
+	if (keyUsage & KU_CRL_SIGN) {
+		g_string_append (text, _("CRL Signer"));
+		g_string_append (text, "\n");
+	}
+
+	return TRUE;
+}
+
+static gboolean
+process_extension_data (SECOidTag oidTag,
+                        SECItem *extData,
+                        GString *str)
+{
+	gboolean rv;
+	switch (oidTag) {
+	case SEC_OID_NS_CERT_EXT_CERT_TYPE:
+		rv = process_ns_cert_type_extensions (extData, str);
+		break;
+	case SEC_OID_X509_KEY_USAGE:
+		rv = process_key_usage_extensions (extData, str);
+		break;
+	default: {
+		gchar *text;
+		rv = process_raw_bytes (extData, &text);
+		g_string_append (str, text);
+		g_free (text);
+		break;
+	}
+	}
+	return rv;
+}
+
+static gboolean
+process_single_extension (CERTCertExtension *extension,
+                          EASN1Object **retExtension)
+{
+	GString *str = g_string_new ("");
+	gchar *text;
+	EASN1Object *extensionItem;
+	SECOidTag oidTag = SECOID_FindOIDTag (&extension->id);
+
+	get_oid_text (&extension->id, &text);
+
+	extensionItem = e_asn1_object_new ();
+
+	e_asn1_object_set_display_name (extensionItem, text);
+	g_free (text);
+
+	if (extension->critical.data != NULL) {
+		if (extension->critical.data[0]) {
+			g_string_append (str, _("Critical"));
+		} else {
+			g_string_append (str, _("Not Critical"));
+		}
+	} else {
+		g_string_append (str, _("Not Critical"));
+	}
+	g_string_append (str, "\n");
+	if (!process_extension_data (oidTag, &extension->value, str)) {
+		g_string_free (str, TRUE);
+		return FALSE;
+	}
+
+	e_asn1_object_set_display_value (extensionItem, str->str);
+	g_string_free (str, TRUE);
+	*retExtension = extensionItem;
+	return TRUE;
+}
+
+static gboolean
+process_extensions (CERTCertExtension **extensions,
+                    EASN1Object *parentSequence)
+{
+	EASN1Object *extensionSequence = e_asn1_object_new ();
+	PRInt32 i;
+
+	e_asn1_object_set_display_name (extensionSequence, _("Extensions"));
+
+	for (i = 0; extensions[i] != NULL; i++) {
+		EASN1Object *newExtension;
+
+		if (!process_single_extension (extensions[i],
+					       &newExtension))
+			return FALSE;
+
+		e_asn1_object_append_child (extensionSequence, newExtension);
+	}
+	e_asn1_object_append_child (parentSequence, extensionSequence);
+	return TRUE;
+}
+
+static gboolean
+process_name (CERTName *name,
+              gchar **value)
+{
+	CERTRDN ** rdns;
+	CERTRDN ** rdn;
+	CERTAVA ** avas;
+	CERTAVA * ava;
+	SECItem *decodeItem = NULL;
+	GString *final_string = g_string_new ("");
+
+	gchar *type;
+	GString *avavalue;
+	gchar *temp;
+	CERTRDN **lastRdn;
+
+	rdns = name->rdns;
+
+	/* find last RDN */
+	lastRdn = rdns;
+	while (*lastRdn) lastRdn++;
+
+	/* The above whille loop will put us at the last member
+	 * of the array which is a NULL pointer.  So let's back
+	 * up one spot so that we have the last non-NULL entry in
+	 * the array in preparation for traversing the
+	 * RDN's (Relative Distinguished Name) in reverse order.
+	 */
+	lastRdn--;
+
+	/*
+	 * Loop over name contents in _reverse_ RDN order appending to string
+	 * When building the Ascii string, NSS loops over these entries in
+	 * reverse order, so I will as well.  The difference is that NSS
+	 * will always place them in a one line string separated by commas,
+	 * where I want each entry on a single line.  I can't just use a comma
+	 * as my delimitter because it is a valid character to have in the
+	 * value portion of the AVA and could cause trouble when parsing.
+	 */
+	for (rdn = lastRdn; rdn >= rdns; rdn--) {
+		avas = (*rdn)->avas;
+		while ((ava = *avas++) != 0) {
+			if (!get_oid_text (&ava->type, &type))
+				return FALSE;
+
+			/* This function returns a string in UTF8 format. */
+			decodeItem = CERT_DecodeAVAValue (&ava->value);
+			if (!decodeItem) {
+				g_free (type);
+				return FALSE;
+			}
+
+			avavalue = g_string_new_len (
+				(gchar *) decodeItem->data, decodeItem->len);
+
+			SECITEM_FreeItem (decodeItem, PR_TRUE);
+
+			/* Translators: This string is used in Certificate
+			 * details for fields like Issuer or Subject, which
+			 * shows the field name on the left and its respective
+			 * value on the right, both as stored in the
+			 * certificate itself.  You probably do not need to
+			 * change this string, unless changing the order of
+			 * name and value.  As a result example:
+			 * "OU = VeriSign Trust Network" */
+			temp = g_strdup_printf (_("%s = %s"), type, avavalue->str);
+
+			g_string_append (final_string, temp);
+			g_string_append (final_string, "\n");
+			g_string_free (avavalue, TRUE);
+			g_free (temp);
+			g_free (type);
+		}
+	}
+	*value = g_string_free (final_string, FALSE);
+	return TRUE;
+}
+
+static gboolean
+create_tbs_certificate_asn1_struct (CERTCertificate *cert,
+                                    EASN1Object **seq)
+{
+	/*
+	**   TBSCertificate  ::=  SEQUENCE  {
+	**        version         [0]  EXPLICIT Version DEFAULT v1,
+	**        serialNumber         CertificateSerialNumber,
+	**        signature            AlgorithmIdentifier,
+	**        issuer               Name,
+	**        validity             Validity,
+	**        subject              Name,
+	**        subjectPublicKeyInfo SubjectPublicKeyInfo,
+	**        issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+	**                             -- If present, version shall be v2 or v3
+	**        subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+	**                             -- If present, version shall be v2 or v3
+	**        extensions      [3]  EXPLICIT Extensions OPTIONAL
+	**                             -- If present, version shall be v3
+	**        }
+	**
+	** This is the ASN1 structure we should be dealing with at this point.
+	** The code in this method will assert this is the structure we're dealing
+	** and then add more user friendly text for that field.
+	*/
+	EASN1Object *sequence = e_asn1_object_new ();
+	gchar *text;
+	EASN1Object *subitem;
+	SECItem data;
+
+	e_asn1_object_set_display_name (sequence, _("Certificate"));
+
+	if (!process_version (&cert->version, &subitem))
+		return FALSE;
+	e_asn1_object_append_child (sequence, subitem);
+	g_object_unref (subitem);
+
+	if (!process_serial_number_der (&cert->serialNumber, &subitem))
+		return FALSE;
+	e_asn1_object_append_child (sequence, subitem);
+	g_object_unref (subitem);
+
+	if (!process_sec_algorithm_id (&cert->signature, &subitem))
+		return FALSE;
+	e_asn1_object_set_display_name (subitem, _("Certificate Signature Algorithm"));
+	e_asn1_object_append_child (sequence, subitem);
+	g_object_unref (subitem);
+
+	process_name (&cert->issuer, &text);
+	subitem = e_asn1_object_new ();
+	e_asn1_object_set_display_value (subitem, text);
+	g_free (text);
+
+	e_asn1_object_set_display_name (subitem, _("Issuer"));
+	e_asn1_object_append_child (sequence, subitem);
+	g_object_unref (subitem);
+
+#ifdef notyet
+	nsCOMPtr < nsIASN1Sequence> validitySequence = new nsNSSASN1Sequence ();
+	nssComponent->GetPIPNSSBundleString (NS_LITERAL_STRING ("CertDumpValidity").get (),
+					    text);
+	validitySequence->SetDisplayName (text);
+	asn1Objects->AppendElement (validitySequence, PR_FALSE);
+	nssComponent->GetPIPNSSBundleString (NS_LITERAL_STRING ("CertDumpNotBefore").get (),
+					    text);
+	nsCOMPtr < nsIX509CertValidity> validityData;
+	GetValidity (getter_AddRefs (validityData));
+	PRTime notBefore, notAfter;
+
+	validityData->GetNotBefore (&notBefore);
+	validityData->GetNotAfter (&notAfter);
+	validityData = 0;
+	rv = ProcessTime (notBefore, text.get (), validitySequence);
+	if (NS_FAILED (rv))
+		return rv;
+
+	nssComponent->GetPIPNSSBundleString (NS_LITERAL_STRING ("CertDumpNotAfter").get (),
+					    text);
+	rv = ProcessTime (notAfter, text.get (), validitySequence);
+	if (NS_FAILED (rv))
+		return rv;
+#endif
+
+	subitem = e_asn1_object_new ();
+	e_asn1_object_set_display_name (subitem, _("Subject"));
+
+	process_name (&cert->subject, &text);
+	e_asn1_object_set_display_value (subitem, text);
+	g_free (text);
+	e_asn1_object_append_child (sequence, subitem);
+	g_object_unref (subitem);
+
+	if (!process_subject_public_key_info (&cert->subjectPublicKeyInfo, sequence))
+		return FALSE;
+
+	/* Is there an issuerUniqueID? */
+	if (cert->issuerID.data) {
+		/* The issuerID is encoded as a bit string.
+		 * The function ProcessRawBytes expects the
+		 * length to be in bytes, so let's convert the
+		 * length in a temporary SECItem
+		*/
+		data.data = cert->issuerID.data;
+		data.len  = cert->issuerID.len / 8;
+
+		subitem = e_asn1_object_new ();
+
+		e_asn1_object_set_display_name (subitem, _("Issuer Unique ID"));
+		process_raw_bytes (&data, &text);
+		e_asn1_object_set_display_value (subitem, text);
+		g_free (text);
+
+		e_asn1_object_append_child (sequence, subitem);
+	}
+
+	if (cert->subjectID.data) {
+		/* The subjectID is encoded as a bit string.
+		 * The function ProcessRawBytes expects the
+		 * length to be in bytes, so let's convert the
+		 * length in a temporary SECItem
+		*/
+		data.data = cert->issuerID.data;
+		data.len  = cert->issuerID.len / 8;
+
+		subitem = e_asn1_object_new ();
+
+		e_asn1_object_set_display_name (subitem, _("Subject Unique ID"));
+		process_raw_bytes (&data, &text);
+		e_asn1_object_set_display_value (subitem, text);
+		g_free (text);
+
+		e_asn1_object_append_child (sequence, subitem);
+	}
+	if (cert->extensions) {
+		if (!process_extensions (cert->extensions, sequence))
+			return FALSE;
+	}
+
+	*seq = sequence;
+
+	return TRUE;
+}
+
+static gboolean
+fill_asn1_from_cert (EASN1Object *asn1,
+		     CERTCertificate *cert)
+{
+	EASN1Object *sequence;
+	SECItem temp;
+	gchar *text;
+
+	g_return_val_if_fail (asn1 != NULL, FALSE);
+	g_return_val_if_fail (cert != NULL, FALSE);
+
+	if (cert->nickname) {
+		e_asn1_object_set_display_name (asn1, cert->nickname);
+	} else {
+		gchar *str;
+
+		str = CERT_GetCommonName (&cert->subject);
+		if (str) {
+			e_asn1_object_set_display_name (asn1, str);
+			PORT_Free (str);
+		} else {
+			e_asn1_object_set_display_name (asn1, cert->subjectName);
+		}
+	}
+
+	/* This sequence will be contain the tbsCertificate, signatureAlgorithm,
+	 * and signatureValue. */
+
+	if (!create_tbs_certificate_asn1_struct (cert, &sequence))
+		return FALSE;
+	e_asn1_object_append_child (asn1, sequence);
+	g_object_unref (sequence);
+
+	if (!process_sec_algorithm_id (&cert->signatureWrap.signatureAlgorithm, &sequence))
+		return FALSE;
+	e_asn1_object_set_display_name (
+		sequence, _("Certificate Signature Algorithm"));
+	e_asn1_object_append_child (asn1, sequence);
+	g_object_unref (sequence);
+
+	sequence = e_asn1_object_new ();
+	e_asn1_object_set_display_name (
+		sequence, _("Certificate Signature Value"));
+
+	/* The signatureWrap is encoded as a bit string.
+	 * The function ProcessRawBytes expects the
+	 * length to be in bytes, so let's convert the
+	 * length in a temporary SECItem */
+	temp.data = cert->signatureWrap.signature.data;
+	temp.len  = cert->signatureWrap.signature.len / 8;
+	process_raw_bytes (&temp, &text);
+	e_asn1_object_set_display_value (sequence, text);
+	e_asn1_object_append_child (asn1, sequence);
+	g_free (text);
+
+	return TRUE;
+}
+
+static void
+e_asn1_object_finalize (GObject *object)
+{
+	EASN1ObjectPrivate *priv;
+
+	priv = E_ASN1_OBJECT_GET_PRIVATE (object);
+
+	g_free (priv->display_name);
+	g_free (priv->value);
+
+	g_list_free_full (priv->children, (GDestroyNotify) g_object_unref);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (e_asn1_object_parent_class)->finalize (object);
+}
+
+static void
+e_asn1_object_class_init (EASN1ObjectClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (EASN1ObjectPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->finalize = e_asn1_object_finalize;
+}
+
+static void
+e_asn1_object_init (EASN1Object *asn1)
+{
+	asn1->priv = E_ASN1_OBJECT_GET_PRIVATE (asn1);
+
+	asn1->priv->valid_container = TRUE;
+}
+
+EASN1Object *
+e_asn1_object_new (void)
+{
+	return E_ASN1_OBJECT (g_object_new (E_TYPE_ASN1_OBJECT, NULL));
+}
+
+EASN1Object *
+e_asn1_object_new_from_cert (CERTCertificate *cert)
+{
+	EASN1Object *asn1;
+
+	g_return_val_if_fail (cert != NULL, NULL);
+
+	asn1 = e_asn1_object_new ();
+	if (!fill_asn1_from_cert (asn1, cert)) {
+		g_object_unref (asn1);
+		return NULL;
+	}
+
+	return asn1;
+}
+
+void
+e_asn1_object_set_valid_container (EASN1Object *obj,
+                                   gboolean flag)
+{
+	obj->priv->valid_container = flag;
+}
+
+gboolean
+e_asn1_object_is_valid_container (EASN1Object *obj)
+{
+	return obj->priv->valid_container;
+}
+
+PRUint32
+e_asn1_object_get_asn1_type (EASN1Object *obj)
+{
+	return obj->priv->type;
+}
+
+PRUint32
+e_asn1_object_get_asn1_tag (EASN1Object *obj)
+{
+	return obj->priv->tag;
+}
+
+GList *
+e_asn1_object_get_children (EASN1Object *obj)
+{
+	GList *children = g_list_copy (obj->priv->children);
+
+	g_list_foreach (children, (GFunc) g_object_ref, NULL);
+
+	return children;
+}
+
+void
+e_asn1_object_append_child (EASN1Object *parent,
+                            EASN1Object *child)
+{
+	parent->priv->children = g_list_append (
+		parent->priv->children, g_object_ref (child));
+}
+
+void
+e_asn1_object_set_display_name (EASN1Object *obj,
+                                const gchar *name)
+{
+	g_free (obj->priv->display_name);
+	obj->priv->display_name = g_strdup (name);
+}
+
+const gchar *
+e_asn1_object_get_display_name (EASN1Object *obj)
+{
+	return obj->priv->display_name;
+}
+
+void
+e_asn1_object_set_display_value (EASN1Object *obj,
+                                 const gchar *value)
+{
+	g_free (obj->priv->value);
+	obj->priv->value = g_strdup (value);
+}
+
+const gchar *
+e_asn1_object_get_display_value (EASN1Object *obj)
+{
+	return obj->priv->value;
+}
+
+void
+e_asn1_object_get_data (EASN1Object *obj,
+                        gchar **data,
+                        guint32 *len)
+{
+	*data = obj->priv->data;
+	*len = obj->priv->data_len;
+}
diff --git a/modules/trust-prompt/e-asn1-object.h b/modules/trust-prompt/e-asn1-object.h
new file mode 100644
index 0000000..39b79a7
--- /dev/null
+++ b/modules/trust-prompt/e-asn1-object.h
@@ -0,0 +1,109 @@
+/*
+ * 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)
+ */
+
+#ifndef E_ASN1_OBJECT_H
+#define E_ASN1_OBJECT_H
+
+#include <glib-object.h>
+
+#include <cert.h>
+
+#define E_TYPE_ASN1_OBJECT            (e_asn1_object_get_type ())
+#define E_ASN1_OBJECT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ASN1_OBJECT, EASN1Object))
+#define E_ASN1_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ASN1_OBJECT, EASN1ObjectClass))
+#define E_IS_ASN1_OBJECT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ASN1_OBJECT))
+#define E_IS_ASN1_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_ASN1_OBJECT))
+#define E_ASN1_OBJECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_ASN1_OBJECT, EASN1ObjectClass))
+
+typedef struct _EASN1Object EASN1Object;
+typedef struct _EASN1ObjectClass EASN1ObjectClass;
+typedef struct _EASN1ObjectPrivate EASN1ObjectPrivate;
+
+enum {
+	/*
+	 *  Identifiers for the possible types of object.
+	 */
+	E_ASN1_OBJECT_TYPE_END_CONTENTS     = 0,
+	E_ASN1_OBJECT_TYPE_BOOLEAN          = 1,
+	E_ASN1_OBJECT_TYPE_INTEGER          = 2,
+	E_ASN1_OBJECT_TYPE_BIT_STRING       = 3,
+	E_ASN1_OBJECT_TYPE_OCTET_STRING     = 4,
+	E_ASN1_OBJECT_TYPE_NULL             = 5,
+	E_ASN1_OBJECT_TYPE_OBJECT_ID        = 6,
+	E_ASN1_OBJECT_TYPE_ENUMERATED       = 10,
+	E_ASN1_OBJECT_TYPE_UTF8_STRING      = 12,
+	E_ASN1_OBJECT_TYPE_SEQUENCE         = 16,
+	E_ASN1_OBJECT_TYPE_SET              = 17,
+	E_ASN1_OBJECT_TYPE_PRINTABLE_STRING = 19,
+	E_ASN1_OBJECT_TYPE_T61_STRING       = 20,
+	E_ASN1_OBJECT_TYPE_IA5_STRING       = 22,
+	E_ASN1_OBJECT_TYPE_UTC_TIME         = 23,
+	E_ASN1_OBJECT_TYPE_GEN_TIME         = 24,
+	E_ASN1_OBJECT_TYPE_VISIBLE_STRING   = 26,
+	E_ASN1_OBJECT_TYPE_UNIVERSAL_STRING = 28,
+	E_ASN1_OBJECT_TYPE_BMP_STRING       = 30,
+	E_ASN1_OBJECT_TYPE_HIGH_TAG_NUMBER  = 31,
+	E_ASN1_OBJECT_TYPE_CONTEXT_SPECIFIC = 32,
+	E_ASN1_OBJECT_TYPE_APPLICATION      = 33,
+	E_ASN1_OBJECT_TYPE_PRIVATE          = 34
+};
+
+struct _EASN1Object {
+	GObject parent;
+
+	EASN1ObjectPrivate *priv;
+};
+
+struct _EASN1ObjectClass {
+	GObjectClass parent_class;
+
+	/* Padding for future expansion */
+	void (*_ecert_reserved0) (void);
+	void (*_ecert_reserved1) (void);
+	void (*_ecert_reserved2) (void);
+	void (*_ecert_reserved3) (void);
+	void (*_ecert_reserved4) (void);
+};
+
+GType		e_asn1_object_get_type			(void);
+EASN1Object *	e_asn1_object_new			(void);
+EASN1Object *	e_asn1_object_new_from_cert		(CERTCertificate *cert);
+
+void		e_asn1_object_set_valid_container	(EASN1Object *obj,
+							 gboolean flag);
+gboolean	e_asn1_object_is_valid_container	(EASN1Object *obj);
+PRUint32	e_asn1_object_get_asn1_type		(EASN1Object *obj);
+PRUint32	e_asn1_object_get_asn1_tag		(EASN1Object *obj);
+GList *		e_asn1_object_get_children		(EASN1Object *obj);
+void		e_asn1_object_append_child		(EASN1Object *parent,
+							 EASN1Object *child);
+void		e_asn1_object_set_display_name		(EASN1Object *obj,
+							 const gchar *name);
+const gchar *	e_asn1_object_get_display_name		(EASN1Object *obj);
+void		e_asn1_object_set_display_value		(EASN1Object *obj,
+							 const gchar *value);
+const gchar *	e_asn1_object_get_display_value		(EASN1Object *obj);
+
+void		e_asn1_object_get_data			(EASN1Object *obj,
+							 gchar **data,
+							 guint32 *len);
+
+#endif /* E_ASN1_OBJECT_H */
diff --git a/modules/trust-prompt/module-trust-prompt.c b/modules/trust-prompt/module-trust-prompt.c
index a818424..567bd41 100644
--- a/modules/trust-prompt/module-trust-prompt.c
+++ b/modules/trust-prompt/module-trust-prompt.c
@@ -27,6 +27,8 @@
 
 #include <libebackend/libebackend.h>
 
+#include "certificate-viewer.h"
+
 /* Standard GObject macros */
 #define E_TYPE_TRUST_PROMPT (e_trust_prompt_get_type ())
 #define E_TRUST_PROMPT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_TRUST_PROMPT, ETrustPrompt))
@@ -142,6 +144,12 @@ e_module_unload (GTypeModule *type_module)
       "certificate" - a base64-encoded DER certificate, for which ask on trust
       "certificate-errors" - a hexa-decimal integer (as string) corresponding to GTlsCertificateFlags
 
+   It can contain, optionally, chain of issuers:
+      "issuer"   - a base64-encoded DER certificate, issuer of "certificate"
+      "issuer-1" - a base64-encoded DER certificate, issuer of "issuer"
+      "issuer-2" - a base64-encoded DER certificate, issuer of "issuer-1"
+      and so on
+
    Result of the dialog is:
       0 - reject
       1 - accept permanently
@@ -264,7 +272,27 @@ trust_prompt_add_info_line (GtkGrid *grid,
 	pango_attr_list_unref (bold);
 }
 
-#define TRUST_PROMP_ID_KEY "ETrustPrompt::prompt-id-key"
+#define TRUST_PROMP_ID_KEY	"ETrustPrompt::prompt-id-key"
+#define TRUST_PROMP_CERT_KEY	"ETrustPrompt::cert-key"
+#define TRUST_PROMP_ISSUERS_KEY	"ETrustPrompt::issuers-key"
+
+static void
+trust_prompt_free_certificate (gpointer cert)
+{
+	if (!cert)
+		return;
+
+	CERT_DestroyCertificate (cert);
+}
+
+static void
+trust_prompt_free_issuers (gpointer issuers)
+{
+	if (!issuers)
+		return;
+
+	g_slist_free_full (issuers, trust_prompt_free_certificate);
+}
 
 static void
 trust_prompt_response_cb (GtkWidget *dialog,
@@ -274,7 +302,15 @@ trust_prompt_response_cb (GtkWidget *dialog,
 	gint prompt_id;
 
 	if (response == GTK_RESPONSE_HELP) {
-		/* view certificate */
+		GtkWidget *viewer;
+
+		viewer = certificate_viewer_new (GTK_WINDOW (dialog),
+			g_object_get_data (G_OBJECT (dialog), TRUST_PROMP_CERT_KEY),
+			g_object_get_data (G_OBJECT (dialog), TRUST_PROMP_ISSUERS_KEY));
+
+		gtk_dialog_run (GTK_DIALOG (viewer));
+		gtk_widget_destroy (viewer);
+
 		return;
 	}
 
@@ -293,6 +329,54 @@ trust_prompt_response_cb (GtkWidget *dialog,
 	e_user_prompter_server_extension_response (extension, prompt_id, response, NULL);
 }
 
+static GSList *
+trust_prompt_get_issuers (CERTCertDBHandle *certdb,
+			  const ENamedParameters *parameters)
+{
+	GSList *issuers = NULL;
+	CERTCertificate *cert;
+	SECItem derCert;
+	gsize derCert_len = 0;
+	gint ii;
+
+	g_return_val_if_fail (certdb != NULL, NULL);
+	g_return_val_if_fail (parameters != NULL, NULL);
+
+	for (ii = 0; ii >= 0; ii++) {
+		const gchar *base64_cert;
+
+		if (ii == 0) {
+			base64_cert = e_named_parameters_get (parameters, "issuer");
+		} else {
+			gchar *key;
+
+			key = g_strdup_printf ("issuer-%d", ii);
+			base64_cert = e_named_parameters_get (parameters, key);
+			g_free (key);
+		}
+
+		if (!base64_cert)
+			break;
+
+		derCert.type = siDERCertBuffer;
+		derCert.data = g_base64_decode (base64_cert, &derCert_len);
+		if (!derCert.data)
+			break;
+
+		derCert.len = derCert_len;
+
+		cert = CERT_NewTempCertificate (certdb, &derCert, NULL, PR_FALSE, PR_TRUE);
+		g_free (derCert.data);
+
+		if (!cert)
+			break;
+
+		issuers = g_slist_prepend (issuers, cert);
+	}
+
+	return g_slist_reverse (issuers);
+}
+
 static gboolean
 trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
 				gint prompt_id,
@@ -306,6 +390,7 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
 	GtkGrid *grid;
 	CERTCertDBHandle *certdb;
 	CERTCertificate *cert;
+	GSList *issuers;
 	SECItem derCert;
 	gsize derCert_len = 0;
 
@@ -329,6 +414,8 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
 	cert = CERT_NewTempCertificate (certdb, &derCert, NULL, PR_FALSE, PR_TRUE);
 	g_return_val_if_fail (cert != NULL, FALSE);
 
+	issuers = trust_prompt_get_issuers (certdb, parameters);
+
 	cert_errs = g_ascii_strtoll (cert_errs_str, NULL, 16);
 
 	dialog = gtk_dialog_new_with_buttons (_("Certificate trust..."), NULL, 0,
@@ -339,7 +426,6 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
 		NULL);
 
 	gtk_window_set_icon_name (GTK_WINDOW (dialog), "evolution");
-	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_HELP, FALSE);
 	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
 
 	grid = g_object_new (GTK_TYPE_GRID,
@@ -394,12 +480,14 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
 	g_free (tmp);
 
 	g_object_set_data (G_OBJECT (dialog), TRUST_PROMP_ID_KEY, GINT_TO_POINTER (prompt_id));
+	g_object_set_data_full (G_OBJECT (dialog), TRUST_PROMP_CERT_KEY, cert, trust_prompt_free_certificate);
+	g_object_set_data_full (G_OBJECT (dialog), TRUST_PROMP_ISSUERS_KEY, issuers, trust_prompt_free_issuers);
+
 	g_signal_connect (dialog, "response", G_CALLBACK (trust_prompt_response_cb), extension);
 
 	gtk_widget_show_all (GTK_WIDGET (grid));
 	gtk_widget_show (dialog);
 
-	CERT_DestroyCertificate (cert);
 	g_free (derCert.data);
 
 	return TRUE;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a66d380..cb46eb9 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -230,6 +230,8 @@ libedataserverui/e-passwords-win32.c
 libedataserverui/e-source-selector-dialog.c
 modules/gnome-online-accounts/goaewsclient.c
 modules/google-backend/module-google-backend.c
+modules/trust-prompt/certificate-viewer.c
+modules/trust-prompt/e-asn1-object.c
 modules/trust-prompt/module-trust-prompt.c
 modules/yahoo-backend/module-yahoo-backend.c
 services/evolution-addressbook-factory/evolution-addressbook-factory.c
diff --git a/tests/libedataserver/e-user-prompter-test.c b/tests/libedataserver/e-user-prompter-test.c
index 8ad04cb..aabb2c3 100644
--- a/tests/libedataserver/e-user-prompter-test.c
+++ b/tests/libedataserver/e-user-prompter-test.c
@@ -22,9 +22,11 @@ typedef struct _TestClosure TestClosure;
 typedef struct _TestFixture TestFixture;
 
 struct _TestClosure {
+	gboolean only_certificate;
 };
 
 struct _TestFixture {
+	gboolean only_certificate;
 	EUserPrompter *prompter;
 	GMainLoop *main_loop;
 };
@@ -93,6 +95,34 @@ test_trust_prompt (EUserPrompter *prompter)
 		"3zOMCaTr7Cq6SJqnlrYUYbdBkobjadcfG2eAKfbhOiVGVEOee4O6JJ+nCrqXpqj42EGuQ8mKvl7Kao+"
 		"xerxctag0jzlLRFWJ69l7DZZyyFzY+/I9IWSVj8i0VCz0FkulK9adKeYD4E4BAOQvDFY4ED2FckW3AZ"
 		"zVueeiqTSIKwkDFhSDwTJsIfsOaEQ==";
+	const gchar *der_certificate_issuer =
+		"MIIGNDCCBBygAwIBAgIBGjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3Rh"
+		"cnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcG"
+		"A1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NzA5WhcNMTcx"
+		"MDI0MjA1NzA5WjCBjDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT"
+		"IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0YXJ0Q29tIENsYXNz"
+		"IDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB"
+		"CgKCAQEA4k85L6GMmoWtCA4IPlfyiAEhG5SpbOK426oZGEY6UqH1D/RujOqWjJaHeRNAUS8i8gyLhw9l"
+		"33F0NENVsTUJm9m8H/rrQtCXQHK3Q5Y9upadXVACHJuRjZzArNe7LxfXyz6CnXPrB0KSss1ks3RVG7RL"
+		"hiEs93iHMuAW5Nq9TJXqpAp+tgoNLorPVavD5d1Bik7mb2VsskDPF125w2oLJxGEd2H2wnztwI14FBiZ"
+		"gZl1Y7foU9O6YekO+qIw80aiuckfbIBaQKwn7UhHM7BUxkYa8zVhwQIpkFR+ZE3EMFICgtffziFuGJHX"
+		"uKuMJxe18KMBL47SLoc6PbQpZ4rEAwIDAQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B"
+		"Af8EBAMCAQYwHQYDVR0OBBYEFBHbI0X9VMxqcW+EigPXvvcBLyaGMB8GA1UdIwQYMBaAFE4L7xqkQFul"
+		"F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDovL29jc3Auc3RhcnRz"
+		"c2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYD"
+		"VR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRw"
+		"Oi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu"
+		"BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0BggrBgEFBQcCARYo"
+		"aHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRlLnBkZjANBgkqhkiG9w0BAQUFAAOCAgEA"
+		"nQfh7pB2MWcWRXCMy4SLS1doRKWJwfJ+yyiL9edwd9W29AshYKWhdHMkIoDW2LqNomJdCTVCKfs5Y0UL"
+		"pLA4Gmj0lRPM4EOU7Os5GuxXKdmZbfWEzY5zrsncavqenRZkkwjHHMKJVJ53gJD2uSl26xNnSFn4Ljox"
+		"uMnTiOVfTtIZPUOO15L/zzi24VuKUx3OrLR2L9j3QGPV7mnzRX2gYsFhw3XtsntNrCEnME5ZRmqTF8rI"
+		"OS0Bc2Vb6UGbERecyMhK76F2YC2uk/8M1TMTn08Tzt2G8fz4NVQVqFvnhX76Nwn/i7gxSZ4Nbt600hIt"
+		"uO3Iw/G2QqBMl3nf/sOjn6H0bSyEd6SiBeEX/zHdmvO4esNSwhERt1Axin/M51qJzPeGmmGSTy+UtpjH"
+		"eOBiS0N9PN7WmrQQoUCcSyrcuNDUnv3xhHgbDlePaVRCaHvqoO91DweijHOZq1X1BwnSrzgDapADDC+P"
+		"4uhDwjHpb62H5Y29TiyJS1HmnExUdsASgVOb7KD8LJzaGJVuHjgmQid4YAjff20y6NjAbx/rJnWfk/x7"
+		"G/41kNxTowemP4NVCitOYoIlzmYwXSzg+RkbdbmdmFamgyd60Y+NWZP8P3PXLrQsldiL98l+x/ydrHIE"
+		"H9LMF/TtNGCbnkqXBP7dcg5XVFEGcE3vqhykguAzx/Q=";
 	ENamedParameters *parameters;
 	GError *error = NULL;
 	gint result;
@@ -101,9 +131,10 @@ test_trust_prompt (EUserPrompter *prompter)
 
 	parameters = e_named_parameters_new ();
 
-	e_named_parameters_set (parameters, "host", "https://bugzilla.gnome.org/";);
+	e_named_parameters_set (parameters, "host", "bugzilla.gnome.org");
 	e_named_parameters_set (parameters, "certificate", der_certificate);
 	e_named_parameters_set (parameters, "certificate-errors", "007f");
+	e_named_parameters_set (parameters, "issuer", der_certificate_issuer);
 
 	result = e_user_prompter_extension_prompt_sync (prompter, "ETrustPrompt::trust-prompt", parameters, NULL, NULL, &error);
 
@@ -186,7 +217,7 @@ test_user_prompts_idle_cb (gpointer user_data)
 	   in the right order and only one at a time, and then run
 	   the last synchronously, to wait for the result */
 	sz = G_N_ELEMENTS (prompts);
-	for (ii = 0; ii < sz && !error; ii++) {
+	for (ii = 0; !fixture->only_certificate && ii < sz && !error; ii++) {
 		gchar *title, *primary, *secondary, **buttons = NULL;
 		GSList *captions = NULL;
 
@@ -244,6 +275,10 @@ static void
 test_user_prompts (TestFixture *fixture,
 		   gconstpointer user_data)
 {
+	const TestClosure *closure = user_data;
+
+	fixture->only_certificate = closure->only_certificate;
+
 	g_idle_add (test_user_prompts_idle_cb, fixture);
 	g_main_loop_run (fixture->main_loop);
 }
@@ -260,6 +295,8 @@ main (gint argc,
 	g_test_init (&argc, &argv, NULL);
 	g_test_bug_base ("http://bugzilla.gnome.org/";);
 
+	closure.only_certificate = argc > 1 && g_ascii_strcasecmp (argv[1], "cert-only") == 0;
+
 	g_test_add (
 		"/e-user-prompter-test/UserPrompts",
 		TestFixture, &closure,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]