summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Walter <stefw@src.gnome.org>2009-01-28 01:38:38 +0000
committerStefan Walter <stefw@src.gnome.org>2009-01-28 01:38:38 +0000
commite4f14652fa7967433f7964a8577f34cf560aa13f (patch)
tree1817d14a2a8ec0d6b62ade321fc9e25ddece7f42
parent3f51008107b675690d705b32cf7e2637a39c0d9b (diff)
Add basic UI widgets for displaying a certificate.
* egg/egg-asn1.c: * egg/egg-asn1.h: * egg/egg-hex.c: * egg/egg-hex.h: * egg/pkix.asn: * egg/tests/unit-test-asn1.c: * egg/tests/unit-test-hex.c: * gcr/gcr-certificate.c: (added) * gcr/gcr-certificate.h: (added) * gcr/gcr-certificate-basics-widget.c: (added) * gcr/gcr-certificate-basics-widget.glade: (added) * gcr/gcr-certificate-basics-widget.h: (added) * gcr/gcr-certificate-details-widget.c: (added) * gcr/gcr-certificate-details-widget.h: (added) * gcr/gcr-library.c: * gcr/gcr-parser.c: * gcr/Makefile.am: * gcr/tests/Makefile.am: * gcr/tests/ui-test-details.c: (added) * gcr/tests/unit-test-certificate.c: (added) Add basic UI widgets for displaying a certificate. svn path=/trunk/; revision=1480
-rw-r--r--ChangeLog24
-rw-r--r--egg/egg-asn1.c387
-rw-r--r--egg/egg-asn1.h26
-rw-r--r--egg/egg-hex.c33
-rw-r--r--egg/egg-hex.h10
-rw-r--r--egg/pkix.asn9
-rw-r--r--egg/tests/unit-test-asn1.c87
-rw-r--r--egg/tests/unit-test-hex.c16
-rw-r--r--gcr/Makefile.am13
-rw-r--r--gcr/gcr-certificate-basics-widget.c306
-rw-r--r--gcr/gcr-certificate-basics-widget.glade512
-rw-r--r--gcr/gcr-certificate-basics-widget.h57
-rw-r--r--gcr/gcr-certificate-details-widget.c462
-rw-r--r--gcr/gcr-certificate-details-widget.h57
-rw-r--r--gcr/gcr-certificate.c398
-rw-r--r--gcr/gcr-certificate.h91
-rw-r--r--gcr/gcr-library.c3
-rw-r--r--gcr/gcr-parser.c4
-rw-r--r--gcr/tests/Makefile.am22
-rw-r--r--gcr/tests/ui-test-details.c45
-rw-r--r--gcr/tests/unit-test-certificate.c139
-rw-r--r--po/ChangeLog7
-rw-r--r--po/POTFILES.in5
-rw-r--r--po/POTFILES.skip1
24 files changed, 2572 insertions, 142 deletions
diff --git a/ChangeLog b/ChangeLog
index 0f8408fe..e8ed81d4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2009-01-27 Stef Walter <stef@memberwebs.com>
+
+ * egg/egg-asn1.c:
+ * egg/egg-asn1.h:
+ * egg/egg-hex.c:
+ * egg/egg-hex.h:
+ * egg/pkix.asn:
+ * egg/tests/unit-test-asn1.c:
+ * egg/tests/unit-test-hex.c:
+ * gcr/gcr-certificate.c: (added)
+ * gcr/gcr-certificate.h: (added)
+ * gcr/gcr-certificate-basics-widget.c: (added)
+ * gcr/gcr-certificate-basics-widget.glade: (added)
+ * gcr/gcr-certificate-basics-widget.h: (added)
+ * gcr/gcr-certificate-details-widget.c: (added)
+ * gcr/gcr-certificate-details-widget.h: (added)
+ * gcr/gcr-library.c:
+ * gcr/gcr-parser.c:
+ * gcr/Makefile.am:
+ * gcr/tests/Makefile.am:
+ * gcr/tests/ui-test-details.c: (added)
+ * gcr/tests/unit-test-certificate.c: (added) Add basic UI widgets for
+ displaying a certificate.
+
2009-01-22 Stef Walter <stef@memberwebs.com>
* daemon/pk/gkr-pk-index.c:
diff --git a/egg/egg-asn1.c b/egg/egg-asn1.c
index 2910ca02..0390e7a5 100644
--- a/egg/egg-asn1.c
+++ b/egg/egg-asn1.c
@@ -29,6 +29,8 @@
#include <string.h>
+#include <glib/gi18n-lib.h>
+
/*
* HACK: asn1Parser defines these arrays as extern const, which gives
* gcc a fit. So we def it out.
@@ -439,25 +441,27 @@ time_t timegm(struct tm *t)
}
#endif //NOT_HAVE_TIMEGM
-time_t
-egg_asn1_parse_utc_time (const gchar *time)
+static gboolean
+parse_utc_time (const gchar *time, struct tm* when, gint *offset)
{
- struct tm when;
guint n_time;
- time_t result;
const char *p, *e;
int year;
- g_assert (time);
+ g_assert (when);
+ g_assert (time);
+ g_assert (offset);
+
n_time = strlen (time);
/* YYMMDDhhmmss.ffff Z | +0000 */
if (n_time < 6 || n_time >= 28)
- return -1;
+ return FALSE;
/* Reset everything to default legal values */
- memset (&when, 0, sizeof (when));
- when.tm_mday = 1;
+ memset (when, 0, sizeof (*when));
+ *offset = 0;
+ when->tm_mday = 1;
/* Select the digits part of it */
p = time;
@@ -471,48 +475,41 @@ egg_asn1_parse_utc_time (const gchar *time)
* 40 years in the past is our century. 60 years
* in the future is the next century.
*/
- when.tm_year = two_to_four_digit_year (year) - 1900;
+ when->tm_year = two_to_four_digit_year (year) - 1900;
}
if (p + 2 <= e) {
- when.tm_mon = atoin (p, 2) - 1;
+ when->tm_mon = atoin (p, 2) - 1;
p += 2;
}
if (p + 2 <= e) {
- when.tm_mday = atoin (p, 2);
+ when->tm_mday = atoin (p, 2);
p += 2;
}
if (p + 2 <= e) {
- when.tm_hour = atoin (p, 2);
+ when->tm_hour = atoin (p, 2);
p += 2;
}
if (p + 2 <= e) {
- when.tm_min = atoin (p, 2);
+ when->tm_min = atoin (p, 2);
p += 2;
}
if (p + 2 <= e) {
- when.tm_sec = atoin (p, 2);
+ when->tm_sec = atoin (p, 2);
p += 2;
}
- if (when.tm_year < 0 || when.tm_year > 9999 ||
- when.tm_mon < 0 || when.tm_mon > 11 ||
- when.tm_mday < 1 || when.tm_mday > 31 ||
- when.tm_hour < 0 || when.tm_hour > 23 ||
- when.tm_min < 0 || when.tm_min > 59 ||
- when.tm_sec < 0 || when.tm_sec > 59)
- return -1;
+ if (when->tm_year < 0 || when->tm_year > 9999 ||
+ when->tm_mon < 0 || when->tm_mon > 11 ||
+ when->tm_mday < 1 || when->tm_mday > 31 ||
+ when->tm_hour < 0 || when->tm_hour > 23 ||
+ when->tm_min < 0 || when->tm_min > 59 ||
+ when->tm_sec < 0 || when->tm_sec > 59)
+ return FALSE;
/* Make sure all that got parsed */
if (p != e)
- return -1;
+ return FALSE;
- /* In order to work with 32 bit time_t. */
- if (sizeof (time_t) <= 4 && when.tm_year >= 2038)
- return (time_t) 2145914603; /* 2037-12-31 23:23:23 */
-
- /* Covnvert to seconds since epoch */
- result = timegm (&when);
-
/* Now the remaining optional stuff */
e = time + n_time;
@@ -543,81 +540,113 @@ egg_asn1_parse_utc_time (const gchar *time)
/* Use TZ offset */
if (neg)
- result -= off;
+ *offset = 0 - off;
else
- result += off;
+ *offset = off;
}
/* Make sure everything got parsed */
if (p != e)
- return -1;
+ return FALSE;
+ return TRUE;
+}
+
+static time_t
+when_to_time (struct tm *when, gint offset)
+{
+ time_t result;
+
+ g_assert (when);
+
+ /* In order to work with 32 bit time_t. */
+ if (sizeof (time_t) <= 4 && when->tm_year >= 2038)
+ return (time_t) 2145914603; /* 2037-12-31 23:23:23 */
+
+ /* Convert to seconds since epoch */
+ result = timegm (when);
+ if (result >= 0)
+ result += offset;
+
return result;
}
time_t
-egg_asn1_parse_general_time (const gchar *time)
+egg_asn1_time_parse_utc (const gchar *time)
{
struct tm when;
- guint n_time;
- time_t result;
+ gint offset;
+
+ g_return_val_if_fail (time, -1);
+
+ if (!parse_utc_time (time, &when, &offset))
+ return -1;
+
+ return when_to_time (&when, offset);
+}
+
+static gboolean
+parse_general_time (const gchar *time, struct tm* when, gint *offset)
+{
const char *p, *e;
+ guint n_time;
- g_assert (time);
+ g_assert (time);
+ g_assert (when);
+ g_assert (offset);
+
n_time = strlen (time);
/* YYYYMMDDhhmmss.ffff Z | +0000 */
if (n_time < 8 || n_time >= 30)
- return -1;
+ return FALSE;
/* Reset everything to default legal values */
- memset (&when, 0, sizeof (when));
- when.tm_mday = 1;
+ memset (when, 0, sizeof (*when));
+ *offset = 0;
+ when->tm_mday = 1;
/* Select the digits part of it */
p = time;
for (e = p; *e >= '0' && *e <= '9'; ++e);
if (p + 4 <= e) {
- when.tm_year = atoin (p, 4) - 1900;
+ when->tm_year = atoin (p, 4) - 1900;
p += 4;
}
if (p + 2 <= e) {
- when.tm_mon = atoin (p, 2) - 1;
+ when->tm_mon = atoin (p, 2) - 1;
p += 2;
}
if (p + 2 <= e) {
- when.tm_mday = atoin (p, 2);
+ when->tm_mday = atoin (p, 2);
p += 2;
}
if (p + 2 <= e) {
- when.tm_hour = atoin (p, 2);
+ when->tm_hour = atoin (p, 2);
p += 2;
}
if (p + 2 <= e) {
- when.tm_min = atoin (p, 2);
+ when->tm_min = atoin (p, 2);
p += 2;
}
if (p + 2 <= e) {
- when.tm_sec = atoin (p, 2);
+ when->tm_sec = atoin (p, 2);
p += 2;
}
- if (when.tm_year < 0 || when.tm_year > 9999 ||
- when.tm_mon < 0 || when.tm_mon > 11 ||
- when.tm_mday < 1 || when.tm_mday > 31 ||
- when.tm_hour < 0 || when.tm_hour > 23 ||
- when.tm_min < 0 || when.tm_min > 59 ||
- when.tm_sec < 0 || when.tm_sec > 59)
- return -1;
+ if (when->tm_year < 0 || when->tm_year > 9999 ||
+ when->tm_mon < 0 || when->tm_mon > 11 ||
+ when->tm_mday < 1 || when->tm_mday > 31 ||
+ when->tm_hour < 0 || when->tm_hour > 23 ||
+ when->tm_min < 0 || when->tm_min > 59 ||
+ when->tm_sec < 0 || when->tm_sec > 59)
+ return FALSE;
/* Make sure all that got parsed */
if (p != e)
- return -1;
+ return FALSE;
- /* Covnvert to seconds since epoch */
- result = timegm (&when);
-
/* Now the remaining optional stuff */
e = time + n_time;
@@ -648,25 +677,43 @@ egg_asn1_parse_general_time (const gchar *time)
/* Use TZ offset */
if (neg)
- result -= off;
+ *offset = 0 - off;
else
- result += off;
+ *offset = off;
}
/* Make sure everything got parsed */
if (p != e)
- return -1;
+ return FALSE;
- return result;
+ return TRUE;
}
-gboolean
-egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val)
+time_t
+egg_asn1_time_parse_general (const gchar *time)
{
- #define MAX_TIME 1024
- gchar ttime[MAX_TIME];
+ struct tm when;
+ gint offset;
+
+ g_return_val_if_fail (time, -1);
+
+ if (!parse_general_time (time, &when, &offset))
+ return -1;
+
+ return when_to_time (&when, offset);
+}
+
+static gboolean
+read_asn1_time (ASN1_TYPE asn, const gchar *part, struct tm *when, gint *offset)
+{
+ gchar ttime[256];
gchar *name;
int len, res;
+
+ g_assert (asn);
+ g_assert (part);
+ g_assert (when);
+ g_assert (offset);
len = sizeof (ttime) - 1;
res = asn1_read_value (asn, part, ttime, &len);
@@ -681,8 +728,7 @@ egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val)
g_free (name);
if (res != ASN1_SUCCESS)
return FALSE;
-
- *val = egg_asn1_parse_general_time (ttime);
+ return parse_general_time (ttime, when, offset);
/* UTCTIME */
} else {
@@ -692,16 +738,45 @@ egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val)
g_free (name);
if (res != ASN1_SUCCESS)
return FALSE;
-
- *val = egg_asn1_parse_utc_time (ttime);
+ return parse_utc_time (ttime, when, offset);
}
- if (*val < (time_t)0)
+ return FALSE;
+}
+
+gboolean
+egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val)
+{
+ struct tm when;
+ gint offset;
+
+ g_return_val_if_fail (asn, FALSE);
+ g_return_val_if_fail (part, FALSE);
+ g_return_val_if_fail (val, FALSE);
+
+ if (!read_asn1_time (asn, part, &when, &offset))
return FALSE;
-
- return TRUE;
+
+ *val = when_to_time (&when, offset);
+ return TRUE;
}
+gboolean
+egg_asn1_read_date (ASN1_TYPE asn, const gchar *part, GDate *date)
+{
+ struct tm when;
+ gint offset;
+
+ g_return_val_if_fail (asn, FALSE);
+ g_return_val_if_fail (part, FALSE);
+ g_return_val_if_fail (date, FALSE);
+
+ if (!read_asn1_time (asn, part, &when, &offset))
+ return FALSE;
+
+ g_date_set_dmy (date, when.tm_mday, when.tm_mon + 1, when.tm_year + 1900);
+ return g_date_valid (date);
+}
/* -------------------------------------------------------------------------------
* Reading DN's
@@ -710,42 +785,43 @@ egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val)
typedef struct _PrintableOid {
GQuark oid;
const gchar *oidstr;
- const gchar *display;
+ const gchar *attr;
+ const gchar *description;
gboolean is_choice;
} PrintableOid;
static PrintableOid printable_oids[] = {
- { 0, "0.9.2342.19200300.100.1.25", "DC", FALSE },
- { 0, "0.9.2342.19200300.100.1.1", "UID", TRUE },
-
- { 0, "1.2.840.113549.1.9.1", "EMAIL", FALSE },
- { 0, "1.2.840.113549.1.9.7", NULL, TRUE },
- { 0, "1.2.840.113549.1.9.20", NULL, FALSE },
-
- { 0, "1.3.6.1.5.5.7.9.1", "dateOfBirth", FALSE },
- { 0, "1.3.6.1.5.5.7.9.2", "placeOfBirth", FALSE },
- { 0, "1.3.6.1.5.5.7.9.3", "gender", FALSE },
- { 0, "1.3.6.1.5.5.7.9.4", "countryOfCitizenship", FALSE },
- { 0, "1.3.6.1.5.5.7.9.5", "countryOfResidence", FALSE },
-
- { 0, "2.5.4.3", "CN", TRUE },
- { 0, "2.5.4.4", "surName", TRUE },
- { 0, "2.5.4.5", "serialNumber", FALSE },
- { 0, "2.5.4.6", "C", FALSE, },
- { 0, "2.5.4.7", "L", TRUE },
- { 0, "2.5.4.8", "ST", TRUE },
- { 0, "2.5.4.9", "STREET", TRUE },
- { 0, "2.5.4.10", "O", TRUE },
- { 0, "2.5.4.11", "OU", TRUE },
- { 0, "2.5.4.12", "T", TRUE },
- { 0, "2.5.4.20", "telephoneNumber", FALSE },
- { 0, "2.5.4.42", "givenName", TRUE },
- { 0, "2.5.4.43", "initials", TRUE },
- { 0, "2.5.4.44", "generationQualifier", TRUE },
- { 0, "2.5.4.46", "dnQualifier", FALSE },
- { 0, "2.5.4.65", "pseudonym", TRUE },
-
- { 0, NULL, NULL, FALSE }
+ { 0, "0.9.2342.19200300.100.1.25", "DC", N_("Domain Component"), FALSE },
+ { 0, "0.9.2342.19200300.100.1.1", "UID", N_("User ID"), TRUE },
+
+ { 0, "1.2.840.113549.1.9.1", "EMAIL", N_("Email"), FALSE },
+ { 0, "1.2.840.113549.1.9.7", NULL, NULL, TRUE },
+ { 0, "1.2.840.113549.1.9.20", NULL, NULL, FALSE },
+
+ { 0, "1.3.6.1.5.5.7.9.1", "dateOfBirth", N_("Date of Birth"), FALSE },
+ { 0, "1.3.6.1.5.5.7.9.2", "placeOfBirth", N_("Place of Birth"), FALSE },
+ { 0, "1.3.6.1.5.5.7.9.3", "gender", N_("Gender"), FALSE },
+ { 0, "1.3.6.1.5.5.7.9.4", "countryOfCitizenship", N_("Country of Citizenship"), FALSE },
+ { 0, "1.3.6.1.5.5.7.9.5", "countryOfResidence", N_("Country of Residence"), FALSE },
+
+ { 0, "2.5.4.3", "CN", N_("Common Name"), TRUE },
+ { 0, "2.5.4.4", "surName", N_("Surname"), TRUE },
+ { 0, "2.5.4.5", "serialNumber", N_("Serial Number"), FALSE },
+ { 0, "2.5.4.6", "C", N_("Country"), FALSE, },
+ { 0, "2.5.4.7", "L", N_("Locality"), TRUE },
+ { 0, "2.5.4.8", "ST", N_("State"), TRUE },
+ { 0, "2.5.4.9", "STREET", N_("Street"), TRUE },
+ { 0, "2.5.4.10", "O", N_("Organization"), TRUE },
+ { 0, "2.5.4.11", "OU", N_("Organizational Unit"), TRUE },
+ { 0, "2.5.4.12", "T", N_("Title"), TRUE },
+ { 0, "2.5.4.20", "telephoneNumber", N_("Telephone Number"), FALSE },
+ { 0, "2.5.4.42", "givenName", N_("Given Name"), TRUE },
+ { 0, "2.5.4.43", "initials", N_("Initials"), TRUE },
+ { 0, "2.5.4.44", "generationQualifier", N_("Generation Qualifier"), TRUE },
+ { 0, "2.5.4.46", "dnQualifier", N_("DN Qualifier"), FALSE },
+ { 0, "2.5.4.65", "pseudonym", N_("Pseudonym"), TRUE },
+
+ { 0, NULL, NULL, NULL, FALSE }
};
static void
@@ -794,7 +870,7 @@ dn_print_hex_value (const guchar *data, gsize len)
}
static gchar*
-dn_print_oid_value_parsed (PrintableOid *printable, guchar *data, gsize len)
+dn_print_oid_value_parsed (PrintableOid *printable, const guchar *data, gsize len)
{
const gchar *asn_name;
ASN1_TYPE asn1;
@@ -857,7 +933,7 @@ dn_print_oid_value_parsed (PrintableOid *printable, guchar *data, gsize len)
}
static gchar*
-dn_print_oid_value (PrintableOid *printable, guchar *data, gsize len)
+dn_print_oid_value (PrintableOid *printable, const guchar *data, gsize len)
{
gchar *value;
@@ -903,7 +979,7 @@ dn_parse_rdn (ASN1_TYPE asn, const gchar *part)
g_return_val_if_fail (value, NULL);
display = dn_print_oid_value (printable, value, n_value);
- result = g_strconcat (printable ? printable->display : g_quark_to_string (oid),
+ result = g_strconcat (printable && printable->attr ? printable->attr : g_quark_to_string (oid),
"=", display, NULL);
g_free (display);
@@ -992,8 +1068,8 @@ egg_asn1_read_dn_part (ASN1_TYPE asn, const gchar *part, const gchar *match)
/* Does it match either the OID or the displayable? */
if (g_ascii_strcasecmp (g_quark_to_string (oid), match) != 0) {
printable = dn_find_printable (oid);
- if (!printable || !printable->display ||
- !g_ascii_strcasecmp (printable->display, match) == 0)
+ if (!printable || !printable->attr ||
+ !g_ascii_strcasecmp (printable->attr, match) == 0)
continue;
}
@@ -1010,3 +1086,98 @@ egg_asn1_read_dn_part (ASN1_TYPE asn, const gchar *part, const gchar *match)
return NULL;
}
+
+gboolean
+egg_asn1_dn_parse (ASN1_TYPE asn, const gchar *part,
+ EggAsn1DnCallback callback, gpointer user_data)
+{
+ gboolean done = FALSE;
+ gchar *path;
+ guchar *value;
+ gsize n_value;
+ GQuark oid;
+ guint i, j;
+
+ g_return_val_if_fail (asn, FALSE);
+
+ init_printable_oids ();
+
+ /* Each (possibly multi valued) RDN */
+ for (i = 1; !done; ++i) {
+
+ /* Each type=value pair of an RDN */
+ for (j = 1; TRUE; ++j) {
+
+ /* Dig out the type */
+ path = g_strdup_printf ("%s%s?%u.?%u.type",
+ part ? part : "",
+ part ? "." : "", i, j);
+ oid = egg_asn1_read_oid (asn, path);
+ g_free (path);
+
+ if (!oid) {
+ done = j == 1;
+ break;
+ }
+
+ /* Print the value as nicely as we can */
+ path = g_strdup_printf ("%s%s?%u.?%u.value",
+ part ? part : "",
+ part ? "." : "", i, j);
+ value = egg_asn1_read_value (asn, path, &n_value, NULL);
+ g_free (path);
+
+ if (!value) {
+ done = j == 1;
+ break;
+ }
+
+ if (callback)
+ (callback) (i, oid, value, n_value, user_data);
+
+ g_free (value);
+ }
+ }
+
+ return i > 1;
+}
+
+const gchar*
+egg_asn1_dn_oid_attr (GQuark oid)
+{
+ PrintableOid *printable;
+
+ g_return_val_if_fail (oid, NULL);
+
+ printable = dn_find_printable (oid);
+ if (!printable)
+ return g_quark_to_string (oid);
+
+ return printable->attr;
+}
+
+const gchar*
+egg_asn1_dn_oid_desc (GQuark oid)
+{
+ PrintableOid *printable;
+
+ g_return_val_if_fail (oid, NULL);
+
+ printable = dn_find_printable (oid);
+ if (!printable)
+ return g_quark_to_string (oid);
+
+ return gettext (printable->description);
+}
+
+gchar*
+egg_asn1_dn_print_value (GQuark oid, const guchar *value, gsize n_value)
+{
+ PrintableOid *printable;
+
+ g_return_val_if_fail (oid, NULL);
+ g_return_val_if_fail (value || !n_value, NULL);
+
+ printable = dn_find_printable (oid);
+ return dn_print_oid_value (printable, value, n_value);
+}
diff --git a/egg/egg-asn1.h b/egg/egg-asn1.h
index a6bfc73c..ab1d3414 100644
--- a/egg/egg-asn1.h
+++ b/egg/egg-asn1.h
@@ -28,7 +28,7 @@
#include <libtasn1.h>
-typedef void* (*EggAllocator) (void* p, unsigned long len);
+typedef void* (*EggAllocator) (void* p, gsize);
ASN1_TYPE egg_asn1_get_pk_asn1type (void);
@@ -56,6 +56,8 @@ gboolean egg_asn1_read_uint (ASN1_TYPE asn, const
gboolean egg_asn1_read_time (ASN1_TYPE asn, const gchar *part, time_t *val);
+gboolean egg_asn1_read_date (ASN1_TYPE asn, const gchar *part, GDate *date);
+
const guchar* egg_asn1_read_content (ASN1_TYPE asn, const guchar *data, gsize n_data,
const gchar *part, gsize *n_content);
@@ -64,16 +66,30 @@ const guchar* egg_asn1_read_element (ASN1_TYPE asn, const
gboolean egg_asn1_write_uint (ASN1_TYPE asn, const gchar *part, guint val);
+gint egg_asn1_element_length (const guchar *data, gsize n_data);
+
+const guchar* egg_asn1_element_content (const guchar *data, gsize n_data, gsize *n_content);
+
gchar* egg_asn1_read_dn (ASN1_TYPE asn, const gchar *part);
gchar* egg_asn1_read_dn_part (ASN1_TYPE asn, const gchar *part, const gchar *match);
-gint egg_asn1_element_length (const guchar *data, gsize n_data);
-const guchar* egg_asn1_element_content (const guchar *data, gsize n_data, gsize *n_content);
+glong egg_asn1_time_parse_utc (const gchar* value);
+
+glong egg_asn1_time_parse_general (const gchar* value);
+
+
+typedef void (*EggAsn1DnCallback) (guint index, GQuark oid, const guchar *value,
+ gsize n_value, gpointer user_data);
+
+gboolean egg_asn1_dn_parse (ASN1_TYPE asn, const gchar *part,
+ EggAsn1DnCallback callback, gpointer user_data);
+
+const gchar* egg_asn1_dn_oid_attr (GQuark oid);
-glong egg_asn1_parse_utc_time (const gchar* value);
+const gchar* egg_asn1_dn_oid_desc (GQuark oid);
-glong egg_asn1_parse_general_time (const gchar* value);
+gchar* egg_asn1_dn_print_value (GQuark oid, const guchar *value, gsize n_value);
#endif /*EGG_ASN1_H_*/
diff --git a/egg/egg-hex.c b/egg/egg-hex.c
index cdfc33e8..50346606 100644
--- a/egg/egg-hex.c
+++ b/egg/egg-hex.c
@@ -81,24 +81,37 @@ egg_hex_decode (const gchar *data, gssize n_data, gsize *n_decoded)
gchar*
egg_hex_encode (const guchar *data, gsize n_data)
{
- gchar *result, *encoded;
+ return egg_hex_encode_full (data, n_data, 0);
+}
+
+gchar*
+egg_hex_encode_full (const guchar *data, gsize n_data, guint group)
+{
+ GString *result;
+ gsize bytes;
guchar j;
g_return_val_if_fail (data || !n_data, NULL);
+
+ result = g_string_sized_new (n_data * 2 + 1);
+ bytes = 0;
- encoded = result = g_malloc0 (n_data * 2 + 1);
-
- while(n_data > 0) {
+ while (n_data > 0) {
+
+ if (group && bytes && (bytes % group) == 0)
+ g_string_append_c (result, ' ');
+
j = *(data) >> 4 & 0xf;
- *(encoded++) = HEXC[j];
-
+ g_string_append_c (result, HEXC[j]);
+
j = *(data++) & 0xf;
- *(encoded++) = HEXC[j];
+ g_string_append_c (result, HEXC[j]);
- n_data--;
+ ++bytes;
+ --n_data;
}
/* Make sure still null terminated */
- g_assert (encoded[n_data * 2] == 0);
- return result;
+ return g_string_free (result, FALSE);
}
+
diff --git a/egg/egg-hex.h b/egg/egg-hex.h
index ddcd9238..fa53e56d 100644
--- a/egg/egg-hex.h
+++ b/egg/egg-hex.h
@@ -25,10 +25,14 @@
#include <glib.h>
guchar* egg_hex_decode (const gchar *data,
- gssize n_data,
- gsize *n_decoded);
+ gssize n_data,
+ gsize *n_decoded);
gchar* egg_hex_encode (const guchar *data,
- gsize n_data);
+ gsize n_data);
+
+gchar* egg_hex_encode_full (const guchar *data,
+ gsize n_data,
+ guint group);
#endif /* EGG_HEX_H_ */
diff --git a/egg/pkix.asn b/egg/pkix.asn
index 1e5d6571..c220c8af 100644
--- a/egg/pkix.asn
+++ b/egg/pkix.asn
@@ -1195,15 +1195,14 @@ pkcs-7-EncryptedContent ::= OCTET STRING
pkcs-7-UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute
-- LDAP stuff
--- may not be correct
-id-at-ldap-DC AttributeType ::= { 0 9 2342 19200300 100 1 25 }
+id-at-domainComponent AttributeType ::= { 0 9 2342 19200300 100 1 25 }
-ldap-DC ::= IA5String
+domainComponent ::= IA5String
-id-at-ldap-UID AttributeType ::= { 0 9 2342 19200300 100 1 1 }
+id-at-userId AttributeType ::= { 0 9 2342 19200300 100 1 1 }
-ldap-UID ::= DirectoryString
+userId ::= DirectoryString
-- rfc3039
diff --git a/egg/tests/unit-test-asn1.c b/egg/tests/unit-test-asn1.c
index f4cc44fe..818869df 100644
--- a/egg/tests/unit-test-asn1.c
+++ b/egg/tests/unit-test-asn1.c
@@ -331,7 +331,7 @@ DEFINE_TEST(general_time)
const TimeTestData *data;
for (data = generalized_time_test_data; data->value; ++data) {
- when = egg_asn1_parse_general_time (data->value);
+ when = egg_asn1_time_parse_general (data->value);
if (data->ref != when) {
printf ("%s", data->value);
printf ("%s != ", ctime (&when));
@@ -349,7 +349,7 @@ DEFINE_TEST(utc_time)
const TimeTestData *data;
for (data = utc_time_test_data; data->value; ++data) {
- when = egg_asn1_parse_utc_time (data->value);
+ when = egg_asn1_time_parse_utc (data->value);
if (data->ref != when) {
printf ("%s", data->value);
printf ("%s != ", ctime (&when));
@@ -370,6 +370,16 @@ DEFINE_TEST(read_time)
g_assert_cmpint (time, ==, 820454400);
}
+DEFINE_TEST(read_date)
+{
+ GDate date;
+ if (!egg_asn1_read_date (asn1_cert, "tbsCertificate.validity.notAfter", &date))
+ g_assert_not_reached ();
+ g_assert_cmpint (date.day, ==, 31);
+ g_assert_cmpint (date.month, ==, 12);
+ g_assert_cmpint (date.year, ==, 2020);
+}
+
DEFINE_TEST(read_dn)
{
gchar *dn;
@@ -384,6 +394,79 @@ DEFINE_TEST(read_dn)
g_assert (dn == NULL);
}
+DEFINE_TEST(dn_oid)
+{
+ GQuark oid;
+
+ oid = g_quark_from_static_string ("0.9.2342.19200300.100.1.25");
+ g_assert_cmpstr (egg_asn1_dn_oid_attr (oid), ==, "DC");
+ g_assert_cmpstr (egg_asn1_dn_oid_desc (oid), ==, "Domain Component");
+
+ /* Should return OID for invalid oids */
+ oid = g_quark_from_static_string ("1.1.1.1.1");
+ g_assert_cmpstr (egg_asn1_dn_oid_attr (oid), ==, "1.1.1.1.1");
+ g_assert_cmpstr (egg_asn1_dn_oid_desc (oid), ==, "1.1.1.1.1");
+}
+
+DEFINE_TEST(dn_value)
+{
+ const guchar value[] = { 0x13, 0x1a, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20, 0x43, 0x41 };
+ gsize n_value = 28;
+ GQuark oid;
+ gchar *text;
+
+ /* Some printable strings */
+ oid = g_quark_from_static_string ("2.5.4.3");
+ text = egg_asn1_dn_print_value (oid, value, n_value);
+ g_assert_cmpstr (text, ==, "Thawte Personal Premium CA");
+ g_free (text);
+
+ /* Unknown oid */
+ oid = g_quark_from_static_string ("1.1.1.1.1.1");
+ text = egg_asn1_dn_print_value (oid, value, n_value);
+ g_assert_cmpstr (text, ==, "#131A54686177746520506572736F6E616C205072656D69756D204341");
+ g_free (text);
+}
+
+static int last_index = 0;
+
+static void
+concatenate_dn (guint index, GQuark oid, const guchar *value, gsize n_value, gpointer user_data)
+{
+ GString *dn = user_data;
+ gchar *text;
+
+ g_assert (oid);
+ g_assert (value);
+ g_assert (n_value);
+
+ g_assert (index == last_index);
+ ++last_index;
+
+ if (index != 1) {
+ g_string_append (dn, ", ");
+ }
+
+ g_string_append (dn, egg_asn1_dn_oid_attr (oid));
+ g_string_append_c (dn, '=');
+
+ text = egg_asn1_dn_print_value (oid, value, n_value);
+ g_string_append (dn, text);
+ g_free (text);
+}
+
+DEFINE_TEST(parse_dn)
+{
+ GString *dn = g_string_new ("");
+ last_index = 1;
+
+ if (!egg_asn1_dn_parse (asn1_cert, "tbsCertificate.issuer.rdnSequence", concatenate_dn, dn))
+ g_assert_not_reached ();
+
+ g_assert_cmpstr (dn->str, ==, "C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting, OU=Certification Services Division, CN=Thawte Personal Premium CA, EMAIL=personal-premium@thawte.com");
+ g_string_free (dn, TRUE);
+}
+
DEFINE_TEST(read_dn_part)
{
gchar *value;
diff --git a/egg/tests/unit-test-hex.c b/egg/tests/unit-test-hex.c
index 1664ef71..5b9ece65 100644
--- a/egg/tests/unit-test-hex.c
+++ b/egg/tests/unit-test-hex.c
@@ -31,6 +31,7 @@
static const guchar TEST_DATA[] = { 0x05, 0xD6, 0x95, 0x96, 0x10, 0x12, 0xAE, 0x35 };
static const gchar *TEST_HEX = "05D695961012AE35";
+static const gchar *TEST_HEX_DELIM = "05 D6 95 96 10 12 AE 35";
static const gchar *TEST_HEX_SPACE = "\n05 D695 \r961012AE35\n\n";
DEFINE_TEST(hex_encode)
@@ -42,6 +43,21 @@ DEFINE_TEST(hex_encode)
g_assert_cmpstr (hex, ==, TEST_HEX);
}
+DEFINE_TEST(hex_encode_spaces)
+{
+ gchar *hex;
+
+ /* Encode without spaces */
+ hex = egg_hex_encode_full (TEST_DATA, sizeof (TEST_DATA), 0);
+ g_assert (hex);
+ g_assert_cmpstr (hex, ==, TEST_HEX);
+
+ /* Encode without spaces */
+ hex = egg_hex_encode_full (TEST_DATA, sizeof (TEST_DATA), 1);
+ g_assert (hex);
+ g_assert_cmpstr (hex, ==, TEST_HEX_DELIM);
+}
+
DEFINE_TEST(hex_decode)
{
guchar *data;
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index c9de7506..44498ea9 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -5,6 +5,7 @@
uidir = $(datadir)/gcr/ui/
GLADE_FILES = \
+ gcr-certificate-basics-widget.glade \
gcr-import-dialog.glade
.glade.ui:
@@ -20,7 +21,9 @@ INCLUDES = \
-I$(top_srcdir) \
$(GTK_CFLAGS) \
$(GOBJECT_CFLAGS) \
- $(GLIB_CFLAGS)
+ $(GLIB_CFLAGS) \
+ $(LIBGCRYPT_CFLAGS) \
+ $(LIBTASN1_CFLAGS)
BUILT_SOURCES = \
gcr-marshal.c gcr-marshal.h
@@ -28,6 +31,9 @@ BUILT_SOURCES = \
lib_LTLIBRARIES = libgcr.la
libgcr_la_SOURCES = \
+ gcr-certificate.c gcr-certificate.h \
+ gcr-certificate-basics-widget.c gcr-certificate-basics-widget.h \
+ gcr-certificate-details-widget.c gcr-certificate-details-widget.h \
gcr-import-dialog.c gcr-import-dialog.h \
gcr-importer.c gcr-importer.h \
gcr-internal.h \
@@ -50,7 +56,9 @@ libgcr_la_LIBADD = \
$(top_builddir)/egg/libegg-secure-entry.la \
$(top_builddir)/gp11/libgp11.la \
$(GOBJECT_LIBS) \
- $(GLIB_LIBS)
+ $(GLIB_LIBS) \
+ $(LIBGCRYPT_LIBS) \
+ $(LIBTASN1_LIBS)
gcr-marshal.h: gcr-marshal.list $(GLIB_GENMARSHAL)
$(GLIB_GENMARSHAL) $< --header --prefix=_gcr_marshal > $@
@@ -70,7 +78,6 @@ gcr-$(GCR_MAJOR).pc: gcr.pc
EXTRA_DIST = \
gcr.pc.in \
gcr-marshal.list \
- gcr-import-dialog.glade \
$(GLADE_FILES)
CLEANFILES = \
diff --git a/gcr/gcr-certificate-basics-widget.c b/gcr/gcr-certificate-basics-widget.c
new file mode 100644
index 00000000..5bd91c98
--- /dev/null
+++ b/gcr/gcr-certificate-basics-widget.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-certificate.h"
+#include "gcr-certificate-basics-widget.h"
+
+#include <glib/gi18n-lib.h>
+
+enum {
+ PROP_0,
+ PROP_CERTIFICATE
+};
+
+struct _GcrCertificateBasicsWidgetPrivate {
+ GcrCertificate *certificate;
+ GtkBuilder *builder;
+};
+
+G_DEFINE_TYPE (GcrCertificateBasicsWidget, gcr_certificate_basics_widget, GTK_TYPE_ALIGNMENT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static void
+set_certificate_part_label (GtkBuilder *builder, const char *name, const gchar *value)
+{
+ GtkWidget *widget;
+ gchar *markup;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, name));
+ g_return_if_fail (GTK_IS_LABEL (widget));
+ if(value)
+ {
+ markup = g_markup_escape_text (value, -1);
+ gtk_label_set_markup (GTK_LABEL (widget), markup);
+ g_free (markup);
+ }
+ else
+ {
+ gtk_label_set_markup (GTK_LABEL (widget), _("<i>Not Part of Certificate</i>"));
+ }
+}
+
+static void
+set_certificate_part_date (GtkBuilder *builder, const char *name, const GDate *value)
+{
+ GtkWidget *widget;
+ gchar *formatted;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, name));
+ g_return_if_fail (GTK_IS_LABEL (widget));
+ if(value)
+ {
+ formatted = g_new (gchar, 11);
+ g_date_strftime (formatted, 11, "%Y-%m-%d", value);
+ gtk_label_set_text (GTK_LABEL (widget), formatted);
+ g_free (formatted);
+ }
+ else
+ {
+ gtk_label_set_markup (GTK_LABEL (widget), _("<i>unknown</i>"));
+ }
+}
+
+static void
+refresh_display (GcrCertificateBasicsWidget *self)
+{
+ gchar *value;
+ GDate *date;
+
+ /* Issued To / Subject */
+
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_subject_cn (self->pv->certificate);
+ set_certificate_part_label (self->pv->builder, "issued-to-cn", value);
+ g_free (value);
+
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_subject_part (self->pv->certificate, "o");
+ set_certificate_part_label (self->pv->builder, "issued-to-o", value);
+ g_free (value);
+
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_subject_part (self->pv->certificate, "ou");
+ set_certificate_part_label (self->pv->builder, "issued-to-ou", value);
+ g_free (value);
+
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_serial_number_hex (self->pv->certificate);
+ set_certificate_part_label (self->pv->builder, "issued-to-serial", value);
+ g_free (value);
+
+
+ /* Issued By / Issuer */
+
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_issuer_cn (self->pv->certificate);
+ set_certificate_part_label (self->pv->builder, "issued-by-cn", value);
+ g_free (value);
+
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_issuer_part (self->pv->certificate, "o");
+ set_certificate_part_label (self->pv->builder, "issued-by-o", value);
+ g_free (value);
+
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_issuer_part (self->pv->certificate, "ou");
+ set_certificate_part_label (self->pv->builder, "issued-by-ou", value);
+ g_free (value);
+
+
+ /* Expiry */
+
+ date = NULL;
+ if (self->pv->certificate)
+ date = gcr_certificate_get_issued_date (self->pv->certificate);
+ set_certificate_part_date (self->pv->builder, "validity-issued-on", date);
+ if (date)
+ g_date_free (date);
+
+ date = NULL;
+ if (self->pv->certificate)
+ date = gcr_certificate_get_expiry_date (self->pv->certificate);
+ set_certificate_part_date (self->pv->builder, "validity-expires-on", date);
+ if (date)
+ g_date_free (date);
+
+
+ /* Fingerprints */
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_fingerprint_hex (self->pv->certificate, G_CHECKSUM_SHA1);
+ set_certificate_part_label (self->pv->builder, "fingerprints-sha1", value);
+ g_free (value);
+
+ value = NULL;
+ if (self->pv->certificate)
+ value = gcr_certificate_get_fingerprint_hex (self->pv->certificate, G_CHECKSUM_SHA1);
+ set_certificate_part_label (self->pv->builder, "fingerprints-md5", value);
+ g_free (value);
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+
+static GObject*
+gcr_certificate_basics_widget_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GObject *obj = G_OBJECT_CLASS (gcr_certificate_basics_widget_parent_class)->constructor (type, n_props, props);
+ GcrCertificateBasicsWidget *self = NULL;
+ GtkWidget *widget;
+
+ if (obj) {
+ self = GCR_CERTIFICATE_BASICS_WIDGET (obj);
+
+ if (!gtk_builder_add_from_file (self->pv->builder, UIDIR "gcr-certificate-basics-widget.ui", NULL))
+ g_return_val_if_reached (obj);
+
+ widget = GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "certificate-basics-widget"));
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), obj);
+ gtk_container_add (GTK_CONTAINER (self), widget);
+ gtk_widget_show (widget);
+ }
+
+ return obj;
+}
+
+static void
+gcr_certificate_basics_widget_init (GcrCertificateBasicsWidget *self)
+{
+ self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_CERTIFICATE_BASICS_WIDGET, GcrCertificateBasicsWidgetPrivate));
+ self->pv->builder = gtk_builder_new ();
+}
+
+static void
+gcr_certificate_basics_widget_dispose (GObject *obj)
+{
+ GcrCertificateBasicsWidget *self = GCR_CERTIFICATE_BASICS_WIDGET (obj);
+
+ if (self->pv->certificate)
+ g_object_unref (self->pv->certificate);
+ self->pv->certificate = NULL;
+
+ G_OBJECT_CLASS (gcr_certificate_basics_widget_parent_class)->dispose (obj);
+}
+
+static void
+gcr_certificate_basics_widget_finalize (GObject *obj)
+{
+ GcrCertificateBasicsWidget *self = GCR_CERTIFICATE_BASICS_WIDGET (obj);
+
+ g_assert (!self->pv->certificate);
+
+ G_OBJECT_CLASS (gcr_certificate_basics_widget_parent_class)->finalize (obj);
+}
+
+static void
+gcr_certificate_basics_widget_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateBasicsWidget *self = GCR_CERTIFICATE_BASICS_WIDGET (obj);
+
+ switch (prop_id) {
+ case PROP_CERTIFICATE:
+ gcr_certificate_basics_widget_set_certificate (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_certificate_basics_widget_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateBasicsWidget *self = GCR_CERTIFICATE_BASICS_WIDGET (obj);
+
+ switch (prop_id) {
+ case PROP_CERTIFICATE:
+ g_value_set_object (value, self->pv->certificate);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_certificate_basics_widget_class_init (GcrCertificateBasicsWidgetClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gcr_certificate_basics_widget_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (GcrCertificateBasicsWidgetPrivate));
+
+ gobject_class->constructor = gcr_certificate_basics_widget_constructor;
+ gobject_class->dispose = gcr_certificate_basics_widget_dispose;
+ gobject_class->finalize = gcr_certificate_basics_widget_finalize;
+ gobject_class->set_property = gcr_certificate_basics_widget_set_property;
+ gobject_class->get_property = gcr_certificate_basics_widget_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_CERTIFICATE,
+ g_param_spec_object("certificate", "Certificate", "Certificate to display.",
+ GCR_TYPE_CERTIFICATE, G_PARAM_READWRITE));
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GcrCertificateBasicsWidget*
+gcr_certificate_basics_widget_new (GcrCertificate *certificate)
+{
+ return g_object_new (GCR_TYPE_CERTIFICATE_BASICS_WIDGET, "certificate", certificate, NULL);
+}
+
+GcrCertificate*
+gcr_certificate_basics_widget_get_certificate (GcrCertificateBasicsWidget *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_BASICS_WIDGET (self), NULL);
+ return self->pv->certificate;
+}
+
+void
+gcr_certificate_basics_widget_set_certificate (GcrCertificateBasicsWidget *self, GcrCertificate *cert)
+{
+ g_return_if_fail (GCR_IS_CERTIFICATE_BASICS_WIDGET (self));
+
+ if (self->pv->certificate)
+ g_object_unref (self->pv->certificate);
+ self->pv->certificate = cert;
+ if (self->pv->certificate)
+ g_object_ref (self->pv->certificate);
+
+ refresh_display (self);
+ g_object_notify (G_OBJECT (self), "certificate");
+}
diff --git a/gcr/gcr-certificate-basics-widget.glade b/gcr/gcr-certificate-basics-widget.glade
new file mode 100644
index 00000000..1df6cdee
--- /dev/null
+++ b/gcr/gcr-certificate-basics-widget.glade
@@ -0,0 +1,512 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+ <widget class="GtkWindow" id="unused-window">
+ <child>
+ <widget class="GtkVBox" id="certificate-basics-widget">
+ <property name="visible">True</property>
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkFrame" id="frame1">
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="border_width">6</property>
+ <child>
+ <widget class="GtkLabel" id="ssl-client-cert-verified-label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">SSL Client Certificate</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="ssl-server-cert-verified-label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">SSL Server Certificate</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="email-signer-cert-verified-label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Email Signer Certificate</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="email-recipient-cert-verified-label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Email Recipient Certificate</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;This certificate has been verified for the following uses:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <property name="n_rows">15</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="label25">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">1</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">&lt;b&gt;Issued To&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label26">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Common Name (CN)</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label27">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Organization (O)</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Organizational Unit (OU)</property>
+ </widget>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Serial Number</property>
+ </widget>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Common Name (CN)</property>
+ </widget>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Organization (O)</property>
+ </widget>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Organizational Unit (OU)</property>
+ </widget>
+ <packing>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Issued On</property>
+ </widget>
+ <packing>
+ <property name="top_attach">10</property>
+ <property name="bottom_attach">11</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">Expires On</property>
+ </widget>
+ <packing>
+ <property name="top_attach">11</property>
+ <property name="bottom_attach">12</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">1</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">&lt;b&gt;Issued By&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">&lt;b&gt;Fingerprints&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">12</property>
+ <property name="bottom_attach">13</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label38">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">SHA1 Fingerprint</property>
+ </widget>
+ <packing>
+ <property name="top_attach">13</property>
+ <property name="bottom_attach">14</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label39">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">6</property>
+ <property name="label" translatable="yes">MD5 Fingerprint</property>
+ </widget>
+ <packing>
+ <property name="top_attach">14</property>
+ <property name="bottom_attach">15</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="issued-to-o">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="issued-to-ou">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="issued-to-serial">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="issued-by-cn">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="issued-by-o">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="issued-by-ou">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="validity-issued-on">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">10</property>
+ <property name="bottom_attach">11</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="validity-expires-on">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">11</property>
+ <property name="bottom_attach">12</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="fingerprints-sha1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">13</property>
+ <property name="bottom_attach">14</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="fingerprints-md5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">14</property>
+ <property name="bottom_attach">15</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="issued-to-cn">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;Not Part of Certificate&gt;</property>
+ <property name="selectable">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">&lt;b&gt;Validity&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/gcr/gcr-certificate-basics-widget.h b/gcr/gcr-certificate-basics-widget.h
new file mode 100644
index 00000000..cf000617
--- /dev/null
+++ b/gcr/gcr-certificate-basics-widget.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_CERTIFICATE_BASICS_WIDGET_H__
+#define __GCR_CERTIFICATE_BASICS_WIDGET_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gcr-certificate.h"
+
+#define GCR_TYPE_CERTIFICATE_BASICS_WIDGET (gcr_certificate_basics_widget_get_type ())
+#define GCR_CERTIFICATE_BASICS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE_BASICS_WIDGET, GcrCertificateBasicsWidget))
+#define GCR_CERTIFICATE_BASICS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE_BASICS_WIDGET, GcrCertificateBasicsWidgetClass))
+#define GCR_IS_CERTIFICATE_BASICS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE_BASICS_WIDGET))
+#define GCR_IS_CERTIFICATE_BASICS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE_BASICS_WIDGET))
+#define GCR_CERTIFICATE_BASICS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE_BASICS_WIDGET, GcrCertificateBasicsWidgetClass))
+
+typedef struct _GcrCertificateBasicsWidget GcrCertificateBasicsWidget;
+typedef struct _GcrCertificateBasicsWidgetClass GcrCertificateBasicsWidgetClass;
+typedef struct _GcrCertificateBasicsWidgetPrivate GcrCertificateBasicsWidgetPrivate;
+
+struct _GcrCertificateBasicsWidget {
+ GtkAlignment parent;
+ GcrCertificateBasicsWidgetPrivate *pv;
+};
+
+struct _GcrCertificateBasicsWidgetClass {
+ GtkAlignmentClass parent_class;
+};
+
+GType gcr_certificate_basics_widget_get_type (void);
+
+GcrCertificateBasicsWidget* gcr_certificate_basics_widget_new (GcrCertificate *cert);
+
+GcrCertificate* gcr_certificate_basics_widget_get_certificate (GcrCertificateBasicsWidget *basics);
+
+void gcr_certificate_basics_widget_set_certificate (GcrCertificateBasicsWidget *basics,
+ GcrCertificate *cert);
+
+#endif /* __GCR_CERTIFICATE_BASICS_WIDGET_H__ */
diff --git a/gcr/gcr-certificate-details-widget.c b/gcr/gcr-certificate-details-widget.c
new file mode 100644
index 00000000..09f80553
--- /dev/null
+++ b/gcr/gcr-certificate-details-widget.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-certificate.h"
+#include "gcr-certificate-details-widget.h"
+
+#include "egg/egg-asn1.h"
+#include "egg/egg-hex.h"
+
+#include <glib/gi18n-lib.h>
+
+enum {
+ PROP_0,
+ PROP_CERTIFICATE
+};
+
+struct _GcrCertificateDetailsWidgetPrivate {
+ GcrCertificate *certificate;
+ GtkTextView *view;
+ GtkTextBuffer *buffer;
+ GtkTextTag *field_tag;
+ gint field_width;
+};
+
+G_DEFINE_TYPE (GcrCertificateDetailsWidget, gcr_certificate_details_widget, GTK_TYPE_ALIGNMENT);
+
+#define FIELD_MARGIN 17
+#define COLUMN_MARGIN 6
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static GtkTextTagTable*
+create_tag_table (GcrCertificateDetailsWidget *self)
+{
+ GtkTextTagTable *tags;
+ GtkTextTag *tag;
+
+ g_assert (GCR_IS_CERTIFICATE_DETAILS_WIDGET (self));
+
+ tags = gtk_text_tag_table_new ();
+
+ tag = g_object_new (GTK_TYPE_TEXT_TAG,
+ "name", "heading",
+ "left-margin", 5,
+ "right-margin", 5,
+ "pixels-above-lines", 9,
+ "pixels-below-lines", 3,
+ "weight", PANGO_WEIGHT_BOLD,
+ NULL);
+
+ gtk_text_tag_table_add (tags, tag);
+ g_object_unref (tag);
+
+ tag = g_object_new (GTK_TYPE_TEXT_TAG,
+ "name", "monospace",
+ "family", "monospace",
+ NULL);
+
+ gtk_text_tag_table_add (tags, tag);
+ g_object_unref (tag);
+
+ g_assert (!self->pv->field_tag);
+ self->pv->field_width = 0;
+ self->pv->field_tag = g_object_new (GTK_TYPE_TEXT_TAG,
+ "name", "field",
+ "left-margin", self->pv->field_width + FIELD_MARGIN,
+ "indent", self->pv->field_width,
+ "right-margin", 5,
+ "pixels-below-lines", 3,
+ "wrap-mode", GTK_WRAP_WORD_CHAR,
+ NULL);
+ gtk_text_tag_table_add (tags, self->pv->field_tag);
+
+ return tags;
+}
+
+static void
+append_field_and_value (GcrCertificateDetailsWidget *self, const gchar *field,
+ const gchar *value, gboolean monospace)
+{
+ PangoRectangle extents;
+ PangoTabArray *tabs;
+ PangoLayout *layout;
+ GtkTextIter iter;
+ gchar *text;
+
+ text = g_strdup_printf ("%s:", field);
+
+ /* Measure the width of the field */
+ layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), text);
+ pango_layout_get_extents (layout, NULL, &extents);
+ pango_extents_to_pixels (&extents, NULL);
+ g_object_unref (layout);
+
+ /* Make the tab wide enough to accomodate */
+ if (extents.width > self->pv->field_width) {
+ self->pv->field_width = extents.width + COLUMN_MARGIN;
+ tabs = pango_tab_array_new (1, TRUE);
+ pango_tab_array_set_tab (tabs, 0, PANGO_TAB_LEFT, self->pv->field_width);
+ g_object_set (self->pv->field_tag,
+ "left-margin", FIELD_MARGIN,
+ "indent", 0 - self->pv->field_width,
+ "tabs", tabs,
+ NULL);
+ pango_tab_array_free (tabs);
+ }
+
+ gtk_text_buffer_get_end_iter (self->pv->buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name (self->pv->buffer, &iter, text, -1, "field", NULL);
+ gtk_text_buffer_insert (self->pv->buffer, &iter, "\t", 1);
+ gtk_text_buffer_insert_with_tags_by_name (self->pv->buffer, &iter, value, -1, "field",
+ monospace ? "monospace" : NULL, NULL);
+ gtk_text_buffer_insert (self->pv->buffer, &iter, "\n", 1);
+
+ g_free (text);
+}
+
+static void
+append_heading (GcrCertificateDetailsWidget *self, const gchar *heading)
+{
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_end_iter (self->pv->buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name (self->pv->buffer, &iter, heading, -1, "heading", NULL);
+ gtk_text_buffer_insert (self->pv->buffer, &iter, "\n", 1);
+}
+
+static void
+append_fingerprint (GcrCertificateDetailsWidget *self, const guchar *data,
+ gsize n_data, const gchar *name, GChecksumType type)
+{
+ GChecksum *checksum;
+ guint8 *buffer;
+ gsize n_buffer;
+ gchar *display;
+
+ checksum = g_checksum_new (type);
+ g_return_if_fail (checksum);
+ g_checksum_update (checksum, data, n_data);
+
+ n_buffer = g_checksum_type_get_length (type);
+ g_return_if_fail (n_buffer);
+ buffer = g_malloc0 (n_buffer);
+
+ g_checksum_get_digest (checksum, buffer, &n_buffer);
+ g_checksum_free (checksum);
+
+ display = egg_hex_encode_full (buffer, n_buffer, 1);
+ append_field_and_value (self, name, display, TRUE);
+ g_free (display);
+
+ g_free (buffer);
+}
+
+static void
+on_parsed_dn_part (guint index, GQuark oid, const guchar *value,
+ gsize n_value, gpointer user_data)
+{
+ GcrCertificateDetailsWidget *self = user_data;
+ const gchar *attr;
+ const gchar *desc;
+ gchar *field;
+ gchar *display;
+
+ g_return_if_fail (GCR_IS_CERTIFICATE_DETAILS_WIDGET (self));
+
+ attr = egg_asn1_dn_oid_attr (oid);
+ desc = egg_asn1_dn_oid_desc (oid);
+
+ /* Combine them into something sane */
+ if (attr && desc) {
+ if (strcmp (attr, desc) == 0)
+ field = g_strdup (attr);
+ else
+ field = g_strdup_printf ("%s (%s)", attr, desc);
+ } else if (!attr && !desc) {
+ field = g_strdup ("");
+ } else if (attr) {
+ field = g_strdup (attr);
+ } else if (desc) {
+ field = g_strdup (desc);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ display = egg_asn1_dn_print_value (oid, value, n_value);
+ if (display == NULL)
+ display = "";
+
+ append_field_and_value (self, field, display, FALSE);
+ g_free (field);
+ g_free (display);
+}
+
+static void
+refresh_display (GcrCertificateDetailsWidget *self)
+{
+ GtkTextIter start, iter;
+ const guchar *data, *value;
+ gsize n_data, n_value;
+ guint version;
+ gchar *display;
+ ASN1_TYPE asn;
+ GDate date;
+
+ gtk_text_buffer_get_start_iter (self->pv->buffer, &start);
+ gtk_text_buffer_get_end_iter (self->pv->buffer, &iter);
+ gtk_text_buffer_delete (self->pv->buffer, &start, &iter);
+
+ if (!self->pv->certificate)
+ return;
+
+ data = gcr_certificate_get_der_data (self->pv->certificate, &n_data);
+ g_return_if_fail (data);
+
+ asn = egg_asn1_decode ("PKIX1.Certificate", data, n_data);
+ g_return_if_fail (asn);
+
+ /* The subject */
+ append_heading (self, _("Subject Name"));
+ egg_asn1_dn_parse (asn, "tbsCertificate.subject.rdnSequence", on_parsed_dn_part, self);
+
+ /* The Issuer */
+ append_heading (self, _("Issuer Name"));
+ egg_asn1_dn_parse (asn, "tbsCertificate.issuer.rdnSequence", on_parsed_dn_part, self);
+
+ /* The Issued Parameters */
+ append_heading (self, _("Issued Certificate"));
+
+ if (!egg_asn1_read_uint (asn, "tbsCertificate.version", &version))
+ g_return_if_reached ();
+ display = g_strdup_printf ("%u", version + 1);
+ append_field_and_value (self, _("Version"), display, FALSE);
+ g_free (display);
+
+ value = egg_asn1_read_content (asn, data, n_data, "tbsCertificate.serialNumber", &n_value);
+ g_return_if_fail (value);
+ display = egg_hex_encode_full (value, n_value, 1);
+ append_field_and_value (self, _("Serial Number"), display, TRUE);
+ g_free (display);
+
+ display = g_malloc0 (128);
+ if (egg_asn1_read_date (asn, "tbsCertificate.validity.notBefore", &date)) {
+ if (!g_date_strftime (display, 128, "%Y-%m-%d", &date))
+ g_return_if_reached ();
+ append_field_and_value (self, _("Not Valid Before"), display, FALSE);
+ }
+ if (egg_asn1_read_date (asn, "tbsCertificate.validity.notAfter", &date)) {
+ if (!g_date_strftime (display, 128, "%Y-%m-%d", &date))
+ g_return_if_reached ();
+ append_field_and_value (self, _("Not Valid After"), display, FALSE);
+ }
+ g_free (display);
+
+ /* Signature */
+ append_heading (self, _("Signature"));
+
+ /* TODO: Complete Signature algorithm, and params */
+ append_field_and_value (self, _("Signature Algorithm"), "TODO", FALSE);
+
+ value = egg_asn1_read_content (asn, data, n_data, "signature", &n_value);
+ g_return_if_fail (value);
+ display = egg_hex_encode_full (value, n_value, 1);
+ append_field_and_value (self, _("Signature"), display, TRUE);
+ g_free (display);
+
+ /* Public Key Info */
+ append_heading (self, _("Public Key Info"));
+
+ /* TODO: Complete algorithm, params, key size */
+ append_field_and_value (self, _("Key Algorithm"), "TODO", FALSE);
+ append_field_and_value (self, _("Key Size"), "TODO", FALSE);
+
+ value = egg_asn1_read_content (asn, data, n_data, "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", &n_value);
+ g_return_if_fail (value);
+ display = egg_hex_encode_full (value, n_value, 1);
+ append_field_and_value (self, _("Public Key"), display, TRUE);
+ g_free (display);
+
+ /* TODO: Implement extensions */
+
+ /* Fingerprints */
+ append_heading (self, _("Fingerprints"));
+
+ append_fingerprint (self, data, n_data, "SHA1", G_CHECKSUM_SHA1);
+ append_fingerprint (self, data, n_data, "MD5", G_CHECKSUM_MD5);
+
+ asn1_delete_structure (&asn);
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static GObject*
+gcr_certificate_details_widget_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GObject *obj = G_OBJECT_CLASS (gcr_certificate_details_widget_parent_class)->constructor (type, n_props, props);
+ GcrCertificateDetailsWidget *self = NULL;
+ GtkTextTagTable *tags;
+ GtkWidget *widget;
+ GtkWidget *scroll;
+
+ g_return_val_if_fail (obj, NULL);
+
+ self = GCR_CERTIFICATE_DETAILS_WIDGET (obj);
+
+ tags = create_tag_table (self);
+ self->pv->buffer = gtk_text_buffer_new (tags);
+ g_object_unref (tags);
+
+ widget = gtk_text_view_new_with_buffer (self->pv->buffer);
+ self->pv->view = GTK_TEXT_VIEW (widget);
+ gtk_text_view_set_editable (self->pv->view, FALSE);
+
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (scroll), widget);
+
+ gtk_container_add (GTK_CONTAINER (self), scroll);
+ gtk_widget_show_all (scroll);
+
+ return obj;
+}
+
+static void
+gcr_certificate_details_widget_init (GcrCertificateDetailsWidget *self)
+{
+ self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, GcrCertificateDetailsWidgetPrivate));
+}
+
+static void
+gcr_certificate_details_widget_dispose (GObject *obj)
+{
+ GcrCertificateDetailsWidget *self = GCR_CERTIFICATE_DETAILS_WIDGET (obj);
+
+ if (self->pv->certificate)
+ g_object_unref (self->pv->certificate);
+ self->pv->certificate = NULL;
+
+ G_OBJECT_CLASS (gcr_certificate_details_widget_parent_class)->dispose (obj);
+}
+
+static void
+gcr_certificate_details_widget_finalize (GObject *obj)
+{
+ GcrCertificateDetailsWidget *self = GCR_CERTIFICATE_DETAILS_WIDGET (obj);
+
+ g_assert (!self->pv->certificate);
+
+ if (self->pv->buffer)
+ g_object_unref (self->pv->buffer);
+ self->pv->buffer = NULL;
+
+ if (self->pv->field_tag)
+ g_object_unref (self->pv->field_tag);
+ self->pv->field_tag = NULL;
+
+ G_OBJECT_CLASS (gcr_certificate_details_widget_parent_class)->finalize (obj);
+}
+
+static void
+gcr_certificate_details_widget_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateDetailsWidget *self = GCR_CERTIFICATE_DETAILS_WIDGET (obj);
+
+ switch (prop_id) {
+ case PROP_CERTIFICATE:
+ gcr_certificate_details_widget_set_certificate (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_certificate_details_widget_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GcrCertificateDetailsWidget *self = GCR_CERTIFICATE_DETAILS_WIDGET (obj);
+
+ switch (prop_id) {
+ case PROP_CERTIFICATE:
+ g_value_set_object (value, self->pv->certificate);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_certificate_details_widget_class_init (GcrCertificateDetailsWidgetClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gcr_certificate_details_widget_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof (GcrCertificateDetailsWidgetPrivate));
+
+ gobject_class->constructor = gcr_certificate_details_widget_constructor;
+ gobject_class->dispose = gcr_certificate_details_widget_dispose;
+ gobject_class->finalize = gcr_certificate_details_widget_finalize;
+ gobject_class->set_property = gcr_certificate_details_widget_set_property;
+ gobject_class->get_property = gcr_certificate_details_widget_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_CERTIFICATE,
+ g_param_spec_object("certificate", "Certificate", "Certificate to display.",
+ GCR_TYPE_CERTIFICATE, G_PARAM_READWRITE));
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GcrCertificateDetailsWidget*
+gcr_certificate_details_widget_new (GcrCertificate *certificate)
+{
+ return g_object_new (GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, "certificate", certificate, NULL);
+}
+
+GcrCertificate*
+gcr_certificate_details_widget_get_certificate (GcrCertificateDetailsWidget *self)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE_DETAILS_WIDGET (self), NULL);
+ return self->pv->certificate;
+}
+
+void
+gcr_certificate_details_widget_set_certificate (GcrCertificateDetailsWidget *self, GcrCertificate *cert)
+{
+ g_return_if_fail (GCR_IS_CERTIFICATE_DETAILS_WIDGET (self));
+
+ if (self->pv->certificate)
+ g_object_unref (self->pv->certificate);
+ self->pv->certificate = cert;
+ if (self->pv->certificate)
+ g_object_ref (self->pv->certificate);
+
+ refresh_display (self);
+ g_object_notify (G_OBJECT (self), "certificate");
+}
diff --git a/gcr/gcr-certificate-details-widget.h b/gcr/gcr-certificate-details-widget.h
new file mode 100644
index 00000000..74338c66
--- /dev/null
+++ b/gcr/gcr-certificate-details-widget.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_CERTIFICATE_DETAILS_WIDGET_H__
+#define __GCR_CERTIFICATE_DETAILS_WIDGET_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gcr-certificate.h"
+
+#define GCR_TYPE_CERTIFICATE_DETAILS_WIDGET (gcr_certificate_details_widget_get_type ())
+#define GCR_CERTIFICATE_DETAILS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, GcrCertificateDetailsWidget))
+#define GCR_CERTIFICATE_DETAILS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, GcrCertificateDetailsWidgetClass))
+#define GCR_IS_CERTIFICATE_DETAILS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET))
+#define GCR_IS_CERTIFICATE_DETAILS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET))
+#define GCR_CERTIFICATE_DETAILS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE_DETAILS_WIDGET, GcrCertificateDetailsWidgetClass))
+
+typedef struct _GcrCertificateDetailsWidget GcrCertificateDetailsWidget;
+typedef struct _GcrCertificateDetailsWidgetClass GcrCertificateDetailsWidgetClass;
+typedef struct _GcrCertificateDetailsWidgetPrivate GcrCertificateDetailsWidgetPrivate;
+
+struct _GcrCertificateDetailsWidget {
+ GtkAlignment parent;
+ GcrCertificateDetailsWidgetPrivate *pv;
+};
+
+struct _GcrCertificateDetailsWidgetClass {
+ GtkAlignmentClass parent_class;
+};
+
+GType gcr_certificate_details_widget_get_type (void);
+
+GcrCertificateDetailsWidget* gcr_certificate_details_widget_new (GcrCertificate *cert);
+
+GcrCertificate* gcr_certificate_details_widget_get_certificate (GcrCertificateDetailsWidget *details);
+
+void gcr_certificate_details_widget_set_certificate (GcrCertificateDetailsWidget *details,
+ GcrCertificate *cert);
+
+#endif /* __GCR_CERTIFICATE_DETAILS_WIDGET_H__ */
diff --git a/gcr/gcr-certificate.c b/gcr/gcr-certificate.c
new file mode 100644
index 00000000..91eb1873
--- /dev/null
+++ b/gcr/gcr-certificate.c
@@ -0,0 +1,398 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-internal.h"
+#include "gcr-certificate.h"
+
+#include "egg/egg-asn1.h"
+#include "egg/egg-hex.h"
+
+#include <string.h>
+
+struct _GcrCertificatePrivate {
+ /* Cache of data returned from get_der_data() */
+ ASN1_TYPE asn1;
+ gconstpointer data;
+ gsize n_data;
+
+ /* When initialized with gcr_certificate_new_for_data() */
+ guchar *owned_data;
+ gsize n_owned_data;
+};
+
+G_DEFINE_TYPE (GcrCertificate, gcr_certificate, G_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static ASN1_TYPE
+parse_certificate_asn1 (GcrCertificate *self)
+{
+ const guchar *data;
+ gsize n_data;
+
+ g_assert (GCR_IS_CERTIFICATE (self));
+
+ data = gcr_certificate_get_der_data (self, &n_data);
+ g_return_val_if_fail (data, NULL);
+
+ if (self->pv->asn1 && n_data == self->pv->n_data &&
+ memcmp (data, self->pv->data, n_data) == 0)
+ return self->pv->asn1;
+
+ if (self->pv->asn1) {
+ asn1_delete_structure (&self->pv->asn1);
+ self->pv->asn1 = NULL;
+ self->pv->data = NULL;
+ self->pv->n_data = 0;
+ }
+
+ /* Cache is invalid or non existent */
+ self->pv->asn1 = egg_asn1_decode ("PKIX1.Certificate", data, n_data);
+ if (self->pv->asn1 == NULL) {
+ g_warning ("encountered invalid or unparseable X509 DER certificate data.");
+ return NULL;
+ }
+
+ self->pv->data = data;
+ self->pv->n_data = n_data;
+
+ return self->pv->asn1;
+}
+
+static GChecksum*
+digest_certificate (GcrCertificate *self, GChecksumType type)
+{
+ GChecksum *digest;
+ const guchar *der;
+ gsize n_der;
+
+ g_assert (GCR_IS_CERTIFICATE (self));
+
+ der = gcr_certificate_get_der_data (self, &n_der);
+ g_return_val_if_fail (der, NULL);
+
+ digest = g_checksum_new (type);
+ g_return_val_if_fail (digest, NULL);
+
+ g_checksum_update (digest, der, n_der);
+ return digest;
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static const guchar*
+gcr_certificate_real_get_der_data (GcrCertificate *self, gsize *n_data)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+ g_return_val_if_fail (n_data, NULL);
+ g_return_val_if_fail (self->pv->owned_data, NULL);
+
+ /* This is called when we're not a base class */
+ *n_data = self->pv->n_owned_data;
+ return self->pv->owned_data;
+}
+
+static GObject*
+gcr_certificate_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GcrCertificate *self = GCR_CERTIFICATE (G_OBJECT_CLASS (gcr_certificate_parent_class)->constructor(type, n_props, props));
+ g_return_val_if_fail (self, NULL);
+
+ return G_OBJECT (self);
+}
+
+static void
+gcr_certificate_init (GcrCertificate *self)
+{
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_CERTIFICATE, GcrCertificatePrivate);
+}
+
+static void
+gcr_certificate_dispose (GObject *obj)
+{
+ GcrCertificate *self = GCR_CERTIFICATE (obj);
+
+ if (self->pv->asn1) {
+ asn1_delete_structure (&self->pv->asn1);
+ self->pv->data = NULL;
+ self->pv->n_data = 0;
+ }
+
+ G_OBJECT_CLASS (gcr_certificate_parent_class)->dispose (obj);
+}
+
+static void
+gcr_certificate_finalize (GObject *obj)
+{
+ GcrCertificate *self = GCR_CERTIFICATE (obj);
+
+ g_assert (self->pv->asn1 == NULL);
+ g_free (self->pv->owned_data);
+ self->pv->owned_data = NULL;
+ self->pv->n_owned_data = 0;
+
+ G_OBJECT_CLASS (gcr_certificate_parent_class)->finalize (obj);
+}
+
+static void
+gcr_certificate_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_certificate_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_certificate_class_init (GcrCertificateClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructor = gcr_certificate_constructor;
+ gobject_class->dispose = gcr_certificate_dispose;
+ gobject_class->finalize = gcr_certificate_finalize;
+ gobject_class->set_property = gcr_certificate_set_property;
+ gobject_class->get_property = gcr_certificate_get_property;
+
+ klass->get_der_data = gcr_certificate_real_get_der_data;
+
+ g_type_class_add_private (gobject_class, sizeof (GcrCertificatePrivate));
+
+ _gcr_initialize ();
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GcrCertificate*
+gcr_certificate_new_for_data (const guchar *data, gsize n_data)
+{
+ GcrCertificate *cert;
+
+ g_return_val_if_fail (data, NULL);
+ g_return_val_if_fail (n_data, NULL);
+
+ cert = g_object_new (GCR_TYPE_CERTIFICATE, NULL);
+
+ cert->pv->owned_data = g_memdup (data, n_data);
+ cert->pv->n_owned_data = n_data;
+ return cert;
+}
+
+const guchar*
+gcr_certificate_get_der_data (GcrCertificate *self, gsize *n_length)
+{
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+ g_return_val_if_fail (GCR_CERTIFICATE_GET_CLASS (self)->get_der_data, NULL);
+ return GCR_CERTIFICATE_GET_CLASS (self)->get_der_data (self, n_length);
+}
+
+gchar*
+gcr_certificate_get_issuer_cn (GcrCertificate *self)
+{
+ return gcr_certificate_get_issuer_part (self, "cn");
+}
+
+gchar*
+gcr_certificate_get_issuer_part (GcrCertificate *self, const char *part)
+{
+ ASN1_TYPE asn1;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ asn1 = parse_certificate_asn1 (self);
+ g_return_val_if_fail (asn1, NULL);
+
+ return egg_asn1_read_dn_part (asn1, "tbsCertificate.issuer.rdnSequence", part);
+}
+
+gchar*
+gcr_certificate_get_issuer_dn (GcrCertificate *self)
+{
+ ASN1_TYPE asn1;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ asn1 = parse_certificate_asn1 (self);
+ g_return_val_if_fail (asn1, NULL);
+
+ return egg_asn1_read_dn (asn1, "tbsCertificate.issuer.rdnSequence");
+}
+
+gchar*
+gcr_certificate_get_subject_cn (GcrCertificate *self)
+{
+ return gcr_certificate_get_subject_part (self, "cn");
+}
+
+gchar*
+gcr_certificate_get_subject_part (GcrCertificate *self, const char *part)
+{
+ ASN1_TYPE asn1;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ asn1 = parse_certificate_asn1 (self);
+ g_return_val_if_fail (asn1, NULL);
+
+ return egg_asn1_read_dn_part (asn1, "tbsCertificate.subject.rdnSequence", part);
+}
+
+gchar*
+gcr_certificate_get_subject_dn (GcrCertificate *self)
+{
+ ASN1_TYPE asn1;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ asn1 = parse_certificate_asn1 (self);
+ g_return_val_if_fail (asn1, NULL);
+
+ return egg_asn1_read_dn (asn1, "tbsCertificate.issuer.rdnSequence");
+}
+
+GDate*
+gcr_certificate_get_issued_date (GcrCertificate *self)
+{
+ ASN1_TYPE asn1;
+ GDate *date;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ asn1 = parse_certificate_asn1 (self);
+ g_return_val_if_fail (asn1, NULL);
+
+ date = g_date_new ();
+ if (!egg_asn1_read_date (asn1, "tbsCertificate.validity.notBefore", date)) {
+ g_date_free (date);
+ return NULL;
+ }
+
+ return date;
+}
+
+GDate*
+gcr_certificate_get_expiry_date (GcrCertificate *self)
+{
+ ASN1_TYPE asn1;
+ GDate *date;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ asn1 = parse_certificate_asn1 (self);
+ g_return_val_if_fail (asn1, NULL);
+
+ date = g_date_new ();
+ if (!egg_asn1_read_date (asn1, "tbsCertificate.validity.notAfter", date)) {
+ g_date_free (date);
+ return NULL;
+ }
+
+ return date;
+}
+
+guchar*
+gcr_certificate_get_fingerprint (GcrCertificate *self, GChecksumType type, gsize *n_digest)
+{
+ GChecksum *sum;
+ guchar *digest;
+ gssize length;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+ g_return_val_if_fail (n_digest, NULL);
+
+ sum = digest_certificate (self, type);
+ g_return_val_if_fail (sum, NULL);
+ length = g_checksum_type_get_length (type);
+ g_return_val_if_fail (length > 0, NULL);
+ digest = g_malloc (length);
+ *n_digest = length;
+ g_checksum_get_digest (sum, digest, n_digest);
+ g_checksum_free (sum);
+
+ return digest;
+}
+
+gchar*
+gcr_certificate_get_fingerprint_hex (GcrCertificate *self, GChecksumType type)
+{
+ GChecksum *sum;
+ gchar *hex;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ sum = digest_certificate (self, type);
+ g_return_val_if_fail (sum, NULL);
+ hex = g_strdup (g_checksum_get_string (sum));
+ g_checksum_free (sum);
+ return hex;
+}
+
+guchar*
+gcr_certificate_get_serial_number (GcrCertificate *self, gsize *n_length)
+{
+ ASN1_TYPE asn1;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ asn1 = parse_certificate_asn1 (self);
+ g_return_val_if_fail (asn1, NULL);
+
+ return egg_asn1_read_value (asn1, "tbsCertificate.serialNumber", n_length, g_realloc);
+}
+
+gchar*
+gcr_certificate_get_serial_number_hex (GcrCertificate *self)
+{
+ guchar *serial;
+ gsize n_serial;
+ gchar *hex;
+
+ g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
+
+ serial = gcr_certificate_get_serial_number (self, &n_serial);
+ if (serial == NULL)
+ return NULL;
+
+ hex = egg_hex_encode (serial, n_serial);
+ g_free (serial);
+ return hex;
+}
diff --git a/gcr/gcr-certificate.h b/gcr/gcr-certificate.h
new file mode 100644
index 00000000..c800f9d8
--- /dev/null
+++ b/gcr/gcr-certificate.h
@@ -0,0 +1,91 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * 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.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_CERTIFICATE_H__
+#define __GCR_CERTIFICATE_H__
+
+#include "gcr.h"
+
+#include <glib-object.h>
+
+#define GCR_TYPE_CERTIFICATE (gcr_certificate_get_type ())
+#define GCR_CERTIFICATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE, GcrCertificate))
+#define GCR_CERTIFICATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE, GcrCertificateClass))
+#define GCR_IS_CERTIFICATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE))
+#define GCR_IS_CERTIFICATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE))
+#define GCR_CERTIFICATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE, GcrCertificateClass))
+
+typedef struct _GcrCertificate GcrCertificate;
+typedef struct _GcrCertificateClass GcrCertificateClass;
+typedef struct _GcrCertificatePrivate GcrCertificatePrivate;
+
+struct _GcrCertificate {
+ GObject parent;
+ GcrCertificatePrivate *pv;
+};
+
+struct _GcrCertificateClass {
+ GObjectClass parent_class;
+
+ /* virtual */
+
+ const guchar* (*get_der_data) (GcrCertificate *self, gsize *n_length);
+};
+
+GType gcr_certificate_get_type (void);
+
+GcrCertificate* gcr_certificate_new_for_data (const guchar *data,
+ gsize n_data);
+
+const guchar* gcr_certificate_get_der_data (GcrCertificate *self,
+ gsize *n_data);
+
+gchar* gcr_certificate_get_issuer_cn (GcrCertificate *self);
+
+gchar* gcr_certificate_get_issuer_dn (GcrCertificate *self);
+
+gchar* gcr_certificate_get_issuer_part (GcrCertificate *self,
+ const gchar *part);
+
+gchar* gcr_certificate_get_subject_cn (GcrCertificate *self);
+
+gchar* gcr_certificate_get_subject_dn (GcrCertificate *self);
+
+gchar* gcr_certificate_get_subject_part (GcrCertificate *self,
+ const gchar *part);
+
+GDate* gcr_certificate_get_issued_date (GcrCertificate *self);
+
+GDate* gcr_certificate_get_expiry_date (GcrCertificate *self);
+
+guchar* gcr_certificate_get_serial_number (GcrCertificate *self,
+ gsize *n_length);
+
+gchar* gcr_certificate_get_serial_number_hex (GcrCertificate *self);
+
+guchar* gcr_certificate_get_fingerprint (GcrCertificate *self,
+ GChecksumType type,
+ gsize *n_length);
+
+gchar* gcr_certificate_get_fingerprint_hex (GcrCertificate *self,
+ GChecksumType type);
+
+#endif /* __GCR_CERTIFICATE_H__ */
diff --git a/gcr/gcr-library.c b/gcr/gcr-library.c
index 12d5ca59..55439897 100644
--- a/gcr/gcr-library.c
+++ b/gcr/gcr-library.c
@@ -180,7 +180,8 @@ _gcr_initialize (void)
/* Only initialize libgcrypt if it hasn't already been initialized */
if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) {
- gcry_control (GCRYCTL_SET_THREAD_CBS, &glib_thread_cbs);
+ if (g_thread_supported())
+ gcry_control (GCRYCTL_SET_THREAD_CBS, &glib_thread_cbs);
gcry_check_version (LIBGCRYPT_VERSION);
gcry_set_log_handler (log_handler, NULL);
gcry_set_outofcore_handler (no_mem_handler, NULL);
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index d508ec97..f9c97f96 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -553,7 +553,7 @@ parse_der_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data)
if (!egg_symkey_read_cipher (scheme, password, -1, params, n_params, &cih))
break;
- crypted = egg_asn1_read_value (asn, "encryptedData", &n_crypted, egg_secure_realloc);
+ crypted = egg_asn1_read_value (asn, "encryptedData", &n_crypted, (EggAllocator)egg_secure_realloc);
if (!crypted)
break;
@@ -892,7 +892,7 @@ handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data)
}
crypted = egg_asn1_read_value (asn, "encryptedContentInfo.encryptedContent",
- &n_crypted, egg_secure_realloc);
+ &n_crypted, (EggAllocator)egg_secure_realloc);
if (!crypted)
goto done;
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index 4a055852..6bc48b35 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -1,10 +1,9 @@
# Test files should be listed in order they need to run
UNIT_AUTO = \
+ unit-test-certificate.c \
unit-test-parser.c
-UNIT_PROMPT =
-
UNIT_LIBS = \
$(top_builddir)/gcr/libgcr.la \
$(top_builddir)/egg/libegg.la \
@@ -15,3 +14,22 @@ UNIT_FLAGS = \
-DGCR_API_SUBJECT_TO_CHANGE
include $(top_srcdir)/tests/gtest.make
+
+# ------------------------------------------------------------------
+
+noinst_PROGRAMS += \
+ ui-test-details
+
+ui_test_details_SOURCES = \
+ ui-test-details.c
+
+ui_test_details_CFLAGS = \
+ -DGCR_API_SUBJECT_TO_CHANGE \
+ $(GTK_CFLAGS)
+
+ui_test_details_LDADD = \
+ $(top_builddir)/gcr/libgcr.la \
+ $(GTK_LIBS) \
+ $(LIBGCRYPT_LIBS) \
+ $(LIBTASN1_LIBS)
+ \ No newline at end of file
diff --git a/gcr/tests/ui-test-details.c b/gcr/tests/ui-test-details.c
new file mode 100644
index 00000000..828b1075
--- /dev/null
+++ b/gcr/tests/ui-test-details.c
@@ -0,0 +1,45 @@
+
+#include "config.h"
+
+#include "gcr-certificate-details-widget.h"
+
+#include <gtk/gtk.h>
+
+static void
+test_details (void)
+{
+ GcrCertificateDetailsWidget *details;
+ GcrCertificate *certificate;
+ GtkDialog *dialog;
+ guchar *data;
+ gsize n_data;
+
+ if (!g_file_get_contents ("test-data/der-certificate.crt", (gchar**)&data, &n_data, NULL))
+ g_assert_not_reached ();
+
+ certificate = gcr_certificate_new_for_data (data, n_data);
+ g_assert (certificate);
+ g_free (data);
+
+ dialog = GTK_DIALOG (gtk_dialog_new ());
+ g_object_ref_sink (dialog);
+
+ details = gcr_certificate_details_widget_new (certificate);
+ gtk_widget_show (GTK_WIDGET (details));
+ gtk_container_add (GTK_CONTAINER (dialog->vbox), GTK_WIDGET (details));
+
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 400);
+ gtk_dialog_run (dialog);
+
+ g_object_unref (dialog);
+ g_object_unref (certificate);
+ g_object_unref (details);
+}
+
+int
+main(int argc, char *argv[])
+{
+ gtk_init (&argc, &argv);
+ test_details ();
+ return 0;
+}
diff --git a/gcr/tests/unit-test-certificate.c b/gcr/tests/unit-test-certificate.c
new file mode 100644
index 00000000..452dacbf
--- /dev/null
+++ b/gcr/tests/unit-test-certificate.c
@@ -0,0 +1,139 @@
+
+#include "config.h"
+#include "run-auto-test.h"
+
+#include "gcr-certificate.h"
+
+#include <glib.h>
+
+#include <string.h>
+
+static GcrCertificate *certificate = NULL;
+
+DEFINE_SETUP(certificate)
+{
+ GError *err = NULL;
+ gchar *contents;
+ gsize n_contents;
+
+ if (!g_file_get_contents ("test-data/der-certificate.crt", &contents, &n_contents, &err)) {
+ g_warning ("couldn't read test-data/test-certificate-1.der: %s", err->message);
+ return;
+ }
+
+ certificate = gcr_certificate_new_for_data ((const guchar*)contents, n_contents);
+ g_assert (certificate);
+ g_free (contents);
+}
+
+DEFINE_TEARDOWN(certificate)
+{
+ if (certificate)
+ g_object_unref (certificate);
+ certificate = NULL;
+}
+
+DEFINE_TEST(issuer_cn)
+{
+ gchar *cn = gcr_certificate_get_issuer_cn (certificate);
+ g_assert (cn);
+ g_assert_cmpstr (cn, ==, "http://www.valicert.com/");
+ g_free (cn);
+}
+
+DEFINE_TEST(issuer_dn)
+{
+ gchar *dn = gcr_certificate_get_issuer_dn (certificate);
+ g_assert (dn);
+ g_assert_cmpstr (dn, ==, "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 3 Policy Validation Authority, CN=http://www.valicert.com/, EMAIL=info@valicert.com");
+ g_free (dn);
+}
+
+DEFINE_TEST(issuer_part)
+{
+ gchar *part = gcr_certificate_get_issuer_part (certificate, "l");
+ g_assert (part);
+ g_assert_cmpstr (part, ==, "ValiCert Validation Network");
+ g_free (part);
+}
+
+DEFINE_TEST(subject_cn)
+{
+ gchar *cn = gcr_certificate_get_subject_cn (certificate);
+ g_assert (cn);
+ g_assert_cmpstr (cn, ==, "http://www.valicert.com/");
+ g_free (cn);
+}
+
+DEFINE_TEST(subject_dn)
+{
+ gchar *dn = gcr_certificate_get_subject_dn (certificate);
+ g_assert (dn);
+ g_assert_cmpstr (dn, ==, "L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 3 Policy Validation Authority, CN=http://www.valicert.com/, EMAIL=info@valicert.com");
+ g_free (dn);
+}
+
+DEFINE_TEST(subject_part)
+{
+ gchar *part = gcr_certificate_get_subject_part (certificate, "OU");
+ g_assert (part);
+ g_assert_cmpstr (part, ==, "ValiCert Class 3 Policy Validation Authority");
+ g_free (part);
+}
+
+DEFINE_TEST(issued_date)
+{
+ GDate *date = gcr_certificate_get_issued_date (certificate);
+ g_assert (date);
+ g_assert_cmpuint (g_date_get_year (date), ==, 1999);
+ g_assert_cmpuint (g_date_get_month (date), ==, 6);
+ g_assert_cmpuint (g_date_get_day (date), ==, 26);
+ g_date_free (date);
+}
+
+DEFINE_TEST(expiry_date)
+{
+ GDate *date = gcr_certificate_get_expiry_date (certificate);
+ g_assert (date);
+ g_assert_cmpuint (g_date_get_year (date), ==, 2019);
+ g_assert_cmpuint (g_date_get_month (date), ==, 6);
+ g_assert_cmpuint (g_date_get_day (date), ==, 26);
+ g_date_free (date);
+}
+
+DEFINE_TEST(serial_number)
+{
+ gsize n_serial;
+ guchar *serial;
+ gchar *hex;
+
+ serial = gcr_certificate_get_serial_number (certificate, &n_serial);
+ g_assert (serial);
+ g_assert_cmpuint (n_serial, ==, 1);
+ g_assert (memcmp (serial, "\1", n_serial) == 0);
+ g_free (serial);
+
+ hex = gcr_certificate_get_serial_number_hex (certificate);
+ g_assert (hex);
+ g_assert_cmpstr (hex, ==, "01");
+ g_free (hex);
+}
+
+DEFINE_TEST(fingerprint)
+{
+ gsize n_print;
+ guchar *print = gcr_certificate_get_fingerprint (certificate, G_CHECKSUM_MD5, &n_print);
+ g_assert (print);
+ g_assert_cmpuint (n_print, ==, g_checksum_type_get_length (G_CHECKSUM_MD5));
+ g_assert (memcmp (print, "\xa2\x6f\x53\xb7\xee\x40\xdb\x4a\x68\xe7\xfa\x18\xd9\x10\x4b\x72", n_print) == 0);
+ g_free (print);
+}
+
+DEFINE_TEST(fingerprint_hex)
+{
+ gchar *print = gcr_certificate_get_fingerprint_hex (certificate, G_CHECKSUM_MD5);
+ g_assert (print);
+ g_assert_cmpstr (print, ==, "a26f53b7ee40db4a68e7fa18d9104b72");
+ g_free (print);
+}
+
diff --git a/po/ChangeLog b/po/ChangeLog
index a491ec6e..26caef32 100644
--- a/po/ChangeLog
+++ b/po/ChangeLog
@@ -1,3 +1,10 @@
+2009-01-27 Stef Walter <stef@memberwebs.com>
+
+ * POTFILES.in: Added egg/egg-asn1.c
+ gcr/gcr-certificate-basics-widget.c
+ gcr/gcr-certificate-basics-widget.glade
+ gcr/gcr-certificate-details-widget.c
+
2009-01-27 Christian Kirbach <Christian.Kirbach@googlemail.com>
* de.po: Updated German translation.
diff --git a/po/POTFILES.in b/po/POTFILES.in
index bad908b3..fdf2b0b8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -15,6 +15,10 @@ daemon/pkcs11/gkr-pkcs11-auth.c
daemon/pkix/gkr-pkix-parser.c
daemon/pkix/gkr-pkix-serialize.c
daemon/ui/gkr-ask-tool.c
+egg/egg-asn1.c
+gcr/gcr-certificate-basics-widget.c
+gcr/gcr-certificate-basics-widget.glade
+gcr/gcr-certificate-details-widget.c
gcr/gcr-import-dialog.glade
gcr/gcr-importer.c
gcr/gcr-parser.c
@@ -22,4 +26,3 @@ gp11/gp11-misc.c
library/gnome-keyring-utils.c
pkcs11/gck/gck-certificate.c
pkcs11/ssh-store/gck-ssh-private-key.c
-
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index f7d718cd..fcfbed80 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -1,2 +1,3 @@
daemon/gnome-keyring-daemon.desktop.in
gcr/gcr-import-dialog.ui
+gcr/gcr-certificate-basics-widget.ui