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