Use AM_CPPFLAGS instead of INCLUDES
[platform/upstream/libsecret.git] / libsecret / secret-prompt.c
1 /* libsecret - GLib wrapper for Secret Prompt
2  *
3  * Copyright 2011 Collabora Ltd.
4  *
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.
9  *
10  * See the included COPYING file for more information.
11  *
12  * Author: Stef Walter <stefw@gnome.org>
13  */
14
15 #include "config.h"
16
17 #include "secret-dbus-generated.h"
18 #include "secret-private.h"
19 #include "secret-prompt.h"
20
21 #include <glib.h>
22 #include <glib/gi18n-lib.h>
23
24 /**
25  * SECTION:secret-prompt
26  * @title: SecretPrompt
27  * @short_description: a prompt in the Service
28  *
29  * A #SecretPrompt represents a prompt in the Secret Service.
30  *
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
35  * automatically.
36  *
37  * In order to customize prompt handling, override the
38  * SecretServiceClass::prompt_async and SecretServiceClass::prompt_finish
39  * virtual methods of the #SecretService class.
40  *
41  * These functions have an unstable API and may change across versions. Use
42  * <literal>libsecret-unstable</literal> package to access them.
43  *
44  * Stability: Unstable
45  */
46
47 /**
48  * SecretPrompt:
49  *
50  * A proxy object representing a prompt that the Secret Service will display
51  * to the user.
52  */
53
54 /**
55  * SecretPromptClass:
56  * @parent_class: the parent class
57  *
58  * The class for #SecretPrompt.
59  */
60
61 struct _SecretPromptPrivate {
62         gint prompted;
63 };
64
65 G_DEFINE_TYPE (SecretPrompt, secret_prompt, G_TYPE_DBUS_PROXY);
66
67 static void
68 secret_prompt_init (SecretPrompt *self)
69 {
70         self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, SECRET_TYPE_PROMPT,
71                                                 SecretPromptPrivate);
72 }
73
74 static void
75 secret_prompt_class_init (SecretPromptClass *klass)
76 {
77         g_type_class_add_private (klass, sizeof (SecretPromptPrivate));
78 }
79
80 typedef struct {
81         GMainLoop *loop;
82         GAsyncResult *result;
83 } RunClosure;
84
85 static void
86 on_prompt_run_complete (GObject *source,
87                         GAsyncResult *result,
88                         gpointer user_data)
89 {
90         RunClosure *closure = user_data;
91         closure->result = g_object_ref (result);
92         g_main_loop_quit (closure->loop);
93 }
94
95 SecretPrompt *
96 _secret_prompt_instance (SecretService *service,
97                          const gchar *prompt_path)
98 {
99         GDBusProxy *proxy;
100         SecretPrompt *prompt;
101         GError *error = NULL;
102
103         g_return_val_if_fail (SECRET_IS_SERVICE (service), NULL);
104         g_return_val_if_fail (prompt_path != NULL, NULL);
105
106         proxy = G_DBUS_PROXY (service);
107         prompt = g_initable_new (SECRET_TYPE_PROMPT, NULL, &error,
108                                  "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
109                                  "g-interface-info", _secret_gen_prompt_interface_info (),
110                                  "g-name", g_dbus_proxy_get_name (proxy),
111                                  "g-connection", g_dbus_proxy_get_connection (proxy),
112                                  "g-object-path", prompt_path,
113                                  "g-interface-name", SECRET_PROMPT_INTERFACE,
114                                  NULL);
115
116         if (error != NULL) {
117                 g_warning ("couldn't create SecretPrompt object: %s", error->message);
118                 g_clear_error (&error);
119                 return NULL;
120         }
121
122         return prompt;
123 }
124
125 /**
126  * secret_prompt_run:
127  * @self: a prompt
128  * @window_id: XWindow id for parent window to be transient for
129  * @cancellable: optional cancellation object
130  * @return_type: the variant type of the prompt result
131  * @error: location to place an error on failure
132  *
133  * Runs a prompt and performs the prompting. Returns a variant result if the
134  * prompt was completed and not dismissed. The type of result depends on the
135  * action the prompt is completing, and is defined in the Secret Service DBus
136  * API specification.
137  *
138  * If @window_id is non-zero then it is used as an XWindow id. The Secret
139  * Service can make its prompt transient for the window with this id. In some
140  * Secret Service implementations this is not possible, so the behavior
141  * depending on this should degrade gracefully.
142  *
143  * This runs the dialog in a recursive mainloop. When run from a user interface
144  * thread, this means the user interface will remain responsive. Care should be
145  * taken that appropriate user interface actions are disabled while running the
146  * prompt.
147  *
148  * Returns: (transfer full): %NULL if the prompt was dismissed or an error occurred
149  */
150 GVariant *
151 secret_prompt_run (SecretPrompt *self,
152                    gulong window_id,
153                    GCancellable *cancellable,
154                    const GVariantType *return_type,
155                    GError **error)
156 {
157         GMainContext *context;
158         RunClosure *closure;
159         GVariant *retval;
160
161         g_return_val_if_fail (SECRET_IS_PROMPT (self), NULL);
162         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
163         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
164
165         context = g_main_context_get_thread_default ();
166
167         closure = g_new0 (RunClosure, 1);
168         closure->loop = g_main_loop_new (context, FALSE);
169
170         secret_prompt_perform (self, window_id, return_type, cancellable,
171                                on_prompt_run_complete, closure);
172
173         g_main_loop_run (closure->loop);
174
175         retval = secret_prompt_perform_finish (self, closure->result, error);
176
177         g_main_loop_unref (closure->loop);
178         g_object_unref (closure->result);
179         g_free (closure);
180
181         return retval;
182 }
183
184 /**
185  * secret_prompt_perform_sync:
186  * @self: a prompt
187  * @window_id: XWindow id for parent window to be transient for
188  * @cancellable: optional cancellation object
189  * @return_type: the variant type of the prompt result
190  * @error: location to place an error on failure
191  *
192  * Runs a prompt and performs the prompting. Returns a variant result if the
193  * prompt was completed and not dismissed. The type of result depends on the
194  * action the prompt is completing, and is defined in the Secret Service DBus
195  * API specification.
196  *
197  * If @window_id is non-zero then it is used as an XWindow id. The Secret
198  * Service can make its prompt transient for the window with this id. In some
199  * Secret Service implementations this is not possible, so the behavior
200  * depending on this should degrade gracefully.
201  *
202  * This method may block indefinitely and should not be used in user interface
203  * threads.
204  *
205  * Returns: (transfer full): %NULL if the prompt was dismissed or an error occurred
206  */
207 GVariant *
208 secret_prompt_perform_sync (SecretPrompt *self,
209                             gulong window_id,
210                             GCancellable *cancellable,
211                             const GVariantType *return_type,
212                             GError **error)
213 {
214         GMainContext *context;
215         GVariant *retval;
216
217         g_return_val_if_fail (SECRET_IS_PROMPT (self), NULL);
218         g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
219         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
220
221         context = g_main_context_new ();
222         g_main_context_push_thread_default (context);
223
224         retval = secret_prompt_run (self, window_id, cancellable, return_type, error);
225
226         /* Needed to prevent memory leaks */
227         while (g_main_context_iteration (context, FALSE));
228
229         g_main_context_pop_thread_default (context);
230         g_main_context_unref (context);
231
232         return retval;
233 }
234
235 typedef struct {
236         GDBusConnection *connection;
237         GCancellable *call_cancellable;
238         GCancellable *async_cancellable;
239         gulong cancelled_sig;
240         gboolean prompting;
241         gboolean dismissed;
242         gboolean vanished;
243         gboolean completed;
244         GVariant *result;
245         guint signal;
246         guint watch;
247         GVariantType *return_type;
248 } PerformClosure;
249
250 static void
251 perform_closure_free (gpointer data)
252 {
253         PerformClosure *closure = data;
254         g_object_unref (closure->call_cancellable);
255         g_clear_object (&closure->async_cancellable);
256         g_object_unref (closure->connection);
257         if (closure->result)
258                 g_variant_unref (closure->result);
259         if (closure->return_type)
260                 g_variant_type_free (closure->return_type);
261         g_assert (closure->signal == 0);
262         g_assert (closure->watch == 0);
263         g_slice_free (PerformClosure, closure);
264 }
265
266 static void
267 perform_prompt_complete (GSimpleAsyncResult *res,
268                          gboolean dismissed)
269 {
270         PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
271
272         closure->dismissed = dismissed;
273         if (closure->completed)
274                 return;
275         closure->completed = TRUE;
276
277         if (closure->signal)
278                 g_dbus_connection_signal_unsubscribe (closure->connection, closure->signal);
279         closure->signal = 0;
280
281         if (closure->watch)
282                 g_bus_unwatch_name (closure->watch);
283         closure->watch = 0;
284
285         if (closure->cancelled_sig)
286                 g_signal_handler_disconnect (closure->async_cancellable, closure->cancelled_sig);
287         closure->cancelled_sig = 0;
288
289         g_simple_async_result_complete (res);
290 }
291
292 static void
293 on_prompt_completed (GDBusConnection *connection,
294                      const gchar *sender_name,
295                      const gchar *object_path,
296                      const gchar *interface_name,
297                      const gchar *signal_name,
298                      GVariant *parameters,
299                      gpointer user_data)
300 {
301         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
302         SecretPrompt *self = SECRET_PROMPT (g_async_result_get_source_object (user_data));
303         PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
304         gboolean dismissed;
305
306         closure->prompting = FALSE;
307
308         if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(bv)"))) {
309                 g_warning ("SecretPrompt received invalid %s signal of type %s",
310                            signal_name, g_variant_get_type_string (parameters));
311                 perform_prompt_complete (res, TRUE);
312
313         } else {
314                 g_variant_get (parameters, "(bv)", &dismissed, &closure->result);
315                 perform_prompt_complete (res, dismissed);
316         }
317
318         g_object_unref (self);
319 }
320
321 static void
322 on_prompt_prompted (GObject *source,
323                     GAsyncResult *result,
324                     gpointer user_data)
325 {
326         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
327         PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
328         SecretPrompt *self = SECRET_PROMPT (source);
329         GError *error = NULL;
330         GVariant *retval;
331
332         retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (self), result, &error);
333
334         if (retval)
335                 g_variant_unref (retval);
336         if (closure->vanished)
337                 g_clear_error (&error);
338
339         if (error != NULL) {
340                 g_simple_async_result_take_error (res, error);
341                 perform_prompt_complete (res, TRUE);
342
343         } else {
344                 closure->prompting = TRUE;
345                 g_atomic_int_set (&self->pv->prompted, 1);
346
347                 /* And now we wait for the signal */
348         }
349
350         g_object_unref (res);
351 }
352
353 static void
354 on_prompt_vanished (GDBusConnection *connection,
355                     const gchar *name,
356                     gpointer user_data)
357 {
358         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
359         PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
360         closure->vanished = TRUE;
361         g_cancellable_cancel (closure->call_cancellable);
362         perform_prompt_complete (res, TRUE);
363 }
364
365 static void
366 on_prompt_dismissed (GObject *source,
367                      GAsyncResult *result,
368                      gpointer user_data)
369 {
370         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
371         PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
372         SecretPrompt *self = SECRET_PROMPT (source);
373         GError *error = NULL;
374         GVariant *retval;
375
376         retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (self), result, &error);
377
378         if (retval)
379                 g_variant_unref (retval);
380         if (closure->vanished)
381                 g_clear_error (&error);
382         if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
383                 g_clear_error (&error);
384
385         if (error != NULL) {
386                 g_simple_async_result_take_error (res, error);
387                 perform_prompt_complete (res, TRUE);
388         }
389
390         g_object_unref (res);
391 }
392
393 static void
394 on_prompt_cancelled (GCancellable *cancellable,
395                      gpointer user_data)
396 {
397         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
398         PerformClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
399         SecretPrompt *self = SECRET_PROMPT (g_async_result_get_source_object (user_data));
400
401         /* Instead of cancelling our dbus calls, we cancel the prompt itself via this dbus call */
402
403         g_dbus_proxy_call (G_DBUS_PROXY (self), "Dismiss", g_variant_new ("()"),
404                            G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
405                            closure->call_cancellable,
406                            on_prompt_dismissed, g_object_ref (res));
407
408         g_object_unref (self);
409 }
410
411 /**
412  * secret_prompt_perform:
413  * @self: a prompt
414  * @window_id: XWindow id for parent window to be transient for
415  * @return_type: the variant type of the prompt result
416  * @cancellable: optional cancellation object
417  * @callback: called when the operation completes
418  * @user_data: data to be passed to the callback
419  *
420  * Runs a prompt and performs the prompting. Returns %TRUE if the prompt
421  * was completed and not dismissed.
422  *
423  * If @window_id is non-zero then it is used as an XWindow id. The Secret
424  * Service can make its prompt transient for the window with this id. In some
425  * Secret Service implementations this is not possible, so the behavior
426  * depending on this should degrade gracefully.
427  *
428  * This method will return immediately and complete asynchronously.
429  */
430 void
431 secret_prompt_perform (SecretPrompt *self,
432                        gulong window_id,
433                        const GVariantType *return_type,
434                        GCancellable *cancellable,
435                        GAsyncReadyCallback callback,
436                        gpointer user_data)
437 {
438         GSimpleAsyncResult *res;
439         PerformClosure *closure;
440         const gchar *owner_name;
441         const gchar *object_path;
442         gboolean prompted;
443         GDBusProxy *proxy;
444         gchar *window;
445
446         g_return_if_fail (SECRET_IS_PROMPT (self));
447         g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
448
449         prompted = g_atomic_int_get (&self->pv->prompted);
450         if (prompted) {
451                 g_warning ("The prompt object has already had its prompt called.");
452                 return;
453         }
454
455         proxy = G_DBUS_PROXY (self);
456
457         res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
458                                          secret_prompt_perform);
459         closure = g_slice_new0 (PerformClosure);
460         closure->connection = g_object_ref (g_dbus_proxy_get_connection (proxy));
461         closure->call_cancellable = g_cancellable_new ();
462         closure->async_cancellable = cancellable ? g_object_ref (cancellable) : NULL;
463         closure->return_type = return_type ? g_variant_type_copy (return_type) : NULL;
464         g_simple_async_result_set_op_res_gpointer (res, closure, perform_closure_free);
465
466         if (window_id == 0)
467                 window = g_strdup ("");
468         else
469                 window = g_strdup_printf ("%lu", window_id);
470
471         owner_name = g_dbus_proxy_get_name_owner (proxy);
472         object_path = g_dbus_proxy_get_object_path (proxy);
473
474         closure->signal = g_dbus_connection_signal_subscribe (closure->connection, owner_name,
475                                                               SECRET_PROMPT_INTERFACE,
476                                                               SECRET_PROMPT_SIGNAL_COMPLETED,
477                                                               object_path, NULL,
478                                                               G_DBUS_SIGNAL_FLAGS_NONE,
479                                                               on_prompt_completed,
480                                                               g_object_ref (res),
481                                                               g_object_unref);
482
483         closure->watch = g_bus_watch_name_on_connection (closure->connection, owner_name,
484                                                          G_BUS_NAME_WATCHER_FLAGS_NONE, NULL,
485                                                          on_prompt_vanished,
486                                                          g_object_ref (res),
487                                                          g_object_unref);
488
489         if (closure->async_cancellable) {
490                 closure->cancelled_sig = g_cancellable_connect (closure->async_cancellable,
491                                                                 G_CALLBACK (on_prompt_cancelled),
492                                                                 res, NULL);
493         }
494
495         g_dbus_proxy_call (proxy, "Prompt", g_variant_new ("(s)", window),
496                            G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
497                            closure->call_cancellable, on_prompt_prompted, g_object_ref (res));
498
499         g_free (window);
500         g_object_unref (res);
501 }
502
503 /**
504  * secret_prompt_perform_finish:
505  * @self: a prompt
506  * @result: the asynchronous result passed to the callback
507  * @error: location to place an error on failure
508  *
509  * Complete asynchronous operation to run a prompt and perform the prompting.
510  *
511  * Returns a variant result if the prompt was completed and not dismissed. The
512  * type of result depends on the action the prompt is completing, and is
513  * defined in the Secret Service DBus API specification.
514  *
515  * Returns: (transfer full): %NULL if the prompt was dismissed or an error occurred,
516  *          a variant result if the prompt was successful
517  */
518 GVariant *
519 secret_prompt_perform_finish (SecretPrompt *self,
520                               GAsyncResult *result,
521                               GError **error)
522 {
523         PerformClosure *closure;
524         GSimpleAsyncResult *res;
525         gchar *string;
526
527         g_return_val_if_fail (SECRET_IS_PROMPT (self), NULL);
528         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
529         g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
530                                                               secret_prompt_perform), NULL);
531
532         res = G_SIMPLE_ASYNC_RESULT (result);
533
534         if (_secret_util_propagate_error (res, error))
535                 return NULL;
536
537         closure = g_simple_async_result_get_op_res_gpointer (res);
538         if (closure->result == NULL)
539                 return NULL;
540         if (closure->return_type != NULL && !g_variant_is_of_type (closure->result, closure->return_type)) {
541                 string = g_variant_type_dup_string (closure->return_type);
542                 g_warning ("received unexpected result type %s from Completed signal instead of expected %s",
543                            g_variant_get_type_string (closure->result), string);
544                 g_free (string);
545                 return NULL;
546         }
547         return g_variant_ref (closure->result);
548 }