1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2011 Collabora, Ltd.
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.
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.
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.
20 * Author: Stef Walter <stefw@collabora.co.uk>
27 #include "gtlsinteraction.h"
28 #include "gtlspassword.h"
29 #include "gasyncresult.h"
30 #include "gcancellable.h"
31 #include "gsimpleasyncresult.h"
32 #include "gioenumtypes.h"
37 * SECTION:gtlsinteraction
38 * @short_description: Interaction with the user during TLS operations.
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.
44 * To use a #GTlsInteraction with a TLS connection use
45 * g_tls_connection_set_interaction().
47 * Callers should instantiate a derived class that implements the various
48 * interaction methods to show the required dialogs.
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
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.
66 * An object representing interaction that the TLS connection and database
67 * might have with the user.
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
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.
84 * The class for #GTlsInteraction. Derived classes implement the various
85 * virtual interaction methods to handle TLS interactions.
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.
92 * The synchronous interaction methods should implement to display modal dialogs,
93 * and the asynchronous methods to display modeless dialogs.
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.
102 struct _GTlsInteractionPrivate
{
103 GMainContext
*context
;
106 G_DEFINE_TYPE (GTlsInteraction
, g_tls_interaction
, G_TYPE_OBJECT
);
111 /* Input arguments */
112 GTlsInteraction
*interaction
;
114 GCancellable
*cancellable
;
116 /* Used when we're invoking async interactions */
117 GAsyncReadyCallback callback
;
120 /* Used when we expect results */
121 GTlsInteractionResult result
;
128 invoke_closure_free (gpointer data
)
130 InvokeClosure
*closure
= data
;
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
);
139 /* Insurance that we've actually used these before freeing */
140 g_assert (closure
->callback
== NULL
);
141 g_assert (closure
->user_data
== NULL
);
146 static InvokeClosure
*
147 invoke_closure_new (GTlsInteraction
*interaction
,
149 GCancellable
*cancellable
)
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
;
161 static GTlsInteractionResult
162 invoke_closure_wait_and_free (InvokeClosure
*closure
,
165 GTlsInteractionResult result
;
167 g_mutex_lock (&closure
->mutex
);
169 while (!closure
->complete
)
170 g_cond_wait (&closure
->cond
, &closure
->mutex
);
172 g_mutex_unlock (&closure
->mutex
);
176 g_propagate_error (error
, closure
->error
);
177 closure
->error
= NULL
;
179 result
= closure
->result
;
181 invoke_closure_free (closure
);
186 g_tls_interaction_init (GTlsInteraction
*interaction
)
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 ();
194 g_tls_interaction_finalize (GObject
*object
)
196 GTlsInteraction
*interaction
= G_TLS_INTERACTION (object
);
198 g_main_context_unref (interaction
->priv
->context
);
200 G_OBJECT_CLASS (g_tls_interaction_parent_class
)->finalize (object
);
204 g_tls_interaction_class_init (GTlsInteractionClass
*klass
)
206 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
208 gobject_class
->finalize
= g_tls_interaction_finalize
;
210 g_type_class_add_private (klass
, sizeof (GTlsInteractionPrivate
));
214 on_invoke_ask_password_sync (gpointer user_data
)
216 InvokeClosure
*closure
= user_data
;
217 GTlsInteractionClass
*klass
;
219 g_mutex_lock (&closure
->mutex
);
221 klass
= G_TLS_INTERACTION_GET_CLASS (closure
->interaction
);
222 g_assert (klass
->ask_password
);
224 closure
->result
= klass
->ask_password (closure
->interaction
,
225 G_TLS_PASSWORD (closure
->argument
),
226 closure
->cancellable
,
229 closure
->complete
= TRUE
;
230 g_cond_signal (&closure
->cond
);
231 g_mutex_unlock (&closure
->mutex
);
233 return FALSE
; /* don't call again */
237 on_async_as_sync_complete (GObject
*source
,
238 GAsyncResult
*result
,
241 InvokeClosure
*closure
= user_data
;
242 GTlsInteractionClass
*klass
;
244 g_mutex_lock (&closure
->mutex
);
246 klass
= G_TLS_INTERACTION_GET_CLASS (closure
->interaction
);
247 g_assert (klass
->ask_password_finish
);
249 closure
->result
= klass
->ask_password_finish (closure
->interaction
,
253 closure
->complete
= TRUE
;
254 g_cond_signal (&closure
->cond
);
255 g_mutex_unlock (&closure
->mutex
);
259 on_invoke_ask_password_async_as_sync (gpointer user_data
)
261 InvokeClosure
*closure
= user_data
;
262 GTlsInteractionClass
*klass
;
264 g_mutex_lock (&closure
->mutex
);
266 klass
= G_TLS_INTERACTION_GET_CLASS (closure
->interaction
);
267 g_assert (klass
->ask_password_async
);
269 klass
->ask_password_async (closure
->interaction
,
270 G_TLS_PASSWORD (closure
->argument
),
271 closure
->cancellable
,
272 on_async_as_sync_complete
,
275 /* Note that we've used these */
276 closure
->callback
= NULL
;
277 closure
->user_data
= NULL
;
279 g_mutex_unlock (&closure
->mutex
);
281 return FALSE
; /* don't call again */
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
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
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.
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.
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.
311 * Returns: The status of the ask password interaction.
315 GTlsInteractionResult
316 g_tls_interaction_invoke_ask_password (GTlsInteraction
*interaction
,
317 GTlsPassword
*password
,
318 GCancellable
*cancellable
,
321 GTlsInteractionResult result
;
322 InvokeClosure
*closure
;
323 GTlsInteractionClass
*klass
;
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
);
329 closure
= invoke_closure_new (interaction
, G_OBJECT (password
), cancellable
);
331 klass
= G_TLS_INTERACTION_GET_CLASS (interaction
);
332 if (klass
->ask_password
)
334 g_main_context_invoke (interaction
->priv
->context
,
335 on_invoke_ask_password_sync
, closure
);
336 result
= invoke_closure_wait_and_free (closure
, error
);
338 else if (klass
->ask_password_async
)
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
);
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.
349 if (g_main_context_acquire (interaction
->priv
->context
))
351 while (!closure
->complete
)
353 g_mutex_unlock (&closure
->mutex
);
354 g_main_context_iteration (interaction
->priv
->context
, TRUE
);
355 g_mutex_lock (&closure
->mutex
);
357 g_main_context_release (interaction
->priv
->context
);
361 g_propagate_error (error
, closure
->error
);
362 closure
->error
= NULL
;
365 result
= closure
->result
;
366 invoke_closure_free (closure
);
370 * Handle the case where we're in a different thread than the main
371 * context and a main loop is running.
375 result
= invoke_closure_wait_and_free (closure
, error
);
380 result
= G_TLS_INTERACTION_UNHANDLED
;
381 invoke_closure_free (closure
);
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
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
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.
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.
409 * Returns: The status of the ask password interaction.
413 GTlsInteractionResult
414 g_tls_interaction_ask_password (GTlsInteraction
*interaction
,
415 GTlsPassword
*password
,
416 GCancellable
*cancellable
,
419 GTlsInteractionClass
*klass
;
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
);
425 klass
= G_TLS_INTERACTION_GET_CLASS (interaction
);
426 if (klass
->ask_password
)
427 return (klass
->ask_password
) (interaction
, password
, cancellable
, error
);
429 return G_TLS_INTERACTION_UNHANDLED
;
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
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
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.
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.
454 * Certain implementations may not support immediate cancellation.
459 g_tls_interaction_ask_password_async (GTlsInteraction
*interaction
,
460 GTlsPassword
*password
,
461 GCancellable
*cancellable
,
462 GAsyncReadyCallback callback
,
465 GTlsInteractionClass
*klass
;
466 GSimpleAsyncResult
*res
;
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
));
472 klass
= G_TLS_INTERACTION_GET_CLASS (interaction
);
473 if (klass
->ask_password_async
)
475 g_return_if_fail (klass
->ask_password_finish
);
476 (klass
->ask_password_async
) (interaction
, password
, cancellable
,
477 callback
, user_data
);
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
);
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
494 * Complete an ask password user interaction request. This should be once
495 * the g_tls_interaction_ask_password_async() completion callback is called.
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.
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.
504 * Returns: The status of the ask password interaction.
508 GTlsInteractionResult
509 g_tls_interaction_ask_password_finish (GTlsInteraction
*interaction
,
510 GAsyncResult
*result
,
513 GTlsInteractionClass
*klass
;
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
);
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
))
522 return G_TLS_INTERACTION_UNHANDLED
;
525 /* Invoke finish of derived class */
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
);