[gnome-keyring] gcr: Complete the PKCS#11 import dialog



commit 502a03e5c9e4bd1e43bea46620a4004c9005f7ba
Author: Stef Walter <stefw collabora co uk>
Date:   Tue Oct 4 12:55:21 2011 +0200

    gcr: Complete the PKCS#11 import dialog
    
     * Add GcrImportInteraction interface which importers
     * Hookup between non-GUI importers and import interaction
       isn't yet completely thought out.

 gcr/Makefile.am                     |    8 +-
 gcr/gcr-deprecated.h                |    1 -
 gcr/gcr-dialog-util.c               |  182 ++++++++++++++
 gcr/gcr-dialog-util.h               |   42 ++++
 gcr/gcr-gnupg-importer.c            |   14 +-
 gcr/gcr-import-button.c             |   12 +
 gcr/gcr-import-dialog.c             |  459 -----------------------------------
 gcr/gcr-import-dialog.h             |   82 -------
 gcr/gcr-import-dialog.ui            |  128 ----------
 gcr/gcr-import-interaction.c        |  178 ++++++++++++++
 gcr/gcr-import-interaction.h        |   89 +++++++
 gcr/gcr-importer.c                  |   54 ++++-
 gcr/gcr-importer.h                  |   45 ++--
 gcr/gcr-parser.c                    |    1 -
 gcr/gcr-pkcs11-import-dialog.c      |  350 ++++++++++++++++++++++++++
 gcr/gcr-pkcs11-import-dialog.h      |   62 +++++
 gcr/gcr-pkcs11-import-dialog.ui     |  251 +++++++++++++++++++
 gcr/gcr-pkcs11-import-interaction.c |  240 ++++++++++++++++++
 gcr/gcr-pkcs11-import-interaction.h |   45 ++++
 gcr/gcr-pkcs11-importer.c           |  184 +++++++++++----
 gcr/gcr-pkcs11-importer.h           |   14 +-
 21 files changed, 1686 insertions(+), 755 deletions(-)
---
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index be21651..3584bda 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -7,7 +7,7 @@ SUBDIRS = . icons tests
 uidir = $(datadir)/gcr- GCR_MAJOR@/ui/
 
 ui_DATA = \
-	gcr-import-dialog.ui \
+	gcr-pkcs11-import-dialog.ui \
 	gcr-unlock-options-widget.ui
 
 # ------------------------------------------------------------------
@@ -22,6 +22,7 @@ HEADER_BASE_FILES = \
 	gcr-deprecated-base.h \
 	gcr-icons.h \
 	gcr-importer.h \
+	gcr-import-interaction.h \
 	gcr-library.h \
 	gcr-parser.h \
 	gcr-pkcs11-certificate.h \
@@ -107,6 +108,7 @@ libgcr_base_ GCR_MAJOR@_la_SOURCES = \
 	gcr-gnupg-util.c gcr-gnupg-util.h \
 	gcr-icons.c gcr-icons.h \
 	gcr-importer.c gcr-importer.h \
+	gcr-import-interaction.c gcr-import-interaction.h \
 	gcr-internal.h \
 	gcr-library.c gcr-library.h \
 	gcr-memory.c \
@@ -137,6 +139,7 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-collection-model.c gcr-collection-model.h \
 	gcr-combo-selector.c gcr-combo-selector.h \
 	gcr-debug.c gcr-debug.h \
+	gcr-dialog-util.c gcr-dialog-util.h \
 	gcr-display-scrolled.c gcr-display-scrolled.h \
 	gcr-display-view.c gcr-display-view.h \
 	gcr-failure-renderer.c gcr-failure-renderer.h \
@@ -144,7 +147,6 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-gnupg-renderer.c gcr-gnupg-renderer.h \
 	gcr-gnupg-records.c gcr-gnupg-records.h \
 	gcr-import-button.c gcr-import-button.h \
-	gcr-import-dialog.c gcr-import-dialog.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 \
@@ -152,6 +154,8 @@ libgcr_ GCR_MAJOR@_la_SOURCES = \
 	gcr-memory.c \
 	gcr-memory-icon.c gcr-memory-icon.h \
 	gcr-menu-button.c gcr-menu-button.h \
+	gcr-pkcs11-import-interaction.c gcr-pkcs11-import-interaction.h \
+	gcr-pkcs11-import-dialog.c gcr-pkcs11-import-dialog.h \
 	gcr-record.c gcr-record.h \
 	gcr-renderer.c gcr-renderer.h \
 	gcr-tree-selector.c gcr-tree-selector.h \
diff --git a/gcr/gcr-deprecated.h b/gcr/gcr-deprecated.h
index 9a21af7..3447c84 100644
--- a/gcr/gcr-deprecated.h
+++ b/gcr/gcr-deprecated.h
@@ -34,7 +34,6 @@
 
 #include "gcr-certificate-basics-widget.h"
 #include "gcr-certificate-details-widget.h"
-#include "gcr-importer.h"
 #include "gcr-viewer.h"
 
 G_BEGIN_DECLS
diff --git a/gcr/gcr-dialog-util.c b/gcr/gcr-dialog-util.c
new file mode 100644
index 0000000..c142c35
--- /dev/null
+++ b/gcr/gcr-dialog-util.c
@@ -0,0 +1,182 @@
+/*
+ * 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-dialog-util.h"
+
+#include <string.h>
+
+typedef struct {
+	GtkDialog *dialog;
+	gint response_id;
+	gboolean was_modal;
+	gboolean destroyed;
+	gulong response_sig;
+	gulong unmap_sig;
+	gulong delete_sig;
+	gulong destroy_sig;
+} DialogRunClosure;
+
+static void
+dialog_run_closure_free (gpointer data)
+{
+	DialogRunClosure *closure = data;
+	g_object_unref (closure->dialog);
+	g_assert (closure->response_sig == 0);
+	g_assert (closure->unmap_sig == 0);
+	g_assert (closure->delete_sig == 0);
+	g_assert (closure->destroy_sig == 0);
+	g_free (closure);
+}
+
+static void
+complete_async_result (GSimpleAsyncResult *res)
+{
+	DialogRunClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+
+	g_object_ref (res);
+
+	if (!closure->destroyed) {
+		if (!closure->was_modal)
+			gtk_window_set_modal (GTK_WINDOW (closure->dialog), FALSE);
+
+		g_signal_handler_disconnect (closure->dialog, closure->response_sig);
+		closure->response_sig = 0;
+		g_signal_handler_disconnect (closure->dialog, closure->unmap_sig);
+		closure->unmap_sig = 0;
+		g_signal_handler_disconnect (closure->dialog, closure->delete_sig);
+		closure->delete_sig = 0;
+		g_signal_handler_disconnect (closure->dialog, closure->destroy_sig);
+		closure->destroy_sig = 0;
+	}
+
+	g_simple_async_result_complete (res);
+	g_object_unref (res);
+}
+
+static void
+on_dialog_unmap (GtkDialog *dialog,
+                 gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+
+	complete_async_result (res);
+}
+
+static void
+on_dialog_response (GtkDialog *dialog,
+                    gint response_id,
+                    gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	DialogRunClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+
+	closure->response_id = response_id;
+	complete_async_result (res);
+}
+
+static gint
+on_dialog_delete (GtkDialog *dialog,
+                  GdkEventAny *event,
+                  gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	complete_async_result (res);
+	return TRUE; /* Do not destroy */
+}
+
+static void
+on_dialog_destroy (GtkDialog *dialog,
+                   gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	DialogRunClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
+
+	/* complete will be called by run_unmap_handler */
+	closure->destroyed = TRUE;
+}
+
+void
+_gcr_dialog_util_run_async (GtkDialog *dialog,
+                            GCancellable *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer user_data)
+{
+	GSimpleAsyncResult *res;
+	DialogRunClosure *closure;
+
+	g_return_if_fail (GTK_IS_DIALOG (dialog));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	res = g_simple_async_result_new (G_OBJECT (dialog), callback, user_data,
+	                                 _gcr_dialog_util_run_async);
+	closure = g_new0 (DialogRunClosure, 1);
+
+	closure->dialog = g_object_ref (dialog);
+	closure->response_id = GTK_RESPONSE_NONE;
+	closure->was_modal = gtk_window_get_modal (GTK_WINDOW (dialog));
+	if (!closure->was_modal)
+		gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+	if (!gtk_widget_get_visible (GTK_WIDGET (dialog)))
+		gtk_widget_show (GTK_WIDGET (dialog));
+
+	g_simple_async_result_set_op_res_gpointer (res, closure, dialog_run_closure_free);
+
+	closure->response_sig = g_signal_connect_data (dialog, "response",
+	                                               G_CALLBACK (on_dialog_response),
+	                                               g_object_ref (res),
+	                                               (GClosureNotify)g_object_unref, 0);
+
+	closure->unmap_sig = g_signal_connect_data (dialog, "unmap",
+	                                            G_CALLBACK (on_dialog_unmap),
+	                                            g_object_ref (res),
+	                                            (GClosureNotify)g_object_unref, 0);
+
+	closure->delete_sig = g_signal_connect_data (dialog, "delete-event",
+	                                             G_CALLBACK (on_dialog_delete),
+	                                             g_object_ref (res),
+	                                             (GClosureNotify)g_object_unref, 0);
+
+	closure->destroy_sig = g_signal_connect_data (dialog, "destroy",
+	                                              G_CALLBACK (on_dialog_destroy),
+	                                              g_object_ref (res),
+	                                              (GClosureNotify)g_object_unref, 0);
+
+	g_object_unref (res);
+}
+
+
+gint
+_gcr_dialog_util_run_finish (GtkDialog *dialog,
+                             GAsyncResult *result)
+{
+	DialogRunClosure *closure;
+
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (dialog),
+	                      _gcr_dialog_util_run_async), GTK_RESPONSE_NONE);
+
+	closure = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+	return closure->response_id;
+}
diff --git a/gcr/gcr-dialog-util.h b/gcr/gcr-dialog-util.h
new file mode 100644
index 0000000..4be48fd
--- /dev/null
+++ b/gcr/gcr-dialog-util.h
@@ -0,0 +1,42 @@
+/*
+ * 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>
+ */
+
+#ifndef GCR_DIALOG_UTIL_H
+#define GCR_DIALOG_UTIL_H
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+void     _gcr_dialog_util_run_async       (GtkDialog *dialog,
+                                           GCancellable *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           gpointer user_data);
+
+gint     _gcr_dialog_util_run_finish      (GtkDialog *dialog,
+                                           GAsyncResult *result);
+
+G_END_DECLS
+
+#endif /* __GCR_TOKEN_MANAGER_H__ */
diff --git a/gcr/gcr-gnupg-importer.c b/gcr/gcr-gnupg-importer.c
index d9563a2..db0fd39 100644
--- a/gcr/gcr-gnupg-importer.c
+++ b/gcr/gcr-gnupg-importer.c
@@ -34,12 +34,14 @@ enum {
 	PROP_LABEL,
 	PROP_ICON,
 	PROP_IMPORTED,
-	PROP_DIRECTORY
+	PROP_DIRECTORY,
+	PROP_INTERACTION
 };
 
 struct _GcrGnupgImporterPrivate {
 	GcrGnupgProcess *process;
 	GMemoryInputStream *packets;
+	GTlsInteraction *interaction;
 	GArray *imported;
 };
 
@@ -66,6 +68,7 @@ _gcr_gnupg_importer_dispose (GObject *obj)
 		g_object_run_dispose (G_OBJECT (self->pv->process));
 	g_clear_object (&self->pv->process);
 	g_clear_object (&self->pv->packets);
+	g_clear_object (&self->pv->interaction);
 
 	G_OBJECT_CLASS (_gcr_gnupg_importer_parent_class)->dispose (obj);
 }
@@ -140,6 +143,10 @@ _gcr_gnupg_importer_set_property (GObject *obj,
 		_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;
+	case PROP_INTERACTION:
+		g_clear_object (&self->pv->interaction);
+		self->pv->interaction = g_value_dup_object (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -167,6 +174,9 @@ _gcr_gnupg_importer_get_property (GObject *obj,
 	case PROP_DIRECTORY:
 		g_value_set_string (value, _gcr_gnupg_process_get_directory (self->pv->process));
 		break;
+	case PROP_INTERACTION:
+		g_value_set_object (value, self->pv->interaction);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -190,6 +200,8 @@ _gcr_gnupg_importer_class_init (GcrGnupgImporterClass *klass)
 
 	g_object_class_override_property (gobject_class, PROP_ICON, "icon");
 
+	g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
+
 	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));
diff --git a/gcr/gcr-import-button.c b/gcr/gcr-import-button.c
index 79c9f5b..f517d9e 100644
--- a/gcr/gcr-import-button.c
+++ b/gcr/gcr-import-button.c
@@ -27,6 +27,7 @@
 #include "gcr-internal.h"
 #include "gcr-marshal.h"
 #include "gcr-parser.h"
+#include "gcr-pkcs11-import-interaction.h"
 
 #include <glib/gi18n-lib.h>
 
@@ -300,12 +301,23 @@ static void
 begin_import (GcrImportButton *self,
               GcrImporter *importer)
 {
+	GTlsInteraction *interaction;
+	GtkWindow *window;
+
 	g_return_if_fail (self->pv->importing == FALSE);
 
 	self->pv->importing = TRUE;
 	g_free (self->pv->imported);
 	self->pv->imported = NULL;
 
+	/* TODO: Hack. Need to figure out how to pair these up... */
+	if (g_strcmp0 (G_OBJECT_TYPE_NAME (importer), "GcrPkcs11Importer") == 0) {
+		window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)));
+		interaction = _gcr_pkcs11_import_interaction_new (window);
+		gcr_importer_set_interaction (importer, interaction);
+		g_object_unref (interaction);
+	}
+
 	gcr_importer_import_async (importer,
 	                           self->pv->cancellable,
 	                           on_import_complete,
diff --git a/gcr/gcr-import-interaction.c b/gcr/gcr-import-interaction.c
new file mode 100644
index 0000000..aee2048
--- /dev/null
+++ b/gcr/gcr-import-interaction.c
@@ -0,0 +1,178 @@
+/*
+ * 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-import-interaction.h"
+
+/**
+ * SECTION:gcr-importer
+ * @title: GcrImporter
+ * @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.
+ *
+ * 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.
+ *
+ * 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 certificates and keys
+ */
+
+/**
+ * GcrImporterIface:
+ * @parent: parent interface
+ * @create_for_parsed: implementation of gcr_importer_create_for_parsed(), required
+ * @queue_for_parsed: implementation of gcr_importer_queue_for_parsed(), required
+ * @import_sync: optional implemantionon of gcr_importer_import()
+ * @import_async: implementation of gcr_importer_import_async(), required
+ * @import_finish: implementation of gcr_importer_import_finish()
+ *
+ * Interface implemented for a #GcrImporter.
+ */
+
+typedef GcrImportInteractionIface GcrImportInteractionInterface;
+
+G_DEFINE_INTERFACE (GcrImportInteraction, gcr_import_interaction, G_TYPE_TLS_INTERACTION);
+
+static void
+gcr_import_interaction_default_init (GcrImportInteractionIface *iface)
+{
+	static volatile gsize initialized = 0;
+
+	if (g_once_init_enter (&initialized)) {
+
+		/**
+		 * GcrImportInteraction:importer:
+		 *
+		 * The importer being imported to
+		 */
+		g_object_interface_install_property (iface,
+		         g_param_spec_object ("importer", "Importer", "The imported imported to",
+		                              GCR_TYPE_IMPORTER, G_PARAM_READWRITE));
+
+		g_once_init_leave (&initialized, 1);
+	}
+}
+
+/**
+ * gcr_import_interaction_get_importer:
+ * @interaction: the interaction
+ *
+ * Get the importer that's using this interaction.
+ *
+ * Returns: (transfer full):
+ */
+GcrImporter *
+gcr_import_interaction_get_importer (GcrImportInteraction *interaction)
+{
+	GcrImporter *importer = NULL;
+
+	g_return_val_if_fail (GCR_IS_IMPORT_INTERACTION (interaction), NULL);
+
+	g_object_get (interaction, "importer", &importer, NULL);
+
+	if (importer != NULL)
+		g_object_unref (importer);
+
+	return importer;
+}
+
+/**
+ * gcr_import_interaction_set_importer:
+ * @interaction: the interaction
+ * @importer: (allow-none): the importer or %NULL
+ *
+ * Set the importer that's using this interaction.
+ */
+void
+gcr_import_interaction_set_importer (GcrImportInteraction *interaction,
+                                     GcrImporter *importer)
+{
+	g_return_if_fail (GCR_IS_IMPORT_INTERACTION (interaction));
+	g_object_set (interaction, "importer", importer, NULL);
+}
+
+GTlsInteractionResult
+gcr_import_interaction_supplement (GcrImportInteraction *interaction,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+	GcrImportInteractionIface *iface;
+
+	g_return_val_if_fail (GCR_IS_IMPORT_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_TLS_INTERACTION_UNHANDLED);
+	g_return_val_if_fail (error == NULL || *error == NULL, G_TLS_INTERACTION_UNHANDLED);
+
+	iface = GCR_IMPORT_INTERACTION_GET_INTERFACE (interaction);
+	g_return_val_if_fail (iface->supplement != NULL, G_TLS_INTERACTION_UNHANDLED);
+
+	return (iface->supplement) (interaction, cancellable, error);
+}
+
+void
+gcr_import_interaction_supplement_async (GcrImportInteraction *interaction,
+                                         GCancellable *cancellable,
+                                         GAsyncReadyCallback callback,
+                                         gpointer user_data)
+{
+	GcrImportInteractionIface *iface;
+
+	g_return_if_fail (GCR_IS_IMPORT_INTERACTION (interaction));
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	iface = GCR_IMPORT_INTERACTION_GET_INTERFACE (interaction);
+	g_return_if_fail (iface->supplement != NULL);
+
+	(iface->supplement_async) (interaction, cancellable, callback, user_data);
+}
+
+GTlsInteractionResult
+gcr_import_interaction_supplement_finish (GcrImportInteraction *interaction,
+                                          GAsyncResult *result,
+                                          GError **error)
+{
+	GcrImportInteractionIface *iface;
+
+	g_return_val_if_fail (GCR_IS_IMPORT_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), G_TLS_INTERACTION_UNHANDLED);
+	g_return_val_if_fail (error == NULL || *error == NULL, G_TLS_INTERACTION_UNHANDLED);
+
+	iface = GCR_IMPORT_INTERACTION_GET_INTERFACE (interaction);
+	g_return_val_if_fail (iface->supplement != NULL, G_TLS_INTERACTION_UNHANDLED);
+
+	return (iface->supplement_finish) (interaction, result, error);
+}
diff --git a/gcr/gcr-import-interaction.h b/gcr/gcr-import-interaction.h
new file mode 100644
index 0000000..79f5970
--- /dev/null
+++ b/gcr/gcr-import-interaction.h
@@ -0,0 +1,89 @@
+/*
+ * 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_INSIDE_HEADER__) && !defined (GCR_COMPILATION)
+#error "Only <gcr/gcr.h> or <gcr/gcr-base.h> can be included directly."
+#endif
+
+#ifndef __GCR_IMPORT_INTERACTION_H__
+#define __GCR_IMPORT_INTERACTION_H__
+
+#include "gcr-importer.h"
+
+#include <glib-object.h>
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_IMPORT_INTERACTION                 (gcr_import_interaction_get_type ())
+#define GCR_IMPORT_INTERACTION(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORT_INTERACTION, GcrImportInteraction))
+#define GCR_IS_IMPORT_INTERACTION(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_IMPORT_INTERACTION))
+#define GCR_IMPORT_INTERACTION_GET_INTERFACE(inst)  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GCR_TYPE_IMPORT_INTERACTION, GcrImportInteractionIface))
+
+typedef struct _GcrImportInteraction GcrImportInteraction;
+typedef struct _GcrImportInteractionIface GcrImportInteractionIface;
+
+struct _GcrImportInteractionIface {
+	GTypeInterface parent;
+
+	GTlsInteractionResult   (*supplement)        (GcrImportInteraction *interaction,
+	                                              GCancellable *cancellable,
+	                                              GError **error);
+
+	void                    (*supplement_async)  (GcrImportInteraction *interaction,
+	                                              GCancellable *cancellable,
+	                                              GAsyncReadyCallback callback,
+	                                              gpointer user_data);
+
+	GTlsInteractionResult   (*supplement_finish) (GcrImportInteraction *interaction,
+	                                              GAsyncResult *result,
+	                                              GError **error);
+
+	/*< private >*/
+	gpointer reserved[6];
+};
+
+GType                  gcr_import_interaction_get_type             (void);
+
+GcrImporter *          gcr_import_interaction_get_importer         (GcrImportInteraction *interaction);
+
+void                   gcr_import_interaction_set_importer         (GcrImportInteraction *interaction,
+                                                                    GcrImporter *importer);
+
+GTlsInteractionResult  gcr_import_interaction_supplement           (GcrImportInteraction *interaction,
+                                                                    GCancellable *cancellable,
+                                                                    GError **error);
+
+void                   gcr_import_interaction_supplement_async     (GcrImportInteraction *interaction,
+                                                                    GCancellable *cancellable,
+                                                                    GAsyncReadyCallback callback,
+                                                                    gpointer user_data);
+
+GTlsInteractionResult  gcr_import_interaction_supplement_finish    (GcrImportInteraction *interaction,
+                                                                    GAsyncResult *result,
+                                                                    GError **error);
+
+G_END_DECLS
+
+#endif /* __GCR_IMPORT_INTERACTION_H__ */
diff --git a/gcr/gcr-importer.c b/gcr/gcr-importer.c
index d67a96f..cb57285 100644
--- a/gcr/gcr-importer.c
+++ b/gcr/gcr-importer.c
@@ -23,7 +23,7 @@
 
 #include "config.h"
 
-#include "gcr-base.h"
+#include "gcr-deprecated-base.h"
 #include "gcr-importer.h"
 #include "gcr-internal.h"
 #include "gcr-marshal.h"
@@ -74,7 +74,7 @@
 
 typedef GcrImporterIface GcrImporterInterface;
 
-G_DEFINE_INTERFACE (GcrImporter, gcr_importer, 0);
+G_DEFINE_INTERFACE (GcrImporter, gcr_importer, G_TYPE_OBJECT);
 
 typedef struct _GcrRegistered {
 	GckAttributes *attrs;
@@ -109,6 +109,16 @@ gcr_importer_default_init (GcrImporterIface *iface)
 		         g_param_spec_object ("icon", "Icon", "The icon for the importer",
 		                              G_TYPE_ICON, G_PARAM_READABLE));
 
+		/**
+		 * GcrImporter:interaction:
+		 *
+		 * The interaction for the importer.
+		 */
+		g_object_interface_install_property (iface,
+		         g_param_spec_object ("interaction", "Interaction",
+		                              "Interaction for prompts",
+		                              G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE));
+
 		g_once_init_leave (&initialized, 1);
 	}
 }
@@ -466,6 +476,46 @@ gcr_importer_import_finish (GcrImporter *importer,
 }
 
 /**
+ * gcr_importer_get_interaction:
+ * @importer: the importer
+ *
+ * Get the interaction used to prompt the user when needed by this
+ * importer.
+ *
+ * Returns: (transfer none) (allow-none): the interaction or %NULL
+ */
+GTlsInteraction *
+gcr_importer_get_interaction (GcrImporter *importer)
+{
+	GTlsInteraction *interaction = NULL;
+
+	g_return_val_if_fail (GCR_IS_IMPORTER (importer), NULL);
+
+	g_object_get (importer, "interaction", &interaction, NULL);
+
+	if (interaction != NULL)
+		g_object_unref (interaction);
+
+	return interaction;
+}
+
+/**
+ * gcr_importer_set_interaction:
+ * @importer: the importer
+ * @interaction: the interaction used by the importer
+ *
+ * Set the interaction used to prompt the user when needed by this
+ * importer.
+ */
+void
+gcr_importer_set_interaction (GcrImporter *importer,
+                              GTlsInteraction *interaction)
+{
+	g_return_if_fail (GCR_IS_IMPORTER (importer));
+	g_object_set (importer, "interaction", interaction, NULL);
+}
+
+/**
  * gcr_importer_register_well_known:
  *
  * Register built-in PKCS\#11 and GnuPG importers.
diff --git a/gcr/gcr-importer.h b/gcr/gcr-importer.h
index e93395e..d915940 100644
--- a/gcr/gcr-importer.h
+++ b/gcr/gcr-importer.h
@@ -32,6 +32,8 @@
 
 #include <glib-object.h>
 
+#include <gio/gio.h>
+
 G_BEGIN_DECLS
 
 #define GCR_TYPE_IMPORTER                 (gcr_importer_get_type ())
@@ -67,33 +69,38 @@ struct _GcrImporterIface {
 	gpointer reserved[14];
 };
 
-GType            gcr_importer_get_type                     (void);
+GType              gcr_importer_get_type                     (void);
+
+GList *            gcr_importer_create_for_parsed            (GcrParsed *parsed);
+
+gboolean           gcr_importer_queue_for_parsed             (GcrImporter *importer,
+                                                              GcrParsed *parsed);
 
-GList *          gcr_importer_create_for_parsed            (GcrParsed *parsed);
+GList *            gcr_importer_queue_and_filter_for_parsed  (GList *importers,
+                                                              GcrParsed *parsed);
 
-gboolean         gcr_importer_queue_for_parsed             (GcrImporter *importer,
-                                                            GcrParsed *parsed);
+gboolean           gcr_importer_import                       (GcrImporter *importer,
+                                                              GCancellable *cancellable,
+                                                              GError **error);
 
-GList *          gcr_importer_queue_and_filter_for_parsed  (GList *importers,
-                                                            GcrParsed *parsed);
+void               gcr_importer_import_async                 (GcrImporter *importer,
+                                                              GCancellable *cancellable,
+                                                              GAsyncReadyCallback callback,
+                                                              gpointer user_data);
 
-gboolean         gcr_importer_import                       (GcrImporter *importer,
-                                                            GCancellable *cancellable,
-                                                            GError **error);
+gboolean           gcr_importer_import_finish                (GcrImporter *importer,
+                                                              GAsyncResult *result,
+                                                              GError **error);
 
-void             gcr_importer_import_async                 (GcrImporter *importer,
-                                                            GCancellable *cancellable,
-                                                            GAsyncReadyCallback callback,
-                                                            gpointer user_data);
+GTlsInteraction *  gcr_importer_get_interaction              (GcrImporter *importer);
 
-gboolean         gcr_importer_import_finish                (GcrImporter *importer,
-                                                            GAsyncResult *result,
-                                                            GError **error);
+void               gcr_importer_set_interaction              (GcrImporter *importer,
+                                                              GTlsInteraction *interaction);
 
-void             gcr_importer_register                     (GType importer_type,
-                                                            GckAttributes *attrs);
+void               gcr_importer_register                     (GType importer_type,
+                                                              GckAttributes *attrs);
 
-void             gcr_importer_register_well_known          (void);
+void               gcr_importer_register_well_known          (void);
 
 G_END_DECLS
 
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index 916f4a5..ad97a6b 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -24,7 +24,6 @@
 #include "gck/gck.h"
 
 #include "gcr-internal.h"
-#include "gcr-importer.h"
 #include "gcr-marshal.h"
 #include "gcr-oids.h"
 #include "gcr-openpgp.h"
diff --git a/gcr/gcr-pkcs11-import-dialog.c b/gcr/gcr-pkcs11-import-dialog.c
new file mode 100644
index 0000000..dce5ae1
--- /dev/null
+++ b/gcr/gcr-pkcs11-import-dialog.c
@@ -0,0 +1,350 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-dialog-util.h"
+#include "gcr-pkcs11-import-dialog.h"
+
+#include "egg/egg-entry-buffer.h"
+#include "egg/egg-secure-memory.h"
+
+#include <gtk/gtk.h>
+
+#include <glib/gi18n-lib.h>
+
+EGG_SECURE_DECLARE (import_dialog);
+
+#define GCR_PKCS11_IMPORT_DIALOG_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_PKCS11_IMPORT_DIALOG, GcrPkcs11ImportDialogClass))
+#define GCR_IS_PKCS11_IMPORT_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_PKCS11_IMPORT_DIALOG))
+#define GCR_PKCS11_IMPORT_DIALOG_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_PKCS11_IMPORT_DIALOG, GcrPkcs11ImportDialogClass))
+
+enum {
+	PROP_0,
+	PROP_IMPORTER
+};
+
+struct _GcrPkcs11ImportDialog {
+	GtkDialog parent;
+	GcrImporter *importer;
+	GtkBuilder *builder;
+	GtkWidget *password_area;
+	GtkLabel *token_label;
+	GtkImage *token_image;
+	GtkEntry *password_entry;
+	GtkEntry *label_entry;
+	gboolean label_changed;
+};
+
+typedef struct _GcrPkcs11ImportDialogClass GcrPkcs11ImportDialogClass;
+
+struct _GcrPkcs11ImportDialogClass {
+	GtkDialogClass parent;
+};
+
+G_DEFINE_TYPE (GcrPkcs11ImportDialog, _gcr_pkcs11_import_dialog, GTK_TYPE_DIALOG);
+
+static void
+on_label_changed (GtkEditable *editable,
+                  gpointer user_data)
+{
+	GcrPkcs11ImportDialog *self = GCR_PKCS11_IMPORT_DIALOG (user_data);
+	self->label_changed = TRUE;
+}
+
+static GList *
+pkcs11_importer_get_queued (GcrImporter *importer)
+{
+	GList *queued = NULL;
+
+	/* TODO: This is ugly */
+
+	g_object_get (importer, "queued", &queued, NULL);
+
+	return queued;
+}
+
+static void
+_gcr_pkcs11_import_dialog_constructed (GObject *obj)
+{
+	GcrPkcs11ImportDialog *self = GCR_PKCS11_IMPORT_DIALOG (obj);
+	GError *error = NULL;
+	GtkEntryBuffer *buffer;
+	GtkWidget *widget;
+	GtkBox *contents;
+	GList *queued, *l;
+	gchar *label = NULL;
+	gchar *value;
+
+	G_OBJECT_CLASS (_gcr_pkcs11_import_dialog_parent_class)->constructed (obj);
+
+	if (!gtk_builder_add_from_file (self->builder, UIDIR "gcr-pkcs11-import-dialog.ui", &error)) {
+		g_warning ("couldn't load ui builder file: %s", error->message);
+		return;
+	}
+
+	/* Fill in the dialog from builder */
+	widget = GTK_WIDGET (gtk_builder_get_object (self->builder, "pkcs11-import-dialog"));
+	contents = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self)));
+	gtk_box_pack_start (contents, widget, TRUE, TRUE, 0);
+
+	/* The password area */
+	self->password_area = GTK_WIDGET (gtk_builder_get_object (self->builder, "unlock-area"));
+	gtk_widget_hide (self->password_area);
+
+	/* Add a secure entry */
+	buffer = egg_entry_buffer_new ();
+	self->password_entry = GTK_ENTRY (gtk_builder_get_object (self->builder, "password-entry"));
+	gtk_entry_set_buffer (self->password_entry, buffer);
+	gtk_entry_set_activates_default (self->password_entry, TRUE);
+	g_object_unref (buffer);
+
+	self->token_label = GTK_LABEL (gtk_builder_get_object (self->builder, "token-description"));
+	self->token_image = GTK_IMAGE (gtk_builder_get_object (self->builder, "token-image"));
+
+	/* Setup the label */
+	self->label_entry = GTK_ENTRY (gtk_builder_get_object (self->builder, "label-entry"));
+	g_signal_connect (self->label_entry, "changed", G_CALLBACK (on_label_changed), self);
+	gtk_entry_set_activates_default (self->label_entry, TRUE);
+
+	queued = pkcs11_importer_get_queued (self->importer);
+	for (l = queued; l != NULL; l = g_list_next (l)) {
+		if (!gck_attributes_find_string (l->data, CKA_LABEL, &value))
+			value = NULL;
+		if (l == queued) {
+			label = value;
+			value = NULL;
+		} else if (g_strcmp0 (label, value) != 0) {
+			g_free (label);
+			label = NULL;
+		}
+		g_free (value);
+	}
+	g_list_free (queued);
+
+	if (label == NULL)
+		gtk_entry_set_placeholder_text (self->label_entry, _("Automatically chosen"));
+	else
+		gtk_entry_set_text (self->label_entry, label);
+	g_free (label);
+
+	/* Add our various buttons */
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_OK, GTK_RESPONSE_OK);
+	gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
+
+	gtk_window_set_modal (GTK_WINDOW (self), TRUE);
+}
+
+static void
+_gcr_pkcs11_import_dialog_init (GcrPkcs11ImportDialog *self)
+{
+	self->builder = gtk_builder_new ();
+}
+
+static void
+_gcr_pkcs11_import_dialog_finalize (GObject *obj)
+{
+	GcrPkcs11ImportDialog *self = GCR_PKCS11_IMPORT_DIALOG (obj);
+
+	g_object_unref (self->builder);
+	g_clear_object (&self->importer);
+
+	G_OBJECT_CLASS (_gcr_pkcs11_import_dialog_parent_class)->finalize (obj);
+}
+
+static void
+_gcr_pkcs11_import_dialog_set_property (GObject *obj,
+                                        guint prop_id,
+                                        const GValue *value,
+                                        GParamSpec *pspec)
+{
+	GcrPkcs11ImportDialog *self = GCR_PKCS11_IMPORT_DIALOG (obj);
+
+	switch (prop_id) {
+	case PROP_IMPORTER:
+		g_return_if_fail (self->importer == NULL);
+		self->importer = g_value_dup_object (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gcr_pkcs11_import_dialog_get_property (GObject *obj,
+                                        guint prop_id,
+                                        GValue *value,
+                                        GParamSpec *pspec)
+{
+	GcrPkcs11ImportDialog *self = GCR_PKCS11_IMPORT_DIALOG (obj);
+
+	switch (prop_id) {
+	case PROP_IMPORTER:
+		g_value_set_object (value, self->importer);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gcr_pkcs11_import_dialog_class_init (GcrPkcs11ImportDialogClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->constructed = _gcr_pkcs11_import_dialog_constructed;
+	gobject_class->finalize = _gcr_pkcs11_import_dialog_finalize;
+	gobject_class->set_property = _gcr_pkcs11_import_dialog_set_property;
+	gobject_class->get_property = _gcr_pkcs11_import_dialog_get_property;
+
+	g_object_class_install_property (gobject_class, PROP_IMPORTER,
+	           g_param_spec_object ("importer", "Importer", "The PKCS#11 importer",
+	                                GCR_TYPE_IMPORTER,
+	                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+GcrPkcs11ImportDialog *
+_gcr_pkcs11_import_dialog_new (GcrImporter *importer,
+                               GtkWindow *parent)
+{
+	GcrPkcs11ImportDialog *dialog;
+
+	g_return_val_if_fail (GCR_IS_IMPORTER (importer), NULL);
+	g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
+
+	dialog = g_object_new (GCR_TYPE_PKCS11_IMPORT_DIALOG,
+	                       "importer", importer,
+	                       "transient-for", parent,
+	                       NULL);
+
+	return g_object_ref_sink (dialog);
+}
+
+static void
+update_importer_labels (GcrPkcs11ImportDialog *self)
+{
+	const gchar *label;
+	GList *queued, *l;
+
+	label = gtk_entry_get_text (self->label_entry);
+	if (self->label_changed && label != NULL && label[0]) {
+		queued = pkcs11_importer_get_queued (self->importer);
+		for (l = queued; l != NULL; l = g_list_next (l))
+			gck_attributes_set_string (l->data, CKA_LABEL, label);
+		g_list_free (queued);
+	}
+}
+
+gboolean
+_gcr_pkcs11_import_dialog_run (GcrPkcs11ImportDialog *self)
+{
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (GCR_IS_PKCS11_IMPORT_DIALOG (self), FALSE);
+
+	if (gtk_dialog_run (GTK_DIALOG (self)) == GTK_RESPONSE_OK) {
+		update_importer_labels (self);
+		ret = TRUE;
+	}
+
+	gtk_widget_hide (GTK_WIDGET (self));
+
+	return ret;
+}
+
+void
+_gcr_pkcs11_import_dialog_run_async (GcrPkcs11ImportDialog *self,
+                                     GCancellable *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
+{
+	g_return_if_fail (GCR_IS_PKCS11_IMPORT_DIALOG (self));
+
+	_gcr_dialog_util_run_async (GTK_DIALOG (self), cancellable, callback, user_data);
+}
+
+gboolean
+_gcr_pkcs11_import_dialog_run_finish (GcrPkcs11ImportDialog *self,
+                                      GAsyncResult *result)
+{
+	gint response;
+
+	g_return_val_if_fail (GCR_IS_PKCS11_IMPORT_DIALOG (self), FALSE);
+
+	response = _gcr_dialog_util_run_finish (GTK_DIALOG (self), result);
+
+	gtk_widget_hide (GTK_WIDGET (self));
+
+	if (response == GTK_RESPONSE_OK) {
+		update_importer_labels (self);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+GTlsInteractionResult
+_gcr_pkcs11_import_dialog_run_ask_password (GcrPkcs11ImportDialog *self,
+                                            GTlsPassword *password,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+	GckTokenInfo *token_info;
+	const gchar *value;
+	GckSlot *slot;
+	GIcon *icon;
+	gboolean ret;
+
+	g_return_val_if_fail (GCR_IS_PKCS11_IMPORT_DIALOG (self), G_TLS_INTERACTION_UNHANDLED);
+	g_return_val_if_fail (G_IS_TLS_PASSWORD (password), G_TLS_INTERACTION_UNHANDLED);
+	g_return_val_if_fail (error == NULL || *error == NULL, G_TLS_INTERACTION_UNHANDLED);
+
+	if (GCK_IS_PASSWORD (password)) {
+		slot = gck_password_get_token (GCK_PASSWORD (password));
+		token_info = gck_slot_get_token_info (slot);
+		icon = gcr_icon_for_token (token_info);
+		gtk_image_set_from_gicon (self->token_image, icon, GTK_ICON_SIZE_BUTTON);
+		gck_token_info_free (token_info);
+		g_object_unref (icon);
+	}
+
+	gtk_label_set_text (self->token_label, g_tls_password_get_description (password));
+
+	gtk_widget_show (self->password_area);
+
+	ret = _gcr_pkcs11_import_dialog_run (self);
+
+	gtk_widget_hide (self->password_area);
+
+	if (!ret) {
+		g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+		             _("The user cancelled the operation"));
+		return G_TLS_INTERACTION_FAILED;
+	}
+
+	value = gtk_entry_get_text (self->password_entry);
+	g_tls_password_set_value_full (password, egg_secure_strdup (value),
+	                               -1, egg_secure_free);
+	return G_TLS_INTERACTION_HANDLED;
+}
diff --git a/gcr/gcr-pkcs11-import-dialog.h b/gcr/gcr-pkcs11-import-dialog.h
new file mode 100644
index 0000000..7c20769
--- /dev/null
+++ b/gcr/gcr-pkcs11-import-dialog.h
@@ -0,0 +1,62 @@
+/*
+ * 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>
+ */
+
+#ifndef __GCR_PKCS11_IMPORT_DIALOG_H__
+#define __GCR_PKCS11_IMPORT_DIALOG_H__
+
+#include "gcr.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_PKCS11_IMPORT_DIALOG               (_gcr_pkcs11_import_dialog_get_type ())
+#define GCR_PKCS11_IMPORT_DIALOG(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PKCS11_IMPORT_DIALOG, GcrPkcs11ImportDialog))
+#define GCR_IS_PKCS11_IMPORT_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PKCS11_IMPORT_DIALOG))
+
+typedef struct _GcrPkcs11ImportDialog GcrPkcs11ImportDialog;
+
+GType                   _gcr_pkcs11_import_dialog_get_type          (void) G_GNUC_CONST;
+
+GcrPkcs11ImportDialog * _gcr_pkcs11_import_dialog_new               (GcrImporter *importer,
+                                                                     GtkWindow *parent);
+
+gboolean                _gcr_pkcs11_import_dialog_run               (GcrPkcs11ImportDialog *self);
+
+void                    _gcr_pkcs11_import_dialog_run_async         (GcrPkcs11ImportDialog *self,
+                                                                     GCancellable *cancellable,
+                                                                     GAsyncReadyCallback callback,
+                                                                     gpointer user_data);
+
+gboolean                _gcr_pkcs11_import_dialog_run_finish        (GcrPkcs11ImportDialog *self,
+                                                                     GAsyncResult *result);
+
+GTlsInteractionResult   _gcr_pkcs11_import_dialog_run_ask_password  (GcrPkcs11ImportDialog *self,
+                                                                     GTlsPassword *password,
+                                                                     GCancellable *cancellable,
+                                                                     GError **error);
+
+G_END_DECLS
+
+#endif /* __GCR_PKCS11_IMPORT_DIALOG_H__ */
diff --git a/gcr/gcr-pkcs11-import-dialog.ui b/gcr/gcr-pkcs11-import-dialog.ui
new file mode 100644
index 0000000..a565cf1
--- /dev/null
+++ b/gcr/gcr-pkcs11-import-dialog.ui
@@ -0,0 +1,251 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 2.12 -->
+  <object class="GtkVBox" id="pkcs11-import-dialog">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">6</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkFrame" id="unlock-area">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">none</property>
+        <child>
+          <object class="GtkAlignment" id="alignment2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="left_padding">12</property>
+            <child>
+              <object class="GtkBox" id="box1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="password-description">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">In order to import, please enter the password.</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkGrid" id="grid2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="row_spacing">6</property>
+                    <property name="column_spacing">12</property>
+                    <child>
+                      <object class="GtkEntry" id="password-entry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">â</property>
+                        <property name="visibility">False</property>
+                        <property name="width_chars">20</property>
+                        <property name="invisible_char_set">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="password-label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Password:</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="token-label">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Token:</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkBox" id="box2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkImage" id="token-image">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="stock">gtk-missing-image</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="token-description">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Label</property>
+                          </object>
+                          <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="label5">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">&lt;b&gt;Unlock&lt;/b&gt;</property>
+            <property name="use_markup">True</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkFrame" id="settings-area">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">none</property>
+        <child>
+          <object class="GtkAlignment" id="alignment1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="left_padding">12</property>
+            <child>
+              <object class="GtkGrid" id="grid1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="row_spacing">6</property>
+                <property name="column_spacing">12</property>
+                <child>
+                  <object class="GtkLabel" id="label-label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Label:</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="label-entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="invisible_char">â</property>
+                    <property name="width_chars">20</property>
+                    <property name="invisible_char_set">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                    <property name="width">1</property>
+                    <property name="height">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="label4">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_bottom">6</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">&lt;b&gt;Import settings&lt;/b&gt;</property>
+            <property name="use_markup">True</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkSizeGroup" id="entry-size-group">
+    <property name="mode">GTK_SIZE_GROUP_HORIZONTAL</property>
+    <widgets>
+      <widget name="password-label"/>
+      <widget name="token-label"/>
+      <widget name="label-label"/>
+    </widgets>
+  </object>
+</interface>
diff --git a/gcr/gcr-pkcs11-import-interaction.c b/gcr/gcr-pkcs11-import-interaction.c
new file mode 100644
index 0000000..3be7c94
--- /dev/null
+++ b/gcr/gcr-pkcs11-import-interaction.c
@@ -0,0 +1,240 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-dialog-util.h"
+#include "gcr-import-interaction.h"
+#include "gcr-pkcs11-import-interaction.h"
+
+#include <glib/gi18n-lib.h>
+
+#define GCR_PKCS11_IMPORT_INTERACTION_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_PKCS11_IMPORT_INTERACTION, GcrPkcs11ImportInteractionClass))
+#define GCR_IS_PKCS11_IMPORT_INTERACTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_PKCS11_IMPORT_INTERACTION))
+#define GCR_PKCS11_IMPORT_INTERACTION_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_PKCS11_IMPORT_INTERACTION, GcrPkcs11ImportInteractionClass))
+
+enum {
+	PROP_0,
+	PROP_IMPORTER,
+	PROP_PARENT_WINDOW
+};
+
+typedef struct _GcrPkcs11ImportInteractionClass GcrPkcs11ImportInteractionClass;
+
+struct _GcrPkcs11ImportInteraction {
+	GTlsInteraction parent;
+	GcrImporter *importer;
+	gboolean dialog_shown;
+	GtkWindow *parent_window;
+	GcrPkcs11ImportDialog *dialog;
+};
+
+struct _GcrPkcs11ImportInteractionClass {
+	GTlsInteractionClass parent_class;
+};
+
+static void _gcr_pkcs11_import_interaction_iface_init (GcrImportInteractionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE(GcrPkcs11ImportInteraction, _gcr_pkcs11_import_interaction, G_TYPE_TLS_INTERACTION,
+                        G_IMPLEMENT_INTERFACE (GCR_TYPE_IMPORT_INTERACTION, _gcr_pkcs11_import_interaction_iface_init));
+
+static void
+_gcr_pkcs11_import_interaction_init (GcrPkcs11ImportInteraction *self)
+{
+
+}
+
+static void
+_gcr_pkcs11_import_interaction_dispose (GObject *obj)
+{
+	GcrPkcs11ImportInteraction *self = GCR_PKCS11_IMPORT_INTERACTION (obj);
+
+	g_clear_object (&self->dialog);
+
+	G_OBJECT_CLASS (_gcr_pkcs11_import_interaction_parent_class)->dispose (obj);
+}
+
+static void
+_gcr_pkcs11_import_interaction_set_property (GObject *obj,
+                                             guint prop_id,
+                                             const GValue *value,
+                                             GParamSpec *pspec)
+{
+	GcrPkcs11ImportInteraction *self = GCR_PKCS11_IMPORT_INTERACTION (obj);
+
+	switch (prop_id) {
+	case PROP_IMPORTER:
+		g_clear_object (&self->dialog);
+		g_clear_object (&self->importer);
+		self->importer = g_value_dup_object (value);
+		self->dialog_shown = FALSE;
+		if (self->importer != NULL)
+			self->dialog = _gcr_pkcs11_import_dialog_new (self->importer, self->parent_window);
+		break;
+	case PROP_PARENT_WINDOW:
+		g_clear_object (&self->parent_window);
+		self->parent_window = g_value_dup_object (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+_gcr_pkcs11_import_interaction_get_property (GObject *obj,
+                                             guint prop_id,
+                                             GValue *value,
+                                             GParamSpec *pspec)
+{
+	GcrPkcs11ImportInteraction *self = GCR_PKCS11_IMPORT_INTERACTION (obj);
+
+	switch (prop_id) {
+	case PROP_IMPORTER:
+		g_value_set_object (value, self->importer);
+		break;
+	case PROP_PARENT_WINDOW:
+		g_value_set_object (value, self->parent_window);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static GTlsInteractionResult
+_gcr_pkcs11_import_interaction_ask_password (GTlsInteraction *interaction,
+                                             GTlsPassword *password,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+	GcrPkcs11ImportInteraction *self = GCR_PKCS11_IMPORT_INTERACTION (interaction);
+
+	g_return_val_if_fail (self->dialog != NULL, G_TLS_INTERACTION_UNHANDLED);
+
+	self->dialog_shown = TRUE;
+	return _gcr_pkcs11_import_dialog_run_ask_password (self->dialog, password, cancellable, error);
+}
+
+static GTlsInteractionResult
+_gcr_pkcs11_import_interaction_supplement (GcrImportInteraction *interaction,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+	GcrPkcs11ImportInteraction *self = GCR_PKCS11_IMPORT_INTERACTION (interaction);
+
+	g_return_val_if_fail (self->dialog != NULL, G_TLS_INTERACTION_UNHANDLED);
+
+	if (self->dialog_shown)
+		return G_TLS_INTERACTION_HANDLED;
+
+	self->dialog_shown = TRUE;
+	if (_gcr_pkcs11_import_dialog_run (self->dialog)) {
+		return G_TLS_INTERACTION_HANDLED;
+
+	} else {
+		g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("The user cancelled the operation"));
+		return G_TLS_INTERACTION_FAILED;
+	}
+}
+
+static void
+_gcr_pkcs11_import_interaction_supplement_async (GcrImportInteraction *interaction,
+                                                 GCancellable *cancellable,
+                                                 GAsyncReadyCallback callback,
+                                                 gpointer user_data)
+{
+	GcrPkcs11ImportInteraction *self = GCR_PKCS11_IMPORT_INTERACTION (interaction);
+	GSimpleAsyncResult *res;
+
+	g_return_if_fail (self->dialog != NULL);
+
+	/* If dialog was already shown, then short circuit */
+	if (self->dialog_shown) {
+		res = g_simple_async_result_new (G_OBJECT (interaction), callback, user_data,
+		                                 _gcr_pkcs11_import_interaction_supplement_async);
+		g_simple_async_result_complete_in_idle (res);
+		g_object_unref (res);
+
+	} else {
+		self->dialog_shown = TRUE;
+		_gcr_pkcs11_import_dialog_run_async (self->dialog, cancellable, callback, user_data);
+	}
+}
+
+static GTlsInteractionResult
+_gcr_pkcs11_import_interaction_supplement_finish (GcrImportInteraction *interaction,
+                                                  GAsyncResult *result,
+                                                  GError **error)
+{
+	GcrPkcs11ImportInteraction *self = GCR_PKCS11_IMPORT_INTERACTION (interaction);
+
+	g_return_val_if_fail (self->dialog != NULL, G_TLS_INTERACTION_UNHANDLED);
+
+	/* If it was short circuited without a dialog */
+	if (g_simple_async_result_is_valid (result, G_OBJECT (interaction),
+	                                    _gcr_pkcs11_import_interaction_supplement_async)) {
+		return G_TLS_INTERACTION_HANDLED;
+
+	} else if (_gcr_pkcs11_import_dialog_run_finish (self->dialog, result)) {
+		return G_TLS_INTERACTION_HANDLED;
+
+	} else {
+		g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("The user cancelled the operation"));
+		return G_TLS_INTERACTION_FAILED;
+	}
+}
+
+static void
+_gcr_pkcs11_import_interaction_iface_init (GcrImportInteractionIface *iface)
+{
+	iface->supplement = _gcr_pkcs11_import_interaction_supplement;
+	iface->supplement_async = _gcr_pkcs11_import_interaction_supplement_async;
+	iface->supplement_finish = _gcr_pkcs11_import_interaction_supplement_finish;
+}
+
+static void
+_gcr_pkcs11_import_interaction_class_init (GcrPkcs11ImportInteractionClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
+
+	gobject_class->dispose = _gcr_pkcs11_import_interaction_dispose;
+	gobject_class->set_property = _gcr_pkcs11_import_interaction_set_property;
+	gobject_class->get_property = _gcr_pkcs11_import_interaction_get_property;
+
+	interaction_class->ask_password = _gcr_pkcs11_import_interaction_ask_password;
+
+	g_object_class_override_property (gobject_class, PROP_IMPORTER, "importer");
+
+	g_object_class_install_property (gobject_class, PROP_PARENT_WINDOW,
+	              g_param_spec_object ("parent-window", "Parent Window", "Prompt Parent Window",
+	                                   GTK_TYPE_WINDOW, G_PARAM_READWRITE));
+}
+
+GTlsInteraction *
+_gcr_pkcs11_import_interaction_new (GtkWindow *parent_window)
+{
+	g_return_val_if_fail (parent_window == NULL || GTK_IS_WINDOW (parent_window), NULL);
+	return g_object_new (GCR_TYPE_PKCS11_IMPORT_INTERACTION,
+	                     "parent-window", parent_window,
+	                     NULL);
+}
diff --git a/gcr/gcr-pkcs11-import-interaction.h b/gcr/gcr-pkcs11-import-interaction.h
new file mode 100644
index 0000000..97027ba
--- /dev/null
+++ b/gcr/gcr-pkcs11-import-interaction.h
@@ -0,0 +1,45 @@
+/*
+ * 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>
+ */
+
+#ifndef __GCR_PKCS11_IMPORT_INTERACTION_H__
+#define __GCR_PKCS11_IMPORT_INTERACTION_H__
+
+#include "gcr.h"
+
+#include "gcr-pkcs11-import-dialog.h"
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_PKCS11_IMPORT_INTERACTION               (_gcr_pkcs11_import_interaction_get_type ())
+#define GCR_PKCS11_IMPORT_INTERACTION(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PKCS11_IMPORT_INTERACTION, GcrPkcs11ImportInteraction))
+#define GCR_IS_PKCS11_IMPORT_INTERACTION(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PKCS11_IMPORT_INTERACTION))
+
+typedef struct _GcrPkcs11ImportInteraction GcrPkcs11ImportInteraction;
+
+GType               _gcr_pkcs11_import_interaction_get_type     (void) G_GNUC_CONST;
+
+GTlsInteraction *   _gcr_pkcs11_import_interaction_new          (GtkWindow *parent_window);
+
+G_END_DECLS
+
+#endif /* __GCR_PKCS11_IMPORT_INTERACTION_H__ */
diff --git a/gcr/gcr-pkcs11-importer.c b/gcr/gcr-pkcs11-importer.c
index feb012c..b22b4d7 100644
--- a/gcr/gcr-pkcs11-importer.c
+++ b/gcr/gcr-pkcs11-importer.c
@@ -27,6 +27,7 @@
 #include "gcr-base.h"
 #include "gcr-internal.h"
 #include "gcr-library.h"
+#include "gcr-import-interaction.h"
 #include "gcr-internal.h"
 #include "gcr-parser.h"
 #include "gcr-pkcs11-importer.h"
@@ -39,18 +40,28 @@ enum {
 	PROP_0,
 	PROP_LABEL,
 	PROP_ICON,
+	PROP_INTERACTION,
 	PROP_SLOT,
-	PROP_IMPORTED
+	PROP_IMPORTED,
+	PROP_QUEUED
 };
 
-struct _GcrPkcs11ImporterPrivate {
+typedef struct _GcrPkcs11ImporterClass GcrPkcs11ImporterClass;
+
+struct _GcrPkcs11Importer {
+	GObject parent;
 	GckSlot *slot;
 	GList *objects;
 	GckSession *session;
 	GQueue queue;
+	GTlsInteraction *interaction;
 	gboolean any_private;
 };
 
+struct _GcrPkcs11ImporterClass {
+	GObjectClass parent_class;
+};
+
 typedef struct  {
 	GcrPkcs11Importer *importer;
 	GCancellable *cancellable;
@@ -68,6 +79,9 @@ static void   state_complete                   (GSimpleAsyncResult *res,
 static void   state_create_object              (GSimpleAsyncResult *res,
                                                 gboolean async);
 
+static void   state_supplement                 (GSimpleAsyncResult *res,
+                                                gboolean async);
+
 static void   state_open_session               (GSimpleAsyncResult *res,
                                                 gboolean async);
 
@@ -111,6 +125,13 @@ static void
 state_complete (GSimpleAsyncResult *res,
                 gboolean async)
 {
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+	GcrPkcs11Importer *self = data->importer;
+
+	/* Disconnect from the interaction */
+	if (data->importer->interaction && GCR_IS_IMPORT_INTERACTION (self->interaction))
+		gcr_import_interaction_set_importer (GCR_IMPORT_INTERACTION (self->interaction), NULL);
+
 	g_simple_async_result_complete (res);
 }
 
@@ -146,7 +167,7 @@ complete_create_object (GSimpleAsyncResult *res,
 		next_state (res, state_complete);
 
 	} else {
-		self->pv->objects = g_list_append (self->pv->objects, object);
+		self->objects = g_list_append (self->objects, object);
 		next_state (res, state_create_object);
 	}
 }
@@ -176,23 +197,23 @@ state_create_object (GSimpleAsyncResult *res,
 	GError *error = NULL;
 
 	/* No more objects */
-	if (g_queue_is_empty (&self->pv->queue)) {
+	if (g_queue_is_empty (&self->queue)) {
 		next_state (res, state_complete);
 
 	} else {
 
 		/* Pop first one off the list */
-		attrs = g_queue_pop_head (&self->pv->queue);
+		attrs = g_queue_pop_head (&self->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,
+			gck_session_create_object_async (self->session, attrs,
 			                                 data->cancellable, on_create_object,
 			                                 g_object_ref (res));
 		} else {
-			object = gck_session_create_object (self->pv->session, attrs,
+			object = gck_session_create_object (self->session, attrs,
 			                                    data->cancellable, &error);
 			complete_create_object (res, object, error);
 		}
@@ -202,6 +223,61 @@ state_create_object (GSimpleAsyncResult *res,
 }
 
 /* ---------------------------------------------------------------------------------
+ * PROMPTING
+ */
+
+static void
+complete_supplement (GSimpleAsyncResult *res,
+                      GError *error)
+{
+	if (error == NULL) {
+		next_state (res, state_create_object);
+	} else {
+		g_simple_async_result_take_error (res, error);
+		next_state (res, state_complete);
+	}
+}
+
+static void
+on_supplement_done (GObject *source,
+                    GAsyncResult *result,
+                    gpointer user_data)
+{
+	GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+	GcrPkcs11Importer *self = data->importer;
+	GError *error = NULL;
+
+	gcr_import_interaction_supplement_finish (GCR_IMPORT_INTERACTION (self->interaction),
+	                                          result, &error);
+	complete_supplement (res, error);
+	g_object_unref (res);
+}
+
+static void
+state_supplement (GSimpleAsyncResult *res,
+                  gboolean async)
+{
+	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
+	GcrPkcs11Importer *self = data->importer;
+	GError *error = NULL;
+
+	if (self->interaction == NULL || !GCR_IS_IMPORT_INTERACTION (self->interaction)) {
+		complete_supplement (res, NULL);
+
+	} else if (async) {
+		gcr_import_interaction_supplement_async (GCR_IMPORT_INTERACTION (self->interaction),
+		                                         data->cancellable, on_supplement_done,
+		                                         g_object_ref (res));
+
+	} else {
+		gcr_import_interaction_supplement (GCR_IMPORT_INTERACTION (self->interaction),
+		                                   data->cancellable, &error);
+		complete_supplement (res, error);
+	}
+}
+
+/* ---------------------------------------------------------------------------------
  * OPEN SESSION
  */
 
@@ -218,9 +294,9 @@ complete_open_session (GSimpleAsyncResult *res,
 		next_state (res, state_complete);
 
 	} else {
-		g_clear_object (&self->pv->session);
-		self->pv->session = session;
-		next_state (res, state_create_object);
+		g_clear_object (&self->session);
+		self->session = session;
+		next_state (res, state_supplement);
 	}
 }
 
@@ -244,19 +320,16 @@ state_open_session (GSimpleAsyncResult *res,
 {
 	GcrImporterData *data = g_simple_async_result_get_op_res_gpointer (res);
 	GcrPkcs11Importer *self = data->importer;
-	guint options = GCK_SESSION_READ_WRITE;
+	guint options = GCK_SESSION_READ_WRITE | GCK_SESSION_LOGIN_USER;
 	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,
+		gck_slot_open_session_async (self->slot, options,
 		                             data->cancellable, on_open_session,
 		                             g_object_ref (res));
 	} else {
-		session = gck_slot_open_session_full (self->pv->slot, options, 0,
+		session = gck_slot_open_session_full (self->slot, options, 0,
 		                                      NULL, NULL, data->cancellable, &error);
 		complete_open_session (res, session, error);
 	}
@@ -265,8 +338,7 @@ state_open_session (GSimpleAsyncResult *res,
 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);
+	g_queue_init (&self->queue);
 }
 
 static void
@@ -274,12 +346,13 @@ _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);
+	gck_list_unref_free (self->objects);
+	self->objects = NULL;
+	g_clear_object (&self->session);
+	g_clear_object (&self->interaction);
 
-	while (!g_queue_is_empty (&self->pv->queue))
-		gck_attributes_unref (g_queue_pop_head (&self->pv->queue));
+	while (!g_queue_is_empty (&self->queue))
+		gck_attributes_unref (g_queue_pop_head (&self->queue));
 
 	G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->dispose (obj);
 }
@@ -289,7 +362,7 @@ _gcr_pkcs11_importer_finalize (GObject *obj)
 {
 	GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
 
-	g_clear_object (&self->pv->slot);
+	g_clear_object (&self->slot);
 
 	G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->finalize (obj);
 }
@@ -304,8 +377,13 @@ _gcr_pkcs11_importer_set_property (GObject *obj,
 
 	switch (prop_id) {
 	case PROP_SLOT:
-		self->pv->slot = g_value_dup_object (value);
-		g_return_if_fail (self->pv->slot);
+		self->slot = g_value_dup_object (value);
+		g_return_if_fail (self->slot);
+		break;
+	case PROP_INTERACTION:
+		g_clear_object (&self->interaction);
+		self->interaction = g_value_dup_object (value);
+		g_object_notify (G_OBJECT (self), "interaction");
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -319,7 +397,7 @@ calculate_label (GcrPkcs11Importer *self)
 	GckTokenInfo *info;
 	gchar *result;
 
-	info = gck_slot_get_token_info (self->pv->slot);
+	info = gck_slot_get_token_info (self->slot);
 	result = g_strdup (info->label);
 	gck_token_info_free (info);
 
@@ -334,7 +412,7 @@ calculate_icon (GcrPkcs11Importer *self,
 	GIcon *result;
 
 	if (token_info == NULL)
-		info = token_info = gck_slot_get_token_info (self->pv->slot);
+		info = token_info = gck_slot_get_token_info (self->slot);
 	result = gcr_icon_for_token (token_info);
 	gck_token_info_free (info);
 
@@ -360,7 +438,13 @@ _gcr_pkcs11_importer_get_property (GObject *obj,
 		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));
+		g_value_take_boxed (value, _gcr_pkcs11_importer_get_imported (self));
+		break;
+	case PROP_QUEUED:
+		g_value_set_pointer (value, _gcr_pkcs11_importer_get_queued (self));
+		break;
+	case PROP_INTERACTION:
+		g_value_set_object (value, self->interaction);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -379,12 +463,12 @@ _gcr_pkcs11_importer_class_init (GcrPkcs11ImporterClass *klass)
 	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_override_property (gobject_class, PROP_INTERACTION, "interaction");
+
 	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));
@@ -393,6 +477,10 @@ _gcr_pkcs11_importer_class_init (GcrPkcs11ImporterClass *klass)
 	           g_param_spec_boxed ("imported", "Imported", "Imported objects",
 	                               GCK_TYPE_LIST, G_PARAM_READABLE));
 
+	g_object_class_install_property (gobject_class, PROP_QUEUED,
+	           g_param_spec_pointer ("queued", "Queued", "Queued attributes",
+	                                 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);
@@ -493,16 +581,10 @@ _gcr_pkcs11_importer_queue_for_parsed (GcrImporter *importer,
 {
 	GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (importer);
 	GckAttributes *attrs;
-	gboolean is_private;
 
 	attrs = gcr_parsed_get_attributes (parsed);
+	_gcr_pkcs11_importer_queue (self, attrs);
 
-	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;
 }
 
@@ -512,6 +594,7 @@ _gcr_pkcs11_importer_import_async (GcrImporter *importer,
                                    GAsyncReadyCallback callback,
                                    gpointer user_data)
 {
+	GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (importer);
 	GSimpleAsyncResult *res;
 	GcrImporterData *data;
 
@@ -523,6 +606,11 @@ _gcr_pkcs11_importer_import_async (GcrImporter *importer,
 	data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
 	g_simple_async_result_set_op_res_gpointer (res, data, gcr_importer_data_free);
 
+	if (GCR_IS_IMPORT_INTERACTION (self->interaction))
+		gcr_import_interaction_set_importer (GCR_IMPORT_INTERACTION (self->interaction),
+		                                     GCR_IMPORTER (self));
+	gck_slot_set_interaction (self->slot, self->interaction);
+
 	next_state (res, state_open_session);
 	g_object_unref (res);
 }
@@ -569,29 +657,29 @@ GckSlot *
 _gcr_pkcs11_importer_get_slot (GcrPkcs11Importer *self)
 {
 	g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL);
-	return self->pv->slot;
+	return self->slot;
 }
 
 GList *
 _gcr_pkcs11_importer_get_imported (GcrPkcs11Importer *self)
 {
 	g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL);
-	return self->pv->objects;
+	return g_list_copy (self->objects);
+}
+
+GList *
+_gcr_pkcs11_importer_get_queued (GcrPkcs11Importer *self)
+{
+	g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL);
+	return g_list_copy (self->queue.head);
 }
 
 void
 _gcr_pkcs11_importer_queue (GcrPkcs11Importer *self,
                             GckAttributes *attrs)
 {
-	gboolean is_private;
-
 	g_return_if_fail (GCR_IS_PKCS11_IMPORTER (self));
 	g_return_if_fail (attrs != NULL);
 
-	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));
+	g_queue_push_tail (&self->queue, gck_attributes_ref (attrs));
 }
diff --git a/gcr/gcr-pkcs11-importer.h b/gcr/gcr-pkcs11-importer.h
index bb0a8f2..a1e47f2 100644
--- a/gcr/gcr-pkcs11-importer.h
+++ b/gcr/gcr-pkcs11-importer.h
@@ -41,19 +41,7 @@ G_BEGIN_DECLS
 #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);
 
@@ -64,6 +52,8 @@ void                      _gcr_pkcs11_importer_queue           (GcrPkcs11Importe
 
 GckSlot *                 _gcr_pkcs11_importer_get_slot        (GcrPkcs11Importer *self);
 
+GList *                   _gcr_pkcs11_importer_get_queued      (GcrPkcs11Importer *self);
+
 GList *                   _gcr_pkcs11_importer_get_imported    (GcrPkcs11Importer *self);
 
 G_END_DECLS



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