[gnome-keyring] gcr: Add Renderer for Gnupg keys



commit 95296d139236df1cb3d916b89f30a1bc31572c1f
Author: Stef Walter <stefw collabora co uk>
Date:   Tue Sep 20 11:56:20 2011 +0200

    gcr: Add Renderer for Gnupg keys
    
     * Uses the colon style GcrRecord data to render gnupg key.

 docs/reference/gcr/gcr-sections.txt |   10 +
 gcr/Makefile.am                     |    6 +
 gcr/gcr-gnupg-collection.c          |    5 +-
 gcr/gcr-gnupg-key.c                 |  127 +-----
 gcr/gcr-gnupg-key.h                 |    4 -
 gcr/gcr-gnupg-records.c             |  244 ++++++++++
 gcr/gcr-gnupg-records.h             |   52 +++
 gcr/gcr-gnupg-renderer.c            |  852 +++++++++++++++++++++++++++++++++++
 gcr/gcr-gnupg-renderer.h            |   77 ++++
 gcr/gcr-openpgp.c                   |   55 ++-
 gcr/gcr-openpgp.h                   |    8 +
 gcr/gcr-parser.c                    |   20 +-
 gcr/gcr-record.c                    |  150 ++++---
 gcr/gcr-record.h                    |   33 +-
 gcr/gcr-renderer.c                  |    2 +
 gcr/gcr-types.h                     |   10 +
 gcr/tests/test-gnupg-collection.c   |    2 +-
 gcr/tests/test-gnupg-key.c          |    3 +-
 gcr/tests/test-openpgp.c            |   16 +
 gcr/tests/test-record.c             |    4 +-
 po/POTFILES.in                      |    1 +
 21 files changed, 1439 insertions(+), 242 deletions(-)
---
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index a881547..08e7dd0 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -611,4 +611,14 @@ GcrCallbackOutputFunc
 GcrCallbackOutputStream
 GcrCallbackOutputStreamClass
 GcrOpenpgpParseFlags
+GCR_GNUPG_RENDERER
+GCR_GNUPG_RENDERER_CLASS
+GCR_GNUPG_RENDERER_GET_CLASS
+GCR_IS_GNUPG_RENDERER
+GCR_IS_GNUPG_RENDERER_CLASS
+GCR_TYPE_GNUPG_RENDERER
+GcrGnupgRenderer
+GcrGnupgRendererClass
+GcrGnupgRendererPrivate
+GcrOpenpgpAlgo
 </SECTION>
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index 48c6243..f4f86e0 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -99,6 +99,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-gnupg-collection.c gcr-gnupg-collection.h \
 	gcr-gnupg-key.c gcr-gnupg-key.h \
 	gcr-gnupg-process.c gcr-gnupg-process.h \
+	gcr-gnupg-records.c gcr-gnupg-records.h \
 	gcr-gnupg-util.c gcr-gnupg-util.h \
 	gcr-library.c gcr-library.h \
 	gcr-internal.h \
@@ -126,10 +127,13 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-certificate-widget.c gcr-certificate-widget.h \
 	gcr-collection-model.c gcr-collection-model.h \
 	gcr-combo-selector.c gcr-combo-selector.h \
+	gcr-debug.c gcr-debug.h \
 	gcr-display-scrolled.c gcr-display-scrolled.h \
 	gcr-display-view.c gcr-display-view.h \
 	gcr-failure-renderer.c gcr-failure-renderer.h \
 	gcr-fingerprint.c gcr-fingerprint.h \
+	gcr-gnupg-renderer.c gcr-gnupg-renderer.h \
+	gcr-gnupg-records.c gcr-gnupg-records.h \
 	gcr-icons.c gcr-icons.h \
 	gcr-import-dialog.c gcr-import-dialog.h \
 	gcr-importer.c gcr-importer.h  \
@@ -138,6 +142,8 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-list-selector.c gcr-list-selector.h gcr-list-selector-private.h \
 	gcr-live-search.c gcr-live-search.h \
 	gcr-memory.c \
+	gcr-memory-icon.c gcr-memory-icon.h \
+	gcr-record.c gcr-record.h \
 	gcr-renderer.c gcr-renderer.h \
 	gcr-tree-selector.c gcr-tree-selector.h \
 	gcr-unlock-options.h \
diff --git a/gcr/gcr-gnupg-collection.c b/gcr/gcr-gnupg-collection.c
index bbcaf20..8e81204 100644
--- a/gcr/gcr-gnupg-collection.c
+++ b/gcr/gcr-gnupg-collection.c
@@ -30,6 +30,7 @@
 #include "gcr-gnupg-collection.h"
 #include "gcr-gnupg-key.h"
 #include "gcr-gnupg-process.h"
+#include "gcr-gnupg-records.h"
 #include "gcr-gnupg-util.h"
 #include "gcr-internal.h"
 #include "gcr-record.h"
@@ -305,7 +306,7 @@ process_records_as_public_key (GcrGnupgCollectionLoad *load, GPtrArray *records,
 	guint i;
 
 	/* Add in any attributes we have loaded */
-	fingerprint = _gcr_gnupg_key_get_fingerprint_for_records (records);
+	fingerprint = _gcr_gnupg_records_get_fingerprint (records);
 	if (fingerprint && load->attributes)
 		attr_records = g_hash_table_lookup (load->attributes, fingerprint);
 	if (attr_records) {
@@ -377,7 +378,7 @@ process_records_as_key (GcrGnupgCollectionLoad *load)
 	records = load->records;
 	load->records = g_ptr_array_new_with_free_func (_gcr_record_free);
 
-	keyid = _gcr_gnupg_key_get_keyid_for_records (records);
+	keyid = _gcr_gnupg_records_get_keyid (records);
 	if (keyid) {
 		schema = _gcr_record_get_schema (records->pdata[0]);
 
diff --git a/gcr/gcr-gnupg-key.c b/gcr/gcr-gnupg-key.c
index 7f269f1..1d1bc42 100644
--- a/gcr/gcr-gnupg-key.c
+++ b/gcr/gcr-gnupg-key.c
@@ -22,6 +22,7 @@
 #include "config.h"
 
 #include "gcr-gnupg-key.h"
+#include "gcr-gnupg-records.h"
 #include "gcr-record.h"
 #include "gcr-memory-icon.h"
 
@@ -143,10 +144,10 @@ calculate_name (GcrGnupgKey *self)
 {
 	GcrRecord* record;
 
-	record = _gcr_record_find (self->pv->public_records, GCR_RECORD_SCHEMA_UID);
+	record = _gcr_records_find (self->pv->public_records, GCR_RECORD_SCHEMA_UID);
 	g_return_val_if_fail (record, NULL);
 
-	return _gcr_record_get_string (record, GCR_RECORD_UID_NAME);
+	return _gcr_record_get_string (record, GCR_RECORD_UID_USERID);
 }
 
 static gchar *
@@ -172,23 +173,6 @@ calculate_markup (GcrGnupgKey *self)
 	return markup;
 }
 
-static const gchar *
-calculate_short_keyid (GcrGnupgKey *self)
-{
-	const gchar *keyid;
-	gsize length;
-
-	keyid = _gcr_gnupg_key_get_keyid_for_records (self->pv->public_records);
-	if (keyid == NULL)
-		return NULL;
-
-	length = strlen (keyid);
-	if (length > 8)
-		keyid += (length - 8);
-
-	return keyid;
-}
-
 static void
 _gcr_gnupg_key_init (GcrGnupgKey *self)
 {
@@ -253,7 +237,7 @@ _gcr_gnupg_key_get_property (GObject *obj, guint prop_id, GValue *value,
 		g_value_take_string (value, calculate_markup (self));
 		break;
 	case PROP_SHORT_KEYID:
-		g_value_set_string (value, calculate_short_keyid (self));
+		g_value_set_string (value, _gcr_gnupg_records_get_short_keyid (self->pv->public_records));
 		break;
 	case PROP_ICON:
 		g_value_set_object (value, _gcr_gnupg_key_get_icon (self));
@@ -403,8 +387,8 @@ _gcr_gnupg_key_set_public_records (GcrGnupgKey *self, GPtrArray *records)
 
 	/* Check that it matches previous */
 	if (self->pv->public_records) {
-		const gchar *old_keyid = _gcr_gnupg_key_get_keyid_for_records (self->pv->public_records);
-		const gchar *new_keyid = _gcr_gnupg_key_get_keyid_for_records (records);
+		const gchar *old_keyid = _gcr_gnupg_records_get_keyid (self->pv->public_records);
+		const gchar *new_keyid = _gcr_gnupg_records_get_keyid (records);
 
 		if (g_strcmp0 (old_keyid, new_keyid) != 0) {
 			g_warning ("it is an error to change a gnupg key so that the "
@@ -458,8 +442,8 @@ _gcr_gnupg_key_set_secret_records (GcrGnupgKey *self, GPtrArray *records)
 
 	/* Check that it matches public key */
 	if (self->pv->public_records && records) {
-		const gchar *pub_keyid = _gcr_gnupg_key_get_keyid_for_records (self->pv->public_records);
-		const gchar *sec_keyid = _gcr_gnupg_key_get_keyid_for_records (records);
+		const gchar *pub_keyid = _gcr_gnupg_records_get_keyid (self->pv->public_records);
+		const gchar *sec_keyid = _gcr_gnupg_records_get_keyid (records);
 
 		if (g_strcmp0 (pub_keyid, sec_keyid) != 0) {
 			g_warning ("it is an error to create a gnupg key so that the "
@@ -493,98 +477,7 @@ const gchar*
 _gcr_gnupg_key_get_keyid (GcrGnupgKey *self)
 {
 	g_return_val_if_fail (GCR_IS_GNUPG_KEY (self), NULL);
-	return _gcr_gnupg_key_get_keyid_for_records (self->pv->public_records);
-}
-
-/**
- * _gcr_gnupg_key_get_keyid_for_records:
- * @records: Array of GcrRecord*
- *
- * Get the keyid for some record data.
- *
- * Returns: (transfer none): The keyid.
- */
-const gchar*
-_gcr_gnupg_key_get_keyid_for_records (GPtrArray *records)
-{
-	GcrRecord *record;
-
-	record = _gcr_record_find (records, GCR_RECORD_SCHEMA_PUB);
-	if (record != NULL)
-		return _gcr_record_get_raw (record, GCR_RECORD_KEY_KEYID);
-	record = _gcr_record_find (records, GCR_RECORD_SCHEMA_SEC);
-	if (record != NULL)
-		return _gcr_record_get_raw (record, GCR_RECORD_KEY_KEYID);
-	return NULL;
-}
-
-/**
- * _gcr_gnupg_key_get_fingerprint_for_records:
- * @records: Array of GcrRecord*
- *
- * Get the fingerprint field for some record data:
- *
- * Returns: (transfer none): The fingerprint.
- */
-const gchar*
-_gcr_gnupg_key_get_fingerprint_for_records (GPtrArray *records)
-{
-	GcrRecord *record;
-
-	record = _gcr_record_find (records, GCR_RECORD_SCHEMA_FPR);
-	if (record != NULL)
-		return _gcr_record_get_raw (record, GCR_RECORD_FPR_FINGERPRINT);
-	return NULL;
-}
-
-#define TYPE_IMAGE 0x01
-#define IMAGE_HEADER_LEN 0x10
-#define IMAGE_JPEG_SIG "\x10\x00\x01\x01"
-#define IMAGE_JPEG_SIG_LEN 4
-
-static GIcon*
-load_user_attribute_icon (GcrGnupgKey *self)
-{
-	GcrRecord *record;
-	guchar *data;
-	gsize n_data;
-	guint type;
-	guint i;
-
-	for (i = 0; i < self->pv->public_records->len; i++) {
-		record = self->pv->public_records->pdata[i];
-		if (GCR_RECORD_SCHEMA_XA1 != _gcr_record_get_schema (record))
-			continue;
-		if (!_gcr_record_get_uint (record, GCR_RECORD_XA1_TYPE, &type))
-			continue;
-		if (type != TYPE_IMAGE)
-			continue;
-
-		/* TODO: Validity? */
-
-		data = _gcr_record_get_base64 (record, GCR_RECORD_XA1_DATA, &n_data);
-		g_return_val_if_fail (data != NULL, NULL);
-
-		/* Header is 16 bytes long */
-		if (n_data <= IMAGE_HEADER_LEN) {
-			g_free (data);
-			continue;
-		}
-
-		/* These are the header bytes. See gnupg doc/DETAILS */
-		g_assert (IMAGE_JPEG_SIG_LEN < IMAGE_HEADER_LEN);
-		if (memcmp (data, IMAGE_JPEG_SIG, IMAGE_JPEG_SIG_LEN) != 0) {
-			g_free (data);
-			continue;
-		}
-
-		/* We have a valid header */
-		return G_ICON (_gcr_memory_icon_new_full ("image/jpeg", data,
-		                                          n_data, IMAGE_HEADER_LEN,
-		                                          g_free));
-	}
-
-	return NULL;
+	return _gcr_gnupg_records_get_keyid (self->pv->public_records);
 }
 
 /**
@@ -601,7 +494,7 @@ _gcr_gnupg_key_get_icon (GcrGnupgKey *self)
 	g_return_val_if_fail (GCR_IS_GNUPG_KEY (self), NULL);
 
 	if (self->pv->icon == NULL) {
-		self->pv->icon = load_user_attribute_icon (self);
+		self->pv->icon = _gcr_gnupg_records_get_icon (self->pv->public_records);
 		if (self->pv->icon == NULL) {
 			if (self->pv->secret_records)
 				self->pv->icon = g_themed_icon_new ("gcr-key-pair");
diff --git a/gcr/gcr-gnupg-key.h b/gcr/gcr-gnupg-key.h
index 2564edb..e442632 100644
--- a/gcr/gcr-gnupg-key.h
+++ b/gcr/gcr-gnupg-key.h
@@ -77,10 +77,6 @@ void                _gcr_gnupg_key_set_secret_records            (GcrGnupgKey *s
 
 GIcon*              _gcr_gnupg_key_get_icon                      (GcrGnupgKey *self);
 
-const gchar*        _gcr_gnupg_key_get_keyid_for_records         (GPtrArray *records);
-
-const gchar*        _gcr_gnupg_key_get_fingerprint_for_records   (GPtrArray *records);
-
 G_END_DECLS
 
 #endif /* __GCR_GNUPG_KEY_H__ */
diff --git a/gcr/gcr-gnupg-records.c b/gcr/gcr-gnupg-records.c
new file mode 100644
index 0000000..1389350
--- /dev/null
+++ b/gcr/gcr-gnupg-records.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gcr-gnupg-records.h"
+#include "gcr-record.h"
+#include "gcr-memory-icon.h"
+
+#include "gck/gck.h"
+
+#include <glib/gi18n-lib.h>
+
+/* Copied from GPGME */
+gboolean
+_gcr_gnupg_records_parse_user_id (const gchar *user_id,
+                                  gchar **rname,
+                                  gchar **remail,
+                                  gchar **rcomment)
+{
+	gchar *src, *tail, *x;
+	int in_name = 0;
+	int in_email = 0;
+	int in_comment = 0;
+	gboolean anything;
+	const gchar *name = NULL;
+	const gchar *email = NULL;
+	const gchar *comment = NULL;
+
+	x = tail = src = g_strdup (user_id);
+
+	while (*src) {
+		if (in_email) {
+			/* Not legal but anyway.  */
+			if (*src == '<')
+				in_email++;
+			else if (*src == '>') {
+				if (!--in_email && !email) {
+					email = tail;
+					*src = 0;
+					tail = src + 1;
+				}
+			}
+		} else if (in_comment) {
+			if (*src == '(')
+				in_comment++;
+			else if (*src == ')') {
+				if (!--in_comment && !comment) {
+					comment = tail;
+					*src = 0;
+					tail = src + 1;
+				}
+			}
+		} else if (*src == '<') {
+			if (in_name) {
+				if (!name) {
+					name = tail;
+					*src = 0;
+					tail = src + 1;
+				}
+				in_name = 0;
+			} else
+				tail = src + 1;
+
+			in_email = 1;
+		} else if (*src == '(') {
+			if (in_name) {
+				if (!name) {
+					name = tail;
+					*src = 0;
+					tail = src + 1;
+				}
+				in_name = 0;
+			}
+			in_comment = 1;
+		} else if (!in_name && *src != ' ' && *src != '\t') {
+			in_name = 1;
+		}
+		src++;
+	}
+
+	if (in_name) {
+		if (!name) {
+			name = tail;
+			*src = 0;
+			tail = src + 1;
+		}
+	}
+
+	anything = FALSE;
+
+	if (rname) {
+		*rname = g_strdup (name);
+		if (name) {
+			g_strstrip (*rname);
+			anything = TRUE;
+		}
+	}
+
+	if (remail) {
+		*remail = g_strdup (email);
+		if (email) {
+			g_strstrip (*remail);
+			anything = TRUE;
+		}
+	}
+
+	if (rcomment) {
+		*rcomment = g_strdup (comment);
+		if (comment) {
+			g_strstrip (*rcomment);
+			anything = TRUE;
+		}
+	}
+
+	g_free (x);
+	return anything;
+}
+
+const gchar *
+_gcr_gnupg_records_get_keyid (GPtrArray *records)
+{
+	GcrRecord *record;
+
+	record = _gcr_records_find (records, GCR_RECORD_SCHEMA_PUB);
+	if (record != NULL)
+		return _gcr_record_get_raw (record, GCR_RECORD_KEY_KEYID);
+	record = _gcr_records_find (records, GCR_RECORD_SCHEMA_SEC);
+	if (record != NULL)
+		return _gcr_record_get_raw (record, GCR_RECORD_KEY_KEYID);
+	return NULL;
+}
+
+const gchar *
+_gcr_gnupg_records_get_short_keyid (GPtrArray *records)
+{
+	const gchar *keyid;
+	gsize length;
+
+	keyid = _gcr_gnupg_records_get_keyid (records);
+	if (keyid == NULL)
+		return NULL;
+
+	length = strlen (keyid);
+	if (length > 8)
+		keyid += (length - 8);
+
+	return keyid;
+}
+
+gchar *
+_gcr_gnupg_records_get_user_id (GPtrArray *records)
+{
+	GcrRecord *record;
+
+	record = _gcr_records_find (records, GCR_RECORD_SCHEMA_UID);
+	if (record != NULL)
+		return _gcr_record_get_string (record, GCR_RECORD_UID_USERID);
+	return NULL;
+}
+
+const gchar *
+_gcr_gnupg_records_get_fingerprint (GPtrArray *records)
+{
+	GcrRecord *record;
+
+	record = _gcr_records_find (records, GCR_RECORD_SCHEMA_FPR);
+	if (record != NULL)
+		return _gcr_record_get_raw (record, GCR_RECORD_FPR_FINGERPRINT);
+	return NULL;
+}
+
+#define TYPE_IMAGE 0x01
+#define IMAGE_HEADER_LEN 0x10
+#define IMAGE_JPEG_SIG "\x10\x00\x01\x01"
+#define IMAGE_JPEG_SIG_LEN 4
+
+GIcon *
+_gcr_gnupg_records_get_icon (GPtrArray *records)
+{
+	GcrRecord *record;
+	guchar *data;
+	gsize n_data;
+	guint type;
+	guint i;
+
+	for (i = 0; i < records->len; i++) {
+		record = records->pdata[i];
+		if (GCR_RECORD_SCHEMA_XA1 != _gcr_record_get_schema (record))
+			continue;
+		if (!_gcr_record_get_uint (record, GCR_RECORD_XA1_TYPE, &type))
+			continue;
+		if (type != TYPE_IMAGE)
+			continue;
+
+		/* TODO: Validity? */
+
+		data = _gcr_record_get_base64 (record, GCR_RECORD_XA1_DATA, &n_data);
+		g_return_val_if_fail (data != NULL, NULL);
+
+		/* Header is 16 bytes long */
+		if (n_data <= IMAGE_HEADER_LEN) {
+			g_free (data);
+			continue;
+		}
+
+		/* These are the header bytes. See gnupg doc/DETAILS */
+		g_assert (IMAGE_JPEG_SIG_LEN < IMAGE_HEADER_LEN);
+		if (memcmp (data, IMAGE_JPEG_SIG, IMAGE_JPEG_SIG_LEN) != 0) {
+			g_free (data);
+			continue;
+		}
+
+		/* We have a valid header */
+		return G_ICON (_gcr_memory_icon_new_full ("image/jpeg", data,
+		                                          n_data, IMAGE_HEADER_LEN,
+		                                          g_free));
+	}
+
+	if (_gcr_records_find (records, GCR_RECORD_SCHEMA_SEC))
+		return g_themed_icon_new ("gcr-key-pair");
+	else
+		return g_themed_icon_new ("gcr-key");
+
+	return NULL;
+}
diff --git a/gcr/gcr-gnupg-records.h b/gcr/gcr-gnupg-records.h
new file mode 100644
index 0000000..4a61ff9
--- /dev/null
+++ b/gcr/gcr-gnupg-records.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#if !defined (__GCR_H_INSIDE__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> can be included directly."
+#endif
+
+#ifndef GCR_GNUPG_RECORDS_H
+#define GCR_GNUPG_RECORDS_H
+
+#include <gio/gio.h>
+
+#include "gcr-record.h"
+
+G_BEGIN_DECLS
+
+const gchar *       _gcr_gnupg_records_get_keyid             (GPtrArray *records);
+
+const gchar *       _gcr_gnupg_records_get_short_keyid       (GPtrArray *records);
+
+const gchar *       _gcr_gnupg_records_get_fingerprint       (GPtrArray *records);
+
+gchar *             _gcr_gnupg_records_get_user_id           (GPtrArray *records);
+
+gboolean            _gcr_gnupg_records_parse_user_id         (const gchar *user_id,
+                                                              gchar **name,
+                                                              gchar **email,
+                                                              gchar **comment);
+
+GIcon *             _gcr_gnupg_records_get_icon              (GPtrArray *records);
+
+G_END_DECLS
+
+#endif /* __GCR_GNUPG_RECORD_H__ */
diff --git a/gcr/gcr-gnupg-renderer.c b/gcr/gcr-gnupg-renderer.c
new file mode 100644
index 0000000..c0b6495
--- /dev/null
+++ b/gcr/gcr-gnupg-renderer.c
@@ -0,0 +1,852 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gcr-display-view.h"
+#include "gcr-icons.h"
+#include "gcr-gnupg-records.h"
+#include "gcr-gnupg-renderer.h"
+#include "gcr-openpgp.h"
+#include "gcr-simple-certificate.h"
+#include "gcr-renderer.h"
+#include "gcr-types.h"
+
+#include "gck/gck.h"
+
+#include "egg/egg-hex.h"
+
+#include <gdk/gdk.h>
+#include <glib/gi18n-lib.h>
+
+#include <stdlib.h>
+
+enum {
+	PROP_0,
+	PROP_RECORDS,
+	PROP_LABEL,
+	PROP_ATTRIBUTES
+};
+
+struct _GcrGnupgRendererPrivate {
+	GPtrArray *records;
+	GckAttributes *attrs;
+	gchar *label;
+};
+
+static void _gcr_gnupg_renderer_iface_init (GcrRendererIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrGnupgRenderer, _gcr_gnupg_renderer, G_TYPE_OBJECT,
+	G_IMPLEMENT_INTERFACE (GCR_TYPE_RENDERER, _gcr_gnupg_renderer_iface_init);
+);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static gchar *
+calculate_label (GcrGnupgRenderer *self)
+{
+	gchar *userid;
+	gchar *label;
+
+	if (self->pv->attrs) {
+		if (gck_attributes_find_string (self->pv->attrs, CKA_LABEL, &label))
+			return label;
+	}
+
+	userid = _gcr_gnupg_records_get_user_id (self->pv->records);
+	if (userid != NULL) {
+		if (!_gcr_gnupg_records_parse_user_id (userid, &label, NULL, NULL))
+			label = NULL;
+	}
+
+	if (label != NULL)
+		return label;
+
+	if (self->pv->label)
+		return g_strdup (self->pv->label);
+
+	return g_strdup (_("PGP Key"));
+}
+
+static void
+_gcr_gnupg_renderer_init (GcrGnupgRenderer *self)
+{
+	self->pv = (G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_GNUPG_RENDERER,
+	                                         GcrGnupgRendererPrivate));
+}
+
+static void
+_gcr_gnupg_renderer_finalize (GObject *obj)
+{
+	GcrGnupgRenderer *self = GCR_GNUPG_RENDERER (obj);
+
+	gck_attributes_unref (self->pv->attrs);
+	g_free (self->pv->label);
+	if (self->pv->records)
+		g_ptr_array_unref (self->pv->records);
+
+	G_OBJECT_CLASS (_gcr_gnupg_renderer_parent_class)->finalize (obj);
+}
+
+static void
+_gcr_gnupg_renderer_set_property (GObject *obj,
+                                  guint prop_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+	GcrGnupgRenderer *self = GCR_GNUPG_RENDERER (obj);
+
+	switch (prop_id) {
+	case PROP_RECORDS:
+		_gcr_gnupg_renderer_set_records (self, g_value_get_boxed (value));
+		break;
+	case PROP_LABEL:
+		g_free (self->pv->label);
+		self->pv->label = g_value_dup_string (value);
+		g_object_notify (obj, "label");
+		gcr_renderer_emit_data_changed (GCR_RENDERER (self));
+		break;
+	case PROP_ATTRIBUTES:
+		_gcr_gnupg_renderer_set_attributes (self, g_value_get_boxed (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gcr_gnupg_renderer_get_property (GObject *obj,
+                                  guint prop_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+	GcrGnupgRenderer *self = GCR_GNUPG_RENDERER (obj);
+
+	switch (prop_id) {
+	case PROP_RECORDS:
+		g_value_set_object (value, self->pv->records);
+		break;
+	case PROP_LABEL:
+		g_value_take_string (value, calculate_label (self));
+		break;
+	case PROP_ATTRIBUTES:
+		g_value_set_boxed (value, self->pv->attrs);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gcr_gnupg_renderer_class_init (GcrGnupgRendererClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GckAttributes *registered;
+
+	_gcr_gnupg_renderer_parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GcrGnupgRendererPrivate));
+
+	gobject_class->finalize = _gcr_gnupg_renderer_finalize;
+	gobject_class->set_property = _gcr_gnupg_renderer_set_property;
+	gobject_class->get_property = _gcr_gnupg_renderer_get_property;
+
+	g_object_class_install_property (gobject_class, PROP_RECORDS,
+	           g_param_spec_boxed ("records", "Records", "Gnupg records to display",
+	                               G_TYPE_PTR_ARRAY, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_ATTRIBUTES,
+	           g_param_spec_boxed ("attributes", "Attributes", "Certificate pkcs11 attributes",
+	                               GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE));
+
+	g_object_class_install_property (gobject_class, PROP_LABEL,
+	           g_param_spec_string ("label", "Label", "Certificate Label",
+	                                "", G_PARAM_READWRITE));
+
+	/* Register this as a renderer which can be loaded */
+	registered = gck_attributes_new ();
+	gck_attributes_add_ulong (registered, CKA_CLASS, CKO_GCR_GNUPG_RECORDS);
+	gcr_renderer_register (GCR_TYPE_GNUPG_RENDERER, registered);
+	gck_attributes_unref (registered);
+}
+
+static const gchar *
+name_for_algo (guint algo)
+{
+	switch (algo)
+	{
+	case GCR_OPENPGP_ALGO_RSA:
+	case GCR_OPENPGP_ALGO_RSA_E:
+	case GCR_OPENPGP_ALGO_RSA_S:
+		return _("RSA");
+	case GCR_OPENPGP_ALGO_ELG_E:
+		return _("Elgamal");
+	case GCR_OPENPGP_ALGO_DSA:
+		return _("DSA");
+	default:
+		return NULL;
+	}
+}
+
+static const gchar *
+capability_for_code (gchar code)
+{
+	switch (code) {
+	case 'e': case 'E':
+		return _("Encrypt");
+	case 's': case 'S':
+		return _("Sign");
+	case 'c': case 'C':
+		return _("Certify");
+	case 'a': case 'A':
+		return _("Authenticate");
+	case 'D':
+		return _("Disabled");
+	default:
+		return NULL;
+	}
+}
+
+static gchar *
+capabilities_for_codes (const gchar *codes)
+{
+	const gchar *cap;
+	GString *result;
+	guint i;
+
+	result = g_string_new ("");
+	for (i = 0; codes[i] != 0; i++) {
+		if (result->len)
+			g_string_append_unichar (result, GCR_DISPLAY_VIEW_LINE_BREAK);
+		cap = capability_for_code (codes[i]);
+		if (cap != NULL)
+			g_string_append (result, cap);
+		else
+			g_string_append_c (result, codes[i]);
+	}
+	return g_string_free (result, FALSE);
+}
+
+static const gchar *
+status_for_code (gchar code)
+{
+	switch (code) {
+	case 'o':
+		return _("Unknown");
+	case 'i':
+		return _("Invalid");
+	case 'd':
+		return _("Disabled");
+	case 'r':
+		return _("Revoked");
+	case 'e':
+		return _("Expired");
+	case 'q': case '-':
+		return _("Undefined trust");
+	case 'n':
+		return _("Distrusted");
+	case 'm':
+		return _("Marginally trusted");
+	case 'f':
+		return _("Fully trusted");
+	case 'u':
+		return _("Ultimately trusted");
+	default:
+		return NULL;
+	}
+}
+
+#ifdef TODO
+static const gchar *
+description_for_code (gchar code, gboolean *warning)
+{
+	*warning = FALSE;
+	switch (code) {
+	case 'o':
+		*warning = TRUE;
+		return _("This key has not been verified");
+	case 'i':
+		*warning = TRUE;
+		return _("This key is invalid");
+	case 'd':
+		*warning = TRUE;
+		return _("This key has been disabled");
+	case 'r':
+		*warning = TRUE;
+		return _("This key has been revoked");
+	case 'e':
+		*warning = TRUE;
+		return _("This key has expired");
+	case 'q': case '-':
+		return _("The trust in this key is undefined");
+	case 'n':
+		*warning = TRUE;
+		return _("This key is distrusted");
+	case 'm':
+		return _("Marginally trusted");
+	case 'f':
+		return _("Fully trusted");
+	case 'u':
+		return _("Ultimately trusted");
+	default:
+		return NULL;
+	}
+}
+#endif
+
+static void
+append_key_record (GcrGnupgRenderer *self,
+                   GcrDisplayView *view,
+                   GcrRecord *record,
+                   const gchar *title)
+{
+	GcrRenderer *renderer = GCR_RENDERER (self);
+	const gchar *value;
+	gchar *display;
+	GDateTime *date;
+	gchar code;
+	guint algo;
+	guint bits;
+
+	_gcr_display_view_append_heading (view, renderer, title);
+
+	/* Key ID */
+	value = _gcr_record_get_raw (record, GCR_RECORD_KEY_KEYID);
+	if (value != NULL)
+		_gcr_display_view_append_value (view, renderer, _("Key ID"), value, TRUE);
+
+	/* Algorithm */
+	if (_gcr_record_get_uint (record, GCR_RECORD_KEY_ALGO, &algo)) {
+		display = NULL;
+		value = name_for_algo (algo);
+		if (value == NULL)
+			value = display = g_strdup_printf ("%u", algo);
+		_gcr_display_view_append_value (view, renderer, _("Algorithm"), value, FALSE);
+		g_free (display);
+	}
+
+	/* Key Size */
+	if (_gcr_record_get_uint (record, GCR_RECORD_KEY_BITS, &bits)) {
+		display = g_strdup_printf ("%u", bits);
+		_gcr_display_view_append_value (view, renderer, _("Key Size"), display, FALSE);
+		g_free (display);
+	}
+
+	/* Created */
+	date = _gcr_record_get_date (record, GCR_RECORD_KEY_TIMESTAMP);
+	if (date != NULL) {
+		display = g_date_time_format (date, "%x");
+		_gcr_display_view_append_value (view, renderer, _("Created"), display, FALSE);
+		g_free (display);
+		g_date_time_unref (date);
+	}
+
+	/* Expiry */
+	date = _gcr_record_get_date (record, GCR_RECORD_KEY_EXPIRY);
+	if (date != NULL) {
+		display = g_date_time_format (date, "%x");
+		_gcr_display_view_append_value (view, renderer, _("Expiry"), display, FALSE);
+		g_free (display);
+		g_date_time_unref (date);
+	}
+
+	/* Capabilities */
+	value = _gcr_record_get_raw (record, GCR_RECORD_PUB_CAPS);
+	if (value != NULL) {
+		display = capabilities_for_codes (value);
+		_gcr_display_view_append_value (view, renderer, _("Capabilities"), display, FALSE);
+		g_free (display);
+	}
+
+	/* Owner Trust */
+	code = _gcr_record_get_char (record, GCR_RECORD_KEY_OWNERTRUST);
+	if (code != 0) {
+		display = NULL;
+		value = status_for_code (code);
+		if (value == NULL) {
+			value = display = g_new0 (gchar, 2);
+			display[0] = code;
+		}
+		_gcr_display_view_append_value (view, renderer, _("Owner trust"), value, FALSE);
+		g_free (display);
+	}
+}
+
+static void
+append_uid_record (GcrGnupgRenderer *self,
+                   GcrDisplayView *view,
+                   GcrRecord *record)
+{
+	GcrRenderer *renderer = GCR_RENDERER (self);
+	gchar *userid;
+	gchar *name;
+	gchar *comment;
+	gchar *email;
+	GDateTime *date;
+	gchar *display;
+
+	_gcr_display_view_append_heading (view, renderer, _("User ID"));
+
+	userid = _gcr_record_get_string (record, GCR_RECORD_UID_USERID);
+	if (userid == NULL) {
+		_gcr_display_view_append_value (view, renderer, _("Value"), _("Unknown"), FALSE);
+		return;
+	}
+
+	if (_gcr_gnupg_records_parse_user_id (userid, &name, &email, &comment)) {
+		if (name != NULL)
+			_gcr_display_view_append_value (view, renderer, _("Name"), name, FALSE);
+		g_free (name);
+		if (email != NULL)
+			_gcr_display_view_append_value (view, renderer, _("Email"), email, FALSE);
+		g_free (email);
+		if (comment != NULL)
+			_gcr_display_view_append_value (view, renderer, _("Comment"), comment, FALSE);
+		g_free (comment);
+
+	/* Unparseable user id */
+	} else {
+		_gcr_display_view_append_value (view, renderer, _("Value"), userid, FALSE);
+	}
+
+	/* Created */
+	date = _gcr_record_get_date (record, GCR_RECORD_UID_TIMESTAMP);
+	if (date != NULL) {
+		display = g_date_time_format (date, "%x");
+		_gcr_display_view_append_value (view, renderer, _("Created"), display, FALSE);
+		g_free (display);
+		g_date_time_unref (date);
+	}
+
+	/* Expiry */
+	date = _gcr_record_get_date (record, GCR_RECORD_UID_EXPIRY);
+	if (date != NULL) {
+		display = g_date_time_format (date, "%x");
+		_gcr_display_view_append_value (view, renderer, _("Expiry"), display, FALSE);
+		g_free (display);
+		g_date_time_unref (date);
+	}
+
+	g_free (userid);
+}
+
+static void
+append_uat_record (GcrGnupgRenderer *self,
+                   GcrDisplayView *view,
+                   GcrRecord *record)
+{
+	GcrRenderer *renderer = GCR_RENDERER (self);
+	gchar **parts;
+	gchar *display;
+	const gchar *value;
+	GDateTime *date;
+
+	_gcr_display_view_append_heading (view, renderer, _("User Attribute"));
+
+	/* Size */
+	value = _gcr_record_get_raw (record, GCR_RECORD_UAT_COUNT_SIZE);
+	if (value != NULL) {
+		parts = g_strsplit (value, " ", 2);
+		if (parts && parts[0] && parts[1])
+			_gcr_display_view_append_value (view, renderer, _("Size"), parts[1], FALSE);
+		g_strfreev (parts);
+	}
+
+	/* Created */
+	date = _gcr_record_get_date (record, GCR_RECORD_KEY_TIMESTAMP);
+	if (date != NULL) {
+		display = g_date_time_format (date, "%x");
+		_gcr_display_view_append_value (view, renderer, _("Created"), display, FALSE);
+		g_free (display);
+		g_date_time_unref (date);
+	}
+
+	/* Expiry */
+	date = _gcr_record_get_date (record, GCR_RECORD_KEY_EXPIRY);
+	if (date != NULL) {
+		display = g_date_time_format (date, "%x");
+		_gcr_display_view_append_value (view, renderer, _("Expiry"), display, FALSE);
+		g_free (display);
+		g_date_time_unref (date);
+	}
+}
+
+static const gchar *
+signature_klass_string (const gchar *klass)
+{
+	char *end;
+	guint val;
+
+	val = strtoul (klass, &end, 16);
+	if (end != klass + 2)
+		return NULL;
+
+	switch (val) {
+	case 0x00:
+		return _("Signature of a binary document");
+	case 0x01:
+		return _("Signature of a canonical text document");
+	case 0x02:
+		return _("Standalone signature");
+	case 0x10:
+		return _("Generic certification of key");
+	case 0x11:
+		return _("Persona certification of key");
+	case 0x12:
+		return _("Casual certification of key");
+	case 0x13:
+		return _("Positive certification of key");
+	case 0x18:
+		return _("Subkey binding signature");
+	case 0x19:
+		return _("Primary key binding signature");
+	case 0x1F:
+		return _("Signature directly on key");
+	case 0x20:
+		return _("Key revocation signature");
+	case 0x28:
+		return _("Subkey revocation signature");
+	case 0x30:
+		return _("Certification revocation signature");
+	case 0x40:
+		return _("Timestamp signature");
+	case 0x50:
+		return _("Third-party confirmation signature");
+	default:
+		return NULL;
+	}
+}
+
+static void
+append_sig_record (GcrGnupgRenderer *self,
+                   GcrDisplayView *view,
+                   GcrRecord *record,
+                   const gchar *keyid)
+{
+	GcrRenderer *renderer = GCR_RENDERER (self);
+	const gchar *sigid;
+	gchar *display;
+	const gchar *value;
+	const gchar *klass;
+	guint algo;
+
+	/* Hide self-signatures. There's so many of them */
+	sigid = _gcr_record_get_raw (record, GCR_RECORD_SIG_KEYID);
+	if (sigid && keyid && g_str_equal (sigid, keyid))
+		return;
+
+	_gcr_display_view_append_heading (view, renderer, _("Signature"));
+
+	/* Key ID */
+	if (sigid != NULL)
+		_gcr_display_view_append_value (view, renderer, _("Key ID"), sigid, TRUE);
+
+	/* Algorithm */
+	if (_gcr_record_get_uint (record, GCR_RECORD_SIG_ALGO, &algo)) {
+		display = NULL;
+		value = name_for_algo (algo);
+		if (value == NULL)
+			value = display = g_strdup_printf ("%u", algo);
+		_gcr_display_view_append_value (view, renderer, _("Algorithm"), value, FALSE);
+		g_free (display);
+	}
+
+	/* User ID */
+	display = _gcr_record_get_string (record, GCR_RECORD_SIG_USERID);
+	if (display != NULL)
+		_gcr_display_view_append_value (view, renderer, _("User ID"), display, FALSE);
+	g_free (display);
+
+	/* Signature class */
+	klass = _gcr_record_get_raw (record, GCR_RECORD_SIG_CLASS);
+	if (klass != NULL) {
+		value = NULL;
+		if (strlen (klass) >= 2) {
+			value = signature_klass_string (klass);
+			if (value != NULL) {
+				_gcr_display_view_append_value (view, renderer, _("Class"), value, FALSE);
+				if (klass[2] == 'l')
+					_gcr_display_view_append_value (view, renderer, _("Type"), _("Local only"), FALSE);
+				else if (klass[2] == 'x')
+					_gcr_display_view_append_value (view, renderer, _("Type"), _("Exportable"), FALSE);
+			}
+		}
+		if (value == NULL)
+			_gcr_display_view_append_value (view, renderer, _("Class"), klass, FALSE);
+	}
+}
+
+static void
+append_rvk_record (GcrGnupgRenderer *self,
+                   GcrDisplayView *view,
+                   GcrRecord *record)
+{
+	GcrRenderer *renderer = GCR_RENDERER (self);
+	const gchar *value;
+	gchar *display;
+	guint algo;
+
+	_gcr_display_view_append_heading (view, renderer, _("Revocation Key"));
+
+	/* Algorithm */
+	if (_gcr_record_get_uint (record, GCR_RECORD_RVK_ALGO, &algo)) {
+		display = NULL;
+		value = name_for_algo (algo);
+		if (value == NULL)
+			value = display = g_strdup_printf ("%u", algo);
+		_gcr_display_view_append_value (view, renderer, _("Algorithm"), value, FALSE);
+		g_free (display);
+	}
+
+	value = _gcr_record_get_raw (record, GCR_RECORD_RVK_FINGERPRINT);
+	if (value != NULL)
+		_gcr_display_view_append_value (view, renderer, _("Fingerprint"), value, TRUE);
+}
+
+static void
+append_fpr_record (GcrGnupgRenderer *self,
+                   GcrDisplayView *view,
+                   GcrRecord *record,
+                   GQuark last_schema)
+{
+	GcrRenderer *renderer = GCR_RENDERER (self);
+	const gchar *value;
+	gpointer raw;
+	gsize n_raw;
+
+	if (last_schema != GCR_RECORD_SCHEMA_PUB &&
+	    last_schema != GCR_RECORD_SCHEMA_SUB &&
+	    last_schema != GCR_RECORD_SCHEMA_SEC &&
+	    last_schema != GCR_RECORD_SCHEMA_SSB)
+		return;
+
+	value = _gcr_record_get_raw (record, GCR_RECORD_FPR_FINGERPRINT);
+	if (value != NULL) {
+		raw = egg_hex_decode (value, -1, &n_raw);
+		if (raw != NULL)
+			_gcr_display_view_append_hex (view, renderer, _("Fingerprint"), raw, n_raw);
+		else
+			_gcr_display_view_append_value (view, renderer, _("Fingerprint"), value, TRUE);
+		g_free (raw);
+	}
+}
+
+static void
+_gcr_gnupg_renderer_render (GcrRenderer *renderer,
+                            GcrViewer *viewer)
+{
+	GcrGnupgRenderer *self;
+	GcrDisplayView *view;
+	GDateTime *date;
+	const gchar *value;
+	gchar *display;
+	gchar *userid;
+	gchar *email;
+	gchar *comment;
+	GIcon *icon;
+	GQuark schema;
+	GQuark last_schema;
+	gchar code;
+	guint i;
+
+	self = GCR_GNUPG_RENDERER (renderer);
+
+	if (GCR_IS_DISPLAY_VIEW (viewer)) {
+		view = GCR_DISPLAY_VIEW (viewer);
+
+	} else {
+		g_warning ("GcrGnupgRenderer only works with internal specific "
+		           "GcrViewer returned by gcr_viewer_new().");
+		return;
+	}
+
+	_gcr_display_view_begin (view, renderer);
+
+	if (self->pv->records == NULL || self->pv->records->len == 0) {
+		_gcr_display_view_end (view, renderer);
+		return;
+	}
+
+	icon = _gcr_gnupg_records_get_icon (self->pv->records);
+	_gcr_display_view_set_icon (view, GCR_RENDERER (self), icon);
+	g_object_unref (icon);
+
+	display = calculate_label (self);
+	_gcr_display_view_append_title (view, renderer, display);
+	g_free (display);
+
+	userid = _gcr_gnupg_records_get_user_id (self->pv->records);
+	if (userid != NULL) {
+		if (_gcr_gnupg_records_parse_user_id (userid, NULL, &email, &comment)) {
+			if (email != NULL)
+				_gcr_display_view_append_content (view, renderer, _("Email"), email);
+			g_free (email);
+			if (comment != NULL)
+				_gcr_display_view_append_content (view, renderer, _("Comment"), comment);
+			g_free (comment);
+		}
+		g_free (userid);
+	}
+
+	value = _gcr_gnupg_records_get_short_keyid (self->pv->records);
+	if (value != NULL)
+		_gcr_display_view_append_content (view, renderer, _("Key ID"), value);
+
+	code = _gcr_record_get_char (self->pv->records->pdata[0], GCR_RECORD_TRUST);
+	if (code != 'e') {
+		date = _gcr_record_get_date (self->pv->records->pdata[0], GCR_RECORD_KEY_EXPIRY);
+		if (date != NULL) {
+			display = g_date_time_format (date, "%x");
+			_gcr_display_view_append_content (view, renderer, _("Expires"), display);
+			g_date_time_unref (date);
+			g_free (display);
+		}
+	}
+
+	/* TODO: Warning */
+
+
+	_gcr_display_view_start_details (view, renderer);
+
+	value = _gcr_gnupg_records_get_keyid (self->pv->records);
+	last_schema = 0;
+
+	for (i = 0; i < self->pv->records->len; i++) {
+		schema = _gcr_record_get_schema (self->pv->records->pdata[i]);
+		if (schema == GCR_RECORD_SCHEMA_PUB)
+			append_key_record (self, view, self->pv->records->pdata[i], _("Public Key"));
+		else if (schema == GCR_RECORD_SCHEMA_SUB)
+			append_key_record (self, view, self->pv->records->pdata[i], _("Public Subkey"));
+		else if (schema == GCR_RECORD_SCHEMA_SEC)
+			append_key_record (self, view, self->pv->records->pdata[i], _("Secret Key"));
+		else if (schema == GCR_RECORD_SCHEMA_SSB)
+			append_key_record (self, view, self->pv->records->pdata[i], _("Secret Subkey"));
+		else if (schema == GCR_RECORD_SCHEMA_UID)
+			append_uid_record (self, view, self->pv->records->pdata[i]);
+		else if (schema == GCR_RECORD_SCHEMA_UAT)
+			append_uat_record (self, view, self->pv->records->pdata[i]);
+		else if (schema == GCR_RECORD_SCHEMA_SIG)
+			append_sig_record (self, view, self->pv->records->pdata[i], value);
+		else if (schema == GCR_RECORD_SCHEMA_RVK)
+			append_rvk_record (self, view, self->pv->records->pdata[i]);
+		else if (schema == GCR_RECORD_SCHEMA_FPR)
+			append_fpr_record (self, view, self->pv->records->pdata[i], last_schema);
+		last_schema = schema;
+	}
+
+	_gcr_display_view_end (view, renderer);
+}
+
+static void
+_gcr_gnupg_renderer_iface_init (GcrRendererIface *iface)
+{
+	iface->render_view = _gcr_gnupg_renderer_render;
+}
+
+GcrGnupgRenderer *
+_gcr_gnupg_renderer_new (GPtrArray *records)
+{
+	g_return_val_if_fail (records != NULL, NULL);
+
+	return g_object_new (GCR_TYPE_GNUPG_RENDERER,
+	                     "records", records,
+	                     NULL);
+}
+
+GcrGnupgRenderer *
+_gcr_gnupg_renderer_new_for_attributes (const gchar *label,
+                                        GckAttributes *attrs)
+{
+	g_return_val_if_fail (attrs != NULL, NULL);
+
+	return g_object_new (GCR_TYPE_GNUPG_RENDERER,
+	                     "label", label,
+	                     "attributes", attrs,
+	                     NULL);
+}
+
+GPtrArray *
+_gcr_gnupg_renderer_get_records (GcrGnupgRenderer *self)
+{
+	g_return_val_if_fail (GCR_IS_GNUPG_RENDERER (self), NULL);
+	return self->pv->records;
+}
+
+void
+_gcr_gnupg_renderer_set_records (GcrGnupgRenderer *self,
+                                 GPtrArray *records)
+{
+	g_return_if_fail (GCR_IS_GNUPG_RENDERER (self));
+
+	if (records)
+		g_ptr_array_ref (records);
+	if (self->pv->records)
+		g_ptr_array_unref (self->pv->records);
+	self->pv->records = records;
+
+	if (self->pv->attrs) {
+		gck_attributes_unref (self->pv->attrs);
+		self->pv->attrs = NULL;
+		g_object_notify (G_OBJECT (self), "attributes");
+	}
+
+	gcr_renderer_emit_data_changed (GCR_RENDERER (self));
+	g_object_notify (G_OBJECT (self), "records");
+}
+
+GckAttributes*
+_gcr_gnupg_renderer_get_attributes (GcrGnupgRenderer *self)
+{
+	g_return_val_if_fail (GCR_IS_GNUPG_RENDERER (self), NULL);
+	return self->pv->attrs;
+}
+
+void
+_gcr_gnupg_renderer_set_attributes (GcrGnupgRenderer *self,
+                                    GckAttributes *attrs)
+{
+	GckAttribute *attr;
+	GPtrArray *records;
+
+	g_return_if_fail (GCR_IS_GNUPG_RENDERER (self));
+
+	attr = gck_attributes_find (attrs, CKA_VALUE);
+	g_return_if_fail (attr != NULL);
+	records = _gcr_records_parse_colons (attr->value, attr->length);
+	g_return_if_fail (records != NULL);
+
+	if (attrs)
+		gck_attributes_ref (attrs);
+	gck_attributes_unref (self->pv->attrs);
+	self->pv->attrs = attrs;
+
+	if (self->pv->records)
+		g_ptr_array_unref (self->pv->records);
+	self->pv->records = records;
+	g_object_notify (G_OBJECT (self), "records");
+
+	gcr_renderer_emit_data_changed (GCR_RENDERER (self));
+	g_object_notify (G_OBJECT (self), "attributes");
+
+}
diff --git a/gcr/gcr-gnupg-renderer.h b/gcr/gcr-gnupg-renderer.h
new file mode 100644
index 0000000..0d09c31
--- /dev/null
+++ b/gcr/gcr-gnupg-renderer.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#if !defined (__GCR_H_INSIDE__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> can be included directly."
+#endif
+
+#ifndef __GCR_GNUPG_RENDERER_H__
+#define __GCR_GNUPG_RENDERER_H__
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gcr-record.h"
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_GNUPG_RENDERER               (_gcr_gnupg_renderer_get_type ())
+#define GCR_GNUPG_RENDERER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_GNUPG_RENDERER, GcrGnupgRenderer))
+#define GCR_GNUPG_RENDERER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_GNUPG_RENDERER, GcrGnupgRendererClass))
+#define GCR_IS_GNUPG_RENDERER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_GNUPG_RENDERER))
+#define GCR_IS_GNUPG_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_GNUPG_RENDERER))
+#define GCR_GNUPG_RENDERER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_GNUPG_RENDERER, GcrGnupgRendererClass))
+
+typedef struct _GcrGnupgRenderer GcrGnupgRenderer;
+typedef struct _GcrGnupgRendererClass GcrGnupgRendererClass;
+typedef struct _GcrGnupgRendererPrivate GcrGnupgRendererPrivate;
+
+struct _GcrGnupgRenderer {
+	GObject parent;
+
+	/*< private >*/
+	GcrGnupgRendererPrivate *pv;
+};
+
+struct _GcrGnupgRendererClass {
+	GObjectClass parent_class;
+};
+
+GType                _gcr_gnupg_renderer_get_type           (void);
+
+GcrGnupgRenderer *   _gcr_gnupg_renderer_new                (GPtrArray *records);
+
+GcrGnupgRenderer *   _gcr_gnupg_renderer_new_for_attributes (const gchar *label,
+                                                             GckAttributes *attrs);
+
+GPtrArray *          _gcr_gnupg_renderer_get_records        (GcrGnupgRenderer *self);
+
+void                 _gcr_gnupg_renderer_set_records        (GcrGnupgRenderer *self,
+                                                             GPtrArray *records);
+
+GckAttributes *      _gcr_gnupg_renderer_get_attributes     (GcrGnupgRenderer *self);
+
+void                 _gcr_gnupg_renderer_set_attributes     (GcrGnupgRenderer *self,
+                                                             GckAttributes *attrs);
+
+G_END_DECLS
+
+#endif /* __GCR_GNUPG_RENDERER_H__ */
diff --git a/gcr/gcr-openpgp.c b/gcr/gcr-openpgp.c
index abf6800..7ae5c47 100644
--- a/gcr/gcr-openpgp.c
+++ b/gcr/gcr-openpgp.c
@@ -35,14 +35,6 @@
 #include <string.h>
 
 typedef enum {
-	OPENPGP_ALGO_RSA = 1,
-	OPENPGP_ALGO_RSA_E = 2,
-	OPENPGP_ALGO_RSA_S = 3,
-	OPENPGP_ALGO_ELG_E = 16,
-	OPENPGP_ALGO_DSA = 17
-} OpenpgpPkAlgo;
-
-typedef enum {
 	OPENPGP_PKT_RESERVED = 0,
 	OPENPGP_PKT_PUBKEY_ENC = 1,
 	OPENPGP_PKT_SIGNATURE = 2,
@@ -317,7 +309,8 @@ parse_v3_rsa_bits_and_keyid (const guchar **at,
 
 static gchar *
 hash_v4_keyid (const guchar *data,
-               const guchar *end)
+               const guchar *end,
+               gchar **fingerprint)
 {
 	gcry_md_hd_t mdh;
 	gcry_error_t gcry;
@@ -349,6 +342,8 @@ hash_v4_keyid (const guchar *data,
 
 	digest = gcry_md_read (mdh, 0);
 	keyid = egg_hex_encode_full (digest + 12, 8, TRUE, 0, 0);
+	if (fingerprint)
+		*fingerprint = egg_hex_encode_full (digest, 20, TRUE, 0, 0);
 	gcry_md_close (mdh);
 
 	return keyid;
@@ -361,21 +356,21 @@ parse_v4_algo_bits (const guchar **at,
                     guint16 *bits)
 {
 	switch (algo) {
-	case OPENPGP_ALGO_RSA:
-	case OPENPGP_ALGO_RSA_E:
-	case OPENPGP_ALGO_RSA_S:
+	case GCR_OPENPGP_ALGO_RSA:
+	case GCR_OPENPGP_ALGO_RSA_E:
+	case GCR_OPENPGP_ALGO_RSA_S:
 		if (!read_mpi (at, end, bits, NULL) ||
 		    !read_mpi (at, end, NULL, NULL))
 			return FALSE;
 		return TRUE;
-	case OPENPGP_ALGO_DSA:
+	case GCR_OPENPGP_ALGO_DSA:
 		if (!read_mpi (at, end, bits, NULL) ||
 		    !read_mpi (at, end, NULL, NULL) ||
 		    !read_mpi (at, end, NULL, NULL) ||
 		    !read_mpi (at, end, NULL, NULL))
 			return FALSE;
 		return TRUE;
-	case OPENPGP_ALGO_ELG_E:
+	case GCR_OPENPGP_ALGO_ELG_E:
 		if (!read_mpi (at, end, bits, NULL) ||
 		    !read_mpi (at, end, NULL, NULL) ||
 		    !read_mpi (at, end, NULL, NULL))
@@ -390,15 +385,15 @@ static const gchar *
 default_caps_for_algo (guint8 algo)
 {
 	switch (algo) {
-	case OPENPGP_ALGO_RSA:
+	case GCR_OPENPGP_ALGO_RSA:
 		return "cse";
-	case OPENPGP_ALGO_RSA_E:
+	case GCR_OPENPGP_ALGO_RSA_E:
 		return "e";
-	case OPENPGP_ALGO_RSA_S:
+	case GCR_OPENPGP_ALGO_RSA_S:
 		return "s";
-	case OPENPGP_ALGO_ELG_E:
+	case GCR_OPENPGP_ALGO_ELG_E:
 		return "e";
-	case OPENPGP_ALGO_DSA:
+	case GCR_OPENPGP_ALGO_DSA:
 		return "sca";
 	default:
 		return "";
@@ -422,6 +417,7 @@ parse_public_key_or_subkey (GQuark schema,
 	guint8 algo;
 	guint16 bits;
 	gulong expiry;
+	gchar *fingerprint;
 	const guchar *data;
 
 	/* Start of actual key data in packet */
@@ -455,7 +451,7 @@ parse_public_key_or_subkey (GQuark schema,
 	} else {
 		if (!parse_v4_algo_bits (at, end, algo, &bits))
 			return FALSE;
-		keyid = hash_v4_keyid (data, *at);
+		keyid = hash_v4_keyid (data, *at, &fingerprint);
 	}
 
 	record = _gcr_record_new (schema, n_columns, ':');
@@ -472,6 +468,15 @@ parse_public_key_or_subkey (GQuark schema,
 	}
 
 	g_ptr_array_add (records, record);
+
+	if (fingerprint && (schema == GCR_RECORD_SCHEMA_PUB || schema == GCR_RECORD_SCHEMA_SEC)) {
+		record = _gcr_record_new (GCR_RECORD_SCHEMA_FPR, GCR_RECORD_FPR_MAX, ':');
+		_gcr_record_take_raw (record, GCR_RECORD_FPR_FINGERPRINT, fingerprint);
+		g_ptr_array_add (records, record);
+		fingerprint = NULL;
+	}
+
+	g_free (fingerprint);
 	return TRUE;
 }
 
@@ -520,7 +525,7 @@ parse_user_id (const guchar *beg,
 	fingerprint = hash_user_id_or_attribute (*at, end);
 	record = _gcr_record_new (GCR_RECORD_SCHEMA_UID, GCR_RECORD_UID_MAX, ':');
 	_gcr_record_take_raw (record, GCR_RECORD_UID_FINGERPRINT, fingerprint);
-	_gcr_record_set_string (record, GCR_RECORD_UID_NAME, string);
+	_gcr_record_set_string (record, GCR_RECORD_UID_USERID, string);
 	g_free (string);
 
 	g_ptr_array_add (records, record);
@@ -608,11 +613,11 @@ skip_signature_mpis (const guchar **at,
 	switch (algo) {
 
 	/* RSA signature value */
-	case OPENPGP_ALGO_RSA:
+	case GCR_OPENPGP_ALGO_RSA:
 		return read_mpi (at, end, NULL, NULL);
 
 	/* DSA values r and s */
-	case OPENPGP_ALGO_DSA:
+	case GCR_OPENPGP_ALGO_DSA:
 		return read_mpi (at, end, NULL, NULL) &&
 		       read_mpi (at, end, NULL, NULL);
 	default:
@@ -757,7 +762,7 @@ parse_v4_signature_subpacket (const guchar **at,
 
 	case OPENPGP_SIG_SIGNER_USERID:
 		value = g_strndup ((gchar *)*at, end - *at);
-		_gcr_record_set_string (record, GCR_RECORD_SIG_NAME, value);
+		_gcr_record_set_string (record, GCR_RECORD_SIG_USERID, value);
 		g_free (value);
 		return TRUE;
 
@@ -1182,7 +1187,7 @@ normalize_key_records (GPtrArray *records)
 					continue;
 			}
 			schema = _gcr_record_get_schema (records->pdata[i]);
-			if (schema != GCR_RECORD_SCHEMA_SIG)
+			if (schema != GCR_RECORD_SCHEMA_SIG && schema != GCR_RECORD_SCHEMA_FPR)
 				_gcr_record_set_char (records->pdata[i], GCR_RECORD_TRUST, trust);
 		}
 	}
diff --git a/gcr/gcr-openpgp.h b/gcr/gcr-openpgp.h
index 5ba4267..31fd295 100644
--- a/gcr/gcr-openpgp.h
+++ b/gcr/gcr-openpgp.h
@@ -33,6 +33,14 @@
 #include <gck/gck.h>
 
 typedef enum {
+	GCR_OPENPGP_ALGO_RSA = 1,
+	GCR_OPENPGP_ALGO_RSA_E = 2,
+	GCR_OPENPGP_ALGO_RSA_S = 3,
+	GCR_OPENPGP_ALGO_ELG_E = 16,
+	GCR_OPENPGP_ALGO_DSA = 17
+} GcrOpenpgpAlgo;
+
+typedef enum {
 	GCR_OPENPGP_PARSE_NONE = 0,
 	GCR_OPENPGP_PARSE_KEYS = 1 << 1,
 	GCR_OPENPGP_PARSE_NO_RECORDS = 1 << 2,
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index 96aedfe..33aaad4 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -30,6 +30,7 @@
 #include "gcr-openpgp.h"
 #include "gcr-openssh.h"
 #include "gcr-parser.h"
+#include "gcr-record.h"
 #include "gcr-types.h"
 
 #include "egg/egg-armor.h"
@@ -292,6 +293,9 @@ parsed_description (GcrParsed *parsed,
 	case CKO_PUBLIC_KEY:
 		parsed->description = _("Public Key");
 		break;
+	case CKO_GCR_GNUPG_RECORDS:
+		parsed->description = _("PGP Key");
+		break;
 	default:
 		parsed->description = NULL;
 		break;
@@ -410,7 +414,7 @@ parsed_fire (GcrParser *self,
              GcrParsed *parsed)
 {
 	g_assert (GCR_IS_PARSER (self));
-	g_assert (parsed);
+	g_assert (parsed != NULL);
 	g_assert (parsed == self->pv->parsed);
 
 	g_object_notify (G_OBJECT (self), "parsed-description");
@@ -1407,14 +1411,26 @@ on_openpgp_packet (GPtrArray *records,
 {
 	GcrParser *self = GCR_PARSER (user_data);
 	GcrParsed *parsed;
+	gchar *string;
+
+	/*
+	 * If it's an openpgp packet that doesn't contain a key, then
+	 * just ignore it here.
+	 */
+	if (records->len == 0)
+		return;
 
 	parsed = push_parsed (self);
 
 	/* All we can do is the packet bounds */
 	parsing_block (parsed, GCR_FORMAT_OPENPGP_PACKET, outer, n_outer);
-	parsing_object (parsed, CKO_DATA);
+	parsing_object (parsed, CKO_GCR_GNUPG_RECORDS);
+	string = _gcr_records_format (records);
+	parsed_attribute (parsed, CKA_VALUE, string, strlen (string));
 	parsed_fire (self, parsed);
 	pop_parsed (self, parsed);
+
+	g_free (string);
 }
 
 static gint
diff --git a/gcr/gcr-record.c b/gcr/gcr-record.c
index 612dc86..c543e97 100644
--- a/gcr/gcr-record.c
+++ b/gcr/gcr-record.c
@@ -147,22 +147,6 @@ _gcr_record_format (GcrRecord *record)
 	return g_string_free (string, FALSE);
 }
 
-gchar *
-_gcr_records_format (GPtrArray *records)
-{
-	GString *string;
-	guint i;
-
-	g_return_val_if_fail (records, NULL);
-
-	string = g_string_new ("");
-	for (i = 0; i < records->len; i++) {
-		print_record_to_string (records->pdata[i], string);
-		g_string_append_c (string, '\n');
-	}
-	return g_string_free (string, FALSE);
-}
-
 GcrRecord *
 _gcr_record_new (GQuark schema,
                  guint n_columns,
@@ -253,22 +237,6 @@ _gcr_record_parse_spaces (const gchar *line, gssize n_line)
 	return take_and_parse_internal (record_block_new (line, n_line), ' ', FALSE);
 }
 
-GcrRecord*
-_gcr_record_find (GPtrArray *records, GQuark schema)
-{
-	guint i;
-
-	g_return_val_if_fail (records, NULL);
-	g_return_val_if_fail (schema, NULL);
-
-	for (i = 0; i < records->len; i++) {
-		if (schema == _gcr_record_get_schema (records->pdata[i]))
-			return records->pdata[i];
-	}
-
-	return NULL;
-}
-
 guint
 _gcr_record_get_count (GcrRecord *record)
 {
@@ -622,60 +590,35 @@ _gcr_record_set_ulong (GcrRecord *record,
 	                    record_block_take (escaped, strlen (escaped)));
 }
 
-gboolean
+GDateTime *
 _gcr_record_get_date (GcrRecord *record,
-                      guint column,
-                      gulong *value)
+                      guint column)
 {
 	const gchar *raw;
 	gulong result;
 	gchar *end = NULL;
 	struct tm tm;
 
-	g_return_val_if_fail (record, FALSE);
+	g_return_val_if_fail (record, NULL);
 
 	raw = _gcr_record_get_raw (record, column);
 	if (raw == NULL)
-		return FALSE;
+		return NULL;
 
 	/* Try to parse as a number */
 	result = strtoul (raw, &end, 10);
+	if (end != NULL && end[0] == '\0')
+		return g_date_time_new_from_unix_utc (result);
+
+	/* Try to parse as a date */
+	memset (&tm, 0, sizeof (tm));
+	end = strptime (raw, "%Y-%m-%d", &tm);
 	if (!end || end[0]) {
-		/* Try to parse as a date */
-		memset (&tm, 0, sizeof (tm));
-		end = strptime (raw, "%Y-%m-%d", &tm);
-		if (!end || end[0]) {
-			_gcr_debug ("invalid date value: %s", raw);
-			return FALSE;
-		}
-		result = timegm (&tm);
+		_gcr_debug ("invalid date value: %s", raw);
+		return NULL;
 	}
 
-	if (value)
-		*value = result;
-	return TRUE;
-}
-
-void
-_gcr_record_set_date (GcrRecord *record,
-                      guint column,
-                      gulong value)
-{
-	GcrRecordBlock *block;
-	time_t time;
-	struct tm tm;
-	gsize len;
-
-	g_return_if_fail (record != NULL);
-	g_return_if_fail (column < record->n_columns);
-
-	time = value;
-	gmtime_r (&time, &tm);
-	block = record_block_new (NULL, 20);
-	len = strftime (block->value, 20, "%Y-%m-%d", &tm);
-	g_assert (len < 20);
-
-	record_take_column (record, column, block);
+	return g_date_time_new_utc (tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 0, 0, 0);
 }
 
 /**
@@ -799,3 +742,70 @@ _gcr_record_get_schema (GcrRecord *record)
 		return g_quark_try_string (value);
 	return 0;
 }
+
+GcrRecord *
+_gcr_records_find (GPtrArray *records,
+                   GQuark schema)
+{
+	guint i;
+
+	g_return_val_if_fail (records, NULL);
+	g_return_val_if_fail (schema, NULL);
+
+	for (i = 0; i < records->len; i++) {
+		if (schema == _gcr_record_get_schema (records->pdata[i]))
+			return records->pdata[i];
+	}
+
+	return NULL;
+}
+
+gchar *
+_gcr_records_format (GPtrArray *records)
+{
+	GString *string;
+	guint i;
+
+	g_return_val_if_fail (records, NULL);
+
+	string = g_string_new ("");
+	for (i = 0; i < records->len; i++) {
+		print_record_to_string (records->pdata[i], string);
+		g_string_append_c (string, '\n');
+	}
+	return g_string_free (string, FALSE);
+}
+
+GPtrArray *
+_gcr_records_parse_colons (gconstpointer data,
+                           gssize n_data)
+{
+	GPtrArray *result = NULL;
+	GcrRecordBlock *block;
+	GcrRecord *record;
+	gchar **lines;
+	guint i;
+
+	lines = g_strsplit (data, "\n", n_data);
+	result = g_ptr_array_new_with_free_func (_gcr_record_free);
+
+	for (i = 0; lines[i] != NULL; i++) {
+		block = record_block_take (lines[i], strlen (lines[i]));
+		record = take_and_parse_internal (block, ':', TRUE);
+		if (record == NULL) {
+			g_ptr_array_unref (result);
+			result = NULL;
+			break;
+		}
+		g_ptr_array_add (result, record);
+	}
+
+	/* Free any not done */
+	for (; lines[i] != NULL; i++)
+		g_free (lines[i]);
+
+	/* Individual lines already freed */
+	g_free (lines);
+
+	return result;
+}
diff --git a/gcr/gcr-record.h b/gcr/gcr-record.h
index a9bf6e0..3915375 100644
--- a/gcr/gcr-record.h
+++ b/gcr/gcr-record.h
@@ -87,7 +87,8 @@ typedef enum {
  * fpr:::::::::ECAF7590EB3443B5C7CF3ACB6C7EE1B8621CC013:
  */
 typedef enum {
-	GCR_RECORD_FPR_FINGERPRINT = 9
+	GCR_RECORD_FPR_FINGERPRINT = 9,
+	GCR_RECORD_FPR_MAX = 10
 } GcrRecordFprColumns;
 
 /*
@@ -118,8 +119,9 @@ typedef enum {
  */
 typedef enum {
 	GCR_RECORD_UID_TIMESTAMP = 5,
+	GCR_RECORD_UID_EXPIRY = 6,
 	GCR_RECORD_UID_FINGERPRINT = 7,
-	GCR_RECORD_UID_NAME = 9,
+	GCR_RECORD_UID_USERID = 9,
 	GCR_RECORD_UID_MAX = 10,
 } GcrRecordUidColumns;
 
@@ -133,7 +135,7 @@ typedef enum {
 	GCR_RECORD_SIG_KEYID = 4,
 	GCR_RECORD_SIG_TIMESTAMP = 5,
 	GCR_RECORD_SIG_EXPIRY = 6,
-	GCR_RECORD_SIG_NAME = 9,
+	GCR_RECORD_SIG_USERID = 9,
 	GCR_RECORD_SIG_CLASS = 10,
 	GCR_RECORD_SIG_MAX = 11,
 } GcrRecordSigColumns;
@@ -196,16 +198,8 @@ GcrRecord*     _gcr_record_parse_spaces         (const gchar *line,
 
 gchar *        _gcr_record_format               (GcrRecord *record);
 
-gchar *        _gcr_records_format              (GPtrArray *records);
-
 void           _gcr_record_free                 (gpointer record);
 
-GcrRecord*     _gcr_record_find                 (GPtrArray *records,
-                                                 GQuark schema);
-
-GcrRecord*     _gcr_record_rfind                (GPtrArray *records,
-                                                 GQuark schema);
-
 guint          _gcr_record_get_count            (GcrRecord *record);
 
 gchar          _gcr_record_get_char             (GcrRecord *record,
@@ -238,13 +232,8 @@ void           _gcr_record_set_ulong            (GcrRecord *record,
                                                  guint column,
                                                  gulong value);
 
-gboolean       _gcr_record_get_date             (GcrRecord *record,
-                                                 guint column,
-                                                 gulong *value);
-
-void           _gcr_record_set_date             (GcrRecord *record,
-                                                 guint column,
-                                                 gulong value);
+GDateTime *    _gcr_record_get_date             (GcrRecord *record,
+                                                 guint column);
 
 gpointer       _gcr_record_get_base64           (GcrRecord *record,
                                                  guint column,
@@ -268,6 +257,14 @@ void           _gcr_record_take_raw             (GcrRecord *record,
 
 GQuark         _gcr_record_get_schema           (GcrRecord *record);
 
+GPtrArray *    _gcr_records_parse_colons        (gconstpointer data,
+                                                 gssize n_data);
+
+gchar *        _gcr_records_format              (GPtrArray *records);
+
+GcrRecord *    _gcr_records_find                (GPtrArray *records,
+                                                 GQuark schema);
+
 G_END_DECLS
 
 #endif /* GCR_RECORD_H */
diff --git a/gcr/gcr-renderer.c b/gcr/gcr-renderer.c
index fd50936..dc542a2 100644
--- a/gcr/gcr-renderer.c
+++ b/gcr/gcr-renderer.c
@@ -25,6 +25,7 @@
 #include "gcr-renderer.h"
 
 #include "gcr-certificate-renderer.h"
+#include "gcr-gnupg-renderer.h"
 #include "gcr-key-renderer.h"
 
 #include "gck/gck.h"
@@ -290,4 +291,5 @@ gcr_renderer_register_well_known (void)
 {
 	g_type_class_unref (g_type_class_ref (GCR_TYPE_CERTIFICATE_RENDERER));
 	g_type_class_unref (g_type_class_ref (GCR_TYPE_KEY_RENDERER));
+	g_type_class_unref (g_type_class_ref (GCR_TYPE_GNUPG_RENDERER));
 }
diff --git a/gcr/gcr-types.h b/gcr/gcr-types.h
index 1cade80..7ac1c38 100644
--- a/gcr/gcr-types.h
+++ b/gcr/gcr-types.h
@@ -87,6 +87,16 @@ typedef enum {
 	GCR_FORMAT_PEM_PRIVATE_KEY
 } GcrDataFormat;
 
+/*
+ * Special PKCS#11 style attributes that we use internally in GCR.
+ * These are used by GcrParser the most
+ */
+
+enum {
+	/* An object class representing GcrRecord/gnupg-colons style data */
+	CKO_GCR_GNUPG_RECORDS = (CKO_VENDOR_DEFINED | 0x47435200UL /* GCR0 */)
+};
+
 G_END_DECLS
 
 #endif /* GCRTYPES_H_ */
diff --git a/gcr/tests/test-gnupg-collection.c b/gcr/tests/test-gnupg-collection.c
index 686e80f..c8304a7 100644
--- a/gcr/tests/test-gnupg-collection.c
+++ b/gcr/tests/test-gnupg-collection.c
@@ -171,7 +171,7 @@ test_load (Test *test, gconstpointer unused)
 	/* Phillip R. Zimmerman's key should have a photo */
 	key = g_hash_table_lookup (test->keys, "C7463639B2D7795E");
 	g_assert (GCR_IS_GNUPG_KEY (key));
-	record = _gcr_record_find (_gcr_gnupg_key_get_public_records (key), GCR_RECORD_SCHEMA_XA1);
+	record = _gcr_records_find (_gcr_gnupg_key_get_public_records (key), GCR_RECORD_SCHEMA_XA1);
 	g_assert (record);
 }
 
diff --git a/gcr/tests/test-gnupg-key.c b/gcr/tests/test-gnupg-key.c
index 152a0c0..5900804 100644
--- a/gcr/tests/test-gnupg-key.c
+++ b/gcr/tests/test-gnupg-key.c
@@ -24,6 +24,7 @@
 
 #include "gcr/gcr-record.h"
 #include "gcr/gcr-gnupg-key.h"
+#include "gcr/gcr-gnupg-records.h"
 
 #include "egg/egg-testing.h"
 
@@ -160,7 +161,7 @@ test_keyid_for_records (Test *test, gconstpointer unused)
 {
 	const gchar *keyid;
 
-	keyid = _gcr_gnupg_key_get_keyid_for_records (test->records);
+	keyid = _gcr_gnupg_records_get_keyid (test->records);
 	g_assert_cmpstr (keyid, ==, "6C7EE1B8621CC013");
 }
 
diff --git a/gcr/tests/test-openpgp.c b/gcr/tests/test-openpgp.c
index 4c8c1d0..be41f40 100644
--- a/gcr/tests/test-openpgp.c
+++ b/gcr/tests/test-openpgp.c
@@ -43,9 +43,11 @@ typedef struct {
 
 static const gchar *werner_koch_records[] = {
 	"pub:e:1024:17:68B7AB8957548DCD:899816990:1136043547::o:::sca:\n"
+	"fpr:::::::::6BD9050FD8FC941B43412DCC68B7AB8957548DCD:\n"
 	"uid:e::::1102866526::B712A25DC2ABEF1579696C2925859931078C2C3E::Werner Koch (gnupg sig) <dd9jn gnu org>:\n",
 
 	"pub:e:1024:17:5DE249965B0358A2:921520361:1247335656::o:::sc:\n"
+	"fpr:::::::::A4D94E92B0986AB5EE9DCD755DE249965B0358A2:\n"
 	"uid:e::::1113145458::F5B5738FAFB7543A01BAB31A6D767FBC789FF8A8::Werner Koch <wk gnupg org>:\n"
 	"uid:e::::1113145466::60095F7DAD08129CCE39E15BEB6BBE21937E3AA6::Werner Koch <wk g10code com>:\n"
 	"uid:e::::921520362::392B892CF897AD0F03EB26343C4C20A48B36513E::Werner Koch:\n"
@@ -55,6 +57,7 @@ static const gchar *werner_koch_records[] = {
 	"sub:e:2048:1:35E52D69C3680A6E:1136137762:1199123362:::::e:\n",
 
 	"pub:e:1024:1:53B620D01CE0C630:1136130759:1230738759::o:::sc:\n"
+	"fpr:::::::::7B96D396E6471601754BE4DB53B620D01CE0C630:\n"
 	"uid:e::::1136130760::142B958D9816ECF810DBB83BD257E5C7DB36C99A::Werner Koch (dist sig) <dd9jn gnu org>:\n",
 
 	NULL
@@ -62,10 +65,12 @@ static const gchar *werner_koch_records[] = {
 
 static const gchar *werner_sig_records[] = {
 	"pub:e:1024:17:68B7AB8957548DCD:899816990:1136043547::o:::sca:\n"
+	"fpr:::::::::6BD9050FD8FC941B43412DCC68B7AB8957548DCD:\n"
 	"uid:e::::1102866526::B712A25DC2ABEF1579696C2925859931078C2C3E::Werner Koch (gnupg sig) <dd9jn gnu org>:\n"
 	"sig:::17:68B7AB8957548DCD:1102866526:::::13x:\n",
 
 	"pub:e:1024:17:5DE249965B0358A2:921520361:1247335656::o:::sc:\n"
+	"fpr:::::::::A4D94E92B0986AB5EE9DCD755DE249965B0358A2:\n"
 	"uid:e::::1113145458::F5B5738FAFB7543A01BAB31A6D767FBC789FF8A8::Werner Koch <wk gnupg org>:\n"
 	"sig:::17:5DE249965B0358A2:1113145458:::::13x:\n"
 	"uid:e::::1113145466::60095F7DAD08129CCE39E15BEB6BBE21937E3AA6::Werner Koch <wk g10code com>:\n"
@@ -82,6 +87,7 @@ static const gchar *werner_sig_records[] = {
 	"sig:::17:5DE249965B0358A2:1136137762:::::18x:\n",
 
 	"pub:e:1024:1:53B620D01CE0C630:1136130759:1230738759::o:::sc:\n"
+	"fpr:::::::::7B96D396E6471601754BE4DB53B620D01CE0C630:\n"
 	"uid:e::::1136130760::142B958D9816ECF810DBB83BD257E5C7DB36C99A::Werner Koch (dist sig) <dd9jn gnu org>:\n"
 	"sig:::1:53B620D01CE0C630:1136130760:::::13x:\n",
 
@@ -90,18 +96,22 @@ static const gchar *werner_sig_records[] = {
 
 static const gchar *pubring_records[] = {
 	"pub:o:2048:1:4842D952AFC000FD:1305189489:::o:::scSCE:\n"
+	"fpr:::::::::61A6EA3E0115080227A32EC94842D952AFC000FD:\n"
 	"uid:o::::1305189489::D449F1605254754B0BBFA424FC34E50609103BBB::Test Number 1 (unlimited) <test-number-1 example com>:\n"
 	"uid:o::::1305189849::D0A8FA7B15DC4BE3F8F03A49C372F2718C78AFC0::Dr. Strangelove <lovingbomb example com>:\n"
 	"sub:o:2048:1:4852132BBED15014:1305189489::::::e:\n",
 
 	"pub:e:1024:1:268FEE686262C395:1305189628:1305276028::o:::sc:\n"
+	"fpr:::::::::A4853C22EA82C8ADC6692751268FEE686262C395:\n"
 	"uid:e::::1305189628::2E9D48BD771DA765D2B48A0233D0E8F393F6E839::Test Number 2 (all gone) <test-number-2 example com>:\n"
 	"sub:e:1024:1:C5877FABF4772E4F:1305189628:1305276028:::::e:\n",
 
 	"pub:e:1024:17:68B7AB8957548DCD:899816990:1136043547::o:::sca:\n"
+	"fpr:::::::::6BD9050FD8FC941B43412DCC68B7AB8957548DCD:\n"
 	"uid:e::::1102866526::B712A25DC2ABEF1579696C2925859931078C2C3E::Werner Koch (gnupg sig) <dd9jn gnu org>:\n",
 
 	"pub:e:1024:17:5DE249965B0358A2:921520361:1247335656::o:::sc:\n"
+	"fpr:::::::::A4D94E92B0986AB5EE9DCD755DE249965B0358A2:\n"
 	"uid:e::::1113145458::F5B5738FAFB7543A01BAB31A6D767FBC789FF8A8::Werner Koch <wk gnupg org>:\n"
 	"uid:e::::1113145466::60095F7DAD08129CCE39E15BEB6BBE21937E3AA6::Werner Koch <wk g10code com>:\n"
 	"uid:e::::921520362::392B892CF897AD0F03EB26343C4C20A48B36513E::Werner Koch:\n"
@@ -111,6 +121,7 @@ static const gchar *pubring_records[] = {
 	"sub:e:2048:1:35E52D69C3680A6E:1136137762:1199123362:::::e:\n",
 
 	"pub:o:1024:17:C7463639B2D7795E:978642983:::o:::scSCE:\n"
+	"fpr:::::::::055FC78F112193492C4F37AFC7463639B2D7795E:\n"
 	"rvk:o::17::::::3FC732041D23E9EA66DDB5009C9DBC21DF74DC61:80:\n"
 	"uid:o::::978642983::44C6F00AAE524A8955CAB76F2BB16126530BB203::Philip R. Zimmermann <prz mit edu>:\n"
 	"uid:o::::978643127::BD93DF0D0D564E85F73ECBECFFB1B5BA5FF2838D::Philip R. Zimmermann <prz acm org>:\n"
@@ -120,15 +131,18 @@ static const gchar *pubring_records[] = {
 	"sub:o:3072:16:C4EB1C56A8E92834:978642983::::::e:\n",
 
 	"pub:o:4096:1:DB698D7199242560:1012189561:::o:::scSCEA:\n"
+	"fpr:::::::::7D92FD313AB6F3734CC59CA1DB698D7199242560:\n"
 	"uid:o::::1012189561::0E5FC22DD5518890217F20F1FF832597932B46C1::David M. Shaw <dshaw jabberwocky com>:\n"
 	"sub:o:2048:16:AE2827D11643B926:1012189956:1327549956:::::e:\n"
 	"sub:o:1024:17:E2665C8749E1CBC9:1012190171:1327550171:::::sca:\n",
 
 	"pub:o:2048:1:9710B89BCA57AD7C:1102303986:::o:::scSC:\n"
+	"fpr:::::::::50BB6FFC9719DFD2BEBB04C69710B89BCA57AD7C:\n"
 	"uid:o::::1112650864::A96F758EFD5D67EA9450860C7D15A96DAA1B40E2::PGP Global Directory Verification Key:\n"
 	"uat:o::::1112650864::83B0B68B95892BBCE32F04BA0FBAC6CEAD4EDE49::1 3422:\n",
 
 	"pub:e:1024:1:53B620D01CE0C630:1136130759:1230738759::o:::sc:\n"
+	"fpr:::::::::7B96D396E6471601754BE4DB53B620D01CE0C630:\n"
 	"uid:e::::1136130760::142B958D9816ECF810DBB83BD257E5C7DB36C99A::Werner Koch (dist sig) <dd9jn gnu org>:\n",
 
 	NULL
@@ -136,11 +150,13 @@ static const gchar *pubring_records[] = {
 
 static const gchar *secring_records[] = {
 	"sec::2048:1:4842D952AFC000FD:1305189489::::::::::\n"
+	"fpr:::::::::61A6EA3E0115080227A32EC94842D952AFC000FD:\n"
 	"uid:::::::D449F1605254754B0BBFA424FC34E50609103BBB::Test Number 1 (unlimited) <test-number-1 example com>:\n"
 	"uid:::::::D0A8FA7B15DC4BE3F8F03A49C372F2718C78AFC0::Dr. Strangelove <lovingbomb example com>:\n"
 	"ssb::2048:1:4852132BBED15014:1305189489::::::::::\n",
 
 	"sec::1024:1:268FEE686262C395:1305189628:1305276028:::::::::\n"
+	"fpr:::::::::A4853C22EA82C8ADC6692751268FEE686262C395:\n"
 	"uid:::::::2E9D48BD771DA765D2B48A0233D0E8F393F6E839::Test Number 2 (all gone) <test-number-2 example com>:\n"
 	"ssb::1024:1:C5877FABF4772E4F:1305189628::::::::::\n",
 
diff --git a/gcr/tests/test-record.c b/gcr/tests/test-record.c
index 4a18d44..cfb6593 100644
--- a/gcr/tests/test-record.c
+++ b/gcr/tests/test-record.c
@@ -128,10 +128,10 @@ test_find (void)
 	uid = _gcr_record_parse_colons ("uid:two", -1);
 	g_ptr_array_add (records, uid);
 
-	check = _gcr_record_find (records, GCR_RECORD_SCHEMA_PUB);
+	check = _gcr_records_find (records, GCR_RECORD_SCHEMA_PUB);
 	g_assert (check == pub);
 
-	check = _gcr_record_find (records, GCR_RECORD_SCHEMA_UID);
+	check = _gcr_records_find (records, GCR_RECORD_SCHEMA_UID);
 	g_assert (check == uid);
 
 	g_ptr_array_unref (records);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a27d835..d10485d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -25,6 +25,7 @@ gcr/gcr-failure-renderer.c
 [type: gettext/glade]gcr/gcr-import-dialog.ui
 gcr/gcr-gnupg-key.c
 gcr/gcr-gnupg-process.c
+gcr/gcr-gnupg-renderer.c
 gcr/gcr-importer.c
 gcr/gcr-library.c
 gcr/gcr-key-renderer.c



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