Merge remote-tracking branch 'gvdb/master'
[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_free (closure->cond);
136   g_mutex_free (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   closure->mutex = g_mutex_new ();
156   closure->cond = g_cond_new ();
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_get_thread_default ();
191   if (interaction->priv->context)
192     g_main_context_ref (interaction->priv->context);
193 }
194
195 static void
196 g_tls_interaction_finalize (GObject *object)
197 {
198   GTlsInteraction *interaction = G_TLS_INTERACTION (object);
199
200   if (interaction->priv->context)
201     g_main_context_unref (interaction->priv->context);
202
203   G_OBJECT_CLASS (g_tls_interaction_parent_class)->finalize (object);
204 }
205
206 static void
207 g_tls_interaction_class_init (GTlsInteractionClass *klass)
208 {
209   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
210
211   gobject_class->finalize = g_tls_interaction_finalize;
212
213   g_type_class_add_private (klass, sizeof (GTlsInteractionPrivate));
214 }
215
216 static gboolean
217 on_invoke_ask_password_sync (gpointer user_data)
218 {
219   InvokeClosure *closure = user_data;
220   GTlsInteractionClass *klass;
221
222   g_mutex_lock (closure->mutex);
223
224   klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
225   g_assert (klass->ask_password);
226
227   closure->result = klass->ask_password (closure->interaction,
228                                          G_TLS_PASSWORD (closure->argument),
229                                          closure->cancellable,
230                                          &closure->error);
231
232   closure->complete = TRUE;
233   g_cond_signal (closure->cond);
234   g_mutex_unlock (closure->mutex);
235
236   return FALSE; /* don't call again */
237 }
238
239 static void
240 on_async_as_sync_complete (GObject      *source,
241                            GAsyncResult *result,
242                            gpointer      user_data)
243 {
244   InvokeClosure *closure = user_data;
245   GTlsInteractionClass *klass;
246
247   g_mutex_lock (closure->mutex);
248
249   klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
250   g_assert (klass->ask_password_finish);
251
252   closure->result = klass->ask_password_finish (closure->interaction,
253                                                 result,
254                                                 &closure->error);
255
256   closure->complete = TRUE;
257   g_cond_signal (closure->cond);
258   g_mutex_unlock (closure->mutex);
259 }
260
261 static gboolean
262 on_invoke_ask_password_async_as_sync (gpointer user_data)
263 {
264   InvokeClosure *closure = user_data;
265   GTlsInteractionClass *klass;
266
267   g_mutex_lock (closure->mutex);
268
269   klass = G_TLS_INTERACTION_GET_CLASS (closure->interaction);
270   g_assert (klass->ask_password_async);
271
272   klass->ask_password_async (closure->interaction,
273                              G_TLS_PASSWORD (closure->argument),
274                              closure->cancellable,
275                              on_async_as_sync_complete,
276                              closure);
277
278   /* Note that we've used these */
279   closure->callback = NULL;
280   closure->user_data = NULL;
281
282   g_mutex_unlock (closure->mutex);
283
284   return FALSE; /* don't call again */
285 }
286
287 /**
288  * g_tls_interaction_invoke_ask_password:
289  * @interaction: a #GTlsInteraction object
290  * @password: a #GTlsPassword object
291  * @cancellable: an optional #GCancellable cancellation object
292  * @error: an optional location to place an error on failure
293  *
294  * Invoke the interaction to ask the user for a password. It invokes this
295  * interaction in the main loop, specifically the #GMainContext returned by
296  * g_main_context_get_thread_default() when the interaction is created. This
297  * is called by called by #GTlsConnection or #GTlsDatabase to ask the user
298  * for a password.
299  *
300  * Derived subclasses usually implement a password prompt, although they may
301  * also choose to provide a password from elsewhere. The @password value will
302  * be filled in and then @callback will be called. Alternatively the user may
303  * abort this password request, which will usually abort the TLS connection.
304  *
305  * The implementation can either be a synchronous (eg: modal dialog) or an
306  * asynchronous one (eg: modeless dialog). This function will take care of
307  * calling which ever one correctly.
308  *
309  * If the interaction is cancelled by the cancellation object, or by the
310  * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
311  * contains a %G_IO_ERROR_CANCELLED error code. Certain implementations may
312  * not support immediate cancellation.
313  *
314  * Returns: The status of the ask password interaction.
315  *
316  * Since: 2.30
317  */
318 GTlsInteractionResult
319 g_tls_interaction_invoke_ask_password (GTlsInteraction    *interaction,
320                                        GTlsPassword       *password,
321                                        GCancellable       *cancellable,
322                                        GError            **error)
323 {
324   GTlsInteractionResult result;
325   InvokeClosure *closure;
326   GTlsInteractionClass *klass;
327
328   g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
329   g_return_val_if_fail (G_IS_TLS_PASSWORD (password), G_TLS_INTERACTION_UNHANDLED);
330   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_TLS_INTERACTION_UNHANDLED);
331
332   closure = invoke_closure_new (interaction, G_OBJECT (password), cancellable);
333
334   klass = G_TLS_INTERACTION_GET_CLASS (interaction);
335   if (klass->ask_password)
336     {
337       g_main_context_invoke (interaction->priv->context,
338                              on_invoke_ask_password_sync, closure);
339       result = invoke_closure_wait_and_free (closure, error);
340     }
341   else if (klass->ask_password_async)
342     {
343       g_return_val_if_fail (klass->ask_password_finish, G_TLS_INTERACTION_UNHANDLED);
344       g_main_context_invoke (interaction->priv->context,
345                              on_invoke_ask_password_async_as_sync, closure);
346
347       /*
348        * Handle the case where we've been called from within the main context
349        * or in the case where the main context is not running. This approximates
350        * the behavior of a modal dialog.
351        */
352       if (g_main_context_acquire (interaction->priv->context))
353         {
354           while (!closure->complete)
355             {
356               g_mutex_unlock (closure->mutex);
357               g_main_context_iteration (interaction->priv->context, TRUE);
358               g_mutex_lock (closure->mutex);
359             }
360           g_main_context_release (interaction->priv->context);
361
362           if (closure->error)
363             {
364               g_propagate_error (error, closure->error);
365               closure->error = NULL;
366             }
367
368           result = closure->result;
369           invoke_closure_free (closure);
370         }
371
372       /*
373        * Handle the case where we're in a different thread than the main
374        * context and a main loop is running.
375        */
376       else
377         {
378           result = invoke_closure_wait_and_free (closure, error);
379         }
380     }
381   else
382     {
383       result = G_TLS_INTERACTION_UNHANDLED;
384       invoke_closure_free (closure);
385     }
386
387   return result;
388 }
389
390
391 /**
392  * g_tls_interaction_ask_password:
393  * @interaction: a #GTlsInteraction object
394  * @password: a #GTlsPassword object
395  * @cancellable: an optional #GCancellable cancellation object
396  * @error: an optional location to place an error on failure
397  *
398  * Run synchronous interaction to ask the user for a password. In general,
399  * g_tls_interaction_invoke_ask_password() should be used instead of this
400  * function.
401  *
402  * Derived subclasses usually implement a password prompt, although they may
403  * also choose to provide a password from elsewhere. The @password value will
404  * be filled in and then @callback will be called. Alternatively the user may
405  * abort this password request, which will usually abort the TLS connection.
406  *
407  * If the interaction is cancelled by the cancellation object, or by the
408  * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
409  * contains a %G_IO_ERROR_CANCELLED error code. Certain implementations may
410  * not support immediate cancellation.
411  *
412  * Returns: The status of the ask password interaction.
413  *
414  * Since: 2.30
415  */
416 GTlsInteractionResult
417 g_tls_interaction_ask_password (GTlsInteraction    *interaction,
418                                 GTlsPassword       *password,
419                                 GCancellable       *cancellable,
420                                 GError            **error)
421 {
422   GTlsInteractionClass *klass;
423
424   g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
425   g_return_val_if_fail (G_IS_TLS_PASSWORD (password), G_TLS_INTERACTION_UNHANDLED);
426   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), G_TLS_INTERACTION_UNHANDLED);
427
428   klass = G_TLS_INTERACTION_GET_CLASS (interaction);
429   if (klass->ask_password)
430     return (klass->ask_password) (interaction, password, cancellable, error);
431   else
432     return G_TLS_INTERACTION_UNHANDLED;
433 }
434
435 /**
436  * g_tls_interaction_ask_password_async:
437  * @interaction: a #GTlsInteraction object
438  * @password: a #GTlsPassword object
439  * @cancellable: an optional #GCancellable cancellation object
440  * @callback: (allow-none): will be called when the interaction completes
441  * @user_data: (allow-none): data to pass to the @callback
442  *
443  * Run asynchronous interaction to ask the user for a password. In general,
444  * g_tls_interaction_invoke_ask_password() should be used instead of this
445  * function.
446  *
447  * Derived subclasses usually implement a password prompt, although they may
448  * also choose to provide a password from elsewhere. The @password value will
449  * be filled in and then @callback will be called. Alternatively the user may
450  * abort this password request, which will usually abort the TLS connection.
451  *
452  * If the interaction is cancelled by the cancellation object, or by the
453  * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
454  * contains a %G_IO_ERROR_CANCELLED error code. Certain implementations may
455  * not support immediate cancellation.
456  *
457  * Certain implementations may not support immediate cancellation.
458  *
459  * Since: 2.30
460  */
461 void
462 g_tls_interaction_ask_password_async (GTlsInteraction    *interaction,
463                                       GTlsPassword       *password,
464                                       GCancellable       *cancellable,
465                                       GAsyncReadyCallback callback,
466                                       gpointer            user_data)
467 {
468   GTlsInteractionClass *klass;
469   GSimpleAsyncResult *res;
470
471   g_return_if_fail (G_IS_TLS_INTERACTION (interaction));
472   g_return_if_fail (G_IS_TLS_PASSWORD (password));
473   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
474
475   klass = G_TLS_INTERACTION_GET_CLASS (interaction);
476   if (klass->ask_password_async)
477     {
478       g_return_if_fail (klass->ask_password_finish);
479       (klass->ask_password_async) (interaction, password, cancellable,
480                                    callback, user_data);
481     }
482   else
483     {
484       res = g_simple_async_result_new (G_OBJECT (interaction), callback, user_data,
485                                        g_tls_interaction_ask_password_async);
486       g_simple_async_result_complete_in_idle (res);
487       g_object_unref (res);
488     }
489 }
490
491 /**
492  * g_tls_interaction_ask_password_finish:
493  * @interaction: a #GTlsInteraction object
494  * @result: the result passed to the callback
495  * @error: an optional location to place an error on failure
496  *
497  * Complete an ask password user interaction request. This should be once
498  * the g_tls_interaction_ask_password_async() completion callback is called.
499  *
500  * If %G_TLS_INTERACTION_HANDLED is returned, then the #GTlsPassword passed
501  * to g_tls_interaction_ask_password() will have its password filled in.
502  *
503  * If the interaction is cancelled by the cancellation object, or by the
504  * user then %G_TLS_INTERACTION_FAILED will be returned with an error that
505  * contains a %G_IO_ERROR_CANCELLED error code.
506  *
507  * Returns: The status of the ask password interaction.
508  *
509  * Since: 2.30
510  */
511 GTlsInteractionResult
512 g_tls_interaction_ask_password_finish (GTlsInteraction    *interaction,
513                                        GAsyncResult       *result,
514                                        GError            **error)
515 {
516   GTlsInteractionClass *klass;
517
518   g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), G_TLS_INTERACTION_UNHANDLED);
519   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), G_TLS_INTERACTION_UNHANDLED);
520
521   /* If it's one of our simple unhandled async results, handle here */
522   if (g_simple_async_result_is_valid (result, G_OBJECT (interaction),
523                                       g_tls_interaction_ask_password_async))
524     {
525       return G_TLS_INTERACTION_UNHANDLED;
526     }
527
528   /* Invoke finish of derived class */
529   else
530     {
531       klass = G_TLS_INTERACTION_GET_CLASS (interaction);
532       g_return_val_if_fail (klass->ask_password_finish, G_TLS_INTERACTION_UNHANDLED);
533       return (klass->ask_password_finish) (interaction, result, error);
534     }
535 }