[gnome-keyring] gcr: Refactor the importer with multiple registered importers



commit 01098245b36a76321b6fc38dca50efa64b0a6b8e
Author: Stef Walter <stefw collabora co uk>
Date:   Wed Sep 21 13:55:51 2011 +0200

    gcr: Refactor the importer with multiple registered importers
    
     * Allow multiple importers to register, and have a way for
       them to identify whether something is importable by the
       importer or not.
     * Create a new GnuPG keyring importer.
     * Add boxed type for a GList of GObject
     * Some other minor cleanups.

 docs/reference/gck/gck-sections.txt |    2 +
 docs/reference/gcr/Makefile.am      |    4 +
 docs/reference/gcr/gcr-sections.txt |   79 +--
 gck/gck-misc.c                      |   12 +
 gck/gck.h                           |    4 +
 gck/gck.symbols                     |    1 +
 gcr/Makefile.am                     |    4 +-
 gcr/gcr-base.symbols                |   12 +
 gcr/gcr-certificate-widget.c        |    7 -
 gcr/gcr-deprecated-base.h           |    6 +
 gcr/gcr-deprecated.h                |    5 -
 gcr/gcr-gnupg-importer.c            |  315 ++++++++++
 gcr/gcr-gnupg-importer.h            |   67 ++
 gcr/gcr-gnupg-process.c             |    7 +
 gcr/gcr-gnupg-process.h             |    2 +
 gcr/gcr-import-dialog.c             |   15 +-
 gcr/gcr-import-dialog.h             |   17 +-
 gcr/gcr-importer.c                  | 1144 ++++++++++-------------------------
 gcr/gcr-importer.h                  |  109 ++--
 gcr/gcr-internal.h                  |    1 +
 gcr/gcr-key-widget.c                |    7 -
 gcr/gcr-pkcs11-importer.c           |  582 ++++++++++++++++++
 gcr/gcr-pkcs11-importer.h           |   68 ++
 gcr/gcr-record.h                    |   11 +
 gcr/gcr.symbols                     |   14 -
 po/POTFILES.in                      |    1 +
 tool/gkr-tool-import.c              |  138 ++++-
 27 files changed, 1602 insertions(+), 1032 deletions(-)
---
diff --git a/docs/reference/gck/gck-sections.txt b/docs/reference/gck/gck-sections.txt
index 21069a8..e200c28 100644
--- a/docs/reference/gck/gck-sections.txt
+++ b/docs/reference/gck/gck-sections.txt
@@ -315,4 +315,6 @@ gck_value_to_ulong
 GCK_INVALID
 <SUBSECTION Private>
 gck_get_error_quark
+GCK_TYPE_LIST
+gck_list_get_boxed_type
 </SECTION>
diff --git a/docs/reference/gcr/Makefile.am b/docs/reference/gcr/Makefile.am
index 47499c9..d13563f 100644
--- a/docs/reference/gcr/Makefile.am
+++ b/docs/reference/gcr/Makefile.am
@@ -64,11 +64,15 @@ IGNORE_HFILES= \
 	gcr-display-scrolled.h \
 	gcr-display-view.h \
 	gcr-failure-renderer.h \
+	gcr-gnupg-renderer.h \
 	gcr-icons.h \
 	gcr-import-dialog.h \
 	gcr-internal.h \
 	gcr-live-search.h \
 	gcr-marshal.h \
+	gcr-openpgp.h \
+	gcr-pkcs11-renderer.h \
+	gcr-record.h \
 	gcr-unlock-renderer.h \
 	gcr-xxx.h \
 	gcr-zzz.h
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index 08e7dd0..22e19b0 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -80,31 +80,40 @@ GcrGeneralNameType
 <SECTION>
 <FILE>gcr-importer</FILE>
 GcrImporter
-GcrImporterClass
-GcrImporterPromptBehavior
-gcr_importer_new
-gcr_importer_listen
-gcr_importer_queue
+GcrImporterIface
 gcr_importer_import
 gcr_importer_import_async
 gcr_importer_import_finish
-gcr_importer_get_slot
-gcr_importer_set_slot
-gcr_importer_get_prompt_behavior
-gcr_importer_set_prompt_behavior
-gcr_importer_get_parser
-gcr_importer_set_parser
+gcr_importer_register
+gcr_importer_register_well_known
+gcr_importer_create_for_parsed
+gcr_importer_queue_for_parsed
+gcr_importer_queue_and_filter_for_parsed
 <SUBSECTION Standard>
-GcrImporterPrivate
 GCR_IMPORTER
 GCR_IS_IMPORTER
 GCR_TYPE_IMPORTER
 gcr_importer_get_type
-GCR_IMPORTER_CLASS
-GCR_IS_IMPORTER_CLASS
-GCR_IMPORTER_GET_CLASS
-GCR_TYPE_IMPORTER_PROMPT_BEHAVIOR
-gcr_importer_prompt_behavior_get_type
+GCR_IMPORTER_GET_INTERFACE
+<SUBSECTION Private>
+GCR_GNUPG_IMPORTER
+GCR_GNUPG_IMPORTER_CLASS
+GCR_GNUPG_IMPORTER_GET_CLASS
+GCR_IS_GNUPG_IMPORTER
+GCR_IS_GNUPG_IMPORTER_CLASS
+GCR_IS_PKCS11_IMPORTER
+GCR_IS_PKCS11_IMPORTER_CLASS
+GCR_PKCS11_IMPORTER
+GCR_PKCS11_IMPORTER_CLASS
+GCR_PKCS11_IMPORTER_GET_CLASS
+GCR_TYPE_GNUPG_IMPORTER
+GCR_TYPE_PKCS11_IMPORTER
+GcrGnupgImporter
+GcrGnupgImporterClass
+GcrGnupgImporterPrivate
+GcrPkcs11Importer
+GcrPkcs11ImporterClass
+GcrPkcs11ImporterPrivate
 </SECTION>
 
 <SECTION>
@@ -533,17 +542,6 @@ GCR_VIEWER_WINDOW_GET_CLASS
 <SECTION>
 <FILE>gcr-private</FILE>
 <SUBSECTION Private>
-GCR_RECORD_SCHEMA_PUB
-GCR_RECORD_SCHEMA_UID
-GCR_RECORD_SCHEMA_SEC
-GCR_RECORD_SCHEMA_ATTRIBUTE
-GCR_RECORD_SCHEMA_FPR
-GCR_RECORD_SCHEMA_XA1
-GCR_RECORD_SCHEMA_RVK
-GCR_RECORD_SCHEMA_SIG
-GCR_RECORD_SCHEMA_SSB
-GCR_RECORD_SCHEMA_SUB
-GCR_RECORD_SCHEMA_UAT
 GCR_GNUPG_COLLECTION
 GCR_GNUPG_COLLECTION_CLASS
 GCR_GNUPG_COLLECTION_GET_CLASS
@@ -557,19 +555,6 @@ GCR_IS_GNUPG_KEY
 GCR_IS_GNUPG_KEY_CLASS
 GCR_TYPE_GNUPG_COLLECTION
 GCR_TYPE_GNUPG_KEY
-GCR_TYPE_RECORD
-GcrRecordColumns
-GcrRecordPubColumns
-GcrRecordUidColumns
-GcrRecordSecColumns
-GcrRecordAttributeColumns
-GcrRecordFprColumns
-GcrRecordXa1Columns
-GcrRecordKeyColumns
-GcrRecordRvkColumns
-GcrRecordSigColumns
-GcrRecordUatColumns
-GcrRecord
 GCR_GNUPG_PROCESS
 GCR_GNUPG_PROCESS_CLASS
 GCR_GNUPG_PROCESS_GET_CLASS
@@ -600,7 +585,6 @@ GcrMemoryIconPrivate
 GCR_ERROR
 gcr_error_get_domain
 GcrOpensshPubCallback
-GcrOpenpgpCallback
 GCR_CALLBACK_OUTPUT_STREAM
 GCR_CALLBACK_OUTPUT_STREAM_CLASS
 GCR_CALLBACK_OUTPUT_STREAM_GET_CLASS
@@ -610,15 +594,4 @@ GCR_TYPE_CALLBACK_OUTPUT_STREAM
 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/gck/gck-misc.c b/gck/gck-misc.c
index 4004954..563f40d 100644
--- a/gck/gck-misc.c
+++ b/gck/gck-misc.c
@@ -212,6 +212,18 @@ _gck_stringize_rv (CK_RV rv)
  * library or PKCS11 in general.
  */
 
+GType
+gck_list_get_boxed_type (void)
+{
+	static GType type = 0;
+	if (!type)
+		type = g_boxed_type_register_static ("GckList",
+		                                     (GBoxedCopyFunc)gck_list_ref_copy,
+		                                     (GBoxedFreeFunc)gck_list_unref_free);
+	return type;
+
+}
+
 /**
  * gck_list_unref_free:
  * @reflist: List of Gobject reference counted pointers.
diff --git a/gck/gck.h b/gck/gck.h
index 1d8ff3d..42064e3 100644
--- a/gck/gck.h
+++ b/gck/gck.h
@@ -50,6 +50,10 @@ G_BEGIN_DECLS
 
 GQuark              gck_get_error_quark                     (void);
 
+#define             GCK_TYPE_LIST                           (gck_list_get_boxed_type ())
+
+GType               gck_list_get_boxed_type                 (void) G_GNUC_CONST;
+
 GList*              gck_list_ref_copy                       (GList *reflist);
 
 void                gck_list_unref_free                     (GList *reflist);
diff --git a/gck/gck.symbols b/gck/gck.symbols
index c6cfb1e..18df7b6 100644
--- a/gck/gck.symbols
+++ b/gck/gck.symbols
@@ -59,6 +59,7 @@ gck_enumerator_next_n
 gck_get_error_quark
 gck_list_ref_copy
 gck_list_unref_free
+gck_list_get_boxed_type
 gck_mechanism_info_free
 gck_mechanisms_check
 gck_message_from_rv
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index f4f86e0..4d31f9a 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -97,11 +97,13 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-comparable.c gcr-comparable.h \
 	gcr-debug.c gcr-debug.h \
 	gcr-gnupg-collection.c gcr-gnupg-collection.h \
+	gcr-gnupg-importer.c gcr-gnupg-importer.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-importer.c gcr-importer.h \
 	gcr-internal.h \
 	gcr-memory.c \
 	gcr-memory-icon.c gcr-memory-icon.h \
@@ -109,6 +111,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-openssh.c gcr-openssh.h \
 	gcr-parser.c gcr-parser.h \
 	gcr-pkcs11-certificate.c gcr-pkcs11-certificate.h \
+	gcr-pkcs11-importer.c gcr-pkcs11-importer.h \
 	gcr-record.c gcr-record.h \
 	gcr-simple-certificate.c gcr-simple-certificate.h \
 	gcr-simple-collection.c gcr-simple-collection.h \
@@ -136,7 +139,6 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	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  \
 	gcr-key-renderer.c gcr-key-renderer.h \
 	gcr-key-widget.c gcr-key-widget.h \
 	gcr-list-selector.c gcr-list-selector.h gcr-list-selector-private.h \
diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols
index 49b54e8..015543c 100644
--- a/gcr/gcr-base.symbols
+++ b/gcr/gcr-base.symbols
@@ -52,6 +52,18 @@ gcr_data_error_get_domain
 gcr_data_error_get_type
 gcr_data_format_get_type
 gcr_error_get_domain
+gcr_importer_get_parser
+gcr_importer_get_prompt_behavior
+gcr_importer_get_slot
+gcr_importer_create_for_parsed
+gcr_importer_get_type
+gcr_importer_import
+gcr_importer_import_async
+gcr_importer_import_finish
+gcr_importer_queue_and_filter_for_parsed
+gcr_importer_queue_for_parsed
+gcr_importer_register
+gcr_importer_register_well_known
 gcr_parser_add_password
 gcr_parser_format_disable
 gcr_parser_format_enable
diff --git a/gcr/gcr-certificate-widget.c b/gcr/gcr-certificate-widget.c
index 82a8d91..272354b 100644
--- a/gcr/gcr-certificate-widget.c
+++ b/gcr/gcr-certificate-widget.c
@@ -169,7 +169,6 @@ static void
 gcr_certificate_widget_class_init (GcrCertificateWidgetClass *klass)
 {
 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-	GckAttributes *registered;
 
 	gcr_certificate_widget_parent_class = g_type_class_peek_parent (klass);
 	g_type_class_add_private (klass, sizeof (GcrCertificateWidgetPrivate));
@@ -186,12 +185,6 @@ gcr_certificate_widget_class_init (GcrCertificateWidgetClass *klass)
 	g_object_class_install_property (gobject_class, PROP_ATTRIBUTES,
 	         g_param_spec_boxed ("attributes", "Attributes", "Attributes which contain the certificate",
 	                             GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE));
-
-	/* Register this as a renderer which can be loaded */
-	registered = gck_attributes_new ();
-	gck_attributes_add_ulong (registered, CKA_CLASS, CKO_CERTIFICATE);
-	gcr_renderer_register (GCR_TYPE_CERTIFICATE_WIDGET, registered);
-	gck_attributes_unref (registered);
 }
 
 /* -----------------------------------------------------------------------------
diff --git a/gcr/gcr-deprecated-base.h b/gcr/gcr-deprecated-base.h
index 9f81281..b853670 100644
--- a/gcr/gcr-deprecated-base.h
+++ b/gcr/gcr-deprecated-base.h
@@ -31,6 +31,7 @@
 
 #include <glib.h>
 
+#include "gcr-importer.h"
 #include "gcr-parser.h"
 #include "gcr-simple-collection.h"
 
@@ -43,6 +44,11 @@ GQuark            gcr_error_get_domain                        (void) G_GNUC_CONS
 gboolean          gcr_simple_collection_contains              (GcrSimpleCollection *self,
                                                                GObject *object);
 
+GcrParser *       gcr_importer_get_parser                     (GcrImporter *self);
+
+void              gcr_importer_set_parser                     (GcrImporter *self,
+                                                               GcrParser *parser);
+
 G_END_DECLS
 
 #endif /* GCR_DISABLE_DEPRECATED */
diff --git a/gcr/gcr-deprecated.h b/gcr/gcr-deprecated.h
index b3bfad7..25cbc1d 100644
--- a/gcr/gcr-deprecated.h
+++ b/gcr/gcr-deprecated.h
@@ -41,11 +41,6 @@ G_BEGIN_DECLS
 void              gcr_renderer_render                         (GcrRenderer *self,
                                                                GcrViewer *viewer);
 
-GcrParser*        gcr_importer_get_parser                     (GcrImporter *self);
-
-void              gcr_importer_set_parser                     (GcrImporter *self,
-                                                               GcrParser *parser);
-
 G_END_DECLS
 
 #endif /* GCR_DISABLE_DEPRECATED */
diff --git a/gcr/gcr-gnupg-importer.c b/gcr/gcr-gnupg-importer.c
new file mode 100644
index 0000000..f527938
--- /dev/null
+++ b/gcr/gcr-gnupg-importer.c
@@ -0,0 +1,315 @@
+/*
+ * gnome-keyring
+ *
+ * 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-importer.h"
+#include "gcr-gnupg-process.h"
+#include "gcr-internal.h"
+
+#include <glib/gi18n-lib.h>
+
+enum {
+	PROP_0,
+	PROP_LABEL,
+	PROP_ICON,
+	PROP_IMPORTED,
+	PROP_DIRECTORY
+};
+
+struct _GcrGnupgImporterPrivate {
+	GcrGnupgProcess *process;
+	GMemoryInputStream *packets;
+	GArray *imported;
+};
+
+static void gcr_gnupg_importer_iface (GcrImporterIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrGnupgImporter, _gcr_gnupg_importer, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_IMPORTER, gcr_gnupg_importer_iface);
+);
+
+static void
+_gcr_gnupg_importer_init (GcrGnupgImporter *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporterPrivate);
+	self->pv->packets = G_MEMORY_INPUT_STREAM (g_memory_input_stream_new ());
+	self->pv->imported = g_array_new (TRUE, TRUE, sizeof (gchar *));
+}
+
+static void
+_gcr_gnupg_importer_dispose (GObject *obj)
+{
+	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj);
+
+	if (self->pv->process)
+		g_object_run_dispose (G_OBJECT (self->pv->process));
+	g_clear_object (&self->pv->process);
+	g_clear_object (&self->pv->packets);
+
+	G_OBJECT_CLASS (_gcr_gnupg_importer_parent_class)->dispose (obj);
+}
+
+static void
+_gcr_gnupg_importer_finalize (GObject *obj)
+{
+	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj);
+
+	g_array_free (self->pv->imported, TRUE);
+
+	G_OBJECT_CLASS (_gcr_gnupg_importer_parent_class)->finalize (obj);
+}
+
+static gchar *
+calculate_label (GcrGnupgImporter *self)
+{
+	const gchar *directory;
+
+	directory = _gcr_gnupg_process_get_directory (self->pv->process);
+	if (directory == NULL)
+		return g_strdup (_("GnuPG Keyring"));
+	else
+		return g_strdup_printf (_("GnuPG Keyring: %s"), directory);
+}
+
+static GIcon *
+calculate_icon (GcrGnupgImporter *self)
+{
+	const gchar *directory;
+
+	directory = _gcr_gnupg_process_get_directory (self->pv->process);
+	if (directory == NULL)
+		return g_themed_icon_new ("user-home");
+	else
+		return g_themed_icon_new ("folder");
+}
+
+static gboolean
+on_process_status_record (GcrGnupgProcess *process,
+                          GcrRecord *record,
+                          gpointer user_data)
+{
+	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (user_data);
+	const gchar *value;
+	gchar *fingerprint;
+
+	if (_gcr_record_get_schema (record) != GCR_RECORD_SCHEMA_IMPORT_OK)
+		return TRUE;
+
+	value = _gcr_record_get_raw (record, GCR_RECORD_IMPORT_FINGERPRINT);
+	if (value != NULL && value[0] != 0) {
+		fingerprint = g_strdup (value);
+		g_array_append_val (self->pv->imported, fingerprint);
+	}
+
+	return TRUE;
+}
+
+static void
+_gcr_gnupg_importer_set_property (GObject *obj,
+                                  guint prop_id,
+                                  const GValue *value,
+                                  GParamSpec *pspec)
+{
+	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj);
+
+	switch (prop_id) {
+	case PROP_DIRECTORY:
+		self->pv->process = _gcr_gnupg_process_new (g_value_get_string (value),
+		                                            NULL);
+		_gcr_gnupg_process_set_input_stream (self->pv->process, G_INPUT_STREAM (self->pv->packets));
+		g_signal_connect (self->pv->process, "status-record", G_CALLBACK (on_process_status_record), self);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gcr_gnupg_importer_get_property (GObject *obj,
+                                  guint prop_id,
+                                  GValue *value,
+                                  GParamSpec *pspec)
+{
+	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (obj);
+
+	switch (prop_id) {
+	case PROP_LABEL:
+		g_value_take_string (value, calculate_label (self));
+		break;
+	case PROP_ICON:
+		g_value_take_object (value, calculate_icon (self));
+		break;
+	case PROP_IMPORTED:
+		g_value_set_boxed (value, _gcr_gnupg_importer_get_imported (self));
+		break;
+	case PROP_DIRECTORY:
+		g_value_set_string (value, _gcr_gnupg_process_get_directory (self->pv->process));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gcr_gnupg_importer_class_init (GcrGnupgImporterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GckAttributes *registered;
+
+	gobject_class->dispose = _gcr_gnupg_importer_dispose;
+	gobject_class->finalize = _gcr_gnupg_importer_finalize;
+	gobject_class->set_property = _gcr_gnupg_importer_set_property;
+	gobject_class->get_property = _gcr_gnupg_importer_get_property;
+
+	g_type_class_add_private (gobject_class, sizeof (GcrGnupgImporterPrivate));
+
+	g_object_class_override_property (gobject_class, PROP_LABEL, "label");
+
+	g_object_class_override_property (gobject_class, PROP_ICON, "icon");
+
+	g_object_class_install_property (gobject_class, PROP_IMPORTED,
+	           g_param_spec_boxed ("imported", "Imported", "Fingerprints of imported keys",
+	                               G_TYPE_STRV, G_PARAM_READABLE));
+
+	g_object_class_install_property (gobject_class, PROP_DIRECTORY,
+	           g_param_spec_string ("directory", "Directory", "Directory to import keys to",
+	                                NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	registered = gck_attributes_new ();
+	gck_attributes_add_ulong (registered, CKA_CLASS, CKO_GCR_GNUPG_RECORDS);
+	gcr_importer_register (GCR_TYPE_GNUPG_IMPORTER, registered);
+	gck_attributes_unref (registered);
+
+	_gcr_initialize_library ();
+}
+
+static GList *
+_gcr_gnupg_importer_create_for_parsed (GcrParser *parser)
+{
+	GcrImporter *self;
+
+	if (gcr_parser_get_parsed_format (parser) != GCR_FORMAT_OPENPGP_PACKET)
+		return FALSE;
+
+	self = _gcr_gnupg_importer_new (NULL);
+	if (!gcr_importer_queue_for_parsed (self, parser))
+		g_assert_not_reached ();
+
+	return g_list_append (NULL, self);
+}
+
+static gboolean
+_gcr_gnupg_importer_queue_for_parsed (GcrImporter *importer,
+                                      GcrParser *parser)
+{
+	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (importer);
+	gconstpointer block;
+	gsize n_block;
+
+	if (gcr_parser_get_parsed_format (parser) != GCR_FORMAT_OPENPGP_PACKET)
+		return FALSE;
+
+	block = gcr_parser_get_parsed_block (parser, &n_block);
+	g_return_val_if_fail (block, FALSE);
+
+	g_memory_input_stream_add_data (self->pv->packets, g_memdup (block, n_block),
+	                                n_block, g_free);
+	return TRUE;
+}
+
+static void
+on_process_run_complete (GObject *source,
+                         GAsyncResult *result,
+                         gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GError *error = NULL;
+
+	if (!_gcr_gnupg_process_run_finish (GCR_GNUPG_PROCESS (source), result, &error))
+		g_simple_async_result_take_error (res, error);
+
+	g_simple_async_result_complete (res);
+	g_object_unref (res);
+}
+
+static void
+_gcr_gnupg_importer_import_async (GcrImporter *importer,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+	GcrGnupgImporter *self = GCR_GNUPG_IMPORTER (importer);
+	GSimpleAsyncResult *res;
+	const gchar *argv[] = { "--import", NULL };
+
+	res = g_simple_async_result_new (G_OBJECT (importer), callback, user_data,
+	                                 _gcr_gnupg_importer_import_async);
+
+	_gcr_gnupg_process_run_async (self->pv->process, argv, NULL,
+	                              GCR_GNUPG_PROCESS_WITH_STATUS,
+	                              cancellable, on_process_run_complete,
+	                              g_object_ref (res));
+
+	g_object_unref (res);
+}
+
+static gboolean
+_gcr_gnupg_importer_import_finish (GcrImporter *importer,
+                                   GAsyncResult *result,
+                                   GError **error)
+{
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (importer),
+	                      _gcr_gnupg_importer_import_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+gcr_gnupg_importer_iface (GcrImporterIface *iface)
+{
+	iface->create_for_parsed = _gcr_gnupg_importer_create_for_parsed;
+	iface->queue_for_parsed = _gcr_gnupg_importer_queue_for_parsed;
+	iface->import_async = _gcr_gnupg_importer_import_async;
+	iface->import_finish = _gcr_gnupg_importer_import_finish;
+}
+
+GcrImporter *
+_gcr_gnupg_importer_new (const gchar *directory)
+{
+	return g_object_new (GCR_TYPE_GNUPG_IMPORTER,
+	                     "directory", directory,
+	                     NULL);
+}
+
+const gchar **
+_gcr_gnupg_importer_get_imported (GcrGnupgImporter *self)
+{
+	g_return_val_if_fail (GCR_IS_GNUPG_IMPORTER (self), NULL);
+	return (const gchar **)self->pv->imported->data;
+}
diff --git a/gcr/gcr-gnupg-importer.h b/gcr/gcr-gnupg-importer.h
new file mode 100644
index 0000000..8511af8
--- /dev/null
+++ b/gcr/gcr-gnupg-importer.h
@@ -0,0 +1,67 @@
+/*
+ * gnome-keyring
+ *
+ * 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_IMPORTER_H__
+#define __GCR_GNUPG_IMPORTER_H__
+
+#include "gcr-importer.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_GNUPG_IMPORTER               (_gcr_gnupg_importer_get_type ())
+#define GCR_GNUPG_IMPORTER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporter))
+#define GCR_GNUPG_IMPORTER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporterClass))
+#define GCR_IS_GNUPG_IMPORTER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_GNUPG_IMPORTER))
+#define GCR_IS_GNUPG_IMPORTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_GNUPG_IMPORTER))
+#define GCR_GNUPG_IMPORTER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_GNUPG_IMPORTER, GcrGnupgImporterClass))
+
+typedef struct _GcrGnupgImporter GcrGnupgImporter;
+typedef struct _GcrGnupgImporterClass GcrGnupgImporterClass;
+typedef struct _GcrGnupgImporterPrivate GcrGnupgImporterPrivate;
+
+struct _GcrGnupgImporter {
+	GObject parent;
+
+	/*< private >*/
+	GcrGnupgImporterPrivate *pv;
+};
+
+struct _GcrGnupgImporterClass {
+	GObjectClass parent_class;
+};
+
+GType                   _gcr_gnupg_importer_get_type         (void) G_GNUC_CONST;
+
+GcrImporter *           _gcr_gnupg_importer_new              (const gchar *directory);
+
+const gchar **          _gcr_gnupg_importer_get_imported     (GcrGnupgImporter *self);
+
+G_END_DECLS
+
+#endif /* __GCR_IMPORTER_H__ */
diff --git a/gcr/gcr-gnupg-process.c b/gcr/gcr-gnupg-process.c
index ed4592a..0e87ea9 100644
--- a/gcr/gcr-gnupg-process.c
+++ b/gcr/gcr-gnupg-process.c
@@ -343,6 +343,13 @@ _gcr_gnupg_process_new (const gchar *directory, const gchar *executable)
 	                     NULL);
 }
 
+const gchar *
+_gcr_gnupg_process_get_directory (GcrGnupgProcess *self)
+{
+	g_return_val_if_fail (GCR_GNUPG_PROCESS (self), NULL);
+	return self->pv->directory;
+}
+
 GInputStream *
 _gcr_gnupg_process_get_input_stream (GcrGnupgProcess *self)
 {
diff --git a/gcr/gcr-gnupg-process.h b/gcr/gcr-gnupg-process.h
index 8eabfeb..820f371 100644
--- a/gcr/gcr-gnupg-process.h
+++ b/gcr/gcr-gnupg-process.h
@@ -68,6 +68,8 @@ GType               _gcr_gnupg_process_get_type                (void) G_GNUC_CON
 GcrGnupgProcess*    _gcr_gnupg_process_new                     (const gchar *directory,
                                                                 const gchar *executable);
 
+const gchar *       _gcr_gnupg_process_get_directory           (GcrGnupgProcess *self);
+
 GInputStream *      _gcr_gnupg_process_get_input_stream        (GcrGnupgProcess *self);
 
 void                _gcr_gnupg_process_set_input_stream        (GcrGnupgProcess *self,
diff --git a/gcr/gcr-import-dialog.c b/gcr/gcr-import-dialog.c
index 1196035..2c73924 100644
--- a/gcr/gcr-import-dialog.c
+++ b/gcr/gcr-import-dialog.c
@@ -27,21 +27,16 @@
 
 #include "egg/egg-entry-buffer.h"
 
+#if TODO
+
 enum {
 	PROP_0,
-	PROP_SELECTED_SLOT,
-	PROP_PASSWORD,
+	PROP_IMPORTER,
+	PROP_IMPORTERS,
 	PROP_PRIMARY_TEXT,
 	PROP_SECONDARY_TEXT
 };
 
-enum {
-	COLUMN_SLOT,
-	COLUMN_ICON,
-	COLUMN_LABEL,
-	N_COLUMNS
-};
-
 struct _GcrImportDialogPrivate {
 	GtkBuilder *builder;
 	GtkEntry *entry;
@@ -460,3 +455,5 @@ _gcr_import_dialog_set_secondary_text (GcrImportDialog *self, const gchar *text)
 	gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "secondary-text")), text);
 	g_object_notify (G_OBJECT (self), "primary-text");
 }
+
+#endif /* TODO */
diff --git a/gcr/gcr-import-dialog.h b/gcr/gcr-import-dialog.h
index 00e147e..692e71a 100644
--- a/gcr/gcr-import-dialog.h
+++ b/gcr/gcr-import-dialog.h
@@ -52,24 +52,15 @@ struct _GcrImportDialogClass {
 
 GType               _gcr_import_dialog_get_type               (void);
 
-GcrImportDialog*    _gcr_import_dialog_new                    (void);
+GcrImportDialog *   _gcr_import_dialog_new                    (GList *importers);
 
 gboolean            _gcr_import_dialog_run                    (GcrImportDialog *self,
                                                                GtkWindow *parent);
 
-GckSlot*            _gcr_import_dialog_get_selected_slot      (GcrImportDialog *self);
+GcrImporter *       _gcr_import_dialog_get_importer           (GcrImportDialog *self);
 
-void                _gcr_import_dialog_set_selected_slot      (GcrImportDialog *self,
-                                                               GckSlot *slot);
-
-void                _gcr_import_dialog_show_selected_slot     (GcrImportDialog *self);
-
-void                _gcr_import_dialog_hide_selected_slot     (GcrImportDialog *self);
-
-const gchar*        _gcr_import_dialog_get_password           (GcrImportDialog *self);
-
-void                _gcr_import_dialog_set_password           (GcrImportDialog *self,
-                                                               const gchar *password);
+void                _gcr_import_dialog_set_importer           (GcrImportDialog *self,
+                                                               GcrImporter *importer);
 
 void                _gcr_import_dialog_show_password          (GcrImportDialog *self);
 
diff --git a/gcr/gcr-importer.c b/gcr/gcr-importer.c
index 1f0f6dc..49d8cd6 100644
--- a/gcr/gcr-importer.c
+++ b/gcr/gcr-importer.c
@@ -1,976 +1,454 @@
-/* 
+/*
  * gnome-keyring
- * 
- * Copyright (C) 2008 Stefan Walter
- * 
- * This program is free software; you can redistribute it and/or modify 
+ *
+ * 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.  
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
  */
 
 #include "config.h"
 
-#include "gcr-import-dialog.h"
+#include "gcr-base.h"
 #include "gcr-importer.h"
 #include "gcr-internal.h"
 #include "gcr-marshal.h"
+#include "gcr-gnupg-importer.h"
 #include "gcr-parser.h"
+#include "gcr-pkcs11-importer.h"
 
 #include <glib/gi18n-lib.h>
 
 /**
  * SECTION:gcr-importer
  * @title: GcrImporter
- * @short_description: Import objects into PKCS\#11 slots.
+ * @short_description: Import certificates and keys
+ *
+ * An interface which allows importing of certificates and keys. Each
+ * #GcrImporter is registered with a set of PKCS#11 attributes to match
+ * stuff that it can import.
  *
- * A #GcrImporter can be used to import items into PKCS\#11 slots. It's most
- * often used to parse the objects parsed with a #GcrParser. Use
- * gcr_importer_listen() to hook up the importer to the parser.
+ * An importer gets passed a #GcrParser and accesses the currently parsed
+ * item. To create a set of importers that can import the currently parsed
+ * item in a #GcrParser, use gcr_importer_create_for_parsed(). The list of
+ * importers returned has the parsed item queued for import.
  *
- * Items are queued, and then imported with gcr_importer_import() or
- * gcr_importer_import_async().
+ * To queue additional items with a importer use gcr_importer_queue_for_parsed().
+ * In addition you can try and queue an additional item with a set of importers
+ * using the gcr_importer_queue_and_filter_for_parsed().
+ *
+ * To start the import use gcr_importer_import() or the async variants.
  */
 
 /**
  * GcrImporter:
  *
- * Imports items into PKCS\#11
+ * Imports certificates and keys
  */
 
 /**
- * GcrImporterClass:
- * @parent_class: The parent class
- * @queued: Signal which is fired when an item is queued
- * @imported: Signal which is fired when an item is imported
+ * GcrImporterIface:
  *
- * The class for #GcrImporter.
+ * Interface implemented for a #GcrImporter.
  */
 
-/**
- * GcrImporterPromptBehavior:
- * @GCR_IMPORTER_PROMPT_NEEDED: Prompt when needed.
- * @GCR_IMPORTER_PROMPT_ALWAYS: Always prompt.
- * @GCR_IMPORTER_PROMPT_NEVER: Never prompt.
- *
- * Flags for the prompting behavior of #GcrImporter.
- */
+typedef GcrImporterIface GcrImporterInterface;
 
-enum {
-	PROP_0,
-	PROP_SLOT,
-	PROP_PROMPT_BEHAVIOR
-};
-
-enum {
-	QUEUED,
-	IMPORTED,
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-struct _GcrImporterPrivate {
-	GckSlot *slot;
-	GcrParser *parser;
-	GcrImporterPromptBehavior behavior;
-	
-	/* Information about last import */
-	GError *error;
-	gboolean succeeded;
-	
-	/* State data during import */
-	gboolean processing;
-	GCancellable *cancel;
-	gboolean prompted;
-	gboolean async;
-	GByteArray *buffer;
-	GckSession *session;
-	GQueue queue;
-	gboolean any_private;
-
-	/* Extra async stuff */
-	GAsyncReadyCallback callback;
-	gpointer user_data;
-};
-
-/* State forward declarations */
-static void state_cancelled (GcrImporter *self, gboolean async);
-static void state_complete (GcrImporter *self, gboolean async);
-static void state_create_object (GcrImporter *self, gboolean async);
-static void state_open_session (GcrImporter *self, gboolean async);
-static void state_initialize_pin (GcrImporter *self, gboolean async);
-
-static void gcr_importer_async_result (GAsyncResultIface *iface);
-G_DEFINE_TYPE_WITH_CODE (GcrImporter, gcr_importer, G_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gcr_importer_async_result));
-
-#define BLOCK 4096
-
-/* -----------------------------------------------------------------------------
- * INTERNAL 
- */
+G_DEFINE_INTERFACE (GcrImporter, gcr_importer, 0);
 
-static void
-cleanup_state_data (GcrImporter *self)
-{
+typedef struct _GcrRegistered {
 	GckAttributes *attrs;
+	GType importer_type;
+} GcrRegistered;
 
-	if (self->pv->buffer)
-		g_byte_array_free (self->pv->buffer, TRUE);
-	self->pv->buffer = NULL;
-
-	if (self->pv->session)
-		g_object_unref (self->pv->session);
-	self->pv->session = NULL;
-	
-	while ((attrs = g_queue_pop_head (&self->pv->queue)) != NULL)
-		gck_attributes_unref (attrs);
-	g_assert (g_queue_is_empty (&self->pv->queue));
-	self->pv->any_private = FALSE;
-
-	if (self->pv->cancel)
-		g_object_unref (self->pv->cancel);
-	self->pv->cancel = NULL;
-}
+static GArray *registered_importers = NULL;
+static gboolean registered_sorted = FALSE;
 
 static void
-cleanup_import_data (GcrImporter *self)
-{
-	if (self->pv->error)
-		g_clear_error (&self->pv->error);
-	self->pv->succeeded = TRUE;
-}
-
-static void
-next_state (GcrImporter *self, void (*state) (GcrImporter*, gboolean))
-{
-	g_assert (GCR_IS_IMPORTER (self));
-	g_assert (self->pv->processing);
-	g_assert (state);
-	
-	if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel))
-		state = state_cancelled;
-	
-	(state) (self, self->pv->async);
-}
-
-static const gchar*
-prepare_auth_primary (CK_OBJECT_CLASS klass)
-{
-	if (klass == CKO_PRIVATE_KEY)
-		return _("Enter password to unlock the private key");
-	else if (klass == CKO_CERTIFICATE)
-		return _("Enter password to unlock the certificate");
-	else
-		return _("Enter password to unlock");
-}
-
-static gchar*
-prepare_auth_secondary (CK_OBJECT_CLASS klass, const gchar *label)
-{
-	if (label == NULL) {
-		if (klass == CKO_PRIVATE_KEY) {
-			/* TRANSLATORS: The key is locked. */
-			return g_strdup (_("In order to import the private key, it must be unlocked"));
-		} else if (klass == CKO_CERTIFICATE) {
-			/* TRANSLATORS: The certificate is locked. */
-			return g_strdup (_("In order to import the certificate, it must be unlocked"));
-		} else {
-			/* TRANSLATORS: The data is locked. */
-			return g_strdup (_("In order to import the data, it must be unlocked"));
-		}
-	} else {
-		if (klass == CKO_PRIVATE_KEY) {
-			/* TRANSLATORS: The key is locked. */
-			return g_strdup_printf (_("In order to import the private key '%s', it must be unlocked"), label);
-		} else if (klass == CKO_CERTIFICATE) {
-			/* TRANSLATORS: The certificate is locked. */
-			return g_strdup_printf (_("In order to import the certificate '%s', it must be unlocked"), label);
-		} else {
-			/* TRANSLATORS: The object '%s' is locked. */
-			return g_strdup_printf (_("In order to import '%s', it must be unlocked"), label);
-		}
+gcr_importer_default_init (GcrImporterIface *iface)
+{
+	static volatile gsize initialized = 0;
+
+	if (g_once_init_enter (&initialized)) {
+
+		/**
+		 * GcrImporter:label:
+		 *
+		 * The label for the importer.
+		 */
+		g_object_interface_install_property (iface,
+		         g_param_spec_string ("label", "Label", "The label for the importer",
+		                              "", G_PARAM_READABLE));
+
+		/**
+		 * GcrImporter:icon:
+		 *
+		 * The icon for the importer.
+		 */
+		g_object_interface_install_property (iface,
+		         g_param_spec_object ("icon", "Icon", "The icon for the importer",
+		                              G_TYPE_ICON, G_PARAM_READABLE));
+
+		g_once_init_leave (&initialized, 1);
 	}
 }
 
-static void
-on_parser_parsed (GcrParser *parser, GcrImporter *self)
+/**
+ * gcr_importer_register:
+ * @importer_type: the GType of the importer being registered
+ * @attrs: the attributes that this importer is compatible with
+ *
+ * Register an importer to handle parsed items that match the given attributes.
+ */
+void
+gcr_importer_register (GType importer_type,
+                       GckAttributes *attrs)
 {
-	GckAttributes *attrs;
+	GcrRegistered registered;
 
-	g_return_if_fail (GCR_IS_PARSER (parser));
-	g_return_if_fail (GCR_IS_IMPORTER (self));
+	if (!registered_importers)
+		registered_importers = g_array_new (FALSE, FALSE, sizeof (GcrRegistered));
 
-	attrs = gcr_parser_get_parsed_attributes (parser);
-	g_return_if_fail (attrs);
-
-	gcr_importer_queue (self, gcr_parser_get_parsed_label (parser), attrs);
+	registered.importer_type = importer_type;
+	registered.attrs = gck_attributes_ref (attrs);
+	g_array_append_val (registered_importers, registered);
+	registered_sorted = FALSE;
 }
 
-static gboolean
-on_parser_authenticate (GcrParser *parser, gint count, GcrImporter *self)
+static gint
+sort_registered_by_n_attrs (gconstpointer a, gconstpointer b)
 {
-	GcrImportDialog *dialog;
-	GckAttributes *attrs;
-	const gchar *password;
-	gchar *text, *label;
-	GckSlot *slot;
-	gulong klass;
-
-	dialog = _gcr_import_dialog_new ();
-
-	if (self->pv->slot)
-		_gcr_import_dialog_set_selected_slot (dialog, self->pv->slot);
-
-	/* Figure out the text for the dialog */
-	attrs = gcr_parser_get_parsed_attributes (parser);
-	g_return_val_if_fail (attrs, FALSE);
-
-	if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &klass))
-		klass = (gulong)-1;
-	if (!gck_attributes_find_string (attrs, CKA_LABEL, &label))
-		label = NULL;
+	const GcrRegistered *ra = a;
+	const GcrRegistered *rb = b;
+	gulong na, nb;
 
-	text = prepare_auth_secondary (klass, label);
-	_gcr_import_dialog_set_primary_text (dialog, prepare_auth_primary (klass));
-	_gcr_import_dialog_set_secondary_text (dialog, text);
-	g_free (label);
-	g_free (text);
+	g_assert (a);
+	g_assert (b);
 
-	if (!_gcr_import_dialog_run (dialog, NULL))
-		return FALSE;
+	na = gck_attributes_count (ra->attrs);
+	nb = gck_attributes_count (rb->attrs);
 
-	slot = _gcr_import_dialog_get_selected_slot (dialog);
-	gcr_importer_set_slot (self, slot);
-
-	password = _gcr_import_dialog_get_password (dialog);
-	gcr_parser_add_password (parser, password);
-
-	g_object_unref (dialog);
-	self->pv->prompted = TRUE;
-	return TRUE;
+	/* Note we're sorting in reverse order */
+	if (na < nb)
+		return 1;
+	return (na == nb) ? 0 : -1;
 }
 
-/* ---------------------------------------------------------------------------------
- * COMPLETE
+/**
+ * gcr_importer_create_for_parsed:
+ * @parsed: a parser with a parsed item to import
+ *
+ * Create a set of importers which can import this parsed item.
+ * The parsed item is represented by the state of the GcrParser at the
+ * time of calling this method.
+ *
+ * Returns: a list of importers which can import the parsed item, which
+ *          should be freed with gck_list_unref_free().
  */
-
-static void
-state_complete (GcrImporter *self, gboolean async)
-{
-	if (async && self->pv->callback != NULL)
-		(self->pv->callback) (G_OBJECT (self), G_ASYNC_RESULT (self), self->pv->user_data);
-	
-	cleanup_state_data (self);
-	self->pv->processing = FALSE;
-}
-
-static void
-state_failure (GcrImporter *self, gboolean async)
-{
-	self->pv->succeeded = FALSE;
-	next_state (self, state_complete);
-}
-
-static void
-state_cancelled (GcrImporter *self, gboolean async)
+GList *
+gcr_importer_create_for_parsed (GcrParser *parser)
 {
-	if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel))
-		g_cancellable_cancel (self->pv->cancel);
-	if (self->pv->error)
-		g_error_free (self->pv->error);
-	self->pv->error = g_error_new_literal (GCR_DATA_ERROR, GCR_ERROR_CANCELLED, _("The operation was cancelled"));
-	next_state (self, state_failure);
-}
+	GcrRegistered *registered;
+	GcrImporterIface *iface;
+	gpointer instance_class;
+	GckAttributes *attrs;
+	gboolean matched;
+	gulong n_attrs;
+	GList *results = NULL;
+	gulong j;
+	gsize i;
 
-/* ---------------------------------------------------------------------------------
- * CREATE OBJECTS
- */
+	g_return_val_if_fail (GCR_IS_PARSER (parser), NULL);
 
-static void
-complete_create_object (GcrImporter *self, GckObject *object, GError *error)
-{
-	if (object == NULL) {
-		g_propagate_error (&self->pv->error, error);
-		next_state (self, state_failure);
-		
-	} else {
-		g_signal_emit (self, signals[IMPORTED], 0, object);
-		g_object_unref (object);
-		next_state (self, state_create_object);
-	}
-}
+	gcr_importer_register_well_known ();
 
-static void
-on_create_object (GObject *obj, GAsyncResult *res, gpointer user_data)
-{
-	GError *error = NULL;
-	GckObject *object = gck_session_create_object_finish (GCK_SESSION (obj), res, &error);
-	complete_create_object (GCR_IMPORTER (user_data), object, error);
-}
+	if (!registered_importers)
+		return NULL;
 
-static void
-state_create_object (GcrImporter *self, gboolean async)
-{
-	GckAttributes *attrs;
-	GckObject *object;
-	GError *error = NULL;
-	
-	/* No more objects */
-	if (g_queue_is_empty (&self->pv->queue)) {
-		next_state (self, state_complete);
-		
-	} else {
-		
-		/* Pop first one off the list */
-		attrs = g_queue_pop_head (&self->pv->queue);
-		g_assert (attrs);
-
-		gck_attributes_add_boolean (attrs, CKA_TOKEN, CK_TRUE);
-
-		if (async) {
-			gck_session_create_object_async (self->pv->session, attrs, self->pv->cancel,
-			                                  on_create_object, self);
-		} else {
-			object = gck_session_create_object (self->pv->session, attrs, self->pv->cancel, &error);
-			complete_create_object (self, object, error);
-		}
-	
-		gck_attributes_unref (attrs);
+	if (!registered_sorted) {
+		g_array_sort (registered_importers, sort_registered_by_n_attrs);
+		registered_sorted = TRUE;
 	}
-}
 
-/* ---------------------------------------------------------------------------------
- * OPEN SESSION
- */
+	attrs = gcr_parser_get_parsed_attributes (parser);
+	if (attrs != NULL)
+		gck_attributes_ref (attrs);
+	else
+		attrs = gck_attributes_new ();
 
-static void
-complete_open_session (GcrImporter *self, GckSession *session, GError *error)
-{
-	if (!session) {
-		g_propagate_error (&self->pv->error, error);
-		next_state (self, state_failure);
-	} else {
-		self->pv->session = session;
-		next_state (self, state_create_object);
-	}
-}
+	for (i = 0; i < registered_importers->len; ++i) {
+		registered = &(g_array_index (registered_importers, GcrRegistered, i));
+		n_attrs = gck_attributes_count (registered->attrs);
 
-static void
-on_open_session (GObject *obj, GAsyncResult *res, gpointer user_data)
-{
-	GError *error = NULL;
-	GckSession *session = gck_slot_open_session_finish (GCK_SLOT (obj), res, &error);
-	complete_open_session (GCR_IMPORTER (user_data), session, error);
-}
+		matched = TRUE;
 
-static void
-state_open_session (GcrImporter *self, gboolean async)
-{
-	guint options = GCK_SESSION_READ_WRITE;
-	GckSession *session;
-	GError *error = NULL;
-	
-	if (!self->pv->slot) {
-		g_set_error (&self->pv->error, GCR_DATA_ERROR, GCR_ERROR_FAILURE, _("No location available to import to"));
-		next_state (self, state_failure);
-		
-	} else {
-		if (self->pv->any_private)
-			options |= GCK_SESSION_LOGIN_USER;
-
-		if (async) {
-			gck_slot_open_session_async (self->pv->slot, options, self->pv->cancel,
-			                             on_open_session, self);
-		} else {
-			session = gck_slot_open_session_full (self->pv->slot, options, 0, NULL, NULL,
-			                                      self->pv->cancel, &error);
-			complete_open_session (self, session, error);
+		for (j = 0; j < n_attrs; ++j) {
+			if (!gck_attributes_contains (attrs, gck_attributes_at (registered->attrs, j))) {
+				matched = FALSE;
+				break;
+			}
 		}
-	}
-}
 
-/* ---------------------------------------------------------------------------------
- * INITIALIZE TOKEN
- * 
- * HACK: This is a big temporary hack to get, until the next version 
- * when we can fix this correctly.  
- */
+		if (matched) {
+			instance_class = g_type_class_ref (registered->importer_type);
 
-static CK_RV
-hacky_perform_initialize_pin (GckSlot *slot)
-{
-	CK_FUNCTION_LIST_PTR funcs;
-	CK_SESSION_HANDLE session;
-	CK_SLOT_ID slot_id;
-	CK_RV rv;
-	
-	/* 
-	 * This hack only works when:
-	 *  
-	 *  - Module is protected authentication path
-	 *  - No other sessions are open.
-	 *  
-	 *  Thankfully this is the case with gnome-keyring-daemon and 
-	 *  the gnome-keyring tool. 
-	 */
-	
-	funcs = gck_module_get_functions (gck_slot_get_module (slot));
-	g_return_val_if_fail (funcs, CKR_GENERAL_ERROR);
-	slot_id = gck_slot_get_handle (slot);
-	
-	rv = funcs->C_OpenSession (slot_id, CKF_RW_SESSION | CKF_SERIAL_SESSION, NULL, NULL, &session);
-	if (rv != CKR_OK)
-		return rv;
-	
-	rv = funcs->C_Login (session, CKU_SO, NULL, 0);
-	if (rv == CKR_OK) {
-		rv = funcs->C_InitPIN (session, NULL, 0);
-		funcs->C_Logout (session);
-	}
-	
-	funcs->C_CloseSession (session);
-	
-	return rv;
-}
+			iface = g_type_interface_peek (instance_class, GCR_TYPE_IMPORTER);
+			g_return_val_if_fail (iface != NULL, NULL);
+			g_return_val_if_fail (iface->create_for_parsed, NULL);
+			results = g_list_concat (results, (iface->create_for_parsed) (parser));
 
-static void
-state_initialize_pin (GcrImporter *self, gboolean async)
-{
-	GckTokenInfo *info;
-	gboolean initialize;
-	CK_RV rv;
-	
-	g_assert (GCR_IS_IMPORTER (self));
-	
-	/* HACK: Doesn't function when async */
-	if (!async) {
-		g_return_if_fail (self->pv->slot);
-		info = gck_slot_get_token_info (self->pv->slot);
-		g_return_if_fail (info);
-	
-		initialize = !(info->flags & CKF_USER_PIN_INITIALIZED);
-		gck_token_info_free (info);
-
-		if (initialize) {
-			rv = hacky_perform_initialize_pin (self->pv->slot);
-			if (rv != CKR_OK) {
-				g_propagate_error (&self->pv->error, g_error_new (GCK_ERROR, rv, "%s", gck_message_from_rv (rv)));
-				next_state (self, state_failure);
-				return;
-			} 
+			g_type_class_unref (instance_class);
 		}
 	}
 
-	next_state (self, state_open_session);
+	gck_attributes_unref (attrs);
+	return results;
 }
 
-/* ---------------------------------------------------------------------------------
- * IMPORT PROMPT
+/**
+ * gcr_importer_queue_for_parsed:
+ * @importer: an importer to add additional items to
+ * @parser: a parser with a parsed item to import
+ *
+ * Queues an additional item to be imported. The parsed item is represented
+ * by the state of the #GcrParser at the time of calling this method.
+ *
+ * If the parsed item is incompatible with the importer, then this will
+ * fail and the item will not be queued.
+ *
+ * Returns: whether the item was queued or not
  */
-
-static void
-complete_import_prompt (GcrImporter *self, GcrImportDialog *dialog, gint response)
+gboolean
+gcr_importer_queue_for_parsed (GcrImporter *importer,
+                               GcrParser *parser)
 {
-	GckSlot *slot;
+	GcrImporterIface *iface;
 
-	gtk_widget_hide (GTK_WIDGET (dialog));
-	self->pv->prompted = TRUE;
+	g_return_val_if_fail (GCR_IS_IMPORTER (importer), FALSE);
+	g_return_val_if_fail (GCR_IS_PARSER (parser), FALSE);
 
-	/* No dialog or dialog completed */
-	if (response == GTK_RESPONSE_OK) {
+	iface = GCR_IMPORTER_GET_INTERFACE (importer);
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (iface->queue_for_parsed != NULL, FALSE);
 
-		slot = _gcr_import_dialog_get_selected_slot (dialog);
-		gcr_importer_set_slot (self, slot);
-		next_state (self, state_initialize_pin);
-		
-	/* The dialog was cancelled or closed */
-	} else {
-		next_state (self, state_cancelled);
-	}
-}
-
-static void
-on_prompt_response (GtkDialog *dialog, gint response, gpointer user_data)
-{
-	complete_import_prompt (GCR_IMPORTER (user_data), GCR_IMPORT_DIALOG (dialog), response);
-	g_object_unref (dialog);
+	return (iface->queue_for_parsed) (importer, parser);
 }
 
-static void 
-state_import_prompt (GcrImporter *self, gboolean async)
-{
-	GcrImportDialog *dialog;
-	gboolean prompt;
-	gint response;
-	
-	g_assert (GCR_IS_IMPORTER (self));
-	
-	/* No need to prompt */
-	if (self->pv->prompted == TRUE)
-		prompt = FALSE;
-	else if (self->pv->behavior == GCR_IMPORTER_PROMPT_ALWAYS)
-		prompt = TRUE;
-	else if (self->pv->behavior == GCR_IMPORTER_PROMPT_NEVER)
-		prompt = FALSE;
-	else 
-		prompt = self->pv->slot ? FALSE : TRUE;
-	
-	if (prompt == FALSE) {
-		next_state (self, state_initialize_pin);
-		
-	} else {
-		
-		dialog = _gcr_import_dialog_new ();
-
-		_gcr_import_dialog_set_primary_text (dialog, _("Import Certificates/Keys"));
-		_gcr_import_dialog_hide_password (dialog);
-		
-		if (self->pv->slot) {
-			_gcr_import_dialog_set_selected_slot (dialog, self->pv->slot);
-			_gcr_import_dialog_hide_selected_slot (dialog);
-		} else {
-			_gcr_import_dialog_set_secondary_text (dialog, _("Choose a location to store the imported certificates/keys."));
-		}
-			
-		/* Prompt without blocking main loop */
-		if (async) {
-			g_signal_connect (dialog, "response", G_CALLBACK (on_prompt_response), self);
-			gtk_widget_show (GTK_WIDGET (dialog));
-			
-		/* Block mainloop */
-		} else {
-			response = gtk_dialog_run (GTK_DIALOG (dialog));
-			complete_import_prompt (self, dialog, response);
-			g_object_unref (dialog);
-		}
-	}
-}
-
-/* -----------------------------------------------------------------------------
- * OBJECT 
+/**
+ * gcr_importer_queue_and_filter_for_parsed:
+ * @importer: a set of importers
+ * @parser: a parser with a parsed item to import
+ *
+ * Queues an additional item to be imported in all compattible importers
+ * in the set. The parsed item is represented by the state of the #GcrParser
+ * at the time of calling this method.
+ *
+ * If the parsed item is incompatible with an importer, then that the item
+ * will not be queued on that importer.
+ *
+ * Returns: a new set of importers that queued the item, which should be freed
+ *          with gck_list_unref_free().
  */
-
-static GObject* 
-gcr_importer_constructor (GType type, guint n_props, GObjectConstructParam *props) 
+GList *
+gcr_importer_queue_and_filter_for_parsed (GList *importers,
+                                          GcrParser *parser)
 {
-	GcrImporter *self = GCR_IMPORTER (G_OBJECT_CLASS (gcr_importer_parent_class)->constructor(type, n_props, props));
-	g_return_val_if_fail (self, NULL);	
-	
-	return G_OBJECT (self);
-}
+	GList *results = NULL;
+	GList *l;
 
-static void
-gcr_importer_init (GcrImporter *self)
-{
-	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_IMPORTER, GcrImporterPrivate);
-	self->pv->behavior = GCR_IMPORTER_PROMPT_NEEDED;
-	g_queue_init (&self->pv->queue);
-}
+	for (l = importers; l != NULL; l = g_list_next (l)) {
+		if (gcr_importer_queue_for_parsed (l->data, parser))
+			results = g_list_prepend (results, g_object_ref (l->data));
+	}
 
-static void
-gcr_importer_dispose (GObject *obj)
-{
-	GcrImporter *self = GCR_IMPORTER (obj);
-	
-	cleanup_state_data (self);
-	cleanup_import_data (self);
-	
-	if (self->pv->parser)
-		g_object_unref (self->pv->parser);
-	self->pv->parser = NULL;
-	
-	if (self->pv->slot)
-		g_object_unref (self->pv->slot);
-	self->pv->slot = NULL;
-
-	G_OBJECT_CLASS (gcr_importer_parent_class)->dispose (obj);
+	return g_list_reverse (results);
 }
 
-static void
-gcr_importer_finalize (GObject *obj)
-{
-	GcrImporter *self = GCR_IMPORTER (obj);
-	
-	g_assert (!self->pv->parser);
-	g_assert (!self->pv->slot);
-	
-	G_OBJECT_CLASS (gcr_importer_parent_class)->finalize (obj);
-}
+typedef struct {
+	gboolean complete;
+	GCond *cond;
+	GMutex *mutex;
+	GError *error;
+	GMainContext *context;
+} ImportClosure;
 
 static void
-gcr_importer_set_property (GObject *obj, guint prop_id, const GValue *value, 
-                           GParamSpec *pspec)
+on_import_async_complete (GObject *source,
+                          GAsyncResult *result,
+                          gpointer user_data)
 {
-	GcrImporter *self = GCR_IMPORTER (obj);
-	
-	switch (prop_id) {
-	case PROP_SLOT:
-		gcr_importer_set_slot (self, g_value_get_object (value));
-		break;
-	case PROP_PROMPT_BEHAVIOR:
-		gcr_importer_set_prompt_behavior (self, (GcrImporterPromptBehavior)g_value_get_int (value));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
-		break;
-	}
-}
+	ImportClosure *closure = user_data;
+	GError *error = NULL;
 
-static void
-gcr_importer_get_property (GObject *obj, guint prop_id, GValue *value, 
-                           GParamSpec *pspec)
-{
-	GcrImporter *self = GCR_IMPORTER (obj);
-	
-	switch (prop_id) {
-	case PROP_SLOT:
-		g_value_set_object (value, gcr_importer_get_slot (self));
-		break;
-	case PROP_PROMPT_BEHAVIOR:
-		g_value_set_int (value, gcr_importer_get_prompt_behavior (self));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
-		break;
+	if (!gcr_importer_import_finish (GCR_IMPORTER (source), result, &error)) {
+		if (error == NULL) {
+			g_warning ("%s::import_finished returned false, but did not set error",
+			           G_OBJECT_TYPE_NAME (source));
+		}
 	}
-}
 
-static void
-gcr_importer_class_init (GcrImporterClass *klass)
-{
-	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-    
-	gobject_class->constructor = gcr_importer_constructor;
-	gobject_class->dispose = gcr_importer_dispose;
-	gobject_class->finalize = gcr_importer_finalize;
-	gobject_class->set_property = gcr_importer_set_property;
-	gobject_class->get_property = gcr_importer_get_property;
-    
-	g_type_class_add_private (gobject_class, sizeof (GcrImporterPrivate));
-
-	g_object_class_install_property (gobject_class, PROP_SLOT,
-	           g_param_spec_object ("slot", "Slot", "PKCS#11 slot to import data into",
-	                                GCK_TYPE_SLOT, G_PARAM_READWRITE));
-
-	g_object_class_install_property (gobject_class, PROP_PROMPT_BEHAVIOR,
-	           g_param_spec_int ("prompt-behavior", "Prompt Behavior", "Import Prompt Behavior",
-	                             0, G_MAXINT, GCR_IMPORTER_PROMPT_NEEDED, G_PARAM_READWRITE));
-
-	/**
-	 * GcrImporter::queued:
-	 * @label: The label of the queued item.
-	 * @attrs: The attributes of the queued item.
-	 *
-	 * This signal is emitted when an item is queued for import.
-	 */
-	signals[QUEUED] = g_signal_new ("queued", GCR_TYPE_IMPORTER,
-	                                G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, queued),
-	                                NULL, NULL, _gcr_marshal_VOID__STRING_BOXED,
-	                                G_TYPE_NONE, 1, G_TYPE_STRING, GCK_TYPE_ATTRIBUTES);
-
-	/**
-	 * GcrImporter::imported:
-	 * @object: The object which was imported.
-	 *
-	 * This signal is emitted when an item has been imported.
-	 */
-	signals[IMPORTED] = g_signal_new ("imported", GCR_TYPE_IMPORTER, 
-	                                G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, imported),
-	                                NULL, NULL, g_cclosure_marshal_VOID__OBJECT, 
-	                                G_TYPE_NONE, 1, GCK_TYPE_OBJECT);
-}
+	g_mutex_lock (closure->mutex);
 
-static gpointer
-gcr_importer_real_get_user_data (GAsyncResult *base)
-{
-	g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL);
-	return GCR_IMPORTER (base)->pv->user_data;
-}
+	closure->complete = TRUE;
+	closure->error = error;
+	g_cond_signal (closure->cond);
 
-static GObject* 
-gcr_importer_real_get_source_object (GAsyncResult *base)
-{
-	g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL);
-	return G_OBJECT (base);
-}
-
-static void 
-gcr_importer_async_result (GAsyncResultIface *iface)
-{
-	iface->get_source_object = gcr_importer_real_get_source_object;
-	iface->get_user_data = gcr_importer_real_get_user_data;
-}
-
-/* -----------------------------------------------------------------------------
- * PUBLIC 
- */
-
-/**
- * gcr_importer_new:
- *
- * Create a new #GcrImporter.
- *
- * Returns: A newly allocated importer, which should be released with
- *    g_object_unref().
- */
-GcrImporter*
-gcr_importer_new (void)
-{
-	return g_object_new (GCR_TYPE_IMPORTER, NULL);
-}
-
-/**
- * gcr_importer_get_slot:
- * @self: The importer
- *
- * Get the PKCS\#11 slot the items will be imported to, or after
- * an import operation, which slot they have been imported to.
- *
- * Returns: The slot.
- */
-GckSlot*
-gcr_importer_get_slot (GcrImporter *self)
-{
-	g_return_val_if_fail (GCR_IS_IMPORTER (self), NULL);
-	return self->pv->slot;
-}
-
-/**
- * gcr_importer_set_slot:
- * @self: The importer
- * @slot: The slot to import to
- *
- * Set the PKCS\#11 slot to import the items to.
- */
-void 
-gcr_importer_set_slot (GcrImporter *self, GckSlot *slot)
-{
-	g_return_if_fail (GCR_IS_IMPORTER (self));
-	
-	if (slot)
-		g_object_ref (slot);
-	if (self->pv->slot)
-		g_object_unref (self->pv->slot);
-	self->pv->slot = slot;
-	g_object_notify (G_OBJECT (self), "slot");
-}
-
-/**
- * gcr_importer_get_prompt_behavior:
- * @self: The importer
- *
- * Get the type of prompting configured for this importer.
- *
- * Returns: The prompting flags.
- */
-GcrImporterPromptBehavior
-gcr_importer_get_prompt_behavior (GcrImporter *self)
-{
-	g_return_val_if_fail (GCR_IS_IMPORTER (self), GCR_IMPORTER_PROMPT_NEEDED);
-	return self->pv->behavior;
-}
-
-/**
- * gcr_importer_set_prompt_behavior:
- * @self: The importer
- * @behavior: The prompt behavior flag
- *
- * Set the type of prompting desired during import.
- */
-void
-gcr_importer_set_prompt_behavior (GcrImporter *self, GcrImporterPromptBehavior behavior)
-{
-	g_return_if_fail (GCR_IMPORTER (self));
-	self->pv->behavior = behavior;
-	g_object_notify (G_OBJECT (self), "prompt-behavior");
+	g_mutex_unlock (closure->mutex);
 }
 
 /**
  * gcr_importer_import:
- * @self: The importer
- * @cancellable: An optional cancellation object
- * @error: A location to raise an error on failure
+ * @importer: the importer
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: the location to place an error on failure, or %NULL
  *
- * Start an synchronous import operation of the items that have been queued.
+ * Import the queued items in the importer. This call will block
+ * until the operation completes.
  *
- * Returns: Whether the import was successful or not.
+ * Returns: whether the items were imported successfully or not
  */
 gboolean
-gcr_importer_import (GcrImporter *self, GCancellable *cancellable, GError **error)
+gcr_importer_import (GcrImporter *importer,
+                     GCancellable *cancellable,
+                     GError **error)
 {
-	g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
-	g_return_val_if_fail (!self->pv->processing, FALSE);
-
-	cleanup_import_data (self);
-
-	if (cancellable)
-		self->pv->cancel = g_object_ref (cancellable);
-	self->pv->processing = TRUE;
-	self->pv->async = FALSE;
-
-	next_state (self, state_import_prompt);
-
-	g_assert (!self->pv->processing);
-	g_assert (!self->pv->cancel);
-	
-	if (!self->pv->succeeded) {
-		g_propagate_error (error, self->pv->error);
-		self->pv->error = NULL;
-		return FALSE;
-	}
-	
-	return TRUE;
-}
+	gboolean result;
+	ImportClosure *closure;
+	GcrImporterIface *iface;
 
-/**
- * gcr_importer_import_async:
- * @self: The importer
- * @cancellable: An optional cancellation object
- * @callback: Call when the operation result is ready
- * @user_data: Data to pass to the callback
- *
- * Start an asynchronous import operation of the items that have been queued.
- */
-void
-gcr_importer_import_async (GcrImporter *self, GCancellable *cancellable,
-                           GAsyncReadyCallback callback, gpointer user_data)
-{
-	g_return_if_fail (GCR_IS_IMPORTER (self));
-	g_return_if_fail (!self->pv->processing);
+	g_return_val_if_fail (GCR_IS_IMPORTER (importer), FALSE);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-	cleanup_import_data (self);
+	iface = GCR_IMPORTER_GET_INTERFACE (importer);
+	if (iface->import_sync)
+		return (iface->import_sync) (importer, cancellable, error);
 
-	if (cancellable)
-		self->pv->cancel = g_object_ref (cancellable);
-	self->pv->processing = TRUE;
-	self->pv->async = TRUE;
-	self->pv->callback = callback;
-	self->pv->user_data = user_data;
+	g_return_val_if_fail (iface->import_async != NULL, FALSE);
+	g_return_val_if_fail (iface->import_finish != NULL, FALSE);
 
-	next_state (self, state_import_prompt);
-	g_assert (self->pv->processing);
-}
+	closure = g_new0 (ImportClosure, 1);
+	closure->cond = g_cond_new ();
+	closure->mutex = g_mutex_new ();
+	closure->context = g_main_context_get_thread_default ();
+	g_mutex_lock (closure->mutex);
 
-/**
- * gcr_importer_import_finish:
- * @self: The importer
- * @result: The operation result
- * @error: A location to raise an error on failure.
- *
- * Complete an asynchronous import operation.
- *
- * Returns: Whether the operation was successful or not.
- */
-gboolean
-gcr_importer_import_finish (GcrImporter *self, GAsyncResult *result, GError **error)
-{
-	g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
-	g_return_val_if_fail (GCR_IMPORTER (result) == self, FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
-	g_return_val_if_fail (!self->pv->processing, FALSE);
+	(iface->import_async) (importer, cancellable, on_import_async_complete, closure);
+
+	/*
+	 * Handle the case where we've been called from within the main context
+	 * or in the case where the main context is not running. This approximates
+	 * the behavior of a modal dialog.
+	 */
+	if (g_main_context_acquire (closure->context)) {
+		while (!closure->complete) {
+			g_mutex_unlock (closure->mutex);
+			g_main_context_iteration (closure->context, TRUE);
+			g_mutex_lock (closure->mutex);
+		}
 
-	g_assert (!self->pv->cancel);
+		g_main_context_release (closure->context);
 
-	if (!self->pv->succeeded) {
-		g_propagate_error (error, self->pv->error);
-		self->pv->error = NULL;
-		return FALSE;
+	/*
+	 * Handle the case where we're in a different thread than the main
+	 * context and a main loop is running.
+	 */
+	} else {
+		while (!closure->complete)
+			g_cond_wait (closure->cond, closure->mutex);
 	}
-	
-	return TRUE;
-}
 
-/**
- * gcr_importer_listen:
- * @self: The importer
- * @parser: The parser to listen to
- *
- * Listen for parse events from the #GcrParser, and queue parsed items for
- * importing.
- */
-void
-gcr_importer_listen (GcrImporter *self, GcrParser *parser)
-{
-	g_return_if_fail (GCR_IS_IMPORTER (self));
-	g_return_if_fail (GCR_IS_PARSER (parser));
+	g_mutex_unlock (closure->mutex);
+
+	result = (closure->error == NULL);
+	if (closure->error)
+		g_propagate_error (error, closure->error);
 
-	/* Listen in to the parser */
-	g_signal_connect_object (parser, "parsed", G_CALLBACK (on_parser_parsed), self, 0);
-	g_signal_connect_object (parser, "authenticate", G_CALLBACK (on_parser_authenticate), self, 0);
+	g_cond_free (closure->cond);
+	g_mutex_free (closure->mutex);
+	g_free (closure);
+
+	return result;
 }
 
 /**
- * gcr_importer_queue:
- * @self: The importer
- * @label: Label of item to import
- * @attrs: Attributes of item to import
+ * gcr_importer_import_async:
+ * @importer: the importer
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: called when the operation completes
+ * @user_data: data to be passed to the callback
  *
- * Queue the importing of an item. Use gcr_importer_listen() to automatically
- * queue items parsed by a #GcrParser.
+ * Import the queued items in the importer. This function returns immediately
+ * and completes asynchronously.
  */
 void
-gcr_importer_queue (GcrImporter *self, const gchar *label, GckAttributes *attrs)
+gcr_importer_import_async (GcrImporter *importer,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
 {
-	gboolean is_private;
+	GcrImporterIface *iface;
 
-	g_return_if_fail (GCR_IS_IMPORTER (self));
-	g_return_if_fail (attrs);
+	g_return_if_fail (GCR_IS_IMPORTER (importer));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
-	if (!gck_attributes_find_boolean (attrs, CKA_PRIVATE, &is_private))
-		is_private = FALSE;
-	if (is_private)
-		self->pv->any_private = TRUE;
+	iface = GCR_IMPORTER_GET_INTERFACE (importer);
+	g_return_if_fail (iface != NULL);
+	g_return_if_fail (iface->import_async != NULL);
 
-	g_queue_push_tail (&self->pv->queue, gck_attributes_ref (attrs));
-	g_signal_emit (self, signals[QUEUED], 0, label, attrs);
+	return (iface->import_async) (importer, cancellable, callback, user_data);
 }
 
-#ifndef GCR_DISABLE_DEPRECATED
-
 /**
- * gcr_importer_get_parser:
- * @self: An importer
+ * gcr_importer_import_finish:
+ * @importer: the importer
+ * @result: an asynchronous result
+ * @error: the location to place an error on failure, or %NULL
  *
- * Has no effect. Use gcr_importer_listen() instead.
+ * Complete an asynchronous operation to import queued items.
  *
- * Returns: %NULL is always returned.
- * Deprecated: Since 3.0.0
+ * Returns: whether the import succeeded or failed
  */
-GcrParser*
-gcr_importer_get_parser (GcrImporter *self)
+gboolean
+gcr_importer_import_finish (GcrImporter *importer,
+                            GAsyncResult *result,
+                            GError **error)
 {
-	g_warning ("gcr_importer_get_parser() is no longer supported "
-	           "Use gcr_importer_listen() instead.");
-	return NULL;
+	GcrImporterIface *iface;
+
+	g_return_val_if_fail (GCR_IS_IMPORTER (importer), FALSE);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+	iface = GCR_IMPORTER_GET_INTERFACE (importer);
+	g_return_val_if_fail (iface != NULL, FALSE);
+	g_return_val_if_fail (iface->import_finish != NULL, FALSE);
+
+	return (iface->import_finish) (importer, result, error);
 }
 
 /**
- * gcr_importer_set_parser:
- * @self: An importer
- * @parser: A parser
- *
- * Has no effect. Use gcr_importer_listen() instead.
+ * gcr_importer_register_well_known:
  *
- * Deprecated: Since 3.0.0
+ * Register built-in PKCS#11 and GnuPG importers.
  */
 void
-gcr_importer_set_parser (GcrImporter *self, GcrParser *parser)
+gcr_importer_register_well_known (void)
 {
-	g_warning ("gcr_importer_set_parser() is no longer supported "
-	           "Use gcr_importer_listen() instead.");
+	g_type_class_unref (g_type_class_ref (GCR_TYPE_PKCS11_IMPORTER));
+	g_type_class_unref (g_type_class_ref (GCR_TYPE_GNUPG_IMPORTER));
 }
-
-#endif /* GCR_DISABLE_DEPRECATED */
diff --git a/gcr/gcr-importer.h b/gcr/gcr-importer.h
index 892b5d4..1441e32 100644
--- a/gcr/gcr-importer.h
+++ b/gcr/gcr-importer.h
@@ -1,22 +1,24 @@
-/* 
+/*
  * gnome-keyring
- * 
- * Copyright (C) 2008 Stefan Walter
- * 
- * This program is free software; you can redistribute it and/or modify 
+ *
+ * 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.  
+ * 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
  */
 
 #if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
@@ -27,77 +29,70 @@
 #define __GCR_IMPORTER_H__
 
 #include "gcr-parser.h"
-#include "gcr-types.h"
 
 #include <glib-object.h>
 
 G_BEGIN_DECLS
 
-typedef enum {
-	GCR_IMPORTER_PROMPT_NEEDED,
-	GCR_IMPORTER_PROMPT_ALWAYS,
-	GCR_IMPORTER_PROMPT_NEVER
-} GcrImporterPromptBehavior;
-
-#define GCR_TYPE_IMPORTER               (gcr_importer_get_type ())
-#define GCR_IMPORTER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORTER, GcrImporter))
-#define GCR_IMPORTER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_IMPORTER, GcrImporterClass))
-#define GCR_IS_IMPORTER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_IMPORTER))
-#define GCR_IS_IMPORTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_IMPORTER))
-#define GCR_IMPORTER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_IMPORTER, GcrImporterClass))
+#define GCR_TYPE_IMPORTER                 (gcr_importer_get_type ())
+#define GCR_IMPORTER(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORTER, GcrImporter))
+#define GCR_IS_IMPORTER(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_IMPORTER))
+#define GCR_IMPORTER_GET_INTERFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GCR_TYPE_IMPORTER, GcrImporterIface))
 
 typedef struct _GcrImporter GcrImporter;
-typedef struct _GcrImporterClass GcrImporterClass;
-typedef struct _GcrImporterPrivate GcrImporterPrivate;
+typedef struct _GcrImporterIface GcrImporterIface;
 
-struct _GcrImporter {
-	GObject parent;
+struct _GcrImporterIface {
+	GTypeInterface parent;
 
-	/*< private >*/
-	GcrImporterPrivate *pv;
-};
+	GList *     (*create_for_parsed)      (GcrParser *parser);
 
-struct _GcrImporterClass {
-	GObjectClass parent_class;
+	gboolean    (*queue_for_parsed)       (GcrImporter *importer,
+	                                       GcrParser *parser);
 
-	/* signals */
-	void (*queued) (GcrImporter *self, const gchar *label, GckAttributes *attrs);
-	void (*imported) (GcrImporter *self, GckObject *object);
-};
+	gboolean    (*import_sync)            (GcrImporter *importer,
+	                                       GCancellable *cancellable,
+	                                       GError **error);
 
-GType                     gcr_importer_get_type               (void);
+	void        (*import_async)           (GcrImporter *importer,
+	                                       GCancellable *cancellable,
+	                                       GAsyncReadyCallback callback,
+	                                       gpointer user_data);
 
-GcrImporter*              gcr_importer_new                    (void);
+	gboolean    (*import_finish)          (GcrImporter *importer,
+	                                       GAsyncResult *result,
+	                                       GError **error);
+
+	gpointer reserved[14];
+};
 
-GckSlot*                  gcr_importer_get_slot               (GcrImporter *self);
+GType          gcr_importer_get_type                       (void);
 
-void                      gcr_importer_set_slot               (GcrImporter *self,
-                                                               GckSlot *slot);
+GList *        gcr_importer_create_for_parsed              (GcrParser *parser);
 
-GcrImporterPromptBehavior gcr_importer_get_prompt_behavior    (GcrImporter *self);
+gboolean       gcr_importer_queue_for_parsed               (GcrImporter *importer,
+                                                            GcrParser *parser);
 
-void                      gcr_importer_set_prompt_behavior    (GcrImporter *self,
-                                                               GcrImporterPromptBehavior behavior);
+GList *        gcr_importer_queue_and_filter_for_parsed    (GList *importers,
+                                                            GcrParser *parser);
 
-void                      gcr_importer_queue                  (GcrImporter *self,
-                                                               const gchar *label,
-                                                               GckAttributes *attrs);
+gboolean       gcr_importer_import                         (GcrImporter *importer,
+                                                            GCancellable *cancellable,
+                                                            GError **error);
 
-void                      gcr_importer_listen                 (GcrImporter *self,
-                                                               GcrParser *parser);
+void           gcr_importer_import_async                   (GcrImporter *importer,
+                                                            GCancellable *cancellable,
+                                                            GAsyncReadyCallback callback,
+                                                            gpointer user_data);
 
-gboolean                  gcr_importer_import                 (GcrImporter *self,
-                                                               GCancellable *cancellable,
-                                                               GError **error);
+gboolean       gcr_importer_import_finish                  (GcrImporter *importer,
+                                                            GAsyncResult *result,
+                                                            GError **error);
 
-void                      gcr_importer_import_async           (GcrImporter *self,
-                                                               GCancellable *cancellable,
-                                                               GAsyncReadyCallback callback,
-                                                               gpointer user_data);
+void           gcr_importer_register                       (GType importer_type,
+                                                            GckAttributes *attrs);
 
-gboolean                  gcr_importer_import_finish          (GcrImporter *self,
-                                                               GAsyncResult *result,
-                                                               GError **error);
+void           gcr_importer_register_well_known            (void);
 
 G_END_DECLS
 
diff --git a/gcr/gcr-internal.h b/gcr/gcr-internal.h
index 9ae3831..fdf3748 100644
--- a/gcr/gcr-internal.h
+++ b/gcr/gcr-internal.h
@@ -25,6 +25,7 @@
 #define GCR_INTERNAL_H_
 
 #include <glib.h>
+#include <gio/gio.h>
 
 /* Should only be used internally */
 #define GCR_SUCCESS 0
diff --git a/gcr/gcr-key-widget.c b/gcr/gcr-key-widget.c
index 59eb04f..be9139e 100644
--- a/gcr/gcr-key-widget.c
+++ b/gcr/gcr-key-widget.c
@@ -160,7 +160,6 @@ static void
 gcr_key_widget_class_init (GcrKeyWidgetClass *klass)
 {
 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-	GckAttributes *registered;
 
 	gcr_key_widget_parent_class = g_type_class_peek_parent (klass);
 	g_type_class_add_private (klass, sizeof (GcrKeyWidgetPrivate));
@@ -173,12 +172,6 @@ gcr_key_widget_class_init (GcrKeyWidgetClass *klass)
 	g_object_class_install_property (gobject_class, PROP_ATTRIBUTES,
 	         g_param_spec_boxed ("attributes", "Attributes", "The data displayed in the widget",
 	                             GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE));
-
-	/* Register this as a view which can be loaded */
-	registered = gck_attributes_new ();
-	gck_attributes_add_ulong (registered, CKA_CLASS, CKO_PRIVATE_KEY);
-	gcr_renderer_register (GCR_TYPE_KEY_WIDGET, registered);
-	gck_attributes_unref (registered);
 }
 
 /* -----------------------------------------------------------------------------
diff --git a/gcr/gcr-pkcs11-importer.c b/gcr/gcr-pkcs11-importer.c
new file mode 100644
index 0000000..714e13a
--- /dev/null
+++ b/gcr/gcr-pkcs11-importer.c
@@ -0,0 +1,582 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * 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-internal.h"
+#include "gcr-library.h"
+#include "gcr-parser.h"
+#include "gcr-pkcs11-importer.h"
+
+#include <gck/gck.h>
+
+#include <glib/gi18n-lib.h>
+
+enum {
+	PROP_0,
+	PROP_LABEL,
+	PROP_ICON,
+	PROP_SLOT,
+	PROP_IMPORTED
+};
+
+struct _GcrPkcs11ImporterPrivate {
+	GckSlot *slot;
+	GList *objects;
+	GckSession *session;
+	GQueue queue;
+	gboolean any_private;
+};
+
+typedef struct  {
+	GcrPkcs11Importer *importer;
+	GCancellable *cancellable;
+	gboolean prompted;
+	gboolean async;
+} GcrImporterData;
+
+/* State forward declarations */
+static void   state_cancelled                  (GSimpleAsyncResult *res,
+                                                gboolean async);
+
+static void   state_complete                   (GSimpleAsyncResult *res,
+                                                gboolean async);
+
+static void   state_create_object              (GSimpleAsyncResult *res,
+                                                gboolean async);
+
+static void   state_open_session               (GSimpleAsyncResult *res,
+                                                gboolean async);
+
+static void   _gcr_pkcs11_importer_init_iface  (GcrImporterIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrPkcs11Importer, _gcr_pkcs11_importer, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_IMPORTER, _gcr_pkcs11_importer_init_iface);
+);
+
+#define BLOCK 4096
+
+static void
+gcr_importer_data_free (gpointer data)
+{
+	GcrImporterData *state = data;
+
+	g_clear_object (&state->cancellable);
+	g_clear_object (&state->importer);
+	g_free (state);
+}
+
+static void
+next_state (GSimpleAsyncResult *res,
+            void (*state) (GSimpleAsyncResult *, gboolean))
+{
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+
+	g_assert (state);
+
+	if (g_cancellable_is_cancelled (data->cancellable))
+		state = state_cancelled;
+
+	(state) (res, data->async);
+}
+
+/* ---------------------------------------------------------------------------------
+ * COMPLETE
+ */
+
+static void
+state_complete (GSimpleAsyncResult *res,
+                gboolean async)
+{
+	g_simple_async_result_complete (res);
+}
+
+static void
+state_cancelled (GSimpleAsyncResult *res,
+                 gboolean async)
+{
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+	GError *error = NULL;
+
+	if (data->cancellable && !g_cancellable_is_cancelled (data->cancellable))
+		g_cancellable_cancel (data->cancellable);
+
+	g_cancellable_set_error_if_cancelled (data->cancellable, &error);
+	g_simple_async_result_take_error (res, error);
+	next_state (res, state_complete);
+}
+
+/* ---------------------------------------------------------------------------------
+ * CREATE OBJECTS
+ */
+
+static void
+complete_create_object (GSimpleAsyncResult *res,
+                        GckObject *object,
+                        GError *error)
+{
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+	GcrPkcs11Importer *self = data->importer;
+
+	if (object == NULL) {
+		g_simple_async_result_take_error (res, error);
+		next_state (res, state_complete);
+
+	} else {
+		self->pv->objects = g_list_append (self->pv->objects, object);
+		next_state (res, state_create_object);
+	}
+}
+
+static void
+on_create_object (GObject *source,
+                  GAsyncResult *result,
+                  gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GError *error = NULL;
+	GckObject *object;
+
+	object = gck_session_create_object_finish (GCK_SESSION (source), result, &error);
+	complete_create_object (res, object, error);
+	g_object_unref (res);
+}
+
+static void
+state_create_object (GSimpleAsyncResult *res,
+                     gboolean async)
+{
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+	GcrPkcs11Importer *self = data->importer;
+	GckAttributes *attrs;
+	GckObject *object;
+	GError *error = NULL;
+
+	/* No more objects */
+	if (g_queue_is_empty (&self->pv->queue)) {
+		next_state (res, state_complete);
+
+	} else {
+
+		/* Pop first one off the list */
+		attrs = g_queue_pop_head (&self->pv->queue);
+		g_assert (attrs != NULL);
+
+		gck_attributes_add_boolean (attrs, CKA_TOKEN, CK_TRUE);
+
+		if (async) {
+			gck_session_create_object_async (self->pv->session, attrs,
+			                                 data->cancellable, on_create_object,
+			                                 g_object_ref (res));
+		} else {
+			object = gck_session_create_object (self->pv->session, attrs,
+			                                    data->cancellable, &error);
+			complete_create_object (res, object, error);
+		}
+
+		gck_attributes_unref (attrs);
+	}
+}
+
+/* ---------------------------------------------------------------------------------
+ * OPEN SESSION
+ */
+
+static void
+complete_open_session (GSimpleAsyncResult *res,
+                       GckSession *session,
+                       GError *error)
+{
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+	GcrPkcs11Importer *self = data->importer;
+
+	if (!session) {
+		g_simple_async_result_take_error (res, error);
+		next_state (res, state_complete);
+
+	} else {
+		g_clear_object (&self->pv->session);
+		self->pv->session = session;
+		next_state (res, state_create_object);
+	}
+}
+
+static void
+on_open_session (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GError *error = NULL;
+	GckSession *session;
+
+	session = gck_slot_open_session_finish (GCK_SLOT (source), result, &error);
+	complete_open_session (res, session, error);
+	g_object_unref (res);
+}
+
+static void
+state_open_session (GSimpleAsyncResult *res,
+                    gboolean async)
+{
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+	GcrPkcs11Importer *self = data->importer;
+	guint options = GCK_SESSION_READ_WRITE;
+	GckSession *session;
+	GError *error = NULL;
+
+	if (self->pv->any_private)
+		options |= GCK_SESSION_LOGIN_USER;
+
+	if (async) {
+		gck_slot_open_session_async (self->pv->slot, options,
+		                             data->cancellable, on_open_session,
+		                             g_object_ref (res));
+	} else {
+		session = gck_slot_open_session_full (self->pv->slot, options, 0,
+		                                      NULL, NULL, data->cancellable, &error);
+		complete_open_session (res, session, error);
+	}
+}
+
+static void
+_gcr_pkcs11_importer_init (GcrPkcs11Importer *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_PKCS11_IMPORTER, GcrPkcs11ImporterPrivate);
+	g_queue_init (&self->pv->queue);
+}
+
+static void
+_gcr_pkcs11_importer_dispose (GObject *obj)
+{
+	GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
+
+	gck_list_unref_free (self->pv->objects);
+	self->pv->objects = NULL;
+	g_clear_object (&self->pv->session);
+
+	while (!g_queue_is_empty (&self->pv->queue))
+		gck_attributes_unref (g_queue_pop_head (&self->pv->queue));
+
+	G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->dispose (obj);
+}
+
+static void
+_gcr_pkcs11_importer_finalize (GObject *obj)
+{
+	GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
+
+	g_clear_object (&self->pv->slot);
+
+	G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->finalize (obj);
+}
+
+static void
+_gcr_pkcs11_importer_set_property (GObject *obj,
+                                   guint prop_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+	GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
+
+	switch (prop_id) {
+	case PROP_SLOT:
+		self->pv->slot = g_value_dup_object (value);
+		g_return_if_fail (self->pv->slot);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static gchar *
+calculate_label (GcrPkcs11Importer *self)
+{
+	GckTokenInfo *info;
+	gchar *result;
+
+	info = gck_slot_get_token_info (self->pv->slot);
+	result = g_strdup (info->label);
+	gck_token_info_free (info);
+
+	return result;
+}
+
+static GIcon *
+calculate_icon (GcrPkcs11Importer *self)
+{
+	GckTokenInfo *info;
+	GIcon *result;
+
+	info = gck_slot_get_token_info (self->pv->slot);
+	if (g_strcmp0 (info->manufacturer_id, "Gnome Keyring") == 0)
+		result = g_themed_icon_new ("home-folder");
+	else
+		result = g_themed_icon_new ("media-flash");
+	gck_token_info_free (info);
+
+	return result;
+}
+
+static void
+_gcr_pkcs11_importer_get_property (GObject *obj,
+                                   guint prop_id,
+                                   GValue *value,
+                                   GParamSpec *pspec)
+{
+	GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
+
+	switch (prop_id) {
+	case PROP_LABEL:
+		g_value_take_string (value, calculate_label (self));
+		break;
+	case PROP_ICON:
+		g_value_take_object (value, calculate_icon (self));
+		break;
+	case PROP_SLOT:
+		g_value_set_object (value, _gcr_pkcs11_importer_get_slot (self));
+		break;
+	case PROP_IMPORTED:
+		g_value_set_boxed (value, _gcr_pkcs11_importer_get_imported (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gcr_pkcs11_importer_class_init (GcrPkcs11ImporterClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GckAttributes *registered;
+
+	gobject_class->dispose = _gcr_pkcs11_importer_dispose;
+	gobject_class->finalize = _gcr_pkcs11_importer_finalize;
+	gobject_class->set_property = _gcr_pkcs11_importer_set_property;
+	gobject_class->get_property = _gcr_pkcs11_importer_get_property;
+
+	g_type_class_add_private (gobject_class, sizeof (GcrPkcs11ImporterPrivate));
+
+	g_object_class_override_property (gobject_class, PROP_LABEL, "label");
+
+	g_object_class_override_property (gobject_class, PROP_ICON, "icon");
+
+	g_object_class_install_property (gobject_class, PROP_SLOT,
+	           g_param_spec_object ("slot", "Slot", "PKCS#11 slot to import data into",
+	                                GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (gobject_class, PROP_IMPORTED,
+	           g_param_spec_boxed ("imported", "Imported", "Imported objects",
+	                               GCK_TYPE_LIST, G_PARAM_READABLE));
+
+	registered = gck_attributes_new ();
+	gck_attributes_add_ulong (registered, CKA_CLASS, CKO_CERTIFICATE);
+	gck_attributes_add_ulong (registered, CKA_CERTIFICATE_TYPE, CKC_X_509);
+	gcr_importer_register (GCR_TYPE_PKCS11_IMPORTER, registered);
+	gck_attributes_unref (registered);
+
+	registered = gck_attributes_new ();
+	gck_attributes_add_ulong (registered, CKA_CLASS, CKO_PRIVATE_KEY);
+	gcr_importer_register (GCR_TYPE_PKCS11_IMPORTER, registered);
+	gck_attributes_unref (registered);
+
+	_gcr_initialize_library ();
+}
+
+static GList *
+list_all_slots (void)
+{
+	GError *error = NULL;
+	GList *modules, *l;
+	GList *results = NULL;
+
+	if (!_gcr_initialize_pkcs11 (NULL, &error)) {
+		g_warning ("couldn't initialize PKCS#11 modules: %s", error->message);
+		g_clear_error (&error);
+		return NULL;
+	}
+
+	modules = gcr_pkcs11_get_modules ();
+	for (l = modules; l != NULL; l = g_list_next (l))
+		results = g_list_concat (results, gck_modules_get_slots (modules, TRUE));
+	gck_list_unref_free (modules);
+
+	return results;
+}
+
+static const char *token_blacklist[] = {
+	"pkcs11:manufacturer=Gnome%20Keyring;serial=1:SECRET:MAIN",
+	NULL
+};
+
+static gboolean
+is_slot_importable (GckSlot *slot,
+                    GckTokenInfo *token)
+{
+	GError *error = NULL;
+	GckUriData *uri;
+	gboolean match;
+	guint i;
+
+	if (token->flags & CKF_WRITE_PROTECTED)
+		return FALSE;
+	if (!(token->flags & CKF_TOKEN_INITIALIZED))
+		return FALSE;
+	if ((token->flags & CKF_LOGIN_REQUIRED) &&
+	    !(token->flags & CKF_USER_PIN_INITIALIZED))
+		return FALSE;
+
+	for (i = 0; token_blacklist[i] != NULL; i++) {
+		uri = gck_uri_parse (token_blacklist[i], GCK_URI_FOR_TOKEN | GCK_URI_FOR_MODULE, &error);
+		if (uri == NULL) {
+			g_warning ("couldn't parse pkcs11 blacklist uri: %s", error->message);
+			g_clear_error (&error);
+			continue;
+		}
+
+		match = gck_slot_match (slot, uri);
+		gck_uri_data_free (uri);
+
+		if (match)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static GList *
+_gcr_pkcs11_importer_create_for_parsed (GcrParser *parser)
+{
+	GcrImporter *self;
+	GList *slots, *l;
+	GList *results = NULL;
+	GckTokenInfo *token_info;
+	gboolean importable;
+
+	slots = list_all_slots ();
+	for (l = slots; l != NULL; l = g_list_next (l)) {
+		token_info = gck_slot_get_token_info (l->data);
+		importable = is_slot_importable (l->data, token_info);
+		gck_token_info_free (token_info);
+
+		if (importable) {
+			self = _gcr_pkcs11_importer_new (l->data);
+			if (!gcr_importer_queue_for_parsed (self, parser))
+				g_assert_not_reached ();
+			results = g_list_prepend (results, self);
+		}
+	}
+	gck_list_unref_free (slots);
+
+	return g_list_reverse (results);
+}
+
+static gboolean
+_gcr_pkcs11_importer_queue_for_parsed (GcrImporter *importer,
+                                       GcrParser *parser)
+{
+	GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (importer);
+	GckAttributes *attrs;
+	gboolean is_private;
+
+	attrs = gcr_parser_get_parsed_attributes (parser);
+
+	if (!gck_attributes_find_boolean (attrs, CKA_PRIVATE, &is_private))
+		is_private = FALSE;
+	if (is_private)
+		self->pv->any_private = TRUE;
+
+	g_queue_push_tail (&self->pv->queue, gck_attributes_ref (attrs));
+	return TRUE;
+}
+
+static void
+_gcr_pkcs11_importer_import_async (GcrImporter *importer,
+                                   GCancellable *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer user_data)
+{
+	GSimpleAsyncResult *res;
+	GcrImporterData *data;
+
+	res = g_simple_async_result_new (G_OBJECT (importer), callback, user_data,
+	                                 _gcr_pkcs11_importer_import_async);
+	data = g_new0 (GcrImporterData, 1);
+	data->async = TRUE;
+	data->importer = g_object_ref (importer);
+	data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	g_simple_async_result_set_op_res_gpointer (res, data, gcr_importer_data_free);
+
+	next_state (res, state_open_session);
+	g_object_unref (res);
+}
+
+static gboolean
+_gcr_pkcs11_importer_import_finish (GcrImporter *importer,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (importer),
+	                      _gcr_pkcs11_importer_import_async), FALSE);
+
+	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+_gcr_pkcs11_importer_init_iface (GcrImporterIface *iface)
+{
+	iface->create_for_parsed = _gcr_pkcs11_importer_create_for_parsed;
+	iface->queue_for_parsed = _gcr_pkcs11_importer_queue_for_parsed;
+	iface->import_async = _gcr_pkcs11_importer_import_async;
+	iface->import_finish = _gcr_pkcs11_importer_import_finish;
+}
+
+GcrImporter *
+_gcr_pkcs11_importer_new (GckSlot *slot)
+{
+	g_return_val_if_fail (GCK_IS_SLOT (slot), NULL);
+
+	return g_object_new (GCR_TYPE_PKCS11_IMPORTER,
+	                     "slot", slot,
+	                     NULL);
+}
+
+GckSlot *
+_gcr_pkcs11_importer_get_slot (GcrPkcs11Importer *self)
+{
+	g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL);
+	return self->pv->slot;
+}
+
+GList *
+_gcr_pkcs11_importer_get_imported (GcrPkcs11Importer *self)
+{
+	g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL);
+	return self->pv->objects;
+}
diff --git a/gcr/gcr-pkcs11-importer.h b/gcr/gcr-pkcs11-importer.h
new file mode 100644
index 0000000..bbf752c
--- /dev/null
+++ b/gcr/gcr-pkcs11-importer.h
@@ -0,0 +1,68 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * 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_PKCS11_IMPORTER_H__
+#define __GCR_PKCS11_IMPORTER_H__
+
+#include "gcr-importer.h"
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_PKCS11_IMPORTER               (_gcr_pkcs11_importer_get_type ())
+#define GCR_PKCS11_IMPORTER(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PKCS11_IMPORTER, GcrPkcs11Importer))
+#define GCR_PKCS11_IMPORTER_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_PKCS11_IMPORTER, GcrPkcs11ImporterClass))
+#define GCR_IS_PKCS11_IMPORTER(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PKCS11_IMPORTER))
+#define GCR_IS_PKCS11_IMPORTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_PKCS11_IMPORTER))
+#define GCR_PKCS11_IMPORTER_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_PKCS11_IMPORTER, GcrPkcs11ImporterClass))
+
+typedef struct _GcrPkcs11Importer GcrPkcs11Importer;
+typedef struct _GcrPkcs11ImporterClass GcrPkcs11ImporterClass;
+typedef struct _GcrPkcs11ImporterPrivate GcrPkcs11ImporterPrivate;
+
+struct _GcrPkcs11Importer {
+	GObject parent;
+
+	/*< private >*/
+	GcrPkcs11ImporterPrivate *pv;
+};
+
+struct _GcrPkcs11ImporterClass {
+	GObjectClass parent_class;
+};
+
+GType                     _gcr_pkcs11_importer_get_type        (void);
+
+GcrImporter *             _gcr_pkcs11_importer_new             (GckSlot *slot);
+
+GckSlot *                 _gcr_pkcs11_importer_get_slot        (GcrPkcs11Importer *self);
+
+GList *                   _gcr_pkcs11_importer_get_imported    (GcrPkcs11Importer *self);
+
+G_END_DECLS
+
+#endif /* __GCR_PKCS11_IMPORTER_H__ */
diff --git a/gcr/gcr-record.h b/gcr/gcr-record.h
index 3915375..37201ce 100644
--- a/gcr/gcr-record.h
+++ b/gcr/gcr-record.h
@@ -52,6 +52,7 @@
 G_BEGIN_DECLS
 
 #define GCR_RECORD_SCHEMA_ATTRIBUTE  (g_quark_from_static_string ("ATTRIBUTE"))
+#define GCR_RECORD_SCHEMA_IMPORT_OK  (g_quark_from_static_string ("IMPORT_OK"))
 #define GCR_RECORD_SCHEMA_FPR  (g_quark_from_static_string ("fpr"))
 #define GCR_RECORD_SCHEMA_PUB  (g_quark_from_static_string ("pub"))
 #define GCR_RECORD_SCHEMA_SUB  (g_quark_from_static_string ("sub"))
@@ -83,6 +84,16 @@ typedef enum {
 } GcrRecordAttributeColumns;
 
 /*
+ * Columns for IMPORT_OK and IMPORT_PROBLEM status message. eg:
+ * [GNUPG:] IMPORT_OK 1 6BD9050FD8FC941B43412DCC68B7AB8957548DCD
+ * [GNUPG:] IMPORT_PROBLEM 1
+ */
+typedef enum {
+	GCR_RECORD_IMPORT_REASON = 1,
+	GCR_RECORD_IMPORT_FINGERPRINT
+} GcrRecordImportColumns;
+
+/*
  * Columns for fpr schema, add them as they're used. eg:
  * fpr:::::::::ECAF7590EB3443B5C7CF3ACB6C7EE1B8621CC013:
  */
diff --git a/gcr/gcr.symbols b/gcr/gcr.symbols
index 5781424..d7703de 100644
--- a/gcr/gcr.symbols
+++ b/gcr/gcr.symbols
@@ -42,20 +42,6 @@ gcr_combo_selector_get_selected
 gcr_combo_selector_get_type
 gcr_combo_selector_new
 gcr_combo_selector_set_selected
-gcr_importer_get_parser
-gcr_importer_get_prompt_behavior
-gcr_importer_get_slot
-gcr_importer_get_type
-gcr_importer_import
-gcr_importer_import_async
-gcr_importer_import_finish
-gcr_importer_listen
-gcr_importer_new
-gcr_importer_prompt_behavior_get_type
-gcr_importer_queue
-gcr_importer_set_parser
-gcr_importer_set_prompt_behavior
-gcr_importer_set_slot
 gcr_key_renderer_get_attributes
 gcr_key_renderer_get_type
 gcr_key_renderer_new
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d10485d..6579f43 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -23,6 +23,7 @@ gcr/gcr-certificate-widget.c
 gcr/gcr-display-view.c
 gcr/gcr-failure-renderer.c
 [type: gettext/glade]gcr/gcr-import-dialog.ui
+gcr/gcr-gnupg-importer.c
 gcr/gcr-gnupg-key.c
 gcr/gcr-gnupg-process.c
 gcr/gcr-gnupg-renderer.c
diff --git a/tool/gkr-tool-import.c b/tool/gkr-tool-import.c
index fdb8e01..05f26dd 100644
--- a/tool/gkr-tool-import.c
+++ b/tool/gkr-tool-import.c
@@ -39,7 +39,16 @@ static GOptionEntry import_entries[] = {
 };
 
 static void
-on_imported (GcrImporter *importer, GckObject *object)
+imported_fingerprint (const gchar *fingerprint,
+                      const gchar *destination)
+{
+	g_print ("%s: imported openpgp\n", destination);
+	g_print ("\tfingerprint: %s\n", fingerprint);
+}
+
+static void
+imported_object (GckObject *object,
+                 const gchar *destination)
 {
 	gulong attr_types[3];
 	GckAttributes *attrs;
@@ -67,30 +76,30 @@ on_imported (GcrImporter *importer, GckObject *object)
 	
 	switch (klass) {
 	case CKO_CERTIFICATE:
-		message = "Imported certificate: %s\n";
+		message = "%s: imported certificate: %s\n";
 		break;
 	case CKO_DATA:
-		message = "Imported data: %s\n";
+		message = "%s: imported data: %s\n";
 		break;
 	case CKO_PRIVATE_KEY:
-		message = "Imported private key: %s\n";
+		message = "%s: imported private key: %s\n";
 		break;
 	case CKO_PUBLIC_KEY:
-		message = "Imported public key: %s\n";
+		message = "%s: imported public key: %s\n";
 		break;
 	case CKO_SECRET_KEY:
-		message = "Imported secret key: %s\n";
+		message = "%s: imported secret key: %s\n";
 		break;
 	default:
-		message = "Imported object: %s\n";
+		message = "%s: imported object: %s\n";
 		break;
 	};
 	
-	g_print (message, label);
+	g_print (message, destination, label);
 
 	if (id) {
 		hex = egg_hex_encode (id->value, id->length);
-		g_print ("\tID: %s\n", hex);
+		g_print ("\tidentifier: %s\n", hex);
 		g_free (hex);
 	}
 
@@ -98,18 +107,70 @@ on_imported (GcrImporter *importer, GckObject *object)
 	g_free (label);
 }
 
+static void
+imported_display (GcrImporter *importer)
+{
+	GParamSpec *spec;
+	gchar *label = NULL;
+
+	spec = g_object_class_find_property (G_OBJECT_GET_CLASS (importer), "imported");
+	if (spec == NULL)
+		return;
+
+	g_object_get (importer, "label", &label, NULL);
+
+	if (spec->value_type == GCK_TYPE_LIST) {
+		GList *list, *l;
+		g_object_get (importer, "imported", &list, NULL);
+		for (l = list; l != NULL; l = g_list_next (l))
+			imported_object (l->data, label);
+		gck_list_unref_free (list);
+
+	} else if (spec->value_type == G_TYPE_STRV) {
+		gchar **fingerprints;
+		guint i;
+		g_object_get (importer, "imported", &fingerprints, NULL);
+		for (i = 0; fingerprints && fingerprints[i] != NULL; i++)
+			imported_fingerprint (fingerprints[i], label);
+		g_strfreev (fingerprints);
+	}
+}
+
+typedef struct {
+	GList *importers;
+	gboolean num_parsed;
+} ImportClosure;
+
+static void
+on_parser_parsed (GcrParser *parser,
+                  gpointer user_data)
+{
+	ImportClosure *closure = user_data;
+	GList *filtered;
+
+	if (closure->num_parsed == 0) {
+		closure->importers = gcr_importer_create_for_parsed (parser);
+	} else {
+		filtered = gcr_importer_queue_and_filter_for_parsed (closure->importers, parser);
+		gck_list_unref_free (closure->importers);
+		closure->importers = filtered;
+	}
+
+	closure->num_parsed++;
+}
+
 int
 gkr_tool_import (int argc, char *argv[])
 {
-	GcrImporter *importer;
 	GcrParser *parser;
 	GError *error = NULL;
 	GInputStream *input;
-	gboolean res;
+	ImportClosure *closure;
 	GFile *file;
 	gchar **imp;
 	int ret = 0;
-	
+	GList *l;
+
 	ret = gkr_tool_parse_options (&argc, &argv, import_entries);
 	if (ret != 0)
 		return ret;
@@ -118,39 +179,50 @@ gkr_tool_import (int argc, char *argv[])
 		gkr_tool_handle_error (NULL, "specify files to import");
 		return 2;
 	}
-	
-	importer = gcr_importer_new ();
-	gcr_importer_set_prompt_behavior (importer, GCR_IMPORTER_PROMPT_NEEDED);
-	
-	if (!gkr_tool_mode_quiet) 
-		g_signal_connect (importer, "imported", G_CALLBACK (on_imported), NULL);
-	
+
+	parser = gcr_parser_new ();
+	closure = g_new0 (ImportClosure, 1);
+	g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), closure);
+
 	for (imp = import_files; *imp; ++imp) {
 		file = g_file_new_for_commandline_arg (*imp);
 		
 		input = G_INPUT_STREAM (g_file_read (file, NULL, &error));
 		g_object_unref (file);
-		if (!input) {
+		if (input == NULL) {
 			gkr_tool_handle_error (&error, "couldn't read file: %s", *imp);
 			ret = 1;
+
 		} else {
-			parser = gcr_parser_new ();
-			gcr_importer_listen (importer, parser);
-			res = gcr_parser_parse_stream (parser, input, NULL, &error);
+			if (!gcr_parser_parse_stream (parser, input, NULL, &error)) {
+				if (error->code != GCR_ERROR_CANCELLED)
+					gkr_tool_handle_error (&error, "couldn't parse: %s", *imp);
+				ret = 1;
+			}
+
 			g_object_unref (input);
-			g_object_unref (parser);
+		}
+	}
 
-			if (res == TRUE)
-				res = gcr_importer_import (importer, NULL, &error);
+	if (closure->importers == NULL) {
+		gkr_tool_handle_error (NULL, "couldn't find any place to import files");
+		ret = 1;
+	}
 
-			if (res == FALSE) {
-				if (!error || error->code != GCR_ERROR_CANCELLED)
-					gkr_tool_handle_error (&error, "couldn't import file: %s", *imp);
-				ret = 1;
-			}
+	for (l = closure->importers; l != NULL; l = g_list_next (l)) {
+		if (gcr_importer_import (l->data, NULL, &error)) {
+			if (!gkr_tool_mode_quiet)
+				imported_display (l->data);
+		} else {
+			if (error->code != GCR_ERROR_CANCELLED)
+				gkr_tool_handle_error (&error, "couldn't import");
+			ret = 1;
 		}
 	}
-	
-	g_object_unref (importer);
+
+	gck_list_unref_free (closure->importers);
+	g_free (closure);
+
+	g_object_unref (parser);
 	return ret;
 }



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