/*
* gnome-keyring
*
* Copyright (C) 2011 Stefan Walter
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*
* Author: Stef Walter
*/
#include "config.h"
#include "gcr-dbus-constants.h"
#include "gcr-internal.h"
#include "gcr-library.h"
#include "gcr-prompt.h"
#include "gcr-secret-exchange.h"
#include "gcr-system-prompt.h"
#include "gcr/gcr-dbus-generated.h"
#include "egg/egg-error.h"
#include
/**
* SECTION:gcr-system-prompt
* @title: GcrSystemPrompt
* @short_description: a system modal prompt
*
* A #GcrPrompt implementation which calls to the system prompter to
* display prompts in a system modal fashion.
*
* Since the system prompter usually only displays one prompt at a time, you
* may have to wait for the prompt to be displayed. Use gcr_system_prompt_open()
* or a related function to open a prompt. Since this can take a long time, you
* should always check that the prompt is still needed after it is opened. A
* previous prompt may have already provided the information needed and you
* may no longer need to prompt.
*
* Use gcr_system_prompt_close() to close the prompt when you're done with it.
*/
/**
* GcrSystemPrompt:
*
* A #GcrPrompt which shows a system prompt. This is usually a system modal
* dialog.
*/
/**
* GcrSystemPromptClass:
* @parent_class: parent class
*
* The class for #GcrSystemPrompt.
*/
/**
* GCR_SYSTEM_PROMPT_ERROR:
*
* The domain for errors returned from GcrSystemPrompt methods.
*/
/**
* GcrSystemPromptError:
* @GCR_SYSTEM_PROMPT_IN_PROGRESS: another prompt is already in progress
*
* No error returned by the #GcrSystemPrompt is suitable for display or
* to the user.
*
* If the system prompter can only show one prompt at a time, and there is
* already a prompt being displayed, and the timeout waiting to open the
* prompt expires, then %GCR_SYSTEM_PROMPT_IN_PROGRESS is returned.
*/
enum {
PROP_0,
PROP_BUS_NAME,
PROP_SECRET_EXCHANGE,
PROP_TIMEOUT_SECONDS,
PROP_TITLE,
PROP_MESSAGE,
PROP_DESCRIPTION,
PROP_WARNING,
PROP_PASSWORD_NEW,
PROP_PASSWORD_STRENGTH,
PROP_CHOICE_LABEL,
PROP_CHOICE_CHOSEN,
PROP_CALLER_WINDOW,
PROP_CONTINUE_LABEL,
PROP_CANCEL_LABEL
};
struct _GcrSystemPromptPrivate {
gchar *prompter_bus_name;
GcrSecretExchange *exchange;
gboolean received;
GHashTable *properties;
GHashTable *dirty_properties;
gint timeout_seconds;
GDBusConnection *connection;
gboolean begun_prompting;
gboolean closed;
guint prompt_registered;
gchar *prompt_path;
gchar *prompt_owner;
GSimpleAsyncResult *pending;
gchar *last_response;
};
static void gcr_system_prompt_prompt_iface (GcrPromptIface *iface);
static void gcr_system_prompt_initable_iface (GInitableIface *iface);
static void gcr_system_prompt_async_initable_iface (GAsyncInitableIface *iface);
static void perform_init_async (GcrSystemPrompt *self,
GSimpleAsyncResult *res);
G_DEFINE_TYPE_WITH_CODE (GcrSystemPrompt, gcr_system_prompt, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, gcr_system_prompt_prompt_iface);
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gcr_system_prompt_initable_iface);
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, gcr_system_prompt_async_initable_iface);
);
static gint unique_prompt_id = 0;
typedef struct {
GSource *timeout;
GSource *waiting;
GMainContext *context;
GCancellable *cancellable;
guint watch_id;
} CallClosure;
static void
call_closure_free (gpointer data)
{
CallClosure *closure = data;
if (closure->timeout) {
g_source_destroy (closure->timeout);
g_source_unref (closure->timeout);
}
if (closure->waiting) {
g_source_destroy (closure->waiting);
g_source_unref (closure->waiting);
}
if (closure->watch_id)
g_bus_unwatch_name (closure->watch_id);
g_object_unref (closure->cancellable);
g_free (data);
}
static void
on_propagate_cancelled (GCancellable *cancellable,
gpointer user_data)
{
/* Propagate the cancelled signal */
GCancellable *cancel = G_CANCELLABLE (user_data);
g_cancellable_cancel (cancel);
}
static CallClosure *
call_closure_new (GCancellable *cancellable)
{
CallClosure *call;
/*
* We use our own cancellable object, since we cancel it it in
* situations other than when the caller cancels.
*/
call = g_new0 (CallClosure, 1);
call->cancellable = g_cancellable_new ();
if (cancellable) {
g_cancellable_connect (cancellable, G_CALLBACK (on_propagate_cancelled),
g_object_ref (call->cancellable), g_object_unref);
}
return call;
}
static void
gcr_system_prompt_init (GcrSystemPrompt *self)
{
self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SYSTEM_PROMPT,
GcrSystemPromptPrivate);
self->pv->timeout_seconds = -1;
self->pv->properties = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify)g_variant_unref);
self->pv->dirty_properties = g_hash_table_new (g_direct_hash, g_direct_equal);
}
static const gchar *
prompt_get_string_property (GcrSystemPrompt *self,
const gchar *property_name,
gboolean collapse_empty_to_null)
{
const gchar *value = NULL;
GVariant *variant;
gconstpointer key;
g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), NULL);
key = g_intern_string (property_name);
variant = g_hash_table_lookup (self->pv->properties, key);
if (variant != NULL) {
value = g_variant_get_string (variant, NULL);
if (collapse_empty_to_null && value != NULL && value[0] == '\0')
value = NULL;
}
return value;
}
static void
prompt_set_string_property (GcrSystemPrompt *self,
const gchar *property_name,
const gchar *value)
{
GVariant *variant;
gpointer key;
g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
key = (gpointer)g_intern_string (property_name);
variant = g_variant_ref_sink (g_variant_new_string (value ? value : ""));
g_hash_table_insert (self->pv->properties, key, variant);
g_hash_table_insert (self->pv->dirty_properties, key, key);
g_object_notify (G_OBJECT (self), property_name);
}
static gint
prompt_get_int_property (GcrSystemPrompt *self,
const gchar *property_name)
{
GVariant *variant;
gconstpointer key;
g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), 0);
key = g_intern_string (property_name);
variant = g_hash_table_lookup (self->pv->properties, key);
if (variant != NULL)
return g_variant_get_int32 (variant);
return 0;
}
static gboolean
prompt_get_boolean_property (GcrSystemPrompt *self,
const gchar *property_name)
{
GVariant *variant;
gconstpointer key;
g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
key = g_intern_string (property_name);
variant = g_hash_table_lookup (self->pv->properties, key);
if (variant != NULL)
return g_variant_get_boolean (variant);
return FALSE;
}
static void
prompt_set_boolean_property (GcrSystemPrompt *self,
const gchar *property_name,
gboolean value)
{
GVariant *variant;
gpointer key;
g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
key = (gpointer)g_intern_string (property_name);
variant = g_variant_ref_sink (g_variant_new_boolean (value));
g_hash_table_insert (self->pv->properties, key, variant);
g_hash_table_insert (self->pv->dirty_properties, key, key);
g_object_notify (G_OBJECT (self), property_name);
}
static void
gcr_system_prompt_set_property (GObject *obj,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
switch (prop_id) {
case PROP_BUS_NAME:
g_assert (self->pv->prompter_bus_name == NULL);
self->pv->prompter_bus_name = g_value_dup_string (value);
break;
case PROP_SECRET_EXCHANGE:
if (self->pv->exchange) {
g_warning ("The secret exchange is already in use, and cannot be changed");
return;
}
self->pv->exchange = g_value_dup_object (value);
g_object_notify (G_OBJECT (self), "secret-exchange");
break;
case PROP_TIMEOUT_SECONDS:
self->pv->timeout_seconds = g_value_get_int (value);
break;
case PROP_TITLE:
prompt_set_string_property (self, "title", g_value_get_string (value));
break;
case PROP_MESSAGE:
prompt_set_string_property (self, "message", g_value_get_string (value));
break;
case PROP_DESCRIPTION:
prompt_set_string_property (self, "description", g_value_get_string (value));
break;
case PROP_WARNING:
prompt_set_string_property (self, "warning", g_value_get_string (value));
break;
case PROP_PASSWORD_NEW:
prompt_set_boolean_property (self, "password-new", g_value_get_boolean (value));
break;
case PROP_CHOICE_LABEL:
prompt_set_string_property (self, "choice-label", g_value_get_string (value));
break;
case PROP_CHOICE_CHOSEN:
prompt_set_boolean_property (self, "choice-chosen", g_value_get_boolean (value));
break;
case PROP_CALLER_WINDOW:
prompt_set_string_property (self, "caller-window", g_value_get_string (value));
break;
case PROP_CONTINUE_LABEL:
prompt_set_string_property (self, "continue-label", g_value_get_string (value));
break;
case PROP_CANCEL_LABEL:
prompt_set_string_property (self, "cancel-label", g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}
static void
gcr_system_prompt_get_property (GObject *obj,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
switch (prop_id) {
case PROP_BUS_NAME:
g_value_set_string (value, self->pv->prompter_bus_name);
break;
case PROP_SECRET_EXCHANGE:
g_value_set_object (value, gcr_system_prompt_get_secret_exchange (self));
break;
case PROP_TITLE:
g_value_set_string (value, prompt_get_string_property (self, "title", FALSE));
break;
case PROP_MESSAGE:
g_value_set_string (value, prompt_get_string_property (self, "message", FALSE));
break;
case PROP_DESCRIPTION:
g_value_set_string (value, prompt_get_string_property (self, "description", FALSE));
break;
case PROP_WARNING:
g_value_set_string (value, prompt_get_string_property (self, "warning", TRUE));
break;
case PROP_PASSWORD_NEW:
g_value_set_boolean (value, prompt_get_boolean_property (self, "password-new"));
break;
case PROP_PASSWORD_STRENGTH:
g_value_set_int (value, prompt_get_int_property (self, "password-strength"));
break;
case PROP_CHOICE_LABEL:
g_value_set_string (value, prompt_get_string_property (self, "choice-label", TRUE));
break;
case PROP_CHOICE_CHOSEN:
g_value_set_boolean (value, prompt_get_boolean_property (self, "choice-chosen"));
break;
case PROP_CALLER_WINDOW:
g_value_set_string (value, prompt_get_string_property (self, "caller-window", TRUE));
break;
case PROP_CONTINUE_LABEL:
g_value_set_string (value, prompt_get_string_property (self, "continue-label", TRUE));
break;
case PROP_CANCEL_LABEL:
g_value_set_string (value, prompt_get_string_property (self, "cancel-label", TRUE));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}
static void
gcr_system_prompt_constructed (GObject *obj)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
gint seed;
G_OBJECT_CLASS (gcr_system_prompt_parent_class)->constructed (obj);
seed = g_atomic_int_add (&unique_prompt_id, 1);
self->pv->prompt_path = g_strdup_printf ("%s/p%d", GCR_DBUS_PROMPT_OBJECT_PREFIX, seed);
if (self->pv->prompter_bus_name == NULL)
self->pv->prompter_bus_name = g_strdup (GCR_DBUS_PROMPTER_SYSTEM_BUS_NAME);
}
static void
on_prompter_stop_prompting (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSimpleAsyncResult *async = NULL;
GError *error = NULL;
GVariant *retval;
retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
if (error != NULL) {
g_debug ("failed to stop prompting: %s", egg_error_message (error));
g_clear_error (&error);
}
if (retval)
g_variant_unref (retval);
if (user_data) {
async = G_SIMPLE_ASYNC_RESULT (user_data);
g_simple_async_result_complete (async);
g_object_unref (async);
}
}
static void
perform_close (GcrSystemPrompt *self,
GSimpleAsyncResult *async,
GCancellable *cancellable)
{
GSimpleAsyncResult *res;
CallClosure *closure;
gboolean called = FALSE;
gboolean closed;
closed = self->pv->closed;
self->pv->closed = TRUE;
if (!closed)
g_debug ("closing prompt");
if (self->pv->pending) {
res = g_object_ref (self->pv->pending);
g_clear_object (&self->pv->pending);
closure = g_simple_async_result_get_op_res_gpointer (res);
g_cancellable_cancel (closure->cancellable);
g_simple_async_result_complete_in_idle (res);
g_object_unref (res);
}
if (self->pv->prompt_registered) {
g_dbus_connection_unregister_object (self->pv->connection,
self->pv->prompt_registered);
self->pv->prompt_registered = 0;
}
if (self->pv->begun_prompting) {
if (self->pv->connection && self->pv->prompt_path && self->pv->prompt_owner) {
g_debug ("Calling the prompter %s method", GCR_DBUS_PROMPTER_METHOD_STOP);
g_dbus_connection_call (self->pv->connection,
self->pv->prompter_bus_name,
GCR_DBUS_PROMPTER_OBJECT_PATH,
GCR_DBUS_PROMPTER_INTERFACE,
GCR_DBUS_PROMPTER_METHOD_STOP,
g_variant_new ("(o)", self->pv->prompt_path),
G_VARIANT_TYPE ("()"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, cancellable,
on_prompter_stop_prompting,
async ? g_object_ref (async) : NULL);
called = TRUE;
}
self->pv->begun_prompting = FALSE;
}
g_free (self->pv->prompt_path);
self->pv->prompt_path = NULL;
g_clear_object (&self->pv->connection);
if (!called && async)
g_simple_async_result_complete_in_idle (async);
/* Emit the signal if necessary, after closed */
if (!closed)
gcr_prompt_close (GCR_PROMPT (self));
}
static void
gcr_system_prompt_dispose (GObject *obj)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
g_clear_object (&self->pv->exchange);
perform_close (self, NULL, NULL);
g_hash_table_remove_all (self->pv->properties);
g_hash_table_remove_all (self->pv->dirty_properties);
G_OBJECT_CLASS (gcr_system_prompt_parent_class)->dispose (obj);
}
static void
gcr_system_prompt_finalize (GObject *obj)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (obj);
g_free (self->pv->prompter_bus_name);
g_free (self->pv->prompt_owner);
g_free (self->pv->last_response);
g_hash_table_destroy (self->pv->properties);
g_hash_table_destroy (self->pv->dirty_properties);
G_OBJECT_CLASS (gcr_system_prompt_parent_class)->finalize (obj);
}
static void
gcr_system_prompt_class_init (GcrSystemPromptClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructed = gcr_system_prompt_constructed;
gobject_class->get_property = gcr_system_prompt_get_property;
gobject_class->set_property = gcr_system_prompt_set_property;
gobject_class->dispose = gcr_system_prompt_dispose;
gobject_class->finalize = gcr_system_prompt_finalize;
g_type_class_add_private (gobject_class, sizeof (GcrSystemPromptPrivate));
/**
* GcrSystemPrompt:bus-name:
*
* The DBus bus name of the prompter to use for prompting, or %NULL
* for the default prompter.
*/
g_object_class_install_property (gobject_class, PROP_BUS_NAME,
g_param_spec_string ("bus-name", "Bus name", "Prompter bus name",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/**
* GcrSystemPrompt:timeout-seconds:
*
* The timeout in seconds to wait when opening the prompt.
*/
g_object_class_install_property (gobject_class, PROP_TIMEOUT_SECONDS,
g_param_spec_int ("timeout-seconds", "Timeout seconds", "Timeout (in seconds) for opening prompt",
-1, G_MAXINT, -1, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
/**
* GcrSystemPrompt:secret-exchange:
*
* The #GcrSecretExchange to use when transferring passwords. A default
* secret exchange will be used if this is not set.
*/
g_object_class_install_property (gobject_class, PROP_SECRET_EXCHANGE,
g_param_spec_object ("secret-exchange", "Secret exchange", "Secret exchange for passing passwords",
GCR_TYPE_SECRET_EXCHANGE, G_PARAM_READWRITE));
g_object_class_override_property (gobject_class, PROP_TITLE, "title");
g_object_class_override_property (gobject_class, PROP_MESSAGE, "message");
g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
g_object_class_override_property (gobject_class, PROP_WARNING, "warning");
g_object_class_override_property (gobject_class, PROP_PASSWORD_NEW, "password-new");
g_object_class_override_property (gobject_class, PROP_PASSWORD_STRENGTH, "password-strength");
g_object_class_override_property (gobject_class, PROP_CHOICE_LABEL, "choice-label");
g_object_class_override_property (gobject_class, PROP_CHOICE_CHOSEN, "choice-chosen");
g_object_class_override_property (gobject_class, PROP_CALLER_WINDOW, "caller-window");
g_object_class_override_property (gobject_class, PROP_CONTINUE_LABEL, "continue-label");
g_object_class_override_property (gobject_class, PROP_CANCEL_LABEL, "cancel-label");
}
/**
* gcr_system_prompt_get_secret_exchange:
* @self: a prompter
*
* Get the current #GcrSecretExchange used to transfer secrets in this prompt.
*
* Returns: (transfer none): the secret exchange
*/
GcrSecretExchange *
gcr_system_prompt_get_secret_exchange (GcrSystemPrompt *self)
{
g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), NULL);
if (!self->pv->exchange) {
g_debug ("creating new secret exchange");
self->pv->exchange = gcr_secret_exchange_new (NULL);
}
return self->pv->exchange;
}
static void
update_properties_from_iter (GcrSystemPrompt *self,
GVariantIter *iter)
{
GObject *obj = G_OBJECT (self);
GVariant *variant;
GVariant *value;
GVariant *already;
gpointer key;
gchar *name;
g_object_freeze_notify (obj);
while (g_variant_iter_loop (iter, "{sv}", &name, &variant)) {
key = (gpointer)g_intern_string (name);
value = g_variant_get_variant (variant);
already = g_hash_table_lookup (self->pv->properties, key);
if (!already || !g_variant_equal (already, value)) {
g_hash_table_replace (self->pv->properties, key, g_variant_ref (value));
g_object_notify (obj, name);
}
g_variant_unref (value);
}
g_object_thaw_notify (obj);
}
static void
prompt_method_ready (GcrSystemPrompt *self,
GDBusMethodInvocation *invocation,
GVariant *parameters)
{
GcrSecretExchange *exchange;
GSimpleAsyncResult *res;
GVariantIter *iter;
gchar *received;
g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (self->pv->pending));
g_free (self->pv->last_response);
g_variant_get (parameters, "(sa{sv}s)",
&self->pv->last_response,
&iter, &received);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
update_properties_from_iter (self, iter);
g_variant_iter_free (iter);
exchange = gcr_system_prompt_get_secret_exchange (self);
if (gcr_secret_exchange_receive (exchange, received))
self->pv->received = TRUE;
else
g_warning ("received invalid secret exchange string");
g_free (received);
res = g_object_ref (self->pv->pending);
g_clear_object (&self->pv->pending);
g_simple_async_result_complete (res);
g_object_unref (res);
}
static void
prompt_method_done (GcrSystemPrompt *self,
GDBusMethodInvocation *invocation,
GVariant *parameters)
{
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
/*
* At this point we're done prompting, and calling StopPrompting
* on the prompter is no longer necessary. It may have already been
* called, or the prompter may have stopped on its own accord.
*/
self->pv->begun_prompting = FALSE;
perform_close (self, NULL, NULL);
}
static void
prompt_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (user_data);
g_return_if_fail (method_name != NULL);
if (g_str_equal (method_name, GCR_DBUS_CALLBACK_METHOD_READY)) {
prompt_method_ready (self, invocation, parameters);
} else if (g_str_equal (method_name, GCR_DBUS_CALLBACK_METHOD_DONE)) {
prompt_method_done (self, invocation, parameters);
} else {
g_return_if_reached ();
}
}
static GVariant *
prompt_get_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
g_return_val_if_reached (NULL);
}
static gboolean
prompt_set_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error,
gpointer user_data)
{
g_return_val_if_reached (FALSE);
}
static GDBusInterfaceVTable prompt_dbus_vtable = {
prompt_method_call,
prompt_get_property,
prompt_set_property,
};
static void
register_prompt_object (GcrSystemPrompt *self,
GError **error)
{
GError *lerror = NULL;
guint id;
/*
* The unregister/reregister allows us to register this under a whatever
* GMainContext has been pushed as the thread default context.
*/
if (self->pv->prompt_registered)
g_dbus_connection_unregister_object (self->pv->connection,
self->pv->prompt_registered);
id = g_dbus_connection_register_object (self->pv->connection,
self->pv->prompt_path,
_gcr_dbus_prompter_callback_interface_info (),
&prompt_dbus_vtable,
self, NULL, &lerror);
self->pv->prompt_registered = id;
if (lerror != NULL) {
g_warning ("error registering prompter %s", egg_error_message (lerror));
g_propagate_error (error, lerror);
}
}
static void
on_prompter_present (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
g_free (self->pv->prompt_owner);
self->pv->prompt_owner = g_strdup (name_owner);
g_object_unref (self);
}
static void
on_prompter_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
CallClosure *call = g_simple_async_result_get_op_res_gpointer (user_data);
if (self->pv->prompt_owner) {
g_free (self->pv->prompt_owner);
self->pv->prompt_owner = NULL;
g_debug ("prompter name owner has vanished: %s", name);
g_cancellable_cancel (call->cancellable);
}
g_object_unref (self);
}
static void
on_bus_connected (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
CallClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
GError *error = NULL;
g_assert (self->pv->connection == NULL);
self->pv->connection = g_bus_get_finish (result, &error);
if (error != NULL) {
g_debug ("failed to connect to bus: %s", egg_error_message (error));
} else {
g_return_if_fail (self->pv->connection != NULL);
g_debug ("connected to bus");
g_main_context_push_thread_default (closure->context);
closure->watch_id = g_bus_watch_name_on_connection (self->pv->connection,
self->pv->prompter_bus_name,
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_prompter_present,
on_prompter_vanished,
res, NULL);
register_prompt_object (self, &error);
g_main_context_pop_thread_default (closure->context);
}
if (error == NULL) {
perform_init_async (self, res);
} else {
g_simple_async_result_take_error (res, error);
g_simple_async_result_complete (res);
}
g_object_unref (self);
g_object_unref (res);
}
static void
on_prompter_begin_prompting (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
GError *error = NULL;
GVariant *ret;
ret = g_dbus_connection_call_finish (self->pv->connection, result, &error);
if (error == NULL) {
self->pv->begun_prompting = TRUE;
g_variant_unref (ret);
g_debug ("registered prompt %s: %s",
self->pv->prompter_bus_name, self->pv->prompt_path);
g_return_if_fail (self->pv->prompt_path != NULL);
perform_init_async (self, res);
} else {
g_debug ("failed to register prompt %s: %s",
self->pv->prompter_bus_name, egg_error_message (error));
g_simple_async_result_take_error (res, error);
g_simple_async_result_complete (res);
}
g_object_unref (self);
g_object_unref (res);
}
static gboolean
on_call_timeout (gpointer user_data)
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
CallClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
g_source_destroy (closure->timeout);
g_source_unref (closure->timeout);
closure->timeout = NULL;
/* Tell the prompter we're no longer interested */
gcr_system_prompt_close_async (self, NULL, NULL, NULL);
g_simple_async_result_set_error (res, GCR_SYSTEM_PROMPT_ERROR,
GCR_SYSTEM_PROMPT_IN_PROGRESS,
_("Another prompt is already in progress"));
g_simple_async_result_complete (res);
g_object_unref (self);
return FALSE; /* Don't call this function again */
}
static gboolean
on_call_cancelled (GCancellable *cancellable,
gpointer user_data)
{
GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
CallClosure *call = g_simple_async_result_get_op_res_gpointer (async);
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
g_source_destroy (call->waiting);
g_source_unref (call->waiting);
call->waiting = NULL;
g_simple_async_result_set_error (async, G_IO_ERROR, G_IO_ERROR_CANCELLED,
_("The operation was cancelled"));
/* Tell the prompter we're no longer interested */
gcr_system_prompt_close_async (self, NULL, NULL, NULL);
g_object_unref (self);
return FALSE; /* Don't call this function again */
}
void
perform_init_async (GcrSystemPrompt *self,
GSimpleAsyncResult *res)
{
CallClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
g_main_context_push_thread_default (closure->context);
/* 1. Connect to the session bus */
if (!self->pv->connection) {
g_debug ("connecting to bus");
g_bus_get (G_BUS_TYPE_SESSION, closure->cancellable,
on_bus_connected, g_object_ref (res));
/* 2. Export our object, BeginPrompting on prompter */
} else if (!self->pv->begun_prompting) {
g_assert (self->pv->prompt_path != NULL);
g_debug ("calling %s method on prompter", GCR_DBUS_PROMPTER_METHOD_BEGIN);
g_dbus_connection_call (self->pv->connection,
self->pv->prompter_bus_name,
GCR_DBUS_PROMPTER_OBJECT_PATH,
GCR_DBUS_PROMPTER_INTERFACE,
GCR_DBUS_PROMPTER_METHOD_BEGIN,
g_variant_new ("(o)", self->pv->prompt_path),
G_VARIANT_TYPE ("()"),
G_DBUS_CALL_FLAGS_NONE,
-1, closure->cancellable,
on_prompter_begin_prompting,
g_object_ref (res));
/* 3. Wait for iterate */
} else if (!self->pv->pending) {
self->pv->pending = g_object_ref (res);
if (self->pv->timeout_seconds > 0) {
g_assert (closure->timeout == NULL);
closure->timeout = g_timeout_source_new_seconds (self->pv->timeout_seconds);
g_source_set_callback (closure->timeout, on_call_timeout, res, NULL);
g_source_attach (closure->timeout, closure->context);
}
g_assert (closure->waiting == NULL);
closure->waiting = g_cancellable_source_new (closure->cancellable);
g_source_set_callback (closure->waiting, (GSourceFunc)on_call_cancelled, res, NULL);
g_source_attach (closure->waiting, closure->context);
/* 4. All done */
} else {
g_assert_not_reached ();
}
g_main_context_pop_thread_default (closure->context);
}
static void
gcr_system_prompt_real_init_async (GAsyncInitable *initable,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (initable);
GSimpleAsyncResult *res;
CallClosure *closure;
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
gcr_system_prompt_real_init_async);
closure = call_closure_new (cancellable);
closure->context = g_main_context_get_thread_default ();
if (closure->context)
g_main_context_ref (closure->context);
g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
perform_init_async (self, res);
g_object_unref (res);
}
static gboolean
gcr_system_prompt_real_init_finish (GAsyncInitable *initable,
GAsyncResult *result,
GError **error)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (initable);
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
gcr_system_prompt_real_init_async), FALSE);
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
return FALSE;
return TRUE;
}
static void
gcr_system_prompt_async_initable_iface (GAsyncInitableIface *iface)
{
iface->init_async = gcr_system_prompt_real_init_async;
iface->init_finish = gcr_system_prompt_real_init_finish;
}
typedef struct {
GAsyncResult *result;
GMainContext *context;
GMainLoop *loop;
} SyncClosure;
static SyncClosure *
sync_closure_new (void)
{
SyncClosure *closure;
closure = g_new0 (SyncClosure, 1);
closure->context = g_main_context_new ();
closure->loop = g_main_loop_new (closure->context, FALSE);
return closure;
}
static void
sync_closure_free (gpointer data)
{
SyncClosure *closure = data;
g_clear_object (&closure->result);
g_main_loop_unref (closure->loop);
g_main_context_unref (closure->context);
g_free (closure);
}
static void
on_sync_result (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
SyncClosure *closure = user_data;
closure->result = g_object_ref (result);
g_main_loop_quit (closure->loop);
}
static gboolean
gcr_system_prompt_real_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
SyncClosure *closure;
gboolean result;
closure = sync_closure_new ();
g_main_context_push_thread_default (closure->context);
gcr_system_prompt_real_init_async (G_ASYNC_INITABLE (initable),
G_PRIORITY_DEFAULT, cancellable,
on_sync_result, closure);
g_main_loop_run (closure->loop);
result = gcr_system_prompt_real_init_finish (G_ASYNC_INITABLE (initable),
closure->result, error);
g_main_context_pop_thread_default (closure->context);
sync_closure_free (closure);
return result;
}
static void
gcr_system_prompt_initable_iface (GInitableIface *iface)
{
iface->init = gcr_system_prompt_real_init;
}
static GVariantBuilder *
build_dirty_properties (GcrSystemPrompt *self)
{
GVariantBuilder *builder;
GHashTableIter iter;
GVariant *variant;
gpointer key;
builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
g_hash_table_iter_init (&iter, self->pv->dirty_properties);
while (g_hash_table_iter_next (&iter, &key, NULL)) {
variant = g_hash_table_lookup (self->pv->properties, key);
g_variant_builder_add (builder, "{sv}", (const gchar *)key, variant);
}
g_hash_table_remove_all (self->pv->dirty_properties);
return builder;
}
static void
on_perform_prompt_complete (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (g_async_result_get_source_object (user_data));
CallClosure *call = g_simple_async_result_get_op_res_gpointer (res);
GError *error = NULL;
GVariant *retval;
retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
if (error != NULL) {
self->pv->pending = NULL;
g_simple_async_result_take_error (res, error);
g_simple_async_result_complete (res);
} else {
g_assert (call->waiting == NULL);
call->waiting = g_cancellable_source_new (call->cancellable);
g_source_set_callback (call->waiting, (GSourceFunc)on_call_cancelled, res, NULL);
g_source_attach (call->waiting, call->context);
}
if (retval)
g_variant_unref (retval);
g_object_unref (self);
g_object_unref (res);
}
static void
perform_prompt_async (GcrSystemPrompt *self,
const gchar *type,
gpointer source_tag,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *res;
GcrSecretExchange *exchange;
GVariantBuilder *builder;
CallClosure *closure;
gchar *sent;
g_return_if_fail (GCR_IS_SYSTEM_PROMPT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
if (self->pv->pending != NULL) {
g_warning ("another operation is already pending on this prompt");
return;
}
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, source_tag);
closure = call_closure_new (cancellable);
g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
if (self->pv->closed) {
g_free (self->pv->last_response);
self->pv->last_response = g_strdup (GCR_DBUS_PROMPT_REPLY_NONE);
g_simple_async_result_complete_in_idle (res);
g_object_unref (res);
return;
}
g_debug ("prompting for password");
exchange = gcr_system_prompt_get_secret_exchange (self);
if (self->pv->received)
sent = gcr_secret_exchange_send (exchange, NULL, 0);
else
sent = gcr_secret_exchange_begin (exchange);
closure->watch_id = g_bus_watch_name_on_connection (self->pv->connection,
self->pv->prompter_bus_name,
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_prompter_present,
on_prompter_vanished,
res, NULL);
builder = build_dirty_properties (self);
/* Reregister the prompt object in the current GMainContext */
register_prompt_object (self, NULL);
g_dbus_connection_call (self->pv->connection,
self->pv->prompter_bus_name,
GCR_DBUS_PROMPTER_OBJECT_PATH,
GCR_DBUS_PROMPTER_INTERFACE,
GCR_DBUS_PROMPTER_METHOD_PERFORM,
g_variant_new ("(osa{sv}s)", self->pv->prompt_path,
type, builder, sent),
G_VARIANT_TYPE ("()"),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, cancellable,
on_perform_prompt_complete,
g_object_ref (res));
g_variant_builder_unref(builder);
self->pv->pending = res;
g_free (sent);
}
static GcrPromptReply
handle_last_response (GcrSystemPrompt *self)
{
GcrPromptReply response;
g_return_val_if_fail (self->pv->last_response != NULL,
GCR_PROMPT_REPLY_CANCEL);
if (g_str_equal (self->pv->last_response, GCR_DBUS_PROMPT_REPLY_YES)) {
response = GCR_PROMPT_REPLY_CONTINUE;
} else if (g_str_equal (self->pv->last_response, GCR_DBUS_PROMPT_REPLY_NO) ||
g_str_equal (self->pv->last_response, GCR_DBUS_PROMPT_REPLY_NONE)) {
response = GCR_PROMPT_REPLY_CANCEL;
} else {
g_warning ("unknown response from prompter: %s", self->pv->last_response);
response = GCR_PROMPT_REPLY_CANCEL;
}
return response;
}
static void
gcr_system_prompt_password_async (GcrPrompt *prompt,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
perform_prompt_async (self, GCR_DBUS_PROMPT_TYPE_PASSWORD,
gcr_system_prompt_password_async,
cancellable, callback, user_data);
}
static const gchar *
gcr_system_prompt_password_finish (GcrPrompt *prompt,
GAsyncResult *result,
GError **error)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
GSimpleAsyncResult *res;
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
gcr_system_prompt_password_async), FALSE);
res = G_SIMPLE_ASYNC_RESULT (result);
if (g_simple_async_result_propagate_error (res, error))
return FALSE;
if (handle_last_response (self) == GCR_PROMPT_REPLY_CONTINUE)
return gcr_secret_exchange_get_secret (self->pv->exchange, NULL);
return NULL;
}
static void
gcr_system_prompt_confirm_async (GcrPrompt *prompt,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
perform_prompt_async (self, GCR_DBUS_PROMPT_TYPE_CONFIRM,
gcr_system_prompt_confirm_async,
cancellable, callback, user_data);
}
static GcrPromptReply
gcr_system_prompt_confirm_finish (GcrPrompt *prompt,
GAsyncResult *result,
GError **error)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
GSimpleAsyncResult *res;
g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
gcr_system_prompt_confirm_async), FALSE);
res = G_SIMPLE_ASYNC_RESULT (result);
if (g_simple_async_result_propagate_error (res, error))
return FALSE;
return handle_last_response (self);
}
static void
gcr_system_prompt_real_close (GcrPrompt *prompt)
{
GcrSystemPrompt *self = GCR_SYSTEM_PROMPT (prompt);
/*
* Setting this before calling close_async allows us to prevent firing
* this signal again in a loop.
*/
if (!self->pv->closed) {
self->pv->closed = TRUE;
perform_close (self, NULL, NULL);
}
}
static void
gcr_system_prompt_prompt_iface (GcrPromptIface *iface)
{
iface->prompt_password_async = gcr_system_prompt_password_async;
iface->prompt_password_finish = gcr_system_prompt_password_finish;
iface->prompt_confirm_async = gcr_system_prompt_confirm_async;
iface->prompt_confirm_finish = gcr_system_prompt_confirm_finish;
iface->prompt_close = gcr_system_prompt_real_close;
}
/**
* gcr_system_prompt_open_async:
* @timeout_seconds: the number of seconds to wait to access the prompt, or -1
* @cancellable: optional cancellation object
* @callback: called when the operation completes
* @user_data: data to pass the callback
*
* Asynchronously open a system prompt with the default system prompter.
*
* Most system prompters only allow showing one prompt at a time, and if
* another prompt is shown then this method will block for up to
* @timeout_seconds seconds. If @timeout_seconds is equal to -1, then this
* will block indefinitely until the prompt can be opened. If @timeout_seconds
* expires, then this operation will fail with a %GCR_SYSTEM_PROMPT_IN_PROGRESS
* error.
*/
void
gcr_system_prompt_open_async (gint timeout_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (timeout_seconds >= -1);
g_return_if_fail (cancellable == NULL || G_CANCELLABLE (cancellable));
gcr_system_prompt_open_for_prompter_async (NULL, timeout_seconds,
cancellable, callback,
user_data);
}
/**
* gcr_system_prompt_open_for_prompter_async:
* @prompter_name: (allow-none): the prompter dbus name
* @timeout_seconds: the number of seconds to wait to access the prompt, or -1
* @cancellable: optional cancellation object
* @callback: called when the operation completes
* @user_data: data to pass the callback
*
* Opens a system prompt asynchronously. If prompter_name is %NULL, then the
* default system prompter is used.
*
* Most system prompters only allow showing one prompt at a time, and if
* another prompt is shown then this method will block for up to
* @timeout_seconds seconds. If @timeout_seconds is equal to -1, then this
* will block indefinitely until the prompt can be opened. If @timeout_seconds
* expires, then this operation will fail with a %GCR_SYSTEM_PROMPT_IN_PROGRESS
* error.
*/
void
gcr_system_prompt_open_for_prompter_async (const gchar *prompter_name,
gint timeout_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (timeout_seconds >= -1);
g_return_if_fail (cancellable == NULL || G_CANCELLABLE (cancellable));
if (prompter_name == NULL)
g_debug ("opening prompt");
else
g_debug ("opening prompt for prompter: %s", prompter_name);
g_async_initable_new_async (GCR_TYPE_SYSTEM_PROMPT,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
"timeout-seconds", timeout_seconds,
"bus-name", prompter_name,
NULL);
}
/**
* gcr_system_prompt_open_finish:
* @result: the asynchronous result
* @error: location to place an error on failure
*
* Complete an operation to asynchronously open a system prompt.
*
* Returns: (transfer full) (type Gcr.SystemPrompt): the prompt, or %NULL if
* prompt could not be opened
*/
GcrPrompt *
gcr_system_prompt_open_finish (GAsyncResult *result,
GError **error)
{
GObject *object;
GObject *source_object;
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
source_object = g_async_result_get_source_object (result);
g_assert (source_object != NULL);
object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
result, error);
g_object_unref (source_object);
if (object != NULL)
return GCR_PROMPT (object);
else
return NULL;
}
/**
* gcr_system_prompt_open:
* @timeout_seconds: the number of seconds to wait to access the prompt, or -1
* @cancellable: optional cancellation object
* @error: location to place error on failure
*
* Opens a system prompt with the default prompter.
*
* Most system prompters only allow showing one prompt at a time, and if
* another prompt is shown then this method will block for up to
* @timeout_seconds seconds. If @timeout_seconds is equal to -1, then this
* will block indefinitely until the prompt can be opened. If @timeout_seconds
* expires, then this function will fail with a %GCR_SYSTEM_PROMPT_IN_PROGRESS
* error.
*
* Returns: (transfer full) (type Gcr.SystemPrompt): the prompt, or %NULL if
* prompt could not be opened
*/
GcrPrompt *
gcr_system_prompt_open (gint timeout_seconds,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (timeout_seconds >= -1, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return gcr_system_prompt_open_for_prompter (NULL, timeout_seconds,
cancellable, error);
}
/**
* gcr_system_prompt_open_for_prompter:
* @prompter_name: (allow-none): the prompter dbus name
* @timeout_seconds: the number of seconds to wait to access the prompt, or -1
* @cancellable: optional cancellation object
* @error: location to place error on failure
*
* Opens a system prompt. If prompter_name is %NULL, then the default
* system prompter is used.
*
* Most system prompters only allow showing one prompt at a time, and if
* another prompt is shown then this method will block for up to
* @timeout_seconds seconds. If @timeout_seconds is equal to -1, then this
* will block indefinitely until the prompt can be opened. If @timeout_seconds
* expires, then this function will fail with a %GCR_SYSTEM_PROMPT_IN_PROGRESS
* error.
*
* Returns: (transfer full) (type Gcr.SystemPrompt): the prompt, or %NULL if
* prompt could not be opened
*/
GcrPrompt *
gcr_system_prompt_open_for_prompter (const gchar *prompter_name,
gint timeout_seconds,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (timeout_seconds >= -1, NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (prompter_name == NULL)
g_debug ("opening prompt");
else
g_debug ("opening prompt for prompter: %s", prompter_name);
return g_initable_new (GCR_TYPE_SYSTEM_PROMPT, cancellable, error,
"timeout-seconds", timeout_seconds,
"bus-name", prompter_name,
NULL);
}
/**
* gcr_system_prompt_close:
* @self: the prompt
* @cancellable: an optional cancellation object
* @error: location to place an error on failure
*
* Close this prompt. After calling this function, no further prompts will
* succeed on this object. The prompt object is not unreferenced by this
* function, and you must unreference it once done.
*
* This call may block, use the gcr_system_prompt_close_async() to perform
* this action indefinitely.
*
* Whether or not this function returns %TRUE, the system prompt object is
* still closed and may not be further used.
*
* Returns: whether close was cleanly completed
*/
gboolean
gcr_system_prompt_close (GcrSystemPrompt *self,
GCancellable *cancellable,
GError **error)
{
SyncClosure *closure;
gboolean result;
closure = sync_closure_new ();
g_main_context_push_thread_default (closure->context);
gcr_system_prompt_close_async (self, cancellable,
on_sync_result, closure);
g_main_loop_run (closure->loop);
result = gcr_system_prompt_close_finish (self, closure->result, error);
g_main_context_pop_thread_default (closure->context);
sync_closure_free (closure);
return result;
}
/**
* gcr_system_prompt_close_async:
* @self: the prompt
* @cancellable: an optional cancellation object
* @callback: called when the operation completes
* @user_data: data to pass to the callback
*
* Close this prompt asynchronously. After calling this function, no further
* methods may be called on this object. The prompt object is not unreferenced
* by this function, and you must unreference it once done.
*
* This call returns immediately and completes asynchronously.
*/
void
gcr_system_prompt_close_async (GcrSystemPrompt *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *res;
CallClosure *closure;
g_return_if_fail (GCR_SYSTEM_PROMPT (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
res = g_simple_async_result_new (NULL, callback, user_data,
gcr_system_prompt_close_async);
closure = call_closure_new (cancellable);
closure->context = g_main_context_get_thread_default ();
if (closure->context != NULL)
g_main_context_ref (closure->context);
g_simple_async_result_set_op_res_gpointer (res, closure, call_closure_free);
perform_close (self, res, closure->cancellable);
g_object_unref (res);
}
/**
* gcr_system_prompt_close_finish:
* @self: the prompt
* @result: asynchronous operation result
* @error: location to place an error on failure
*
* Complete operation to close this prompt.
*
* Whether or not this function returns %TRUE, the system prompt object is
* still closed and may not be further used.
*
* Returns: whether close was cleanly completed
*/
gboolean
gcr_system_prompt_close_finish (GcrSystemPrompt *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GCR_IS_SYSTEM_PROMPT (self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
gcr_system_prompt_close_async), FALSE);
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
return FALSE;
return TRUE;
}
static const GDBusErrorEntry SYSTEM_PROMPT_ERRORS[] = {
{ GCR_SYSTEM_PROMPT_IN_PROGRESS, GCR_DBUS_PROMPT_ERROR_IN_PROGRESS },
};
GQuark
gcr_system_prompt_error_get_domain (void)
{
static volatile gsize quark_volatile = 0;
g_dbus_error_register_error_domain ("gcr-system-prompt-error-domain",
&quark_volatile,
SYSTEM_PROMPT_ERRORS,
G_N_ELEMENTS (SYSTEM_PROMPT_ERRORS));
G_STATIC_ASSERT (G_N_ELEMENTS (SYSTEM_PROMPT_ERRORS) == GCR_SYSTEM_PROMPT_IN_PROGRESS);
return (GQuark) quark_volatile;
}