[trust-prompt] Add certificate viewer and "issuer*" parameters
[platform/upstream/evolution-data-server.git] / modules / trust-prompt / certificate-viewer.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU Lesser General Public
4  * License as published by the Free Software Foundation; either
5  * version 2 of the License, or (at your option) version 3.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  * Lesser General Public License for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public
13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
14  *
15  *
16  * Authors:
17  *              Chris Toshok <toshok@ximian.com>
18  *
19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib/gi18n.h>
27
28 #include "pk11pub.h"
29 #include "hasht.h"
30
31 #include <libedataserver/libedataserver.h>
32
33 #include "e-asn1-object.h"
34 #include "certificate-viewer.h"
35
36 #define CERTIFICATE_VIEWER_PRIV_KEY "CertificateViewerPriv-key"
37
38 typedef struct _CertificateViewerPriv
39 {
40         GtkWidget *issued_to_cn;
41         GtkWidget *issued_to_o;
42         GtkWidget *issued_to_ou;
43         GtkWidget *issued_to_serial;
44         GtkWidget *issued_by_cn;
45         GtkWidget *issued_by_o;
46         GtkWidget *issued_by_ou;
47         GtkWidget *validity_issued_on;
48         GtkWidget *validity_expires_on;
49         GtkWidget *fingerprints_sha1;
50         GtkWidget *fingerprints_md5;
51         GtkWidget *cert_hierarchy_treeview;
52         GtkWidget *cert_fields_treeview;
53         GtkWidget *cert_field_value_textview;
54
55         CERTCertificate *cert;
56         GSList *issuers;
57         GtkTextTag *monospace_tag;
58 } CertificateViewerPriv;
59
60 static void
61 free_priv_struct (gpointer ptr)
62 {
63         CertificateViewerPriv *priv = ptr;
64         GSList *iter;
65
66         if (!priv)
67                 return;
68
69         if (priv->cert)
70                 CERT_DestroyCertificate (priv->cert);
71
72         for (iter = priv->issuers; iter; iter = iter->next) {
73                 CERTCertificate *cert = iter->data;
74
75                 if (cert)
76                         CERT_DestroyCertificate (cert);
77         }
78
79         g_slist_free (priv->issuers);
80
81         g_free (priv);
82 }
83
84 static void
85 begin_section (GtkGrid *add_to,
86                const gchar *caption,
87                gint *from_row,
88                gint for_rows)
89 {
90         GtkWidget *widget;
91         PangoAttribute *attr;
92         PangoAttrList *bold;
93
94         g_return_if_fail (add_to != NULL);
95         g_return_if_fail (caption != NULL);
96         g_return_if_fail (from_row != NULL);
97
98         bold = pango_attr_list_new ();
99         attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
100         pango_attr_list_insert (bold, attr);
101
102         widget = gtk_label_new (caption);
103         g_object_set (G_OBJECT (widget),
104                 "hexpand", TRUE,
105                 "halign", GTK_ALIGN_START,
106                 "justify", GTK_JUSTIFY_LEFT,
107                 "attributes", bold,
108                 "ellipsize", PANGO_ELLIPSIZE_NONE,
109                 NULL);
110
111         pango_attr_list_unref (bold);
112
113         gtk_grid_attach (add_to, widget, 0, *from_row, 3, 1);
114         (*from_row)++;
115
116         widget = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
117         gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
118
119         gtk_grid_attach (add_to, widget, 0, *from_row, 1, for_rows);
120 }
121
122 static GtkWidget *
123 add_info_label (GtkGrid *add_to,
124                 const gchar *caption,
125                 gint *at_row)
126 {
127         GtkWidget *widget;
128
129         g_return_val_if_fail (add_to != NULL, NULL);
130         g_return_val_if_fail (at_row != NULL, NULL);
131
132         if (caption) {
133                 widget = gtk_label_new (caption);
134                 g_object_set (G_OBJECT (widget),
135                         "hexpand", FALSE,
136                         "halign", GTK_ALIGN_START,
137                         "justify", GTK_JUSTIFY_LEFT,
138                         "ellipsize", PANGO_ELLIPSIZE_NONE,
139                         NULL);
140
141                 gtk_grid_attach (add_to, widget, 1, *at_row, 1, 1);
142         }
143
144         widget = gtk_label_new ("");
145         g_object_set (G_OBJECT (widget),
146                 "hexpand", TRUE,
147                 "halign", GTK_ALIGN_START,
148                 "justify", GTK_JUSTIFY_LEFT,
149                 "ellipsize", PANGO_ELLIPSIZE_NONE,
150                 "selectable", caption != NULL,
151                 NULL);
152
153         gtk_grid_attach (add_to, widget, caption ? 2 : 1, *at_row, caption ? 1 : 2, 1);
154
155         (*at_row)++;
156
157         return widget;
158 }
159
160 static GtkWidget *
161 add_scrolled_window (GtkGrid *add_to,
162                      const gchar *caption,
163                      gint *at_row,
164                      GtkWidget *add_widget)
165 {
166         GtkWidget *widget;
167         PangoAttribute *attr;
168         PangoAttrList *bold;
169
170         g_return_val_if_fail (add_to != NULL, NULL);
171         g_return_val_if_fail (caption != NULL, NULL);
172         g_return_val_if_fail (at_row != NULL, NULL);
173
174         bold = pango_attr_list_new ();
175         attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
176         pango_attr_list_insert (bold, attr);
177
178         widget = gtk_label_new (caption);
179         g_object_set (G_OBJECT (widget),
180                 "hexpand", TRUE,
181                 "halign", GTK_ALIGN_START,
182                 "justify", GTK_JUSTIFY_LEFT,
183                 "attributes", bold,
184                 "ellipsize", PANGO_ELLIPSIZE_NONE,
185                 NULL);
186
187         pango_attr_list_unref (bold);
188
189         gtk_grid_attach (add_to, widget, 0, *at_row, 1, 1);
190         (*at_row)++;
191
192         widget = gtk_scrolled_window_new (NULL, NULL);
193         g_object_set (G_OBJECT (widget),
194                 "hexpand", TRUE,
195                 "halign", GTK_ALIGN_FILL,
196                 "vexpand", TRUE,
197                 "valign", GTK_ALIGN_FILL,
198                 "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
199                 "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
200                 "shadow-type", GTK_SHADOW_ETCHED_IN,
201                 NULL);
202
203         gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (widget), add_widget);
204
205         gtk_grid_attach (add_to, widget, 0, *at_row, 1, 1);
206         (*at_row)++;
207
208         return add_widget;
209 }
210
211 #define FLAG_NONE        (0)
212 #define FLAG_PORT_MEMORY (1 << 0)
213 #define FLAG_MARKUP      (1 << 1)
214
215 static void
216 set_label_text (GtkWidget *label,
217                 const gchar *not_part_markup,
218                 gchar *text,
219                 guint32 flags)
220 {
221         if (text) {
222                 if ((flags & FLAG_MARKUP) != 0)
223                         gtk_label_set_markup (GTK_LABEL (label), text);
224                 else
225                         gtk_label_set_text (GTK_LABEL (label), text);
226
227                 if ((flags & FLAG_PORT_MEMORY) != 0)
228                         PORT_Free (text);
229                 else
230                         g_free (text);
231         } else {
232                 gtk_label_set_markup (GTK_LABEL (label), not_part_markup);
233         }
234 }
235
236 static void
237 get_cert_times (CERTCertificate *cert,
238                 gchar **issued_on,
239                 gchar **expires_on)
240 {
241         PRTime time_issued_on;
242         PRTime time_expires_on;
243         PRExplodedTime explodedTime;
244         struct tm exploded_tm;
245         gchar buf[128];
246
247         g_return_if_fail (cert != NULL);
248         g_return_if_fail (issued_on != NULL);
249         g_return_if_fail (expires_on != NULL);
250
251         if (SECSuccess != CERT_GetCertTimes (cert, &time_issued_on, &time_expires_on))
252                 return;
253
254         PR_ExplodeTime (time_issued_on, PR_LocalTimeParameters, &explodedTime);
255         exploded_tm.tm_sec = explodedTime.tm_sec;
256         exploded_tm.tm_min = explodedTime.tm_min;
257         exploded_tm.tm_hour = explodedTime.tm_hour;
258         exploded_tm.tm_mday = explodedTime.tm_mday;
259         exploded_tm.tm_mon = explodedTime.tm_month;
260         exploded_tm.tm_year = explodedTime.tm_year - 1900;
261         e_utf8_strftime (buf, sizeof (buf), "%x", &exploded_tm);
262         *issued_on = g_strdup (buf);
263
264         PR_ExplodeTime (time_expires_on, PR_LocalTimeParameters, &explodedTime);
265         exploded_tm.tm_sec = explodedTime.tm_sec;
266         exploded_tm.tm_min = explodedTime.tm_min;
267         exploded_tm.tm_hour = explodedTime.tm_hour;
268         exploded_tm.tm_mday = explodedTime.tm_mday;
269         exploded_tm.tm_mon = explodedTime.tm_month;
270         exploded_tm.tm_year = explodedTime.tm_year - 1900;
271         e_utf8_strftime (buf, sizeof (buf), "%x", &exploded_tm);
272         *expires_on = g_strdup (buf);
273 }
274
275 static void
276 fill_general_page (CertificateViewerPriv *priv)
277 {
278         gchar *not_part_markup;
279         gchar *issued_on = NULL;
280         gchar *expires_on = NULL;
281         gchar *port_str;
282         guchar fingerprint[128];
283         SECItem fpItem;
284
285         g_return_if_fail (priv != NULL);
286
287         not_part_markup = g_strconcat ("<i>&lt;", _("Not part of certificate"), "&gt;</i>", NULL);
288
289         set_label_text (priv->issued_to_cn, not_part_markup, CERT_GetCommonName (&priv->cert->subject), FLAG_PORT_MEMORY);
290         set_label_text (priv->issued_to_o, not_part_markup, CERT_GetOrgName (&priv->cert->subject), FLAG_PORT_MEMORY);
291         set_label_text (priv->issued_to_ou, not_part_markup, CERT_GetOrgUnitName (&priv->cert->subject), FLAG_PORT_MEMORY);
292         set_label_text (priv->issued_to_serial, not_part_markup, CERT_Hexify (&priv->cert->serialNumber, TRUE), FLAG_PORT_MEMORY);
293
294         set_label_text (priv->issued_by_cn, not_part_markup, CERT_GetCommonName (&priv->cert->issuer), FLAG_PORT_MEMORY);
295         set_label_text (priv->issued_by_o, not_part_markup, CERT_GetOrgName (&priv->cert->issuer), FLAG_PORT_MEMORY);
296         set_label_text (priv->issued_by_ou, not_part_markup, CERT_GetOrgUnitName (&priv->cert->issuer), FLAG_PORT_MEMORY);
297
298         get_cert_times (priv->cert, &issued_on, &expires_on);
299         set_label_text (priv->validity_issued_on, not_part_markup, issued_on, FLAG_NONE);
300         set_label_text (priv->validity_expires_on, not_part_markup, expires_on, FLAG_NONE);
301
302         memset (fingerprint, 0, sizeof fingerprint);
303         PK11_HashBuf (
304                 SEC_OID_SHA1, fingerprint,
305                 priv->cert->derCert.data,
306                 priv->cert->derCert.len);
307         fpItem.data = fingerprint;
308         fpItem.len = SHA1_LENGTH;
309         port_str = CERT_Hexify (&fpItem, TRUE);
310         set_label_text (priv->fingerprints_sha1, not_part_markup, g_strconcat ("<tt>", port_str, "</tt>", NULL), FLAG_MARKUP);
311         PORT_Free (port_str);
312
313         memset (fingerprint, 0, sizeof fingerprint);
314         PK11_HashBuf (
315                 SEC_OID_MD5, fingerprint,
316                 priv->cert->derCert.data,
317                 priv->cert->derCert.len);
318         fpItem.data = fingerprint;
319         fpItem.len = MD5_LENGTH;
320         port_str = CERT_Hexify (&fpItem, TRUE);
321         set_label_text (priv->fingerprints_md5, not_part_markup,  g_strconcat ("<tt>", port_str, "</tt>", NULL), FLAG_MARKUP);
322         PORT_Free (port_str);
323
324         g_free (not_part_markup);
325 }
326
327 static void
328 populate_fields_tree (CertificateViewerPriv *priv,
329                       EASN1Object *asn1,
330                       GtkTreeIter *root)
331 {
332         GtkTreeStore *fields_store;
333         GtkTreeIter new_iter;
334
335         if (!asn1)
336                 return;
337
338         fields_store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->cert_fields_treeview)));
339
340         /* first insert a node for the current asn1 */
341         gtk_tree_store_insert (fields_store, &new_iter, root, -1);
342         gtk_tree_store_set (
343                 fields_store, &new_iter,
344                 0, e_asn1_object_get_display_name (asn1),
345                 1, asn1,
346                 -1);
347
348         if (e_asn1_object_is_valid_container (asn1)) {
349                 GList *children = e_asn1_object_get_children (asn1);
350
351                 if (children) {
352                         GList *iter;
353                         for (iter = children; iter; iter = iter->next) {
354                                 EASN1Object *subasn1 = iter->data;
355
356                                 populate_fields_tree (priv, subasn1, &new_iter);
357                         }
358                 }
359
360                 g_list_free_full (children, g_object_unref);
361         }
362 }
363
364 static void
365 hierarchy_selection_changed_cb (GtkTreeSelection *selection,
366                                 CertificateViewerPriv *priv)
367 {
368         GtkTreeIter iter;
369         GtkTreeModel *model;
370
371         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
372                 CERTCertificate *cert;
373                 EASN1Object *asn1;
374                 GtkTreeStore *fields_store;
375
376                 gtk_tree_model_get (model, &iter, 1, &cert, -1);
377
378                 if (!cert)
379                         return;
380
381                 /* display the cert's ASN1 structure */
382                 asn1 = e_asn1_object_new_from_cert (cert);
383
384                 /* wipe out the old model */
385                 fields_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_OBJECT);
386                 gtk_tree_view_set_model (
387                         GTK_TREE_VIEW (priv->cert_fields_treeview),
388                         GTK_TREE_MODEL (fields_store));
389
390                 /* populate the fields from the newly selected cert */
391                 populate_fields_tree (priv, asn1, NULL);
392                 gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->cert_fields_treeview));
393                 if (asn1)
394                         g_object_unref (asn1);
395
396                 /* and blow away the field value */
397                 gtk_text_buffer_set_text (
398                         gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->cert_field_value_textview)),
399                         "", 0);
400         }
401 }
402
403 static void
404 fields_selection_changed_cb (GtkTreeSelection *selection,
405                              CertificateViewerPriv *priv)
406 {
407         GtkTreeIter iter;
408         GtkTreeModel *model;
409
410         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
411                 EASN1Object *asn1 = NULL;
412                 const gchar *value = NULL;
413                 GtkTextView *textview;
414                 GtkTextBuffer *textbuffer;
415
416                 gtk_tree_model_get (model, &iter, 1, &asn1, -1);
417
418                 if (asn1)
419                         value = e_asn1_object_get_display_value (asn1);
420
421                 textview = GTK_TEXT_VIEW (priv->cert_field_value_textview);
422                 textbuffer = gtk_text_view_get_buffer (textview);
423
424                 gtk_text_buffer_set_text (textbuffer, "", 0);
425
426                 if (value) {
427                         GtkTextIter text_iter;
428
429                         gtk_text_buffer_get_start_iter (textbuffer, &text_iter);
430
431                         gtk_text_buffer_insert_with_tags (textbuffer, &text_iter,
432                                 value, strlen (value),
433                                 priv->monospace_tag, NULL);
434                 }
435
436                 if (asn1)
437                         g_object_unref (asn1);
438         }
439 }
440
441 static void
442 fill_details_page (CertificateViewerPriv *priv)
443 {
444         GSList *iter;
445         GtkTreeIter root;
446         GtkTreeSelection *selection;
447         gboolean root_set = FALSE;
448         GtkTreeStore *hierarchy_store;
449
450         g_return_if_fail (priv != NULL);
451
452         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->cert_hierarchy_treeview), FALSE);
453
454         hierarchy_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
455         gtk_tree_view_set_model (
456                 GTK_TREE_VIEW (priv->cert_hierarchy_treeview),
457                 GTK_TREE_MODEL (hierarchy_store));
458
459         gtk_tree_view_insert_column_with_attributes (
460                 GTK_TREE_VIEW (priv->cert_hierarchy_treeview),
461                 -1, "Cert", gtk_cell_renderer_text_new (),
462                 "text", 0, NULL);
463
464         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->cert_hierarchy_treeview));
465         g_signal_connect (
466                 selection, "changed",
467                 G_CALLBACK (hierarchy_selection_changed_cb), priv);
468
469         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->cert_fields_treeview), FALSE);
470
471         gtk_tree_view_insert_column_with_attributes (
472                 GTK_TREE_VIEW (priv->cert_fields_treeview),
473                 -1, "Field", gtk_cell_renderer_text_new (),
474                 "text", 0, NULL);
475
476         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->cert_fields_treeview));
477         g_signal_connect (
478                 selection, "changed",
479                 G_CALLBACK (fields_selection_changed_cb), priv);
480
481         /* set the font of the field value viewer to be some fixed
482          * width font to the hex display looks nice. */
483         priv->monospace_tag = gtk_text_buffer_create_tag (
484                 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->cert_field_value_textview)),
485                 "mono", "font", "Mono", NULL);
486
487         /* initially populate the hierarchy from the issuers' chain */
488         for (iter = priv->issuers; iter; iter = g_slist_next (iter)) {
489                 CERTCertificate *cert = iter->data;
490                 gchar *str;
491                 GtkTreeIter new_iter;
492
493                 if (!cert)
494                         continue;
495
496                 str = CERT_GetCommonName (&cert->subject);
497
498                 gtk_tree_store_insert (hierarchy_store, &new_iter, root_set ? &root : NULL, -1);
499                 gtk_tree_store_set (
500                         hierarchy_store, &new_iter,
501                         0, str ? str : cert->subjectName,
502                         1, cert,
503                         -1);
504
505                 root = new_iter;
506                 root_set = TRUE;
507
508                 if (str)
509                         PORT_Free (str);
510         }
511
512         gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->cert_hierarchy_treeview));
513 }
514
515 static gchar *
516 get_window_title (CERTCertificate *cert)
517 {
518         gchar *str;
519
520         g_return_val_if_fail (cert != NULL, NULL);
521
522         if (cert->nickname)
523                 return g_strdup (cert->nickname);
524
525         str = CERT_GetCommonName (&cert->subject);
526         if (str) {
527                 gchar *title;
528
529                 title = g_strdup (str);
530                 PORT_Free (str);
531
532                 return title;
533         }
534
535         return cert->subjectName;
536 }
537
538 GtkWidget *
539 certificate_viewer_new (GtkWindow *parent,
540                         const CERTCertificate *cert,
541                         const GSList *issuers_chain_certs)
542 {
543         CertificateViewerPriv *priv;
544         GtkWidget *dialog, *notebook, *widget;
545         GtkGrid *grid;
546         gint row;
547         GSList *iter;
548         gchar *title;
549
550         g_return_val_if_fail (cert != NULL, NULL);
551
552         priv = g_new0 (CertificateViewerPriv, 1);
553         priv->cert = CERT_DupCertificate ((CERTCertificate *) cert);
554         priv->issuers = g_slist_copy ((GSList *) issuers_chain_certs);
555
556         /* root issuer first, then bottom down to certificate itself */
557         priv->issuers = g_slist_reverse (priv->issuers);
558         priv->issuers = g_slist_append (priv->issuers, priv->cert);
559
560         for (iter = priv->issuers; iter; iter = g_slist_next (iter)) {
561                 iter->data = CERT_DupCertificate (iter->data);
562         }
563
564         title = get_window_title (priv->cert);
565
566         dialog = gtk_dialog_new_with_buttons (title, parent,
567                 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
568                 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
569                 NULL);
570
571         g_free (title);
572
573         g_object_set_data_full (G_OBJECT (dialog), CERTIFICATE_VIEWER_PRIV_KEY, priv, free_priv_struct);
574
575         notebook = gtk_notebook_new ();
576         gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), notebook);
577         gtk_container_set_border_width (GTK_CONTAINER (notebook), 12);
578
579         /* General page */
580         row = 0;
581         grid = GTK_GRID (gtk_grid_new ());
582         g_object_set (G_OBJECT (grid),
583                 "hexpand", TRUE,
584                 "halign", GTK_ALIGN_FILL,
585                 "vexpand", FALSE,
586                 "valign", GTK_ALIGN_START,
587                 "border-width", 12,
588                 "row-spacing", 6,
589                 "column-spacing", 6,
590                 NULL);
591
592         begin_section (grid, _("This certificate has been verified for the following uses:"), &row, 4);
593
594         if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageSSLClient) != 0) {
595                 widget = add_info_label (grid, NULL, &row);
596                 gtk_label_set_text (GTK_LABEL (widget), _("SSL Client Certificate"));
597         }
598
599         if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & (certificateUsageSSLServer | certificateUsageSSLCA)) != 0) {
600                 widget = add_info_label (grid, NULL, &row);
601                 gtk_label_set_text (GTK_LABEL (widget), _("SSL Server Certificate"));
602         }
603
604         if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageEmailSigner) != 0) {
605                 widget = add_info_label (grid, NULL, &row);
606                 gtk_label_set_text (GTK_LABEL (widget), _("Email Signer Certificate"));
607         }
608
609         if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageEmailRecipient) != 0) {
610                 widget = add_info_label (grid, NULL, &row);
611                 gtk_label_set_text (GTK_LABEL (widget), _("Email Recipient Certificate"));
612         }
613
614         widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
615         g_object_set (G_OBJECT (widget),
616                 "hexpand", TRUE,
617                 "halign", GTK_ALIGN_FILL,
618                 "vexpand", FALSE,
619                 "valign", GTK_ALIGN_START,
620                 NULL);
621
622         gtk_grid_attach (grid, widget, 0, row, 3, 1);
623         row++;
624
625         begin_section (grid, _("Issued To"), &row, 4);
626         priv->issued_to_cn = add_info_label (grid, _("Common Name (CN)"), &row);
627         priv->issued_to_o = add_info_label (grid, _("Organization (O)"), &row);
628         priv->issued_to_ou = add_info_label (grid, _("Organizational Unit (OU)"), &row);
629         priv->issued_to_serial = add_info_label (grid, _("Serial Number"), &row);
630
631         begin_section (grid, _("Issued By"), &row, 3);
632         priv->issued_by_cn = add_info_label (grid, _("Common Name (CN)"), &row);
633         priv->issued_by_o = add_info_label (grid, _("Organization (O)"), &row);
634         priv->issued_by_ou = add_info_label (grid, _("Organizational Unit (OU)"), &row);
635
636         begin_section (grid, _("Validity"), &row, 2);
637         priv->validity_issued_on = add_info_label (grid, _("Issued On"), &row);
638         priv->validity_expires_on = add_info_label (grid, _("Expires On"), &row);
639
640         begin_section (grid, _("Fingerprints"), &row, 2);
641         priv->fingerprints_sha1 = add_info_label (grid, _("SHA1 Fingerprint"), &row);
642         priv->fingerprints_md5 = add_info_label (grid, _("MD5 Fingerprint"), &row);
643
644         widget = gtk_label_new (_("General"));
645         gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (grid), widget);
646
647         /* Details page */
648         row = 0;
649         grid = GTK_GRID (gtk_grid_new ());
650         g_object_set (G_OBJECT (grid),
651                 "hexpand", TRUE,
652                 "halign", GTK_ALIGN_FILL,
653                 "vexpand", TRUE,
654                 "valign", GTK_ALIGN_FILL,
655                 "border-width", 12,
656                 "row-spacing", 6,
657                 "column-spacing", 6,
658                 NULL);
659
660         priv->cert_hierarchy_treeview = add_scrolled_window (grid,
661                 _("Certificate Hierarchy"), &row, gtk_tree_view_new ());
662
663         priv->cert_fields_treeview = add_scrolled_window (grid,
664                 _("Certificate Fields"), &row, gtk_tree_view_new ());
665
666         priv->cert_field_value_textview = add_scrolled_window (grid,
667                 _("Field Value"), &row, gtk_text_view_new ());
668
669         widget = gtk_label_new (_("Details"));
670         gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (grid), widget);
671
672         gtk_widget_show_all (notebook);
673
674         fill_general_page (priv);
675         fill_details_page (priv);
676
677         return dialog;
678 }