1 /* libsecret - GLib wrapper for Secret Prompt
3 * Copyright 2011 Collabora Ltd.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; either version 2.1 of the licence or (at
8 * your option) any later version.
10 * See the included COPYING file for more information.
12 * Author: Stef Walter <stefw@gnome.org>
17 #include "secret-dbus-generated.h"
18 #include "secret-private.h"
19 #include "secret-prompt.h"
22 #include <glib/gi18n-lib.h>
25 * SECTION:secret-prompt
26 * @title: SecretPrompt
27 * @short_description: a prompt in the Service
29 * A #SecretPrompt represents a prompt in the Secret Service.
31 * Certain actions on the Secret Service require user prompting to complete,
32 * such as creating a collection, or unlocking a collection. When such a prompt
33 * is necessary, then a #SecretPrompt object is created by this library, and
34 * passed to the secret_service_prompt() method. In this way it is handled
37 * In order to customize prompt handling, override the
38 * SecretServiceClass::prompt_async and SecretServiceClass::prompt_finish
39 * virtual methods of the #SecretService class.
47 * A proxy object representing a prompt that the Secret Service will display
53 * @parent_class: the parent class
55 * The class for #SecretPrompt.
58 struct _SecretPromptPrivate {
62 G_DEFINE_TYPE (SecretPrompt, secret_prompt, G_TYPE_DBUS_PROXY);
65 secret_prompt_init (SecretPrompt *self)
67 self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, SECRET_TYPE_PROMPT,
72 secret_prompt_class_init (SecretPromptClass *klass)
74 g_type_class_add_private (klass, sizeof (SecretPromptPrivate));
83 on_prompt_run_complete (GObject *source,
87 RunClosure *closure = user_data;
88 closure->result = g_object_ref (result);
89 g_main_loop_quit (closure->loop);
93 _secret_prompt_instance (SecretService *service,
94 const gchar *prompt_path)
100 g_return_val_if_fail (SECRET_IS_SERVICE (service), NULL);
101 g_return_val_if_fail (prompt_path != NULL, NULL);
103 proxy = G_DBUS_PROXY (service);
104 prompt = g_initable_new (SECRET_TYPE_PROMPT, NULL, &error,
105 "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
106 "g-interface-info", _secret_gen_prompt_interface_info (),
107 "g-name", g_dbus_proxy_get_name (proxy),
108 "g-connection", g_dbus_proxy_get_connection (proxy),
109 "g-object-path", prompt_path,
110 "g-interface-name", SECRET_PROMPT_INTERFACE,
114 g_warning ("couldn't create SecretPrompt object: %s", error->message);
115 g_clear_error (&error);
125 * @window_id: (allow-none): string form of XWindow id for parent window to be transient for
126 * @cancellable: optional cancellation object
127 * @return_type: the variant type of the prompt result
128 * @error: location to place an error on failure
130 * Runs a prompt and performs the prompting. Returns a variant result if the
131 * prompt was completed and not dismissed. The type of result depends on the
132 * action the prompt is completing, and is defined in the Secret Service DBus
135 * If @window_id is non-null then it is used as an XWindow id on Linux. The API
136 * expects this id to be converted to a string using the <literal>%d</literal>
137 * printf format. The Secret Service can make its prompt transient for the window
138 * with this id. In some Secret Service implementations this is not possible, so
139 * the behavior depending on this should degrade gracefully.
141 * This runs the dialog in a recursive mainloop. When run from a user interface
142 * thread, this means the user interface will remain responsive. Care should be
143 * taken that appropriate user interface actions are disabled while running the
146 * Returns: (transfer full): %NULL if the prompt was dismissed or an error occurred
149 secret_prompt_run (SecretPrompt *self,
150 const gchar *window_id,
151 GCancellable *cancellable,
152 const GVariantType *return_type,
155 GMainContext *context;
159 g_return_val_if_fail (SECRET_IS_PROMPT (self), NULL);
160 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
161 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
163 context = g_main_context_get_thread_default ();
165 closure = g_new0 (RunClosure, 1);
166 closure->loop = g_main_loop_new (context, FALSE);
168 secret_prompt_perform (self, window_id, return_type, cancellable,
169 on_prompt_run_complete, closure);
171 g_main_loop_run (closure->loop);
173 retval = secret_prompt_perform_finish (self, closure->result, error);
175 g_main_loop_unref (closure->loop);
176 g_object_unref (closure->result);
183 * secret_prompt_perform_sync:
185 * @window_id: (allow-none): string form of XWindow id for parent window to be transient for
186 * @cancellable: optional cancellation object
187 * @return_type: the variant type of the prompt result
188 * @error: location to place an error on failure
190 * Runs a prompt and performs the prompting. Returns a variant result if the
191 * prompt was completed and not dismissed. The type of result depends on the
192 * action the prompt is completing, and is defined in the Secret Service DBus
195 * If @window_id is non-null then it is used as an XWindow id on Linux. The API
196 * expects this id to be converted to a string using the <literal>%d</literal>
197 * printf format. The Secret Service can make its prompt transient for the window
198 * with this id. In some Secret Service implementations this is not possible,
199 * so the behavior depending on this should degrade gracefully.
201 * This method may block indefinitely and should not be used in user interface
204 * Returns: (transfer full): %NULL if the prompt was dismissed or an error occurred
207 secret_prompt_perform_sync (SecretPrompt *self,
208 const gchar *window_id,
209 GCancellable *cancellable,
210 const GVariantType *return_type,
213 GMainContext *context;
216 g_return_val_if_fail (SECRET_IS_PROMPT (self), NULL);
217 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
218 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
220 context = g_main_context_new ();
221 g_main_context_push_thread_default (context);
223 retval = secret_prompt_run (self, window_id, cancellable, return_type, error);
225 /* Needed to prevent memory leaks */
226 while (g_main_context_iteration (context, FALSE));
228 g_main_context_pop_thread_default (context);
229 g_main_context_unref (context);
235 GDBusConnection *connection;
236 GCancellable *call_cancellable;
237 GCancellable *async_cancellable;
238 gulong cancelled_sig;
246 GVariantType *return_type;
250 perform_closure_free (gpointer data)
252 PerformClosure *closure = data;
253 g_object_unref (closure->call_cancellable);
254 g_clear_object (&closure->async_cancellable);
255 g_object_unref (closure->connection);
257 g_variant_unref (closure->result);
258 if (closure->return_type)
259 g_variant_type_free (closure->return_type);
260 g_assert (closure->signal == 0);
261 g_assert (closure->watch == 0);
262 g_slice_free (PerformClosure, closure);
266 perform_prompt_complete (GSimpleAsyncResult *res,
269 PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
271 closure->dismissed = dismissed;
272 if (closure->completed)
274 closure->completed = TRUE;
277 g_dbus_connection_signal_unsubscribe (closure->connection, closure->signal);
281 g_bus_unwatch_name (closure->watch);
284 if (closure->cancelled_sig)
285 g_signal_handler_disconnect (closure->async_cancellable, closure->cancelled_sig);
286 closure->cancelled_sig = 0;
288 g_simple_async_result_complete (res);
292 on_prompt_completed (GDBusConnection *connection,
293 const gchar *sender_name,
294 const gchar *object_path,
295 const gchar *interface_name,
296 const gchar *signal_name,
297 GVariant *parameters,
300 GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
301 SecretPrompt *self = SECRET_PROMPT (g_async_result_get_source_object (user_data));
302 PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
305 closure->prompting = FALSE;
307 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(bv)"))) {
308 g_warning ("SecretPrompt received invalid %s signal of type %s",
309 signal_name, g_variant_get_type_string (parameters));
310 perform_prompt_complete (res, TRUE);
313 g_variant_get (parameters, "(bv)", &dismissed, &closure->result);
314 perform_prompt_complete (res, dismissed);
317 g_object_unref (self);
321 on_prompt_prompted (GObject *source,
322 GAsyncResult *result,
325 GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
326 PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
327 SecretPrompt *self = SECRET_PROMPT (source);
328 GError *error = NULL;
331 retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (self), result, &error);
334 g_variant_unref (retval);
335 if (closure->vanished)
336 g_clear_error (&error);
339 g_simple_async_result_take_error (res, error);
340 perform_prompt_complete (res, TRUE);
343 closure->prompting = TRUE;
344 g_atomic_int_set (&self->pv->prompted, 1);
346 /* And now we wait for the signal */
349 g_object_unref (res);
353 on_prompt_vanished (GDBusConnection *connection,
357 GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
358 PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
359 closure->vanished = TRUE;
360 g_cancellable_cancel (closure->call_cancellable);
361 perform_prompt_complete (res, TRUE);
365 on_prompt_dismissed (GObject *source,
366 GAsyncResult *result,
369 GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
370 PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
371 SecretPrompt *self = SECRET_PROMPT (source);
372 GError *error = NULL;
375 retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (self), result, &error);
378 g_variant_unref (retval);
379 if (closure->vanished)
380 g_clear_error (&error);
381 if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
382 g_clear_error (&error);
385 g_simple_async_result_take_error (res, error);
386 perform_prompt_complete (res, TRUE);
389 g_object_unref (res);
393 on_prompt_cancelled (GCancellable *cancellable,
396 GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
397 PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
398 SecretPrompt *self = SECRET_PROMPT (g_async_result_get_source_object (user_data));
400 /* Instead of cancelling our dbus calls, we cancel the prompt itself via this dbus call */
402 g_dbus_proxy_call (G_DBUS_PROXY (self), "Dismiss", g_variant_new ("()"),
403 G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
404 closure->call_cancellable,
405 on_prompt_dismissed, g_object_ref (res));
407 g_object_unref (self);
411 * secret_prompt_perform:
413 * @window_id: (allow-none): string form of XWindow id for parent window to be transient for
414 * @return_type: the variant type of the prompt result
415 * @cancellable: optional cancellation object
416 * @callback: called when the operation completes
417 * @user_data: data to be passed to the callback
419 * Runs a prompt and performs the prompting. Returns %TRUE if the prompt
420 * was completed and not dismissed.
422 * If @window_id is non-null then it is used as an XWindow id on Linux. The API
423 * expects this id to be converted to a string using the <literal>%d</literal>
424 * printf format. The Secret Service can make its prompt transient for the window
425 * with this id. In some Secret Service implementations this is not possible, so
426 * the behavior depending on this should degrade gracefully.
428 * This method will return immediately and complete asynchronously.
431 secret_prompt_perform (SecretPrompt *self,
432 const gchar *window_id,
433 const GVariantType *return_type,
434 GCancellable *cancellable,
435 GAsyncReadyCallback callback,
438 GSimpleAsyncResult *res;
439 PerformClosure *closure;
440 const gchar *owner_name;
441 const gchar *object_path;
445 g_return_if_fail (SECRET_IS_PROMPT (self));
446 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
448 prompted = g_atomic_int_get (&self->pv->prompted);
450 g_warning ("The prompt object has already had its prompt called.");
454 proxy = G_DBUS_PROXY (self);
456 res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
457 secret_prompt_perform);
458 closure = g_slice_new0 (PerformClosure);
459 closure->connection = g_object_ref (g_dbus_proxy_get_connection (proxy));
460 closure->call_cancellable = g_cancellable_new ();
461 closure->async_cancellable = cancellable ? g_object_ref (cancellable) : NULL;
462 closure->return_type = return_type ? g_variant_type_copy (return_type) : NULL;
463 g_simple_async_result_set_op_res_gpointer (res, closure, perform_closure_free);
465 if (window_id == NULL)
468 owner_name = g_dbus_proxy_get_name_owner (proxy);
469 object_path = g_dbus_proxy_get_object_path (proxy);
471 closure->signal = g_dbus_connection_signal_subscribe (closure->connection, owner_name,
472 SECRET_PROMPT_INTERFACE,
473 SECRET_PROMPT_SIGNAL_COMPLETED,
475 G_DBUS_SIGNAL_FLAGS_NONE,
480 closure->watch = g_bus_watch_name_on_connection (closure->connection, owner_name,
481 G_BUS_NAME_WATCHER_FLAGS_NONE, NULL,
486 if (closure->async_cancellable) {
487 closure->cancelled_sig = g_cancellable_connect (closure->async_cancellable,
488 G_CALLBACK (on_prompt_cancelled),
492 g_dbus_proxy_call (proxy, "Prompt", g_variant_new ("(s)", window_id),
493 G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
494 closure->call_cancellable, on_prompt_prompted, g_object_ref (res));
496 g_object_unref (res);
500 * secret_prompt_perform_finish:
502 * @result: the asynchronous result passed to the callback
503 * @error: location to place an error on failure
505 * Complete asynchronous operation to run a prompt and perform the prompting.
507 * Returns a variant result if the prompt was completed and not dismissed. The
508 * type of result depends on the action the prompt is completing, and is
509 * defined in the Secret Service DBus API specification.
511 * Returns: (transfer full): %NULL if the prompt was dismissed or an error occurred,
512 * a variant result if the prompt was successful
515 secret_prompt_perform_finish (SecretPrompt *self,
516 GAsyncResult *result,
519 PerformClosure *closure;
520 GSimpleAsyncResult *res;
523 g_return_val_if_fail (SECRET_IS_PROMPT (self), NULL);
524 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
525 g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
526 secret_prompt_perform), NULL);
528 res = G_SIMPLE_ASYNC_RESULT (result);
530 if (_secret_util_propagate_error (res, error))
533 closure = g_simple_async_result_get_op_res_gpointer (res);
534 if (closure->result == NULL)
536 if (closure->return_type != NULL && !g_variant_is_of_type (closure->result, closure->return_type)) {
537 string = g_variant_type_dup_string (closure->return_type);
538 g_warning ("received unexpected result type %s from Completed signal instead of expected %s",
539 g_variant_get_type_string (closure->result), string);
543 return g_variant_ref (closure->result);