[gcr] gck: Add methods for opening and logging in that accept interactions



commit cb5ac66f0773c439c00b8a87f4a43e2532402412
Author: Stef Walter <stefw collabora co uk>
Date:   Tue Nov 1 17:42:26 2011 +0100

    gck: Add methods for opening and logging in that accept interactions
    
     * Make GckSession GInitable and GAsyncInitable
     * With convenience functions gck_session_open and
       gck_session_open_async
     * Also add gck_session_login_interactive and
       gck_session_login_interactive_async

 docs/reference/gck/gck-sections.txt |    6 +
 docs/reference/gck/gck.interfaces   |    1 +
 gck/gck-call.c                      |    6 +-
 gck/gck-session.c                   |  441 ++++++++++++++++++++++++++++++++--
 gck/gck-slot.c                      |  158 ++-----------
 gck/gck.h                           |   33 +++
 gck/gck.symbols                     |    6 +
 gck/tests/Makefile.am               |    4 +
 gck/tests/test-gck-session.c        |  191 +++++++++++++++-
 9 files changed, 675 insertions(+), 171 deletions(-)
---
diff --git a/docs/reference/gck/gck-sections.txt b/docs/reference/gck/gck-sections.txt
index 00c2dd6..4f96348 100644
--- a/docs/reference/gck/gck-sections.txt
+++ b/docs/reference/gck/gck-sections.txt
@@ -175,6 +175,9 @@ gck_session_options_get_type
 <FILE>gck-session</FILE>
 GckSession
 gck_session_from_handle
+gck_session_open
+gck_session_open_async
+gck_session_open_finish
 gck_session_get_module
 gck_session_get_slot
 gck_session_get_handle
@@ -203,6 +206,9 @@ gck_session_decrypt
 gck_session_decrypt_full
 gck_session_decrypt_async
 gck_session_decrypt_finish
+gck_session_login_interactive
+gck_session_login_interactive_async
+gck_session_login_interactive_finish
 gck_session_sign
 gck_session_sign_full
 gck_session_sign_async
diff --git a/docs/reference/gck/gck.interfaces b/docs/reference/gck/gck.interfaces
index e69de29..2ea03e1 100644
--- a/docs/reference/gck/gck.interfaces
+++ b/docs/reference/gck/gck.interfaces
@@ -0,0 +1 @@
+GckSession GInitable GAsyncInitable
diff --git a/gck/gck-call.c b/gck/gck-call.c
index f839330..fa17b48 100644
--- a/gck/gck-call.c
+++ b/gck/gck-call.c
@@ -257,8 +257,12 @@ _gck_call_get_user_data (GAsyncResult *async_result)
 static GObject*
 _gck_call_get_source_object (GAsyncResult *async_result)
 {
+	GObject *source;
+
 	g_return_val_if_fail (GCK_IS_CALL (async_result), NULL);
-	return GCK_CALL (async_result)->object;
+
+	source = GCK_CALL (async_result)->object;
+	return source ? g_object_ref (source): NULL;
 }
 
 static void
diff --git a/gck/gck-session.c b/gck/gck-session.c
index b71b5af..8b4ca76 100644
--- a/gck/gck-session.c
+++ b/gck/gck-session.c
@@ -79,20 +79,31 @@ enum {
 	PROP_INTERACTION,
 	PROP_SLOT,
 	PROP_OPTIONS,
+	PROP_OPENING_FLAGS,
+	PROP_APP_DATA
 };
 
 struct _GckSessionPrivate {
+	/* Not modified after construct/init */
 	GckSlot *slot;
-	GckModule *module;
 	CK_SESSION_HANDLE handle;
 	GTlsInteraction *interaction;
 	GckSessionOptions options;
+	gulong opening_flags;
+	gpointer app_data;
 
 	/* Modified atomically */
 	gint discarded;
 };
 
-G_DEFINE_TYPE (GckSession, gck_session, G_TYPE_OBJECT);
+static void    gck_session_initable_iface        (GInitableIface *iface);
+
+static void    gck_session_async_initable_iface  (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GckSession, gck_session, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gck_session_initable_iface);
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gck_session_async_initable_iface);
+);
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
@@ -104,14 +115,15 @@ static gboolean
 gck_session_real_discard_handle (GckSession *self, CK_OBJECT_HANDLE handle)
 {
 	CK_FUNCTION_LIST_PTR funcs;
+	GckModule *module;
 	CK_RV rv;
 
 	/* The default functionality, close the handle */
 
-	g_return_val_if_fail (self->pv->module, FALSE);
-	g_object_ref (self->pv->module);
+	module = gck_session_get_module (self);
+	g_return_val_if_fail (module != NULL, FALSE);
 
-	funcs = gck_module_get_functions (self->pv->module);
+	funcs = gck_module_get_functions (module);
 	g_return_val_if_fail (funcs, FALSE);
 
 	rv = (funcs->C_CloseSession) (handle);
@@ -120,7 +132,7 @@ gck_session_real_discard_handle (GckSession *self, CK_OBJECT_HANDLE handle)
 		           gck_message_from_rv (rv));
 	}
 
-	g_object_unref (self->pv->module);
+	g_object_unref (module);
 	return TRUE;
 }
 
@@ -167,11 +179,6 @@ gck_session_set_property (GObject *obj, guint prop_id, const GValue *value,
 	/* Only valid calls are from constructor */
 
 	switch (prop_id) {
-	case PROP_MODULE:
-		g_return_if_fail (!self->pv->module);
-		self->pv->module = g_value_dup_object (value);
-		g_return_if_fail (self->pv->module);
-		break;
 	case PROP_HANDLE:
 		g_return_if_fail (!self->pv->handle);
 		self->pv->handle = g_value_get_ulong (value);
@@ -189,6 +196,12 @@ gck_session_set_property (GObject *obj, guint prop_id, const GValue *value,
 		g_return_if_fail (!self->pv->options);
 		self->pv->options = g_value_get_flags (value);
 		break;
+	case PROP_OPENING_FLAGS:
+		self->pv->opening_flags = g_value_get_ulong (value);
+		break;
+	case PROP_APP_DATA:
+		self->pv->app_data = g_value_get_pointer (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -196,6 +209,18 @@ gck_session_set_property (GObject *obj, guint prop_id, const GValue *value,
 }
 
 static void
+gck_session_constructed (GObject *obj)
+{
+	GckSession *self = GCK_SESSION (obj);
+
+	G_OBJECT_CLASS (gck_session_parent_class)->constructed (obj);
+
+	self->pv->opening_flags |= CKF_SERIAL_SESSION;
+	if (self->pv->options & GCK_SESSION_READ_WRITE)
+		self->pv->opening_flags |= CKF_RW_SESSION;
+}
+
+static void
 gck_session_dispose (GObject *obj)
 {
 	GckSession *self = GCK_SESSION (obj);
@@ -226,7 +251,6 @@ gck_session_finalize (GObject *obj)
 
 	g_clear_object (&self->pv->interaction);
 	g_clear_object (&self->pv->slot);
-	g_clear_object (&self->pv->module);
 
 	G_OBJECT_CLASS (gck_session_parent_class)->finalize (obj);
 }
@@ -237,6 +261,7 @@ gck_session_class_init (GckSessionClass *klass)
 	GObjectClass *gobject_class = (GObjectClass*)klass;
 	gck_session_parent_class = g_type_class_peek_parent (klass);
 
+	gobject_class->constructed = gck_session_constructed;
 	gobject_class->get_property = gck_session_get_property;
 	gobject_class->set_property = gck_session_set_property;
 	gobject_class->dispose = gck_session_dispose;
@@ -250,8 +275,8 @@ gck_session_class_init (GckSessionClass *klass)
 	 * The GckModule that this session is opened on.
 	 */
 	g_object_class_install_property (gobject_class, PROP_MODULE,
-		g_param_spec_object ("module", "Module", "PKCS11 Module",
-		                     GCK_TYPE_MODULE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	            g_param_spec_object ("module", "Module", "PKCS11 Module",
+	                                 GCK_TYPE_MODULE, G_PARAM_READABLE));
 
 	/**
 	 * GckSession:handle:
@@ -293,6 +318,24 @@ gck_session_class_init (GckSessionClass *klass)
 		                     G_TYPE_TLS_INTERACTION, G_PARAM_READWRITE));
 
 	/**
+	 * GckSession:opening-flags:
+	 *
+	 * Raw PKCS#11 flags used to open the PKCS#11 session.
+	 */
+	g_object_class_install_property (gobject_class, PROP_OPENING_FLAGS,
+	             g_param_spec_ulong ("opening-flags", "Opening flags", "PKCS#11 open session flags",
+	                                 0, G_MAXULONG, 0, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GckSession:app-data:
+	 *
+	 * Raw PKCS#11 application data used to open the PKCS#11 session.
+	 */
+	g_object_class_install_property (gobject_class, PROP_APP_DATA,
+	           g_param_spec_pointer ("app-data", "App data", "PKCS#11 application data",
+	                                 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+	/**
 	 * GckSession::discard-handle:
 	 * @session: The session.
 	 * @handle: The handle being discarded.
@@ -312,6 +355,173 @@ gck_session_class_init (GckSessionClass *klass)
 	g_type_class_add_private (klass, sizeof (GckSessionPrivate));
 }
 
+typedef struct OpenSession {
+	GckArguments base;
+	GTlsInteraction *interaction;
+	GckSlot *slot;
+	gulong flags;
+	gpointer app_data;
+	CK_NOTIFY notify;
+	gboolean auto_login;
+	CK_SESSION_HANDLE session;
+} OpenSession;
+
+static void
+free_open_session (OpenSession *args)
+{
+	g_clear_object (&args->interaction);
+	g_clear_object (&args->slot);
+	g_free (args);
+}
+
+static CK_RV
+perform_open_session (OpenSession *args)
+{
+	GTlsInteraction *interaction;
+	CK_RV rv = CKR_OK;
+
+	/* First step, open session */
+	if (!args->session) {
+		rv = (args->base.pkcs11->C_OpenSession) (args->base.handle, args->flags,
+		                                         args->app_data, args->notify, &args->session);
+	}
+
+	if (rv != CKR_OK || !args->auto_login)
+		return rv;
+
+	/* Compatibility, hook into GckModule signals if no interaction set */
+	if (args->interaction)
+		interaction = g_object_ref (args->interaction);
+	else
+		interaction = _gck_interaction_new (args->slot);
+
+	rv = _gck_session_authenticate_token (args->base.pkcs11, args->session,
+	                                      args->slot, interaction, NULL);
+
+	g_object_unref (interaction);
+
+	return rv;
+}
+
+static gboolean
+gck_session_initable_init (GInitable *initable,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+	GckSession *self = GCK_SESSION (initable);
+	OpenSession args = { GCK_ARGUMENTS_INIT, 0,  };
+	GckModule *module = NULL;
+	gboolean ret = FALSE;
+	gboolean want_login;
+
+	want_login = (self->pv->options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER;
+
+	/* Already have a session setup? */
+	if (self->pv->handle && !want_login)
+		return TRUE;
+
+	g_object_ref (self);
+	module = gck_session_get_module (self);
+
+	/* Open a new session */
+	args.slot = self->pv->slot;
+	args.app_data = self->pv->app_data;
+	args.notify = NULL;
+	args.session = self->pv->handle;
+	args.flags = self->pv->opening_flags;
+	args.interaction = self->pv->interaction ? g_object_ref (self->pv->interaction) : NULL;
+	args.auto_login = want_login;
+
+	if (_gck_call_sync (self->pv->slot, perform_open_session, NULL, &args, cancellable, error)) {
+		self->pv->handle = args.session;
+		ret = TRUE;
+	}
+
+	g_clear_object (&args.interaction);
+	g_object_unref (module);
+	g_object_unref (self);
+
+	return ret;
+}
+
+static void
+gck_session_initable_iface (GInitableIface *iface)
+{
+	iface->init = gck_session_initable_init;
+}
+
+static void
+gck_session_initable_init_async (GAsyncInitable *initable,
+                                 int io_priority,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GckSession *self = GCK_SESSION (initable);
+	OpenSession *args;
+	gboolean want_login;
+	GckCall *call;
+
+	g_object_ref (self);
+
+	args =  _gck_call_async_prep (self->pv->slot, self, perform_open_session, NULL,
+	                              sizeof (*args), free_open_session);
+
+	want_login = (self->pv->options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER;
+	args->session = self->pv->handle;
+
+	call = 	_gck_call_async_ready (args, cancellable, callback, user_data);
+
+	/* Already have a session setup? */
+	if (self->pv->handle && !want_login) {
+		_gck_call_async_short (call, CKR_OK);
+		g_object_unref (self);
+		return;
+	}
+
+	args->app_data = self->pv->app_data;
+	args->notify = NULL;
+	args->slot = g_object_ref (self->pv->slot);
+	args->interaction = self->pv->interaction ? g_object_ref (self->pv->interaction) : NULL;
+	args->auto_login = want_login;
+	args->flags = self->pv->opening_flags;
+
+	_gck_call_async_go (call);
+	g_object_unref (self);
+}
+
+static gboolean
+gck_session_initable_init_finish (GAsyncInitable *initable,
+                                  GAsyncResult *result,
+                                  GError **error)
+{
+	GckSession *self = GCK_SESSION (initable);
+	gboolean ret = FALSE;
+
+	g_object_ref (self);
+
+	{
+		OpenSession *args;
+
+		if (_gck_call_basic_finish (result, error)) {
+			args = _gck_call_arguments (result, OpenSession);
+			self->pv->handle = args->session;
+			ret = TRUE;
+		}
+	}
+
+	g_object_unref (self);
+
+	return ret;
+}
+
+static void
+gck_session_async_initable_iface (GAsyncInitableIface *iface)
+{
+	iface->init_async = gck_session_initable_init_async;
+	iface->init_finish = gck_session_initable_init_finish;
+}
+
 /* ----------------------------------------------------------------------------
  * PUBLIC
  */
@@ -397,7 +607,6 @@ gck_session_from_handle (GckSlot *slot,
 	interaction = gck_slot_get_interaction (slot);
 
 	session = g_object_new (GCK_TYPE_SESSION,
-	                        "module", module,
 	                        "interaction", interaction,
 	                        "handle", session_handle,
 	                        "slot", slot,
@@ -437,8 +646,7 @@ GckModule *
 gck_session_get_module (GckSession *self)
 {
 	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
-	g_return_val_if_fail (GCK_IS_MODULE (self->pv->module), NULL);
-	return g_object_ref (self->pv->module);
+	return gck_slot_get_module (self->pv->slot);
 }
 
 /**
@@ -472,20 +680,21 @@ gck_session_get_info (GckSession *self)
 	GckSessionInfo *sessioninfo;
 	CK_FUNCTION_LIST_PTR funcs;
 	CK_SESSION_INFO info;
+	GckModule *module;
 	CK_RV rv;
 
 	g_return_val_if_fail (GCK_IS_SESSION (self), NULL);
-	g_return_val_if_fail (GCK_IS_MODULE (self->pv->module), NULL);
 
-	g_object_ref (self->pv->module);
+	module = gck_session_get_module (self);
+	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);
 
-	funcs = gck_module_get_functions (self->pv->module);
+	funcs = gck_module_get_functions (module);
 	g_return_val_if_fail (funcs, NULL);
 
 	memset (&info, 0, sizeof (info));
 	rv = (funcs->C_GetSessionInfo) (self->pv->handle, &info);
 
-	g_object_unref (self->pv->module);
+	g_object_unref (module);
 
 	if (rv != CKR_OK) {
 		g_warning ("couldn't get session info: %s", gck_message_from_rv (rv));
@@ -514,20 +723,21 @@ gck_session_get_state (GckSession *self)
 {
 	CK_FUNCTION_LIST_PTR funcs;
 	CK_SESSION_INFO info;
+	GckModule *module;
 	CK_RV rv;
 
 	g_return_val_if_fail (GCK_IS_SESSION (self), 0);
-	g_return_val_if_fail (GCK_IS_MODULE (self->pv->module), 0);
 
-	g_object_ref (self->pv->module);
+	module = gck_session_get_module (self);
+	g_return_val_if_fail (GCK_IS_MODULE (module), 0);
 
-	funcs = gck_module_get_functions (self->pv->module);
+	funcs = gck_module_get_functions (module);
 	g_return_val_if_fail (funcs, 0);
 
 	memset (&info, 0, sizeof (info));
 	rv = (funcs->C_GetSessionInfo) (self->pv->handle, &info);
 
-	g_object_unref (self->pv->module);
+	g_object_unref (module);
 
 	if (rv != CKR_OK) {
 		g_warning ("couldn't get session info: %s", gck_message_from_rv (rv));
@@ -572,6 +782,82 @@ gck_session_get_interaction (GckSession *self)
 	return NULL;
 }
 
+/**
+ * gck_session_open:
+ * @slot: the slot to open session on
+ * @options: session options
+ * @interaction: (allow-none): optional interaction for logins or object authentication
+ * @cancellable: optional cancellation object
+ * @error: location to place error or %NULL
+ *
+ * Open a session on the slot. This call may block for an indefinite period.
+ *
+ * Returns: (transfer full): the new session
+ */
+GckSession *
+gck_session_open (GckSlot *slot,
+                  GckSessionOptions options,
+                  GTlsInteraction *interaction,
+                  GCancellable *cancellable,
+                  GError **error)
+{
+	return g_initable_new (GCK_TYPE_SESSION, cancellable, error,
+	                       "slot", slot,
+	                       "interaction", interaction,
+	                       "options", options,
+	                       NULL);
+}
+
+/**
+ * gck_session_open_async:
+ * @slot: the slot to open session on
+ * @options: session options
+ * @interaction: (allow-none): optional interaction for logins or object authentication
+ * @callback: called when the operation completes
+ * @user_data: data to pass to callback
+ *
+ * Open a session on the slot. This call will return immediately and complete
+ * asynchronously.
+ */
+void
+gck_session_open_async (GckSlot *slot,
+                        GckSessionOptions options,
+                        GTlsInteraction *interaction,
+                        GCancellable *cancellable,
+                        GAsyncReadyCallback callback,
+                        gpointer user_data)
+{
+	g_async_initable_new_async (GCK_TYPE_SESSION, G_PRIORITY_DEFAULT,
+	                            cancellable, callback, user_data,
+	                            "slot", slot,
+	                            "interaction", interaction,
+	                            "options", options,
+	                            NULL);
+}
+
+/**
+ * gck_session_open_finish:
+ * @result: the result passed to the callback
+ * @error: location to return an error or %NULL
+ *
+ * Get the result of an open session operation.
+ *
+ * Returns: (transfer full): the new session
+ */
+GckSession *
+gck_session_open_finish (GAsyncResult *result,
+                         GError **error)
+{
+	GObject *ret;
+	GObject *source;
+
+	source = g_async_result_get_source_object (result);
+	ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source), result, error);
+	g_object_unref (source);
+
+	return ret ? GCK_SESSION (ret) : NULL;
+}
+
 /* ---------------------------------------------------------------------------------------------
  * INIT PIN
  */
@@ -843,7 +1129,6 @@ gck_session_login_async (GckSession *self, gulong user_type, const guchar *pin,
 	args->n_pin = n_pin;
 
 	_gck_call_async_ready_go (args, cancellable, callback, user_data);
-
 }
 
 /**
@@ -862,8 +1147,112 @@ gck_session_login_finish (GckSession *self, GAsyncResult *result, GError **error
 	return _gck_call_basic_finish (result, error);
 }
 
+typedef struct _Interactive {
+	GckArguments base;
+	GTlsInteraction *interaction;
+	GCancellable *cancellable;
+	GckSlot *token;
+} Interactive;
+
+static void
+free_interactive (Interactive *args)
+{
+	g_clear_object (&args->token);
+	g_clear_object (&args->cancellable);
+	g_clear_object (&args->interaction);
+	g_free (args);
+}
+
+static CK_RV
+perform_interactive (Interactive *args)
+{
+	return _gck_session_authenticate_token (args->base.pkcs11, args->base.handle,
+	                                        args->token, args->interaction, args->cancellable);
+}
+
+/**
+ * gck_session_login_interactive:
+ * @self: session to use for login
+ * @user_type: the type of login user
+ * @interaction: interaction to request PIN when necessary
+ * @cancellable: optional cancellation object, or %NULL
+ * @error: location to return an error
+ *
+ * Login the user on the session requesting the password interactively
+ * when necessary. This call may block for an indefinite period.
+ *
+ * Return value: Whether successful or not.
+ */
+gboolean
+gck_session_login_interactive (GckSession *self,
+                               gulong user_type,
+                               GTlsInteraction *interaction,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+	Interactive args = { GCK_ARGUMENTS_INIT, interaction, cancellable, NULL, };
+
+	g_return_val_if_fail (GCK_IS_SESSION (self), FALSE);
+
+	/* TODO: For now this is all we support */
+	g_return_val_if_fail (user_type == CKU_USER, FALSE);
+
+	args.token = self->pv->slot;
+
+	return _gck_call_sync (self, perform_interactive, NULL, &args, cancellable, error);
+}
+
+/**
+ * gck_session_login_interactive_async:
+ * @self: session to use for login
+ * @user_type: the type of login user
+ * @interaction: interaction to request PIN when necessary
+ * @cancellable: optional cancellation object, or %NULL
+ * @callback: called when the operation completes
+ * @user_data: data to pass to the callback
+ *
+ * Login the user on the session prompting for passwords interactively when
+ * necessary. This call will return immediately and completes asynchronously.
+ **/
+void
+gck_session_login_interactive_async (GckSession *self,
+                                     gulong user_type,
+                                     GTlsInteraction *interaction,
+                                     GCancellable *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data)
+{
+	Interactive* args = _gck_call_async_prep (self, self, perform_interactive, NULL, sizeof (*args), free_interactive);
+
+	g_return_if_fail (GCK_IS_SESSION (self));
 
+	/* TODO: For now this is all we support */
+	g_return_if_fail (user_type == CKU_USER);
 
+	args->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+	args->interaction = interaction ? g_object_ref (interaction) : NULL;
+	args->token = g_object_ref (self->pv->slot);
+
+	_gck_call_async_ready_go (args, cancellable, callback, user_data);
+}
+
+/**
+ * gck_session_login_interactive_finish:
+ * @self: the session logged into
+ * @result: the result passed to the callback
+ * @error: location to return an error
+ *
+ * Get the result of a login operation.
+ *
+ * Return value: Whether the operation was successful or not.
+ **/
+gboolean
+gck_session_login_interactive_finish (GckSession *self,
+                                      GAsyncResult *result,
+                                      GError **error)
+{
+	return _gck_call_basic_finish (result, error);
+}
 
 /* LOGOUT */
 
diff --git a/gck/gck-slot.c b/gck/gck-slot.c
index 65cf5b3..5dd9b9c 100644
--- a/gck/gck-slot.c
+++ b/gck/gck-slot.c
@@ -64,30 +64,6 @@ struct _GckSlotPrivate {
 G_DEFINE_TYPE (GckSlot, gck_slot, G_TYPE_OBJECT);
 
 /* ----------------------------------------------------------------------------
- * HELPERS
- */
-
-static GckSession*
-make_session_object (GckSlot *self,
-                     GckSessionOptions options,
-                     CK_SESSION_HANDLE handle)
-{
-	GckSession *session;
-	GckModule *module;
-
-	g_return_val_if_fail (handle != 0, NULL);
-
-	module = gck_slot_get_module (self);
-
-	session = gck_session_from_handle (self, handle, options);
-	g_return_val_if_fail (session != NULL, NULL);
-
-	g_object_unref (module);
-
-	return session;
-}
-
-/* ----------------------------------------------------------------------------
  * OBJECT
  */
 
@@ -1056,58 +1032,6 @@ gck_slots_enumerate_objects (GList *slots,
 }
 
 
-typedef struct OpenSession {
-	GckArguments base;
-	GTlsInteraction *interaction;
-	GckSlot *slot;
-	gulong flags;
-	gpointer app_data;
-	CK_NOTIFY notify;
-	gchar *password;
-	gboolean auto_login;
-	CK_SESSION_HANDLE session;
-} OpenSession;
-
-static CK_RV
-perform_open_session (OpenSession *args)
-{
-	GTlsInteraction *interaction;
-	CK_RV rv = CKR_OK;
-
-	/* Can be called multiple times */
-
-	/* First step, open session */
-	if (!args->session) {
-		rv = (args->base.pkcs11->C_OpenSession) (args->base.handle, args->flags,
-		                                         args->app_data, args->notify, &args->session);
-	}
-
-	if (rv != CKR_OK || !args->auto_login)
-		return rv;
-
-	/* Compatibility, hook into GckModule signals if no interaction set */
-	if (args->interaction)
-		interaction = g_object_ref (args->interaction);
-	else
-		interaction = _gck_interaction_new (args->slot);
-
-	rv = _gck_session_authenticate_token (args->base.pkcs11, args->session,
-	                                      args->slot, interaction, NULL);
-
-	g_object_unref (interaction);
-
-	return rv;
-}
-
-static void
-free_open_session (OpenSession *args)
-{
-	g_clear_object (&args->interaction);
-	g_clear_object (&args->slot);
-	g_assert (!args->password);
-	g_free (args);
-}
-
 /**
  * gck_slot_open_session:
  * @self: The slot ot open a session on.
@@ -1157,36 +1081,12 @@ gck_slot_open_session_full (GckSlot *self,
                             GCancellable *cancellable,
                             GError **error)
 {
-	OpenSession args = { GCK_ARGUMENTS_INIT, 0,  };
-	GckSession *session = NULL;
-	GckModule *module = NULL;
-
-	g_object_ref (self);
-
-	module = gck_slot_get_module (self);
-
-	/* Open a new session */
-	args.slot = self;
-	args.app_data = app_data;
-	args.notify = notify;
-	args.password = NULL;
-	args.session = 0;
-	args.interaction = gck_slot_get_interaction (self);
-
-	args.auto_login = ((options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER);
-
-	args.flags = pkcs11_flags | CKF_SERIAL_SESSION;
-	if ((options & GCK_SESSION_READ_WRITE) == GCK_SESSION_READ_WRITE)
-		args.flags |= CKF_RW_SESSION;
-
-	if (_gck_call_sync (self, perform_open_session, NULL, &args, cancellable, error))
-		session = make_session_object (self, options, args.session);
-
-	g_clear_object (&args.interaction);
-	g_object_unref (module);
-	g_object_unref (self);
-
-	return session;
+	return g_initable_new (GCK_TYPE_SESSION, cancellable, error,
+	                       "options", options,
+	                       "slot", self,
+	                       "opening-flags", pkcs11_flags,
+	                       "app-data", app_data,
+	                       NULL);
 }
 
 /**
@@ -1238,26 +1138,13 @@ gck_slot_open_session_full_async (GckSlot *self,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data)
 {
-	OpenSession *args;
-
-	g_object_ref (self);
-
-	args =  _gck_call_async_prep (self, self, perform_open_session, NULL,
-	                              sizeof (*args), free_open_session);
-
-	args->app_data = app_data;
-	args->notify = notify;
-	args->slot = g_object_ref (self);
-	args->interaction = gck_slot_get_interaction (self);
-
-	args->auto_login = ((options & GCK_SESSION_LOGIN_USER) == GCK_SESSION_LOGIN_USER);
-
-	args->flags = pkcs11_flags | CKF_SERIAL_SESSION;
-	if ((options & GCK_SESSION_READ_WRITE) == GCK_SESSION_READ_WRITE)
-		args->flags |= CKF_RW_SESSION;
-
-	_gck_call_async_ready_go (args, cancellable, callback, user_data);
-	g_object_unref (self);
+	g_async_initable_new_async (GCK_TYPE_SESSION, G_PRIORITY_DEFAULT,
+	                            cancellable, callback, user_data,
+	                            "options", options,
+	                            "slot", self,
+	                            "opening-flags", pkcs11_flags,
+	                            "app-data", app_data,
+	                            NULL);
 }
 
 /**
@@ -1271,25 +1158,10 @@ gck_slot_open_session_full_async (GckSlot *self,
  *
  * Returns: (transfer full): the new session or %NULL if an error occurs
  */
-GckSession*
+GckSession *
 gck_slot_open_session_finish (GckSlot *self, GAsyncResult *result, GError **err)
 {
-	GckSession *session = NULL;
-
-	g_object_ref (self);
-
-	{
-		OpenSession *args;
-
-		if (_gck_call_basic_finish (result, err)) {
-			args = _gck_call_arguments (result, OpenSession);
-			session = make_session_object (self, args->flags, args->session);
-		}
-	}
-
-	g_object_unref (self);
-
-	return session;
+	return gck_session_open_finish (result, err);
 }
 
 /**
diff --git a/gck/gck.h b/gck/gck.h
index 4a5981c..8cbcb45 100644
--- a/gck/gck.h
+++ b/gck/gck.h
@@ -727,6 +727,22 @@ GckSessionOptions   gck_session_get_options                 (GckSession *self);
 
 GTlsInteraction *   gck_session_get_interaction             (GckSession *self);
 
+GckSession *        gck_session_open                        (GckSlot *slot,
+                                                             GckSessionOptions options,
+                                                             GTlsInteraction *interaction,
+                                                             GCancellable *cancellable,
+                                                             GError **error);
+
+void                gck_session_open_async                  (GckSlot *slot,
+                                                             GckSessionOptions options,
+                                                             GTlsInteraction *interaction,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+GckSession *        gck_session_open_finish                 (GAsyncResult *result,
+                                                             GError **error);
+
 gboolean            gck_session_init_pin                    (GckSession *self,
                                                              const guchar *pin,
                                                              gsize n_pin,
@@ -784,6 +800,23 @@ gboolean            gck_session_login_finish                (GckSession *self,
                                                              GAsyncResult *result,
                                                              GError **error);
 
+gboolean            gck_session_login_interactive           (GckSession *self,
+                                                             gulong user_type,
+                                                             GTlsInteraction *interaction,
+                                                             GCancellable *cancellable,
+                                                             GError **error);
+
+void                gck_session_login_interactive_async     (GckSession *self,
+                                                             gulong user_type,
+                                                             GTlsInteraction *interaction,
+                                                             GCancellable *cancellable,
+                                                             GAsyncReadyCallback callback,
+                                                             gpointer user_data);
+
+gboolean            gck_session_login_interactive_finish    (GckSession *self,
+                                                             GAsyncResult *result,
+                                                             GError **error);
+
 gboolean            gck_session_logout                      (GckSession *self,
                                                              GCancellable *cancellable,
                                                              GError **error);
diff --git a/gck/gck.symbols b/gck/gck.symbols
index 80f7e7c..ac1a276 100644
--- a/gck/gck.symbols
+++ b/gck/gck.symbols
@@ -180,9 +180,15 @@ gck_session_init_pin_finish
 gck_session_login
 gck_session_login_async
 gck_session_login_finish
+gck_session_login_interactive
+gck_session_login_interactive_async
+gck_session_login_interactive_finish
 gck_session_logout
 gck_session_logout_async
 gck_session_logout_finish
+gck_session_open
+gck_session_open_async
+gck_session_open_finish
 gck_session_options_get_type
 gck_session_set_pin
 gck_session_set_pin_async
diff --git a/gck/tests/Makefile.am b/gck/tests/Makefile.am
index abddc6a..113813e 100644
--- a/gck/tests/Makefile.am
+++ b/gck/tests/Makefile.am
@@ -31,6 +31,10 @@ test_gck_enumerator_SOURCES = \
 	test-gck-enumerator.c \
 	mock-interaction.c mock-interaction.h
 
+test_gck_session_SOURCES = \
+	test-gck-session.c \
+	mock-interaction.c mock-interaction.h
+
 check_PROGRAMS = $(TEST_PROGS)
 
 test: $(TEST_PROGS)
diff --git a/gck/tests/test-gck-session.c b/gck/tests/test-gck-session.c
index 632a760..7f9e576 100644
--- a/gck/tests/test-gck-session.c
+++ b/gck/tests/test-gck-session.c
@@ -23,6 +23,8 @@
 
 #include "config.h"
 
+#include "mock-interaction.h"
+
 #include "gck/gck.h"
 #include "gck/gck-test.h"
 
@@ -140,6 +142,126 @@ test_open_close_session (Test *test, gconstpointer unused)
 }
 
 static void
+test_session_initable (Test *test,
+                       gconstpointer unused)
+{
+	GckSession *sess;
+	GAsyncResult *result = NULL;
+	GError *err = NULL;
+
+	sess = gck_session_open (test->slot, 0, NULL, NULL, &err);
+	g_assert_no_error (err);
+	g_assert (GCK_IS_SESSION (sess));
+
+	g_object_unref (sess);
+
+	/* Test opening async */
+	gck_session_open_async (test->slot, 0, NULL, NULL, fetch_async_result, &result);
+
+	egg_test_wait_until (500);
+	g_assert (result != NULL);
+
+	/* Get the result */
+	sess = gck_session_open_finish (result, &err);
+	g_assert_no_error (err);
+	g_assert (GCK_IS_SESSION (sess));
+
+	g_object_unref (result);
+	g_object_unref (sess);
+}
+
+static void
+test_session_already (Test *test,
+                      gconstpointer unused)
+{
+	CK_FUNCTION_LIST_PTR funcs;
+	gulong handle;
+	GckSession *session;
+	GAsyncResult *result = NULL;
+	GError *error = NULL;
+	GObject *source;
+	CK_RV rv;
+
+	funcs = gck_module_get_functions (test->module);
+	g_assert (funcs != NULL);
+
+	rv = funcs->C_OpenSession (gck_slot_get_handle (test->slot), CKF_SERIAL_SESSION,
+	                           NULL, NULL, &handle);
+	gck_assert_cmprv (rv, ==, CKR_OK);
+	g_assert (handle != 0);
+
+	session = g_initable_new (GCK_TYPE_SESSION, NULL, &error,
+	                          "slot", test->slot,
+	                          "handle", handle,
+	                          NULL);
+
+	g_assert_no_error (error);
+	g_assert (GCK_IS_SESSION (session));
+	gck_assert_cmpulong (handle, ==, gck_session_get_handle (session));
+	g_object_unref (session);
+
+	rv = funcs->C_OpenSession (gck_slot_get_handle (test->slot), CKF_SERIAL_SESSION,
+	                           NULL, NULL, &handle);
+	gck_assert_cmprv (rv, ==, CKR_OK);
+	g_assert (handle != 0);
+
+	/* Test opening async */
+	g_async_initable_new_async (GCK_TYPE_SESSION, G_PRIORITY_DEFAULT, NULL,
+	                            fetch_async_result, &result,
+	                            "slot", test->slot,
+	                            "handle", handle,
+	                            NULL);
+
+	egg_test_wait_until (500);
+	g_assert (result != NULL);
+
+	/* Get the result */
+	source = g_async_result_get_source_object (result);
+	session = GCK_SESSION (g_async_initable_new_finish (G_ASYNC_INITABLE (source), result, &error));
+	g_object_unref (source);
+	g_assert_no_error (error);
+	g_assert (GCK_IS_SESSION (session));
+	gck_assert_cmpulong (handle, ==, gck_session_get_handle (session));
+
+	g_object_unref (result);
+	g_object_unref (session);
+}
+
+static void
+test_open_interaction (Test *test,
+                       gconstpointer unused)
+{
+	GckSession *sess;
+	GAsyncResult *result = NULL;
+	GError *err = NULL;
+	GTlsInteraction *interaction;
+
+	interaction = mock_interaction_new ("booo");
+
+	sess = gck_session_open (test->slot, GCK_SESSION_LOGIN_USER, interaction, NULL, &err);
+	g_assert_no_error (err);
+	g_assert (GCK_IS_SESSION (sess));
+
+	g_object_unref (sess);
+
+	/* Test opening async */
+	gck_session_open_async (test->slot, GCK_SESSION_LOGIN_USER, interaction, NULL, fetch_async_result, &result);
+
+	egg_test_wait_until (500);
+	g_assert (result != NULL);
+
+	/* Get the result */
+	sess = gck_session_open_finish (result, &err);
+	g_assert_no_error (err);
+	g_assert (GCK_IS_SESSION (sess));
+
+	g_object_unref (interaction);
+	g_object_unref (result);
+	g_object_unref (sess);
+
+}
+
+static void
 test_init_set_pin (Test *test, gconstpointer unused)
 {
 	GAsyncResult *result = NULL;
@@ -177,23 +299,29 @@ test_init_set_pin (Test *test, gconstpointer unused)
 	result = NULL;
 }
 
-
 static void
 test_login_logout (Test *test, gconstpointer unused)
 {
 	GAsyncResult *result = NULL;
 	GError *err = NULL;
 	gboolean ret;
+	gulong state;
 
 	/* login/logout */
 	ret = gck_session_login (test->session, CKU_USER, (guchar*)"booo", 4, NULL, &err);
 	g_assert_no_error (err);
 	g_assert (ret);
 
+	state = gck_session_get_state (test->session);
+	gck_assert_cmpulong (state, ==, CKS_RO_USER_FUNCTIONS);
+
 	ret = gck_session_logout (test->session, NULL, &err);
 	g_assert_no_error (err);
 	g_assert (ret);
 
+	state = gck_session_get_state (test->session);
+	gck_assert_cmpulong (state, ==, CKS_RO_PUBLIC_SESSION);
+
 	/* login async */
 	gck_session_login_async (test->session, CKU_USER, (guchar*)"booo", 4, NULL, fetch_async_result, &result);
 	egg_test_wait_until (500);
@@ -206,6 +334,9 @@ test_login_logout (Test *test, gconstpointer unused)
 	g_object_unref (result);
 	result = NULL;
 
+	state = gck_session_get_state (test->session);
+	gck_assert_cmpulong (state, ==, CKS_RO_USER_FUNCTIONS);
+
 	/* logout async */
 	gck_session_logout_async (test->session, NULL, fetch_async_result, &result);
 	egg_test_wait_until (500);
@@ -215,9 +346,63 @@ test_login_logout (Test *test, gconstpointer unused)
 	g_assert_no_error (err);
 	g_assert (ret);
 
+	state = gck_session_get_state (test->session);
+	gck_assert_cmpulong (state, ==, CKS_RO_PUBLIC_SESSION);
+
 	g_object_unref (result);
 	result = NULL;
+}
+
+static void
+test_login_interactive (Test *test,
+                        gconstpointer unused)
+{
+	GAsyncResult *result = NULL;
+	GError *error = NULL;
+	gboolean ret;
+	gulong state;
+	GTlsInteraction *interaction;
+
+	interaction = mock_interaction_new ("booo");
+
+	/* login/logout */
+	ret = gck_session_login_interactive (test->session, CKU_USER, interaction, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (ret);
+
+	state = gck_session_get_state (test->session);
+	gck_assert_cmpulong (state, ==, CKS_RO_USER_FUNCTIONS);
+
+	ret = gck_session_logout (test->session, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (ret);
+
+	state = gck_session_get_state (test->session);
+	gck_assert_cmpulong (state, ==, CKS_RO_PUBLIC_SESSION);
+
+	/* login async */
+	gck_session_login_interactive_async (test->session, CKU_USER, interaction, NULL, fetch_async_result, &result);
+	egg_test_wait_until (500);
+	g_assert (result != NULL);
+
+	ret = gck_session_login_interactive_finish (test->session, result, &error);
+	g_assert_no_error (error);
+	g_assert (ret);
+
+	g_object_unref (result);
+	result = NULL;
+
+	state = gck_session_get_state (test->session);
+	gck_assert_cmpulong (state, ==, CKS_RO_USER_FUNCTIONS);
+
+	ret = gck_session_logout (test->session, NULL, &error);
+	g_assert_no_error (error);
+	g_assert (ret);
+
+	state = gck_session_get_state (test->session);
+	gck_assert_cmpulong (state, ==, CKS_RO_PUBLIC_SESSION);
 
+	g_object_unref (interaction);
 }
 
 static gboolean
@@ -311,8 +496,12 @@ main (int argc, char **argv)
 	g_test_add ("/gck/session/session_props", Test, NULL, setup, test_session_props, teardown);
 	g_test_add ("/gck/session/session_info", Test, NULL, setup, test_session_info, teardown);
 	g_test_add ("/gck/session/open_close_session", Test, NULL, setup, test_open_close_session, teardown);
+	g_test_add ("/gck/session/open_initable", Test, NULL, setup, test_session_initable, teardown);
+	g_test_add ("/gck/session/open_already", Test, NULL, setup, test_session_already, teardown);
+	g_test_add ("/gck/session/open_interaction", Test, NULL, setup, test_open_interaction, teardown);
 	g_test_add ("/gck/session/init_set_pin", Test, NULL, setup, test_init_set_pin, teardown);
 	g_test_add ("/gck/session/login_logout", Test, NULL, setup, test_login_logout, teardown);
+	g_test_add ("/gck/session/login_interactive", Test, NULL, setup, test_login_interactive, teardown);
 	g_test_add ("/gck/session/auto_login", Test, NULL, setup, test_auto_login, teardown);
 
 	return egg_tests_run_in_thread_with_loop ();



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