From: Stef Walter Date: Mon, 20 Aug 2012 14:03:48 +0000 (+0200) Subject: gcr: Cancel the prompt when prompter goes away X-Git-Tag: upstream/3.7.5~32 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=064d7588d1272481b22e70ed2c3901e7673b23f7;p=platform%2Fupstream%2Fgcr.git gcr: Cancel the prompt when prompter goes away * If the prompter quits, cancel any prompting that's going on. https://bugzilla.gnome.org/show_bug.cgi?id=684478 --- diff --git a/gcr/gcr-base.symbols b/gcr/gcr-base.symbols index c794829..35a74a1 100644 --- a/gcr/gcr-base.symbols +++ b/gcr/gcr-base.symbols @@ -111,6 +111,7 @@ gcr_mock_prompter_is_prompting 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 diff --git a/gcr/gcr-mock-prompter.c b/gcr/gcr-mock-prompter.c index 564ed2d..31cbba2 100644 --- a/gcr/gcr-mock-prompter.c +++ b/gcr/gcr-mock-prompter.c @@ -105,7 +105,7 @@ typedef struct { /* Owned by the prompter thread*/ GcrSystemPrompter *prompter; - const gchar *bus_name; + GDBusConnection *connection; GMainLoop *loop; } ThreadData; @@ -943,9 +943,9 @@ mock_prompter_thread (gpointer data) 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); @@ -978,10 +978,19 @@ mock_prompter_thread (gpointer data) 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); } @@ -1030,7 +1039,22 @@ gcr_mock_prompter_start (void) 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); + } } /** diff --git a/gcr/gcr-mock-prompter.h b/gcr/gcr-mock-prompter.h index 3e2cf26..f89f762 100644 --- a/gcr/gcr-mock-prompter.h +++ b/gcr/gcr-mock-prompter.h @@ -36,6 +36,8 @@ G_BEGIN_DECLS 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); diff --git a/gcr/gcr-system-prompt.c b/gcr/gcr-system-prompt.c index 640aa77..b370b1c 100644 --- a/gcr/gcr-system-prompt.c +++ b/gcr/gcr-system-prompt.c @@ -143,8 +143,10 @@ static gint unique_prompt_id = 0; typedef struct { GSource *timeout; + GSource *waiting; GMainContext *context; GCancellable *cancellable; + guint watch_id; } CallClosure; static void @@ -153,11 +155,45 @@ call_closure_free (gpointer data) 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, @@ -742,6 +778,16 @@ register_prompt_object (GcrSystemPrompt *self, } 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) @@ -763,6 +809,12 @@ on_bus_connected (GObject *source, 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); @@ -835,6 +887,27 @@ on_call_timeout (gpointer user_data) 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) @@ -896,7 +969,7 @@ gcr_system_prompt_real_init_async (GAsyncInitable *initable, 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); @@ -1029,6 +1102,7 @@ on_perform_prompt_complete (GObject *source, { 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; @@ -1037,6 +1111,11 @@ on_perform_prompt_complete (GObject *source, 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) @@ -1069,8 +1148,7 @@ perform_prompt_async (GcrSystemPrompt *self, } 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) { @@ -1088,6 +1166,12 @@ perform_prompt_async (GcrSystemPrompt *self, 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 */ @@ -1470,8 +1554,7 @@ gcr_system_prompt_close_async (GcrSystemPrompt *self, 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); diff --git a/gcr/tests/test-system-prompt.c b/gcr/tests/test-system-prompt.c index 0d48124..b5cae2c 100644 --- a/gcr/tests/test-system-prompt.c +++ b/gcr/tests/test-system-prompt.c @@ -641,6 +641,89 @@ test_after_close_dismisses (Test *test, 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) { @@ -667,6 +750,7 @@ 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 (); }