* If the prompter quits, cancel any prompting that's going on.
https://bugzilla.gnome.org/show_bug.cgi?id=684478
gcr_mock_prompter_set_delay_msec
gcr_mock_prompter_start
gcr_mock_prompter_stop
+gcr_mock_prompter_disconnect
gcr_parsed_get_attributes
gcr_parsed_get_data
gcr_parsed_get_description
/* Owned by the prompter thread*/
GcrSystemPrompter *prompter;
- const gchar *bus_name;
+ GDBusConnection *connection;
GMainLoop *loop;
} ThreadData;
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
NULL, NULL, &error);
if (error == NULL) {
+ thread_data->connection = connection;
gcr_system_prompter_register (GCR_SYSTEM_PROMPTER (thread_data->prompter),
connection);
- thread_data->bus_name = g_dbus_connection_get_unique_name (connection);
} else {
g_critical ("couldn't create connection: %s", error->message);
g_error_free (error);
thread_data->prompter = NULL;
if (connection) {
- if (!g_dbus_connection_flush_sync (connection, NULL, &error)) {
- g_critical ("connection flush failed: %s", error->message);
- g_error_free (error);
+ thread_data->connection = NULL;
+
+ if (!g_dbus_connection_is_closed (connection)) {
+ if (!g_dbus_connection_flush_sync (connection, NULL, &error)) {
+ g_critical ("connection flush failed: %s", error->message);
+ g_error_free (error);
+ }
+ if (!g_dbus_connection_close_sync (connection, NULL, &error)) {
+ g_critical ("connection close failed: %s", error->message);
+ g_error_free (error);
+ }
}
+
g_object_unref (connection);
}
g_assert (running->prompter);
g_mutex_unlock (running->mutex);
- return running->bus_name;
+ return g_dbus_connection_get_unique_name (running->connection);
+}
+
+void
+gcr_mock_prompter_disconnect (void)
+{
+ GError *error = NULL;
+
+ g_assert (running != NULL);
+ g_assert (running->connection);
+
+ g_dbus_connection_close_sync (running->connection, NULL, &error);
+ if (error != NULL) {
+ g_critical ("disconnect connection close failed: %s", error->message);
+ g_error_free (error);
+ }
}
/**
const gchar * gcr_mock_prompter_start (void);
+void gcr_mock_prompter_disconnect (void);
+
void gcr_mock_prompter_stop (void);
gboolean gcr_mock_prompter_is_prompting (void);
typedef struct {
GSource *timeout;
+ GSource *waiting;
GMainContext *context;
GCancellable *cancellable;
+ guint watch_id;
} CallClosure;
static void
CallClosure *closure = data;
if (closure->timeout)
g_source_destroy (closure->timeout);
- g_clear_object (&closure->cancellable);
+ if (closure->waiting)
+ g_source_destroy (closure->waiting);
+ if (closure->watch_id)
+ g_bus_unwatch_name (closure->watch_id);
+ g_object_unref (closure->cancellable);
g_free (data);
}
static void
+on_propagate_cancelled (GCancellable *cancellable,
+ gpointer user_data)
+{
+ /* Propagate the cancelled signal */
+ GCancellable *cancel = G_CANCELLABLE (user_data);
+ g_cancellable_cancel (cancel);
+}
+
+static CallClosure *
+call_closure_new (GCancellable *cancellable)
+{
+ CallClosure *call;
+
+ /*
+ * We use our own cancellable object, since we cancel it it in
+ * situations other than when the caller cancels.
+ */
+
+ call = g_new0 (CallClosure, 1);
+ call->cancellable = g_cancellable_new ();
+
+ if (cancellable) {
+ g_cancellable_connect (cancellable, G_CALLBACK (on_propagate_cancelled),
+ g_object_ref (call->cancellable), g_object_unref);
+ }
+
+ return call;
+}
+
+static void
gcr_system_prompt_init (GcrSystemPrompt *self)
{
self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SYSTEM_PROMPT,
}
static void
+on_prompter_vanished (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+ CallClosure *call = g_simple_async_result_get_op_res_gpointer (async);
+ g_cancellable_cancel (call->cancellable);
+}
+
+static void
on_bus_connected (GObject *source,
GAsyncResult *result,
gpointer user_data)
g_main_context_push_thread_default (closure->context);
+ closure->watch_id = g_bus_watch_name_on_connection (self->pv->connection,
+ self->pv->prompter_bus_name,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ NULL, on_prompter_vanished,
+ res, NULL);
+
register_prompt_object (self, &error);
g_main_context_pop_thread_default (closure->context);
return FALSE; /* Don't call this function again */
}
+static gboolean
+on_call_cancelled (GCancellable *cancellable,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+ CallClosure *call = g_simple_async_result_get_op_res_gpointer (async);
+ GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
+
+ g_source_destroy (call->waiting);
+ call->waiting = NULL;
+
+ g_simple_async_result_set_error (async, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ _("The operation was cancelled"));
+
+ /* Tell the prompter we're no longer interested */
+ gcr_system_prompt_close_async (self, NULL, NULL, NULL);
+
+ g_object_unref (self);
+ return FALSE; /* Don't call this function again */
+}
+
void
perform_init_async (GcrSystemPrompt *self,
GSimpleAsyncResult *res)
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
gcr_system_prompt_real_init_async);
- closure = g_new0 (CallClosure, 1);
+ closure = call_closure_new (cancellable);
closure->context = g_main_context_get_thread_default ();
if (closure->context)
g_main_context_ref (closure->context);
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
+ CallClosure *call = g_simple_async_result_get_op_res_gpointer (res);
GError *error = NULL;
GVariant *retval;
self->pv->pending = NULL;
g_simple_async_result_take_error (res, error);
g_simple_async_result_complete (res);
+ } else {
+ g_assert (call->waiting == NULL);
+ call->waiting = g_cancellable_source_new (call->cancellable);
+ g_source_set_callback (call->waiting, (GSourceFunc)on_call_cancelled, res, NULL);
+ g_source_attach (call->waiting, call->context);
}
if (retval)
}
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, source_tag);
- closure = g_new0 (CallClosure, 1);
- closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable;
+ closure = call_closure_new (cancellable);
g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
if (self->pv->closed) {
else
sent = gcr_secret_exchange_begin (exchange);
+ closure->watch_id = g_bus_watch_name_on_connection (self->pv->connection,
+ self->pv->prompter_bus_name,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ NULL, on_prompter_vanished,
+ res, NULL);
+
builder = build_dirty_properties (self);
/* Reregister the prompt object in the current GMainContext */
res = g_simple_async_result_new (NULL, callback, user_data,
gcr_system_prompt_close_async);
- closure = g_new0 (CallClosure, 1);
- closure->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+ closure = call_closure_new (cancellable);
closure->context = g_main_context_get_thread_default ();
if (closure->context != NULL)
g_main_context_ref (closure->context);
egg_assert_not_object (prompt);
}
+typedef struct {
+ GAsyncResult *result1;
+ GAsyncResult *result2;
+} ResultPair;
+
+static void
+on_result_pair_one (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ResultPair *pair = user_data;
+ g_assert (pair->result1 == NULL);
+ pair->result1 = g_object_ref (result);
+ if (pair->result1 && pair->result2)
+ egg_test_wait_stop ();
+}
+
+static void
+on_result_pair_two (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ResultPair *pair = user_data;
+ g_assert (pair->result2 == NULL);
+ pair->result2 = g_object_ref (result);
+ if (pair->result1 && pair->result2)
+ egg_test_wait_stop ();
+}
+
+static void
+test_watch_cancels (Test *test,
+ gconstpointer unused)
+{
+ GDBusConnection *connection;
+ GcrPrompt *prompt;
+ GcrPrompt *prompt2;
+ GError *error = NULL;
+ const gchar *password;
+ ResultPair pair = { NULL, NULL };
+
+ gcr_mock_prompter_set_delay_msec (3000);
+ gcr_mock_prompter_expect_password_ok ("booo", NULL);
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ g_assert_no_error (error);
+
+ /* This should happen immediately */
+ prompt = gcr_system_prompt_open_for_prompter (test->prompter_name, 0, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (GCR_IS_SYSTEM_PROMPT (prompt));
+
+ /* Show a password prompt */
+ gcr_prompt_password_async (prompt, NULL, on_result_pair_one, &pair);
+
+ /* This prompt should wait, block */
+ gcr_system_prompt_open_for_prompter_async (test->prompter_name, 0, NULL,
+ on_result_pair_two, &pair);
+
+ /* Wait a bit before stopping, so outgoing request is done */
+ g_usleep (G_TIME_SPAN_SECOND / 4);
+
+ /* Kill the mock prompter */
+ gcr_mock_prompter_disconnect ();
+
+ /* Both the above operations should cancel */
+ egg_test_wait ();
+
+ prompt2 = gcr_system_prompt_open_finish (pair.result2, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
+ g_assert (prompt2 == NULL);
+
+ password = gcr_prompt_password_finish (prompt, pair.result1, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
+ g_assert (password == NULL);
+
+ g_object_unref (prompt);
+ g_object_unref (pair.result1);
+ g_object_unref (pair.result2);
+ g_object_unref (connection);
+}
+
int
main (int argc, char **argv)
{
g_test_add ("/gcr/system-prompt/close-cancels", Test, NULL, setup, test_close_cancels, teardown);
g_test_add ("/gcr/system-prompt/after-close-dismisses", Test, NULL, setup, test_after_close_dismisses, teardown);
g_test_add ("/gcr/system-prompt/close-from-prompter", Test, NULL, setup, test_close_from_prompter, teardown);
+ g_test_add ("/gcr/system-prompt/watch-cancels", Test, NULL, setup, test_watch_cancels, teardown);
return egg_tests_run_with_loop ();
}