Add g_main_context_ref_thread_default()
[platform/upstream/glib.git] / gio / gtlsinteraction.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2011 Collabora, Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Stef Walter <stefw@collabora.co.uk>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include "gtlsinteraction.h"
28 #include "gtlspassword.h"
29 #include "gasyncresult.h"
30 #include "gcancellable.h"
31 #include "gsimpleasyncresult.h"
32 #include "gioenumtypes.h"
33 #include "glibintl.h"
34
35
36 /**
37  * SECTION:gtlsinteraction
38  * @short_description: Interaction with the user during TLS operations.
39  * @include: gio/gio.h
40  *
41  * #GTlsInteraction provides a mechanism for the TLS connection and database
42  * code to interact with the user. It can be used to ask the user for passwords.
43  *
44  * To use a #GTlsInteraction with a TLS connection use
45  * g_tls_connection_set_interaction().
46  *
47  * Callers should instantiate a derived class that implements the various
48  * interaction methods to show the required dialogs.
49  *
50  * Callers should use the 'invoke' functions like
51  * g_tls_interaction_invoke_ask_password() to run interaction methods. These
52  * functions make sure that the interaction is invoked in the main loop
53  * and not in the current thread, if the current thread is not running the
54  * main loop.
55  *
56  * Derived classes can choose to implement whichever interactions methods they'd
57  * like to support by overriding those virtual methods in their class
58  * initialization function. Any interactions not implemented will return
59  * %G_TLS_INTERACTION_UNHANDLED. If a derived class implements an async method,
60  * it must also implement the corresponding finish method.
61  */
62
63 /**
64  * GTlsInteraction:
65  *
66  * An object representing interaction that the TLS connection and database
67  * might have with the user.
68  *
69  * Since: 2.30
70  */
71
72 /**
73  * GTlsInteractionClass:
74  * @ask_password: ask for a password synchronously. If the implementation
75  *     returns %G_TLS_INTERACTION_HANDLED, then the password argument should
76  *     have been filled in by using g_tls_password_set_value() or a similar
77  *     function.
78  * @ask_password_async: ask for a password asynchronously.
79  * @ask_password_finish: complete operation to ask for a password asynchronously.
80  *     If the implementation returns %G_TLS_INTERACTION_HANDLED, then the
81  *     password argument of the async method should have been filled in by using
82  *     g_tls_password_set_value() or a similar function.
83  *
84  * The class for #GTlsInteraction. Derived classes implement the various
85  * virtual interaction methods to handle TLS interactions.
86  *
87  * Derived classes can choose to implement whichever interactions methods they'd
88  * like to support by overriding those virtual methods in their class
89  * initialization function. If a derived class implements an async method,
90  * it must also implement the corresponding finish method.
91  *
92  * The synchronous interaction methods should implement to display modal dialogs,
93  * and the asynchronous methods to display modeless dialogs.
94  *
95  * If the user cancels an interaction, then the result should be
96  * %G_TLS_INTERACTION_FAILED and the error should be set with a domain of
97  * %G_IO_ERROR and code of %G_IO_ERROR_CANCELLED.
98  *
99  * Since: 2.30
100  */
101
102 struct _GTlsInteractionPrivate {
103   GMainContext *context;
104 };
105
106 G_DEFINE_TYPE (GTlsInteraction, g_tls_interaction, G_TYPE_OBJECT);
107
108 typedef struct {
109   GMutex mutex;
110
111   /* Input arguments */
112   GTlsInteraction *interaction;
113   GObject *argument;
114   GCancellable *cancellable;
115
116   /* Used when we're invoking async interactions */
117   GAsyncReadyCallback callback;
118   gpointer user_data;
119
120   /* Used when we expect results */
121   GTlsInteractionResult result;
122   GError *error;
123   gboolean complete;
124   GCond cond;
125 } InvokeClosure;
126
127 static void
128 invoke_closure_free (gpointer data)
129 {
130   InvokeClosure *closure = data;
131   g_assert (closure);
132   g_object_unref (closure->interaction);
133   g_clear_object (&closure->argument);
134   g_clear_object (&closure->cancellable);
135   g_cond_clear (&closure->cond);
136   g_mutex_clear (&closure->mutex);
137   g_clear_error (&closure->error);
138
139   /* Insurance that we've actually used these before freeing */
140   g_assert (closure->callback == NULL);
141   g_assert (closure->user_data == NULL);
142
143   g_free (closure);
144 }
145
146 static InvokeClosure *
147 invoke_closure_new (GTlsInteraction *interaction,
148                     GObject         *argument,
149                     GCancellable    *cancellable)
150 {
151   InvokeClosure *closure = g_new0 (InvokeClosure, 1);
152   closure->interaction = g_object_ref (interaction);
153   closure->argument = argument ? g_object_ref (argument) : NULL;
154   closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
155   g_mutex_init (&closure->mutex);
156   g_cond_init (&closure->cond);
157   closure->result = G_TLS_INTERACTION_UNHANDLED;
158   return closure;
159 }
160
161 static GTlsInteractionResult
162 invoke_closure_wait_and_free (InvokeClosure *closure,
163                               GError       **error)
164 {
165   GTlsInteractionResult result;
166
167   g_mutex_lock (&closure->mutex);
168
169   while (!closure->complete)
170     g_cond_wait (&closure->cond, &closure->mutex);
171
172   g_mutex_unlock (&closure->mutex);
173
174   if (closure->error)
175     {
176       g_propagate_error (error, closure->error);
177       closure->error = NULL;
178     }
179   result = closure->result;
180
181   invoke_closure_free (closure);
182   return result;
183 }
184
185 static void
186 g_tls_interaction_init (GTlsInteraction *interaction)
187 {
188   interaction->priv = G_TYPE_INSTANCE_GET_PRIVATE (interaction, G_TYPE_TLS_INTERACTION,
189                                                    GTlsInteractionPrivate);
190   interaction->priv->context = g_main_context_ref_thread_default ();
191 }
192
193 static void
194 g_tls_interaction_finalize (GObject *object)
195 {
196   GTlsInteraction *interaction = G_TLS_INTERACTION (object);
197
198   g_main_context_unref (interaction->priv->context);
199
200   G_OBJECT_CLASS (g_tls_interaction_parent_class)->finalize (object);
201 }
202
203 static void
204 g_tls_interaction_class_init (GTlsInteractionClass *klass)
205 {
206   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
207
208   gobject_class->finalize = g_tls_interaction_finalize;
209
210   g_type_class_add_private (klass, sizeof (GTlsInteractionPrivate));
211 }
212
213 static gboolean
214 on_invoke_ask_password_sync (gpointer user_data)
215 {
216   InvokeClosure *closure = user_data;
217   GTlsInteractionClass *klass;
218
219   g_mutex_lock (&closure->mutex);
220
221   klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
222   g_assert (klass->ask_password);
223
224   closure->result = klass->ask_password (closure->interaction,
225                                          G_TLS_PASSWORD (closure->argument),
226                                          closure->cancellable,
227                                          &closure->error);
228
229   closure->complete = TRUE;
230   g_cond_signal (&closure->cond);
231   g_mutex_unlock (&closure->mutex);
232
233   return FALSE; /* don't call again */
234 }
235
236 static void
237 on_async_as_sync_complete (GObject      *source,
238                            GAsyncResult *result,
239                            gpointer      user_data)
240 {
241   InvokeClosure *closure = user_data;
242   GTlsInteractionClass *klass;
243
244   g_mutex_lock (&closure->mutex);
245
246   klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
247   g_assert (klass->ask_password_finish);
248
249   closure->result = klass->ask_password_finish (closure->interaction,
250                                                 result,
251                                                 &closure->error);
252
253   closure->complete = TRUE;
254   g_cond_signal (&closure->cond);
255   g_mutex_unlock (&closure->mutex);
256 }
257
258 static gboolean
259 on_invoke_ask_password_async_as_sync (gpointer user_data)
260 {
261   InvokeClosure *closure = user_data;
262   GTlsInteractionClass *klass;
263
264   g_mutex_lock (&closure->mutex);
265
266   klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
267   g_assert (klass->ask_password_async);
268
269   klass->ask_password_async (closure->interaction,
270                              G_TLS_PASSWORD (closure->argument),
271                              closure->cancellable,
272                              on_async_as_sync_complete,
273                              closure);
274
275   /* Note that we've used these */
276   closure->callback = NULL;
277   closure->user_data = NULL;
278
279   g_mutex_unlock (&closure->mutex);
280
281   return FALSE; /* don't call again */
282 }
283
284 /**
285  * g_tls_interaction_invoke_ask_password:
286  * @interaction: a #GTlsInteraction object
287  * @password: a #GTlsPassword object
288  * @cancellable: an optional #GCancellable cancellation object
289  * @error: an optional location to place an error on failure
290  *
291  * Invoke the interaction to ask the user for a password. It invokes this
292  * interaction in the main loop, specifically the #GMainContext returned by
293  * g_main_context_get_thread_default() when the interaction is created. This
294  * is called by called by #GTlsConnection or #GTlsDatabase to ask the user
295  * for a password.
296  *
297  * Derived subclasses usually implement a password prompt, although they may
298  * also choose to provide a password from elsewhere. The @password value will
299  * be filled in and then @callback will be called. Alternatively the user may
300  * abort this password request, which will usually abort the TLS connection.
301  *
302  * The implementation can either be a synchronous (eg: modal dialog) or an
303  * asynchronous one (eg: modeless dialog). This function will take care of
304  * calling which ever one correctly.
305  *
306  * If the interaction is cancelled by the cancellation object, or by the
307  * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
308  * contains a %G_IO_ERROR_CANCELLED error code. Certain implementations may
309  * not support immediate cancellation.
310  *
311  * Returns: The status of the ask password interaction.
312  *
313  * Since: 2.30
314  */
315 GTlsInteractionResult
316 g_tls_interaction_invoke_ask_password (GTlsInteraction    *interaction,
317                                        GTlsPassword       *password,
318                                        GCancellable       *cancellable,
319                                        GError            **error)
320 {
321   GTlsInteractionResult result;
322   InvokeClosure *closure;
323   GTlsInteractionClass *klass;
324
325   g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
326   g_return_val_if_fail (G_IS_TLS_PASSWORD (password), G_TLS_INTERACTION_UNHANDLED);
327   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_TLS_INTERACTION_UNHANDLED);
328
329   closure = invoke_closure_new (interaction, G_OBJECT (password), cancellable);
330
331   klass = G_TLS_INTERACTION_GET_CLASS (interaction);
332   if (klass->ask_password)
333     {
334       g_main_context_invoke (interaction->priv->context,
335                              on_invoke_ask_password_sync, closure);
336       result = invoke_closure_wait_and_free (closure, error);
337     }
338   else if (klass->ask_password_async)
339     {
340       g_return_val_if_fail (klass->ask_password_finish, G_TLS_INTERACTION_UNHANDLED);
341       g_main_context_invoke (interaction->priv->context,
342                              on_invoke_ask_password_async_as_sync, closure);
343
344       /*
345        * Handle the case where we've been called from within the main context
346        * or in the case where the main context is not running. This approximates
347        * the behavior of a modal dialog.
348        */
349       if (g_main_context_acquire (interaction->priv->context))
350         {
351           while (!closure->complete)
352             {
353               g_mutex_unlock (&closure->mutex);
354               g_main_context_iteration (interaction->priv->context, TRUE);
355               g_mutex_lock (&closure->mutex);
356             }
357           g_main_context_release (interaction->priv->context);
358
359           if (closure->error)
360             {
361               g_propagate_error (error, closure->error);
362               closure->error = NULL;
363             }
364
365           result = closure->result;
366           invoke_closure_free (closure);
367         }
368
369       /*
370        * Handle the case where we're in a different thread than the main
371        * context and a main loop is running.
372        */
373       else
374         {
375           result = invoke_closure_wait_and_free (closure, error);
376         }
377     }
378   else
379     {
380       result = G_TLS_INTERACTION_UNHANDLED;
381       invoke_closure_free (closure);
382     }
383
384   return result;
385 }
386
387
388 /**
389  * g_tls_interaction_ask_password:
390  * @interaction: a #GTlsInteraction object
391  * @password: a #GTlsPassword object
392  * @cancellable: an optional #GCancellable cancellation object
393  * @error: an optional location to place an error on failure
394  *
395  * Run synchronous interaction to ask the user for a password. In general,
396  * g_tls_interaction_invoke_ask_password() should be used instead of this
397  * function.
398  *
399  * Derived subclasses usually implement a password prompt, although they may
400  * also choose to provide a password from elsewhere. The @password value will
401  * be filled in and then @callback will be called. Alternatively the user may
402  * abort this password request, which will usually abort the TLS connection.
403  *
404  * If the interaction is cancelled by the cancellation object, or by the
405  * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
406  * contains a %G_IO_ERROR_CANCELLED error code. Certain implementations may
407  * not support immediate cancellation.
408  *
409  * Returns: The status of the ask password interaction.
410  *
411  * Since: 2.30
412  */
413 GTlsInteractionResult
414 g_tls_interaction_ask_password (GTlsInteraction    *interaction,
415                                 GTlsPassword       *password,
416                                 GCancellable       *cancellable,
417                                 GError            **error)
418 {
419   GTlsInteractionClass *klass;
420
421   g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
422   g_return_val_if_fail (G_IS_TLS_PASSWORD (password), G_TLS_INTERACTION_UNHANDLED);
423   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_TLS_INTERACTION_UNHANDLED);
424
425   klass = G_TLS_INTERACTION_GET_CLASS (interaction);
426   if (klass->ask_password)
427     return (klass->ask_password) (interaction, password, cancellable, error);
428   else
429     return G_TLS_INTERACTION_UNHANDLED;
430 }
431
432 /**
433  * g_tls_interaction_ask_password_async:
434  * @interaction: a #GTlsInteraction object
435  * @password: a #GTlsPassword object
436  * @cancellable: an optional #GCancellable cancellation object
437  * @callback: (allow-none): will be called when the interaction completes
438  * @user_data: (allow-none): data to pass to the @callback
439  *
440  * Run asynchronous interaction to ask the user for a password. In general,
441  * g_tls_interaction_invoke_ask_password() should be used instead of this
442  * function.
443  *
444  * Derived subclasses usually implement a password prompt, although they may
445  * also choose to provide a password from elsewhere. The @password value will
446  * be filled in and then @callback will be called. Alternatively the user may
447  * abort this password request, which will usually abort the TLS connection.
448  *
449  * If the interaction is cancelled by the cancellation object, or by the
450  * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
451  * contains a %G_IO_ERROR_CANCELLED error code. Certain implementations may
452  * not support immediate cancellation.
453  *
454  * Certain implementations may not support immediate cancellation.
455  *
456  * Since: 2.30
457  */
458 void
459 g_tls_interaction_ask_password_async (GTlsInteraction    *interaction,
460                                       GTlsPassword       *password,
461                                       GCancellable       *cancellable,
462                                       GAsyncReadyCallback callback,
463                                       gpointer            user_data)
464 {
465   GTlsInteractionClass *klass;
466   GSimpleAsyncResult *res;
467
468   g_return_if_fail (G_IS_TLS_INTERACTION (interaction));
469   g_return_if_fail (G_IS_TLS_PASSWORD (password));
470   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
471
472   klass = G_TLS_INTERACTION_GET_CLASS (interaction);
473   if (klass->ask_password_async)
474     {
475       g_return_if_fail (klass->ask_password_finish);
476       (klass->ask_password_async) (interaction, password, cancellable,
477                                    callback, user_data);
478     }
479   else
480     {
481       res = g_simple_async_result_new (G_OBJECT (interaction), callback, user_data,
482                                        g_tls_interaction_ask_password_async);
483       g_simple_async_result_complete_in_idle (res);
484       g_object_unref (res);
485     }
486 }
487
488 /**
489  * g_tls_interaction_ask_password_finish:
490  * @interaction: a #GTlsInteraction object
491  * @result: the result passed to the callback
492  * @error: an optional location to place an error on failure
493  *
494  * Complete an ask password user interaction request. This should be once
495  * the g_tls_interaction_ask_password_async() completion callback is called.
496  *
497  * If %G_TLS_INTERACTION_HANDLED is returned, then the #GTlsPassword passed
498  * to g_tls_interaction_ask_password() will have its password filled in.
499  *
500  * If the interaction is cancelled by the cancellation object, or by the
501  * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
502  * contains a %G_IO_ERROR_CANCELLED error code.
503  *
504  * Returns: The status of the ask password interaction.
505  *
506  * Since: 2.30
507  */
508 GTlsInteractionResult
509 g_tls_interaction_ask_password_finish (GTlsInteraction    *interaction,
510                                        GAsyncResult       *result,
511                                        GError            **error)
512 {
513   GTlsInteractionClass *klass;
514
515   g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
516   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), G_TLS_INTERACTION_UNHANDLED);
517
518   /* If it's one of our simple unhandled async results, handle here */
519   if (g_simple_async_result_is_valid (result, G_OBJECT (interaction),
520                                       g_tls_interaction_ask_password_async))
521     {
522       return G_TLS_INTERACTION_UNHANDLED;
523     }
524
525   /* Invoke finish of derived class */
526   else
527     {
528       klass = G_TLS_INTERACTION_GET_CLASS (interaction);
529       g_return_val_if_fail (klass->ask_password_finish, G_TLS_INTERACTION_UNHANDLED);
530       return (klass->ask_password_finish) (interaction, result, error);
531     }
532 }