[glib/wip/danw/clicert: 20/22] Add a request_certificate virtual method to GTlsInteraction



commit dd3b14dc8706434779baf256a3945621d92dadcb
Author: Stef Walter <stefw gnome org>
Date:   Wed Nov 28 22:01:21 2012 +0100

    Add a request_certificate virtual method to GTlsInteraction
    
    This allows GTlsConnection implementations to request a certificate
    from the user.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=637257

 docs/reference/gio/gio-sections.txt |    6 +-
 gio/gtlsinteraction.c               |  353 +++++++++++++++++++++---
 gio/gtlsinteraction.h               |   47 +++-
 gio/tests/Makefile.am               |    2 +
 gio/tests/tls-interaction.c         |  508 +++++++++++++++++++++++++++++++----
 5 files changed, 815 insertions(+), 101 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index d03520b..e0cb917 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -3551,10 +3551,14 @@ G_TYPE_TLS_PASSWORD_FLAGS
 <TITLE>GTlsInteraction</TITLE>
 GTlsInteraction
 GTlsInteractionResult
+g_tls_interaction_invoke_ask_password
+g_tls_interaction_invoke_request_certificate
 g_tls_interaction_ask_password
 g_tls_interaction_ask_password_async
 g_tls_interaction_ask_password_finish
-g_tls_interaction_invoke_ask_password
+g_tls_interaction_request_certificate
+g_tls_interaction_request_certificate_async
+g_tls_interaction_request_certificate_finish
 <SUBSECTION Standard>
 GTlsInteractionClass
 G_IS_TLS_INTERACTION
diff --git a/gio/gtlsinteraction.c b/gio/gtlsinteraction.c
index 0701ddb..b4800a1 100644
--- a/gio/gtlsinteraction.c
+++ b/gio/gtlsinteraction.c
@@ -24,6 +24,8 @@
 
 #include <string.h>
 
+#include "gtlscertificate.h"
+#include "gtlsconnection.h"
 #include "gtlsinteraction.h"
 #include "gtlspassword.h"
 #include "gasyncresult.h"
@@ -182,6 +184,55 @@ invoke_closure_wait_and_free (InvokeClosure *closure,
   return result;
 }
 
+static GTlsInteractionResult
+invoke_closure_complete_and_free (GTlsInteraction *interaction,
+                                  InvokeClosure *closure,
+                                  GError **error)
+{
+  GTlsInteractionResult result;
+  gboolean complete;
+
+  /*
+   * Handle the case where we've been called from within the main context
+   * or in the case where the main context is not running. This approximates
+   * the behavior of a modal dialog.
+   */
+  if (g_main_context_acquire (interaction->priv->context))
+    {
+      for (;;)
+        {
+          g_mutex_lock (&closure->mutex);
+          complete = closure->complete;
+          g_mutex_unlock (&closure->mutex);
+          if (complete)
+            break;
+          g_main_context_iteration (interaction->priv->context, TRUE);
+        }
+
+      g_main_context_release (interaction->priv->context);
+
+      if (closure->error)
+        {
+          g_propagate_error (error, closure->error);
+          closure->error = NULL;
+        }
+
+      result = closure->result;
+      invoke_closure_free (closure);
+    }
+
+  /*
+   * Handle the case where we're in a different thread than the main
+   * context and a main loop is running.
+   */
+  else
+    {
+      result = invoke_closure_wait_and_free (closure, error);
+    }
+
+  return result;
+}
+
 static void
 g_tls_interaction_init (GTlsInteraction *interaction)
 {
@@ -231,9 +282,9 @@ on_invoke_ask_password_sync (gpointer user_data)
 }
 
 static void
-on_async_as_sync_complete (GObject      *source,
-                           GAsyncResult *result,
-                           gpointer      user_data)
+on_ask_password_complete (GObject      *source,
+                          GAsyncResult *result,
+                          gpointer      user_data)
 {
   InvokeClosure *closure = user_data;
   GTlsInteractionClass *klass;
@@ -266,7 +317,7 @@ on_invoke_ask_password_async_as_sync (gpointer user_data)
   klass->ask_password_async (closure->interaction,
                              G_TLS_PASSWORD (closure->argument),
                              closure->cancellable,
-                             on_async_as_sync_complete,
+                             on_ask_password_complete,
                              closure);
 
   /* Note that we've used these */
@@ -318,7 +369,6 @@ g_tls_interaction_invoke_ask_password (GTlsInteraction    *interaction,
   GTlsInteractionResult result;
   InvokeClosure *closure;
   GTlsInteractionClass *klass;
-  gboolean complete;
 
   g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
   g_return_val_if_fail (G_IS_TLS_PASSWORD (password), G_TLS_INTERACTION_UNHANDLED);
@@ -339,43 +389,7 @@ g_tls_interaction_invoke_ask_password (GTlsInteraction    *interaction,
       g_main_context_invoke (interaction->priv->context,
                              on_invoke_ask_password_async_as_sync, closure);
 
-      /*
-       * Handle the case where we've been called from within the main context
-       * or in the case where the main context is not running. This approximates
-       * the behavior of a modal dialog.
-       */
-      if (g_main_context_acquire (interaction->priv->context))
-        {
-          for (;;)
-            {
-              g_mutex_lock (&closure->mutex);
-              complete = closure->complete;
-              g_mutex_unlock (&closure->mutex);
-              if (complete)
-                break;
-              g_main_context_iteration (interaction->priv->context, TRUE);
-            }
-
-          g_main_context_release (interaction->priv->context);
-
-          if (closure->error)
-            {
-              g_propagate_error (error, closure->error);
-              closure->error = NULL;
-            }
-
-          result = closure->result;
-          invoke_closure_free (closure);
-        }
-
-      /*
-       * Handle the case where we're in a different thread than the main
-       * context and a main loop is running.
-       */
-      else
-        {
-          result = invoke_closure_wait_and_free (closure, error);
-        }
+      result = invoke_closure_complete_and_free (interaction, closure, error);
     }
   else
     {
@@ -386,7 +400,6 @@ g_tls_interaction_invoke_ask_password (GTlsInteraction    *interaction,
   return result;
 }
 
-
 /**
  * g_tls_interaction_ask_password:
  * @interaction: a #GTlsInteraction object
@@ -531,3 +544,257 @@ g_tls_interaction_ask_password_finish (GTlsInteraction    *interaction,
       return g_task_propagate_int (G_TASK (result), error);
     }
 }
+
+static gboolean
+on_invoke_request_certificate_sync (gpointer user_data)
+{
+  InvokeClosure *closure = user_data;
+  GTlsInteractionClass *klass;
+
+  g_mutex_lock (&closure->mutex);
+
+  klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
+  g_assert (klass->request_certificate != NULL);
+
+  closure->result = klass->request_certificate (closure->interaction,
+                                                G_TLS_CONNECTION (closure->argument),
+                                                0,
+                                                closure->cancellable,
+                                                &closure->error);
+
+  closure->complete = TRUE;
+  g_cond_signal (&closure->cond);
+  g_mutex_unlock (&closure->mutex);
+
+  return FALSE; /* don't call again */
+}
+
+static void
+on_request_certificate_complete (GObject      *source,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  InvokeClosure *closure = user_data;
+  GTlsInteractionClass *klass;
+
+  g_mutex_lock (&closure->mutex);
+
+  klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
+  g_assert (klass->request_certificate_finish != NULL);
+
+  closure->result = klass->request_certificate_finish (closure->interaction,
+                                                       result, &closure->error);
+
+  closure->complete = TRUE;
+  g_cond_signal (&closure->cond);
+  g_mutex_unlock (&closure->mutex);
+}
+
+static gboolean
+on_invoke_request_certificate_async_as_sync (gpointer user_data)
+{
+  InvokeClosure *closure = user_data;
+  GTlsInteractionClass *klass;
+
+  g_mutex_lock (&closure->mutex);
+
+  klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
+  g_assert (klass->request_certificate_async);
+
+  klass->request_certificate_async (closure->interaction,
+                                    G_TLS_CONNECTION (closure->argument), 0,
+                                    closure->cancellable,
+                                    on_request_certificate_complete,
+                                    closure);
+
+  /* Note that we've used these */
+  closure->callback = NULL;
+  closure->user_data = NULL;
+
+  g_mutex_unlock (&closure->mutex);
+
+  return FALSE; /* don't call again */
+}
+
+GTlsInteractionResult
+g_tls_interaction_invoke_request_certificate (GTlsInteraction    *interaction,
+                                              GTlsConnection     *connection,
+                                              gint                unused_flags,
+                                              GCancellable       *cancellable,
+                                              GError            **error)
+{
+  GTlsInteractionResult result;
+  InvokeClosure *closure;
+  GTlsInteractionClass *klass;
+
+  g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
+  g_return_val_if_fail (G_IS_TLS_CONNECTION (connection), G_TLS_INTERACTION_UNHANDLED);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_TLS_INTERACTION_UNHANDLED);
+
+  closure = invoke_closure_new (interaction, G_OBJECT (connection), cancellable);
+
+  klass = G_TLS_INTERACTION_GET_CLASS (interaction);
+  if (klass->request_certificate)
+    {
+      g_main_context_invoke (interaction->priv->context,
+                             on_invoke_request_certificate_sync, closure);
+      result = invoke_closure_wait_and_free (closure, error);
+    }
+  else if (klass->request_certificate_async)
+    {
+      g_return_val_if_fail (klass->request_certificate_finish, G_TLS_INTERACTION_UNHANDLED);
+      g_main_context_invoke (interaction->priv->context,
+                             on_invoke_request_certificate_async_as_sync, closure);
+
+      result = invoke_closure_complete_and_free (interaction, closure, error);
+    }
+  else
+    {
+      result = G_TLS_INTERACTION_UNHANDLED;
+      invoke_closure_free (closure);
+    }
+
+  return result;
+}
+
+/**
+ * g_tls_interaction_request_certificate:
+ * @interaction: a #GTlsInteraction object
+ * @connection: a #GTlsConnection object
+ * @cancellable: an optional #GCancellable cancellation object
+ * @error: an optional location to place an error on failure
+ *
+ * Run synchronous interaction to ask the user to choose a certificate to use
+ * with the connection. In general, g_tls_interaction_invoke_request_certificate()
+ * should be used instead of this function.
+ *
+ * Derived subclasses usually implement a certificate selector, although they may
+ * also choose to provide a certificate from elsewhere. Alternatively the user may
+ * abort this certificate request, which will usually abort the TLS connection.
+ *
+ * If %G_TLS_INTERACTION_HANDLED is returned, then the #GTlsConnection passed
+ * to g_tls_interaction_request_certificate() will have its certificate filled in.
+ *
+ * If the interaction is cancelled by the cancellation object, or by the
+ * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
+ * contains a %G_IO_ERROR_CANCELLED error code. Certain implementations may
+ * not support immediate cancellation.
+ *
+ * Returns: The status of the request certificate interaction.
+ *
+ * Since: 2.36
+ */
+GTlsInteractionResult
+g_tls_interaction_request_certificate (GTlsInteraction    *interaction,
+                                       GTlsConnection     *connection,
+                                       gint                unused_flags,
+                                       GCancellable       *cancellable,
+                                       GError            **error)
+{
+  GTlsInteractionClass *klass;
+
+  g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
+  g_return_val_if_fail (G_IS_TLS_CONNECTION (connection), G_TLS_INTERACTION_UNHANDLED);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_TLS_INTERACTION_UNHANDLED);
+
+  klass = G_TLS_INTERACTION_GET_CLASS (interaction);
+  if (klass->request_certificate)
+    return (klass->request_certificate) (interaction, connection, unused_flags, cancellable, error);
+  else
+    return G_TLS_INTERACTION_UNHANDLED;
+}
+
+/**
+ * g_tls_interaction_request_certificate_async:
+ * @interaction: a #GTlsInteraction object
+ * @connection: a #GTlsConnection object
+ * @cancellable: an optional #GCancellable cancellation object
+ * @callback: (allow-none): will be called when the interaction completes
+ * @user_data: (allow-none): data to pass to the @callback
+ *
+ * Run asynchronous interaction to ask the user for a certificate to use with
+ * the connection. In general, g_tls_interaction_invoke_request_certificate() should
+ * be used instead of this function.
+ *
+ * Derived subclasses usually implement a certificate selector, although they may
+ * also choose to provide a certificate from elsewhere. @callback will be called
+ * when the operation completes. Alternatively the user may abort this certificate
+ * request, which will usually abort the TLS connection.
+ *
+ * Since: 2.36
+ */
+void
+g_tls_interaction_request_certificate_async (GTlsInteraction    *interaction,
+                                             GTlsConnection     *connection,
+                                             gint                unused_flags,
+                                             GCancellable       *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer            user_data)
+{
+  GTlsInteractionClass *klass;
+  GTask *task;
+
+  g_return_if_fail (G_IS_TLS_INTERACTION (interaction));
+  g_return_if_fail (G_IS_TLS_CONNECTION (connection));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+  klass = G_TLS_INTERACTION_GET_CLASS (interaction);
+  if (klass->request_certificate_async)
+    {
+      g_return_if_fail (klass->request_certificate_finish);
+      (klass->request_certificate_async) (interaction, connection, unused_flags,
+                                          cancellable, callback, user_data);
+    }
+  else
+    {
+      task = g_task_new (interaction, cancellable, callback, user_data);
+      g_task_set_source_tag (task, g_tls_interaction_request_certificate_async);
+      g_task_return_int (task, G_TLS_INTERACTION_UNHANDLED);
+      g_object_unref (task);
+    }
+}
+
+/**
+ * g_tls_interaction_request_certificate_finish:
+ * @interaction: a #GTlsInteraction object
+ * @result: the result passed to the callback
+ * @error: an optional location to place an error on failure
+ *
+ * Complete an request certificate user interaction request. This should be once
+ * the g_tls_interaction_request_certificate_async() completion callback is called.
+ *
+ * If %G_TLS_INTERACTION_HANDLED is returned, then the #GTlsConnection passed
+ * to g_tls_interaction_request_certificate() will have its certificate filled in.
+ *
+ * If the interaction is cancelled by the cancellation object, or by the
+ * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
+ * contains a %G_IO_ERROR_CANCELLED error code.
+ *
+ * Returns: The status of the request certificate interaction.
+ *
+ * Since: 2.36
+ */
+GTlsInteractionResult
+g_tls_interaction_request_certificate_finish (GTlsInteraction    *interaction,
+                                              GAsyncResult       *result,
+                                              GError            **error)
+{
+  GTlsInteractionClass *klass;
+
+  g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), G_TLS_INTERACTION_UNHANDLED);
+
+  klass = G_TLS_INTERACTION_GET_CLASS (interaction);
+  if (klass->request_certificate_finish)
+    {
+      g_return_val_if_fail (klass->request_certificate_async != NULL, G_TLS_INTERACTION_UNHANDLED);
+
+      return (klass->request_certificate_finish) (interaction, result, error);
+    }
+  else
+    {
+      g_return_val_if_fail (g_async_result_is_tagged (result, g_tls_interaction_request_certificate_async), 
G_TLS_INTERACTION_UNHANDLED);
+
+      return g_task_propagate_int (G_TASK (result), error);
+    }
+}
diff --git a/gio/gtlsinteraction.h b/gio/gtlsinteraction.h
index 283464e..7b2e761 100644
--- a/gio/gtlsinteraction.h
+++ b/gio/gtlsinteraction.h
@@ -69,9 +69,26 @@ struct _GTlsInteractionClass
                                                   GAsyncResult       *result,
                                                   GError            **error);
 
+  GTlsInteractionResult  (* request_certificate)        (GTlsInteraction    *interaction,
+                                                         GTlsConnection     *connection,
+                                                         gint                unused_flags,
+                                                         GCancellable       *cancellable,
+                                                         GError            **error);
+
+  void                   (* request_certificate_async)  (GTlsInteraction    *interaction,
+                                                         GTlsConnection     *connection,
+                                                         gint                unused_flags,
+                                                         GCancellable       *cancellable,
+                                                         GAsyncReadyCallback callback,
+                                                         gpointer            user_data);
+
+  GTlsInteractionResult  (* request_certificate_finish) (GTlsInteraction    *interaction,
+                                                         GAsyncResult       *result,
+                                                         GError            **error);
+
   /*< private >*/
   /* Padding for future expansion */
-  gpointer padding[24];
+  gpointer padding[21];
 };
 
 GLIB_AVAILABLE_IN_ALL
@@ -83,7 +100,6 @@ GTlsInteractionResult  g_tls_interaction_invoke_ask_password (GTlsInteraction
                                                               GCancellable       *cancellable,
                                                               GError            **error);
 
-
 GLIB_AVAILABLE_IN_ALL
 GTlsInteractionResult  g_tls_interaction_ask_password        (GTlsInteraction    *interaction,
                                                               GTlsPassword       *password,
@@ -102,6 +118,33 @@ GTlsInteractionResult  g_tls_interaction_ask_password_finish (GTlsInteraction
                                                               GAsyncResult       *result,
                                                               GError            **error);
 
+GLIB_AVAILABLE_IN_2_36
+GTlsInteractionResult  g_tls_interaction_invoke_request_certificate (GTlsInteraction    *interaction,
+                                                                     GTlsConnection     *connection,
+                                                                     gint                unused_flags,
+                                                                     GCancellable       *cancellable,
+                                                                     GError            **error);
+
+GLIB_AVAILABLE_IN_2_36
+GTlsInteractionResult  g_tls_interaction_request_certificate        (GTlsInteraction    *interaction,
+                                                                     GTlsConnection     *connection,
+                                                                     gint                unused_flags,
+                                                                     GCancellable       *cancellable,
+                                                                     GError            **error);
+
+GLIB_AVAILABLE_IN_2_36
+void                   g_tls_interaction_request_certificate_async  (GTlsInteraction    *interaction,
+                                                                     GTlsConnection     *connection,
+                                                                     gint                unused_flags,
+                                                                     GCancellable       *cancellable,
+                                                                     GAsyncReadyCallback callback,
+                                                                     gpointer            user_data);
+
+GLIB_AVAILABLE_IN_2_36
+GTlsInteractionResult  g_tls_interaction_request_certificate_finish (GTlsInteraction    *interaction,
+                                                                     GAsyncResult       *result,
+                                                                     GError            **error);
+
 G_END_DECLS
 
 #endif /* __G_TLS_INTERACTION_H__ */
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 55991b9..0e1c316 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -386,6 +386,8 @@ CLEANFILES += gdbus-test-codegen-generated.[ch] gdbus-test-codegen-generated-doc
 endif # OS_UNIX
 endif # HAVE_DBUS_DAEMON
 
+tls_interaction_SOURCES = tls-interaction.c gtesttlsbackend.c gtesttlsbackend.h
+
 # -----------------------------------------------------------------------------
 
 if OS_WIN32
diff --git a/gio/tests/tls-interaction.c b/gio/tests/tls-interaction.c
index a34ff29..ce2aa88 100644
--- a/gio/tests/tls-interaction.c
+++ b/gio/tests/tls-interaction.c
@@ -24,11 +24,18 @@
 
 #include <gio/gio.h>
 
+#include "gtesttlsbackend.h"
+
+static GPtrArray *fixtures = NULL;
+
 typedef struct {
   /* Class virtual interaction methods */
   gpointer ask_password_func;
   gpointer ask_password_async_func;
   gpointer ask_password_finish_func;
+  gpointer request_certificate_func;
+  gpointer request_certificate_async_func;
+  gpointer request_certificate_finish_func;
 
   /* Expected results */
   GTlsInteractionResult result;
@@ -40,6 +47,7 @@ typedef struct {
 typedef struct {
   GTlsInteraction *interaction;
   GTlsPassword *password;
+  GTlsConnection *connection;
   GMainLoop *loop;
   GThread *interaction_thread;
   GThread *test_thread;
@@ -213,6 +221,155 @@ test_interaction_ask_password_sync_failure (GTlsInteraction    *interaction,
   return G_TLS_INTERACTION_FAILED;
 }
 
+static void
+test_interaction_request_certificate_async_success (GTlsInteraction    *interaction,
+                                                    GTlsConnection     *connection,
+                                                    gint                unused_flags,
+                                                    GCancellable       *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer            user_data)
+{
+  GTask *task;
+  TestInteraction *self;
+
+  g_assert (TEST_IS_INTERACTION (interaction));
+  self = TEST_INTERACTION (interaction);
+
+  g_assert (g_thread_self () == self->test->interaction_thread);
+
+  g_assert (G_IS_TLS_CONNECTION (connection));
+  g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+  g_assert (unused_flags == 0);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+
+  /*
+   * IRL would call g_tls_connection_set_certificate(). But here just touch
+   * the connection in a detectable way.
+   */
+  g_object_set_data (G_OBJECT (connection), "chosen-certificate", "my-certificate");
+  g_task_return_int (task, G_TLS_INTERACTION_HANDLED);
+  g_object_unref (task);
+}
+
+static GTlsInteractionResult
+test_interaction_request_certificate_finish_success (GTlsInteraction    *interaction,
+                                                     GAsyncResult       *result,
+                                                     GError            **error)
+{
+  TestInteraction *self;
+
+  g_assert (TEST_IS_INTERACTION (interaction));
+  self = TEST_INTERACTION (interaction);
+
+  g_assert (g_thread_self () == self->test->interaction_thread);
+
+  g_assert (g_task_is_valid (result, interaction));
+  g_assert (error != NULL);
+  g_assert (*error == NULL);
+
+  return g_task_propagate_int (G_TASK (result), error);
+}
+
+static void
+test_interaction_request_certificate_async_failure (GTlsInteraction    *interaction,
+                                                    GTlsConnection     *connection,
+                                                    gint                unused_flags,
+                                                    GCancellable       *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer            user_data)
+{
+  GTask *task;
+  TestInteraction *self;
+
+  g_assert (TEST_IS_INTERACTION (interaction));
+  self = TEST_INTERACTION (interaction);
+
+  g_assert (g_thread_self () == self->test->interaction_thread);
+
+  g_assert (G_IS_TLS_CONNECTION (connection));
+  g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+  g_assert (unused_flags == 0);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+
+  g_task_return_new_error (task, G_FILE_ERROR, G_FILE_ERROR_NOENT, "Another message");
+  g_object_unref (task);
+}
+
+static GTlsInteractionResult
+test_interaction_request_certificate_finish_failure (GTlsInteraction    *interaction,
+                                                     GAsyncResult       *result,
+                                                     GError            **error)
+{
+  TestInteraction *self;
+
+  g_assert (TEST_IS_INTERACTION (interaction));
+  self = TEST_INTERACTION (interaction);
+
+  g_assert (g_thread_self () == self->test->interaction_thread);
+
+  g_assert (g_task_is_valid (result, interaction));
+  g_assert (error != NULL);
+  g_assert (*error == NULL);
+
+  if (g_task_propagate_int (G_TASK (result), error) != -1)
+    g_assert_not_reached ();
+
+  return G_TLS_INTERACTION_FAILED;
+}
+
+static GTlsInteractionResult
+test_interaction_request_certificate_sync_success (GTlsInteraction    *interaction,
+                                                   GTlsConnection      *connection,
+                                                   gint                 unused_flags,
+                                                   GCancellable        *cancellable,
+                                                   GError             **error)
+{
+  TestInteraction *self;
+
+  g_assert (TEST_IS_INTERACTION (interaction));
+  self = TEST_INTERACTION (interaction);
+
+  g_assert (g_thread_self () == self->test->interaction_thread);
+
+  g_assert (G_IS_TLS_CONNECTION (connection));
+  g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+  g_assert (error != NULL);
+  g_assert (*error == NULL);
+
+  /*
+   * IRL would call g_tls_connection_set_certificate(). But here just touch
+   * the connection in a detectable way.
+   */
+  g_object_set_data (G_OBJECT (connection), "chosen-certificate", "my-certificate");
+  return G_TLS_INTERACTION_HANDLED;
+}
+
+static GTlsInteractionResult
+test_interaction_request_certificate_sync_failure (GTlsInteraction    *interaction,
+                                                   GTlsConnection     *connection,
+                                                   gint                unused_flags,
+                                                   GCancellable       *cancellable,
+                                                   GError            **error)
+{
+  TestInteraction *self;
+
+  g_assert (TEST_IS_INTERACTION (interaction));
+  self = TEST_INTERACTION (interaction);
+
+  g_assert (g_thread_self () == self->test->interaction_thread);
+
+  g_assert (G_IS_TLS_CONNECTION (connection));
+  g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+  g_assert (unused_flags == 0);
+  g_assert (error != NULL);
+  g_assert (*error == NULL);
+
+  g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, "Another message");
+  return G_TLS_INTERACTION_FAILED;
+}
+
 /* ----------------------------------------------------------------------------
  * ACTUAL TESTS
  */
@@ -344,6 +501,133 @@ test_ask_password (Test         *test,
     g_main_loop_quit (test->loop);
 }
 
+static void
+on_request_certificate_async_call (GObject      *source,
+                                   GAsyncResult *result,
+                                   gpointer      user_data)
+{
+  Test *test = user_data;
+  GTlsInteractionResult res;
+  GError *error = NULL;
+
+  g_assert (G_IS_TLS_INTERACTION (source));
+  g_assert (G_TLS_INTERACTION (source) == test->interaction);
+
+  /* Check that this callback is being run in the right place */
+  g_assert (g_thread_self () == test->interaction_thread);
+
+  res = g_tls_interaction_request_certificate_finish (test->interaction, result, &error);
+
+  /* Check that the results match the fixture */
+  g_assert_cmpuint (test->fixture->result, ==, res);
+  switch (test->fixture->result)
+    {
+      case G_TLS_INTERACTION_HANDLED:
+        g_assert_no_error (error);
+        g_assert_cmpstr (g_object_get_data (G_OBJECT (test->connection), "chosen-certificate"), ==, 
"my-certificate");
+        break;
+      case G_TLS_INTERACTION_FAILED:
+        g_assert_error (error, test->fixture->error_domain, test->fixture->error_code);
+        g_assert_cmpstr (error->message, ==, test->fixture->error_message);
+        g_clear_error (&error);
+        break;
+      case G_TLS_INTERACTION_UNHANDLED:
+        g_assert_no_error (error);
+        break;
+      default:
+        g_assert_not_reached ();
+    }
+
+  /* Signal the end of the test */
+  g_main_loop_quit (test->loop);
+}
+
+static void
+test_request_certificate_async (Test            *test,
+                                gconstpointer    unused)
+{
+  /* This test only works with a main loop */
+  g_assert (test->loop);
+
+  g_tls_interaction_request_certificate_async (test->interaction,
+                                               test->connection, 0, NULL,
+                                               on_request_certificate_async_call,
+                                               test);
+
+  /* teardown waits until g_main_loop_quit(). called from callback */
+}
+
+static void
+test_invoke_request_certificate (Test         *test,
+                                 gconstpointer unused)
+{
+  GTlsInteractionResult res;
+  GError *error = NULL;
+
+  res = g_tls_interaction_invoke_request_certificate (test->interaction,
+                                                      test->connection,
+                                                      0, NULL, &error);
+
+  /* Check that the results match the fixture */
+  g_assert_cmpuint (test->fixture->result, ==, res);
+  switch (test->fixture->result)
+    {
+      case G_TLS_INTERACTION_HANDLED:
+        g_assert_no_error (error);
+        g_assert_cmpstr (g_object_get_data (G_OBJECT (test->connection), "chosen-certificate"), ==, 
"my-certificate");
+        break;
+      case G_TLS_INTERACTION_FAILED:
+        g_assert_error (error, test->fixture->error_domain, test->fixture->error_code);
+        g_assert_cmpstr (error->message, ==, test->fixture->error_message);
+        g_clear_error (&error);
+        break;
+      case G_TLS_INTERACTION_UNHANDLED:
+        g_assert_no_error (error);
+        break;
+      default:
+        g_assert_not_reached ();
+    }
+
+  /* This allows teardown to stop if running with loop */
+  if (test->loop)
+    g_main_loop_quit (test->loop);
+}
+
+static void
+test_request_certificate (Test         *test,
+                          gconstpointer unused)
+{
+  GTlsInteractionResult res;
+  GError *error = NULL;
+
+  res = g_tls_interaction_request_certificate (test->interaction, test->connection,
+                                               0, NULL, &error);
+
+  /* Check that the results match the fixture */
+  g_assert_cmpuint (test->fixture->result, ==, res);
+  switch (test->fixture->result)
+    {
+      case G_TLS_INTERACTION_HANDLED:
+        g_assert_no_error (error);
+        g_assert_cmpstr (g_object_get_data (G_OBJECT (test->connection), "chosen-certificate"), ==, 
"my-certificate");
+        break;
+      case G_TLS_INTERACTION_FAILED:
+        g_assert_error (error, test->fixture->error_domain, test->fixture->error_code);
+        g_assert_cmpstr (error->message, ==, test->fixture->error_message);
+        g_clear_error (&error);
+        break;
+      case G_TLS_INTERACTION_UNHANDLED:
+        g_assert_no_error (error);
+        break;
+      default:
+        g_assert_not_reached ();
+    }
+
+  /* This allows teardown to stop if running with loop */
+  if (test->loop)
+    g_main_loop_quit (test->loop);
+}
+
 /* ----------------------------------------------------------------------------
  * TEST SETUP
  */
@@ -354,6 +638,9 @@ setup_without_loop (Test           *test,
 {
   const Fixture *fixture = user_data;
   GTlsInteractionClass *klass;
+  GTlsBackend *backend;
+  GError *error = NULL;
+
   test->fixture = fixture;
 
   test->interaction = g_object_new (TEST_TYPE_INTERACTION, NULL);
@@ -365,6 +652,14 @@ setup_without_loop (Test           *test,
   klass->ask_password = fixture->ask_password_func;
   klass->ask_password_async = fixture->ask_password_async_func;
   klass->ask_password_finish = fixture->ask_password_finish_func;
+  klass->request_certificate = fixture->request_certificate_func;
+  klass->request_certificate_async = fixture->request_certificate_async_func;
+  klass->request_certificate_finish = fixture->request_certificate_finish_func;
+
+  backend = g_object_new (G_TYPE_TEST_TLS_BACKEND, NULL);
+  test->connection = g_object_new (g_tls_backend_get_server_connection_type (backend), NULL);
+  g_assert_no_error (error);
+  g_object_unref (backend);
 
   test->password = g_tls_password_new (0, "Description");
   test->test_thread = g_thread_self ();
@@ -384,6 +679,8 @@ teardown_without_loop (Test            *test,
 
   g_object_add_weak_pointer (weak_pointer, &weak_pointer);
 
+  g_object_unref (test->connection);
+
   g_object_unref (test->password);
 
   g_object_unref (test->interaction);
@@ -507,11 +804,10 @@ teardown_with_normal_loop (Test            *test,
 typedef void (*TestFunc) (Test *test, gconstpointer data);
 
 static void
-test_with_async_ask_password_implementations (const gchar *name,
-                                              TestFunc     setup,
-                                              TestFunc     func,
-                                              TestFunc     teardown,
-                                              GPtrArray   *fixtures)
+test_with_async_ask_password (const gchar *name,
+                              TestFunc     setup,
+                              TestFunc     func,
+                              TestFunc     teardown)
 {
   gchar *test_name;
   Fixture *fixture;
@@ -541,12 +837,12 @@ test_with_async_ask_password_implementations (const gchar *name,
   g_free (test_name);
   g_ptr_array_add (fixtures, fixture);
 }
+
 static void
-test_with_unhandled_ask_password_implementations (const gchar *name,
-                                                  TestFunc     setup,
-                                                  TestFunc     func,
-                                                  TestFunc     teardown,
-                                                  GPtrArray   *fixtures)
+test_with_unhandled_ask_password (const gchar *name,
+                                  TestFunc     setup,
+                                  TestFunc     func,
+                                  TestFunc     teardown)
 {
   gchar *test_name;
   Fixture *fixture;
@@ -564,11 +860,10 @@ test_with_unhandled_ask_password_implementations (const gchar *name,
 }
 
 static void
-test_with_sync_ask_password_implementations (const gchar *name,
+test_with_sync_ask_password (const gchar *name,
                                              TestFunc     setup,
                                              TestFunc     func,
-                                             TestFunc     teardown,
-                                             GPtrArray   *fixtures)
+                                             TestFunc     teardown)
 {
   gchar *test_name;
   Fixture *fixture;
@@ -599,11 +894,122 @@ test_with_sync_ask_password_implementations (const gchar *name,
   g_ptr_array_add (fixtures, fixture);
 }
 
+static void
+test_with_all_ask_password (const gchar *name,
+                            TestFunc setup,
+                            TestFunc func,
+                            TestFunc teardown)
+{
+  test_with_unhandled_ask_password (name, setup, func, teardown);
+  test_with_async_ask_password (name, setup, func, teardown);
+  test_with_sync_ask_password (name, setup, func, teardown);
+}
+
+static void
+test_with_async_request_certificate (const gchar *name,
+                                     TestFunc     setup,
+                                     TestFunc     func,
+                                     TestFunc     teardown)
+{
+  gchar *test_name;
+  Fixture *fixture;
+
+  /* Async implementation that succeeds */
+  fixture = g_new0 (Fixture, 1);
+  fixture->request_certificate_async_func = test_interaction_request_certificate_async_success;
+  fixture->request_certificate_finish_func = test_interaction_request_certificate_finish_success;
+  fixture->request_certificate_func = NULL;
+  fixture->result = G_TLS_INTERACTION_HANDLED;
+  test_name = g_strdup_printf ("%s/async-implementation-success", name);
+  g_test_add (test_name, Test, fixture, setup, func, teardown);
+  g_free (test_name);
+  g_ptr_array_add (fixtures, fixture);
+
+  /* Async implementation that fails */
+  fixture = g_new0 (Fixture, 1);
+  fixture->request_certificate_async_func = test_interaction_request_certificate_async_failure;
+  fixture->request_certificate_finish_func = test_interaction_request_certificate_finish_failure;
+  fixture->request_certificate_func = NULL;
+  fixture->result = G_TLS_INTERACTION_FAILED;
+  fixture->error_domain = G_FILE_ERROR;
+  fixture->error_code = G_FILE_ERROR_NOENT;
+  fixture->error_message = "Another message";
+  test_name = g_strdup_printf ("%s/async-implementation-failure", name);
+  g_test_add (test_name, Test, fixture, setup, func, teardown);
+  g_free (test_name);
+  g_ptr_array_add (fixtures, fixture);
+}
+
+static void
+test_with_unhandled_request_certificate (const gchar *name,
+                                         TestFunc     setup,
+                                         TestFunc     func,
+                                         TestFunc     teardown)
+{
+  gchar *test_name;
+  Fixture *fixture;
+
+  /* Unhandled implementation */
+  fixture = g_new0 (Fixture, 1);
+  fixture->request_certificate_async_func = NULL;
+  fixture->request_certificate_finish_func = NULL;
+  fixture->request_certificate_func = NULL;
+  fixture->result = G_TLS_INTERACTION_UNHANDLED;
+  test_name = g_strdup_printf ("%s/unhandled-implementation", name);
+  g_test_add (test_name, Test, fixture, setup, func, teardown);
+  g_free (test_name);
+  g_ptr_array_add (fixtures, fixture);
+}
+
+static void
+test_with_sync_request_certificate (const gchar *name,
+                                    TestFunc     setup,
+                                    TestFunc     func,
+                                    TestFunc     teardown)
+{
+  gchar *test_name;
+  Fixture *fixture;
+
+  /* Sync implementation that succeeds */
+  fixture = g_new0 (Fixture, 1);
+  fixture->request_certificate_async_func = NULL;
+  fixture->request_certificate_finish_func = NULL;
+  fixture->request_certificate_func = test_interaction_request_certificate_sync_success;
+  fixture->result = G_TLS_INTERACTION_HANDLED;
+  test_name = g_strdup_printf ("%s/sync-implementation-success", name);
+  g_test_add (test_name, Test, fixture, setup, func, teardown);
+  g_free (test_name);
+  g_ptr_array_add (fixtures, fixture);
+
+  /* Async implementation that fails */
+  fixture = g_new0 (Fixture, 1);
+  fixture->request_certificate_async_func = NULL;
+  fixture->request_certificate_finish_func = NULL;
+  fixture->request_certificate_func = test_interaction_request_certificate_sync_failure;
+  fixture->result = G_TLS_INTERACTION_FAILED;
+  fixture->error_domain = G_FILE_ERROR;
+  fixture->error_code = G_FILE_ERROR_NOENT;
+  fixture->error_message = "Another message";
+  test_name = g_strdup_printf ("%s/sync-implementation-failure", name);
+  g_test_add (test_name, Test, fixture, setup, func, teardown);
+  g_free (test_name);
+  g_ptr_array_add (fixtures, fixture);
+}
+
+static void
+test_with_all_request_certificate (const gchar *name,
+                                   TestFunc setup,
+                                   TestFunc func,
+                                   TestFunc teardown)
+{
+  test_with_unhandled_request_certificate (name, setup, func, teardown);
+  test_with_async_request_certificate (name, setup, func, teardown);
+  test_with_sync_request_certificate (name, setup, func, teardown);
+}
 int
 main (int   argc,
       char *argv[])
 {
-  GPtrArray *fixtures;
   gint ret;
 
   g_test_init (&argc, &argv, NULL);
@@ -611,52 +1017,44 @@ main (int   argc,
   fixtures = g_ptr_array_new_with_free_func (g_free);
 
   /* Tests for g_tls_interaction_invoke_ask_password */
+  test_with_all_ask_password ("/tls-interaction/ask-password/invoke-with-loop",
+                              setup_with_thread_loop, test_invoke_ask_password, teardown_with_thread_loop);
+  test_with_all_ask_password ("/tls-interaction/ask-password/invoke-without-loop",
+                              setup_without_loop, test_invoke_ask_password, teardown_without_loop);
+  test_with_all_ask_password ("/tls-interaction/ask-password/invoke-in-loop",
+                                              setup_with_normal_loop, test_invoke_ask_password, 
teardown_with_normal_loop);
+
+  /* Tests for g_tls_interaction_ask_password */
+  test_with_unhandled_ask_password ("/tls-interaction/ask-password/sync",
+                                    setup_without_loop, test_ask_password, teardown_without_loop);
+  test_with_sync_ask_password ("/tls-interaction/ask-password/sync",
+                               setup_without_loop, test_ask_password, teardown_without_loop);
 
-  test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/invoke-with-loop",
-                                                    setup_with_thread_loop, test_invoke_ask_password,
-                                                    teardown_with_thread_loop, fixtures);
-  test_with_async_ask_password_implementations ("/tls-interaction/ask-password/invoke-with-loop",
-                                                setup_with_thread_loop, test_invoke_ask_password,
-                                                teardown_with_thread_loop, fixtures);
-  test_with_sync_ask_password_implementations ("/tls-interaction/ask-password/invoke-with-loop",
-                                               setup_with_thread_loop, test_invoke_ask_password,
-                                               teardown_with_thread_loop, fixtures);
-
-  test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/invoke-without-loop",
-                                                    setup_without_loop, test_invoke_ask_password,
-                                                    teardown_without_loop, fixtures);
-  test_with_async_ask_password_implementations ("/tls-interaction/ask-password/invoke-without-loop",
-                                                setup_without_loop, test_invoke_ask_password,
-                                                teardown_without_loop, fixtures);
-  test_with_sync_ask_password_implementations ("/tls-interaction/ask-password/invoke-without-loop",
-                                               setup_without_loop, test_invoke_ask_password,
-                                               teardown_without_loop, fixtures);
-
-  test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/invoke-in-loop",
-                                                    setup_with_normal_loop, test_invoke_ask_password,
-                                                    teardown_with_normal_loop, fixtures);
-  test_with_async_ask_password_implementations ("/tls-interaction/ask-password/invoke-in-loop",
-                                                setup_with_normal_loop, test_invoke_ask_password,
-                                                teardown_with_normal_loop, fixtures);
-  test_with_sync_ask_password_implementations ("/tls-interaction/ask-password/invoke-in-loop",
-                                               setup_with_normal_loop, test_invoke_ask_password,
-                                               teardown_with_normal_loop, fixtures);
+  /* Tests for g_tls_interaction_ask_password_async */
+  test_with_unhandled_ask_password ("/tls-interaction/ask-password/async",
+                                    setup_with_normal_loop, test_ask_password_async, 
teardown_with_normal_loop);
+  test_with_async_ask_password ("/tls-interaction/ask-password/async",
+                                setup_with_normal_loop, test_ask_password_async, teardown_with_normal_loop);
+
+  /* Tests for g_tls_interaction_invoke_request_certificate */
+  test_with_all_request_certificate ("/tls-interaction/request-certificate/invoke-with-loop",
+                                     setup_with_thread_loop, test_invoke_request_certificate, 
teardown_with_thread_loop);
+  test_with_all_request_certificate ("/tls-interaction/request-certificate/invoke-without-loop",
+                                     setup_without_loop, test_invoke_request_certificate, 
teardown_without_loop);
+  test_with_all_request_certificate ("/tls-interaction/request-certificate/invoke-in-loop",
+                              setup_with_normal_loop, test_invoke_request_certificate, 
teardown_with_normal_loop);
 
   /* Tests for g_tls_interaction_ask_password */
-  test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/sync",
-                                                    setup_without_loop, test_ask_password,
-                                                    teardown_without_loop, fixtures);
-  test_with_sync_ask_password_implementations ("/tls-interaction/ask-password/sync",
-                                               setup_without_loop, test_ask_password,
-                                               teardown_without_loop, fixtures);
+  test_with_unhandled_request_certificate ("/tls-interaction/request-certificate/sync",
+                                           setup_without_loop, test_request_certificate, 
teardown_without_loop);
+  test_with_sync_request_certificate ("/tls-interaction/request-certificate/sync",
+                                      setup_without_loop, test_request_certificate, teardown_without_loop);
 
   /* Tests for g_tls_interaction_ask_password_async */
-  test_with_unhandled_ask_password_implementations ("/tls-interaction/ask-password/async",
-                                                    setup_with_normal_loop, test_ask_password_async,
-                                                    teardown_with_normal_loop, fixtures);
-  test_with_async_ask_password_implementations ("/tls-interaction/ask-password/async",
-                                                setup_with_normal_loop, test_ask_password_async,
-                                                teardown_with_normal_loop, fixtures);
+  test_with_unhandled_request_certificate ("/tls-interaction/request-certificate/async",
+                                           setup_with_normal_loop, test_request_certificate_async, 
teardown_with_normal_loop);
+  test_with_async_request_certificate ("/tls-interaction/request-certificate/async",
+                                       setup_with_normal_loop, test_request_certificate_async, 
teardown_with_normal_loop);
 
   ret = g_test_run();
   g_ptr_array_free (fixtures, TRUE);



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