summaryrefslogtreecommitdiffstats
path: root/plugins/smartcard/gsd-smartcard-service.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/smartcard/gsd-smartcard-service.c')
-rw-r--r--plugins/smartcard/gsd-smartcard-service.c849
1 files changed, 849 insertions, 0 deletions
diff --git a/plugins/smartcard/gsd-smartcard-service.c b/plugins/smartcard/gsd-smartcard-service.c
new file mode 100644
index 0000000..4d529c3
--- /dev/null
+++ b/plugins/smartcard/gsd-smartcard-service.c
@@ -0,0 +1,849 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ray Strode
+ */
+
+#include "config.h"
+
+#include "gsd-smartcard-service.h"
+#include "org.gnome.SettingsDaemon.Smartcard.h"
+#include "gsd-smartcard-manager.h"
+#include "gsd-smartcard-enum-types.h"
+#include "gsd-smartcard-utils.h"
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+struct _GsdSmartcardService
+{
+ GsdSmartcardServiceManagerSkeleton parent;
+
+ GDBusConnection *bus_connection;
+ GDBusObjectManagerServer *object_manager_server;
+ GsdSmartcardManager *smartcard_manager;
+ GCancellable *cancellable;
+ GHashTable *tokens;
+
+ gboolean login_token_bound;
+ guint name_id;
+};
+
+#define GSD_DBUS_NAME "org.gnome.SettingsDaemon"
+#define GSD_DBUS_PATH "/org/gnome/SettingsDaemon"
+#define GSD_DBUS_BASE_INTERFACE "org.gnome.SettingsDaemon"
+
+#define GSD_SMARTCARD_DBUS_NAME GSD_DBUS_NAME ".Smartcard"
+#define GSD_SMARTCARD_DBUS_PATH GSD_DBUS_PATH "/Smartcard"
+#define GSD_SMARTCARD_MANAGER_DBUS_PATH GSD_SMARTCARD_DBUS_PATH "/Manager"
+#define GSD_SMARTCARD_MANAGER_DRIVERS_DBUS_PATH GSD_SMARTCARD_MANAGER_DBUS_PATH "/Drivers"
+#define GSD_SMARTCARD_MANAGER_TOKENS_DBUS_PATH GSD_SMARTCARD_MANAGER_DBUS_PATH "/Tokens"
+
+enum {
+ PROP_0,
+ PROP_MANAGER,
+ PROP_BUS_CONNECTION
+};
+
+static void gsd_smartcard_service_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *param_spec);
+static void gsd_smartcard_service_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *param_spec);
+static void async_initable_interface_init (GAsyncInitableIface *interface);
+static void smartcard_service_manager_interface_init (GsdSmartcardServiceManagerIface *interface);
+
+G_LOCK_DEFINE_STATIC (gsd_smartcard_tokens);
+
+G_DEFINE_TYPE_WITH_CODE (GsdSmartcardService,
+ gsd_smartcard_service,
+ GSD_SMARTCARD_SERVICE_TYPE_MANAGER_SKELETON,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+ async_initable_interface_init)
+ G_IMPLEMENT_INTERFACE (GSD_SMARTCARD_SERVICE_TYPE_MANAGER,
+ smartcard_service_manager_interface_init));
+
+static void
+set_bus_connection (GsdSmartcardService *self,
+ GDBusConnection *connection)
+{
+ if (self->bus_connection != connection) {
+ g_clear_object (&self->bus_connection);
+ self->bus_connection = g_object_ref (connection);
+ g_object_notify (G_OBJECT (self), "bus-connection");
+ }
+}
+
+static void
+register_object_manager (GsdSmartcardService *self)
+{
+ GsdSmartcardServiceObjectSkeleton *object;
+
+ self->object_manager_server = g_dbus_object_manager_server_new (GSD_SMARTCARD_DBUS_PATH);
+
+ object = gsd_smartcard_service_object_skeleton_new (GSD_SMARTCARD_MANAGER_DBUS_PATH);
+ gsd_smartcard_service_object_skeleton_set_manager (object,
+ GSD_SMARTCARD_SERVICE_MANAGER (self));
+
+ g_dbus_object_manager_server_export (self->object_manager_server,
+ G_DBUS_OBJECT_SKELETON (object));
+ g_object_unref (object);
+
+ g_dbus_object_manager_server_set_connection (self->object_manager_server,
+ self->bus_connection);
+}
+
+static const char *
+get_login_token_object_path (GsdSmartcardService *self)
+{
+ return GSD_SMARTCARD_MANAGER_TOKENS_DBUS_PATH "/login_token";
+}
+
+static void
+register_login_token_alias (GsdSmartcardService *self)
+{
+ GDBusObjectSkeleton *object;
+ GDBusInterfaceSkeleton *interface;
+ const char *object_path;
+ const char *token_name;
+
+ token_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME");
+
+ if (token_name == NULL)
+ return;
+
+ object_path = get_login_token_object_path (self);
+ object = G_DBUS_OBJECT_SKELETON (gsd_smartcard_service_object_skeleton_new (object_path));
+ interface = G_DBUS_INTERFACE_SKELETON (gsd_smartcard_service_token_skeleton_new ());
+
+ g_dbus_object_skeleton_add_interface (object, interface);
+ g_object_unref (interface);
+
+ g_object_set (G_OBJECT (interface),
+ "name", token_name,
+ "used-to-login", TRUE,
+ "is-inserted", FALSE,
+ NULL);
+
+ g_dbus_object_manager_server_export (self->object_manager_server,
+ object);
+
+ G_LOCK (gsd_smartcard_tokens);
+ g_hash_table_insert (self->tokens, g_strdup (object_path), interface);
+ G_UNLOCK (gsd_smartcard_tokens);
+}
+
+static void
+on_bus_gotten (GObject *source_object,
+ GAsyncResult *result,
+ GTask *task)
+{
+ GsdSmartcardService *self;
+ GDBusConnection *connection;
+ GError *error = NULL;
+
+ connection = g_bus_get_finish (result, &error);
+ if (connection == NULL) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ g_debug ("taking name %s on session bus", GSD_SMARTCARD_DBUS_NAME);
+
+ self = g_task_get_source_object (task);
+
+ set_bus_connection (self, connection);
+
+ register_object_manager (self);
+ self->name_id = g_bus_own_name_on_connection (connection,
+ GSD_SMARTCARD_DBUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ /* In case the login token is removed at start up, register an
+ * an alias interface that's always around
+ */
+ register_login_token_alias (self);
+ g_task_return_boolean (task, TRUE);
+
+out:
+ g_object_unref (task);
+ return;
+}
+
+static gboolean
+gsd_smartcard_service_initable_init_finish (GAsyncInitable *initable,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task;
+
+ task = G_TASK (result);
+
+ return g_task_propagate_boolean (task, error);
+}
+
+static void
+gsd_smartcard_service_initable_init_async (GAsyncInitable *initable,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (initable);
+ GTask *task;
+
+ task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
+ g_task_set_priority (task, io_priority);
+
+ g_bus_get (G_BUS_TYPE_SESSION, cancellable, (GAsyncReadyCallback) on_bus_gotten, task);
+}
+
+static void
+async_initable_interface_init (GAsyncInitableIface *interface)
+{
+ interface->init_async = gsd_smartcard_service_initable_init_async;
+ interface->init_finish = gsd_smartcard_service_initable_init_finish;
+}
+
+static char *
+get_object_path_for_token (GsdSmartcardService *self,
+ PK11SlotInfo *card_slot)
+{
+ char *object_path;
+ char *escaped_library_path;
+ SECMODModule *driver;
+ CK_SLOT_ID slot_id;
+
+ driver = PK11_GetModule (card_slot);
+ slot_id = PK11_GetSlotID (card_slot);
+
+ escaped_library_path = gsd_smartcard_utils_escape_object_path (driver->dllName);
+
+ object_path = g_strdup_printf ("%s/token_from_%s_slot_%lu",
+ GSD_SMARTCARD_MANAGER_TOKENS_DBUS_PATH,
+ escaped_library_path,
+ (gulong) slot_id);
+ g_free (escaped_library_path);
+
+ return object_path;
+}
+
+static gboolean
+gsd_smartcard_service_handle_get_login_token (GsdSmartcardServiceManager *manager,
+ GDBusMethodInvocation *invocation)
+{
+ GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (manager);
+ PK11SlotInfo *card_slot;
+ char *object_path;
+
+ card_slot = gsd_smartcard_manager_get_login_token (self->smartcard_manager);
+
+ if (card_slot == NULL) {
+ const char *login_token_object_path;
+
+ /* If we know there's a login token but it was removed before the
+ * smartcard manager could examine it, just return the generic login
+ * token object path
+ */
+ login_token_object_path = get_login_token_object_path (self);
+
+ if (g_hash_table_contains (self->tokens, login_token_object_path)) {
+ gsd_smartcard_service_manager_complete_get_login_token (manager,
+ invocation,
+ login_token_object_path);
+ return TRUE;
+ }
+
+ g_dbus_method_invocation_return_error (invocation,
+ GSD_SMARTCARD_MANAGER_ERROR,
+ GSD_SMARTCARD_MANAGER_ERROR_FINDING_SMARTCARD,
+ _("User was not logged in with smartcard."));
+
+ return TRUE;
+ }
+
+ object_path = get_object_path_for_token (self, card_slot);
+ gsd_smartcard_service_manager_complete_get_login_token (manager,
+ invocation,
+ object_path);
+ g_free (object_path);
+
+ return TRUE;
+}
+
+static gboolean
+gsd_smartcard_service_handle_get_inserted_tokens (GsdSmartcardServiceManager *manager,
+ GDBusMethodInvocation *invocation)
+{
+ GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (manager);
+ GList *inserted_tokens, *node;
+ GPtrArray *object_paths;
+
+ inserted_tokens = gsd_smartcard_manager_get_inserted_tokens (self->smartcard_manager,
+ NULL);
+
+ object_paths = g_ptr_array_new ();
+ for (node = inserted_tokens; node != NULL; node = node->next) {
+ PK11SlotInfo *card_slot = node->data;
+ char *object_path;
+
+ object_path = get_object_path_for_token (self, card_slot);
+ g_ptr_array_add (object_paths, object_path);
+ }
+ g_ptr_array_add (object_paths, NULL);
+ g_list_free (inserted_tokens);
+
+ gsd_smartcard_service_manager_complete_get_inserted_tokens (manager,
+ invocation,
+ (const char * const *) object_paths->pdata);
+
+ g_ptr_array_free (object_paths, TRUE);
+
+ return TRUE;
+}
+
+static void
+smartcard_service_manager_interface_init (GsdSmartcardServiceManagerIface *interface)
+{
+ interface->handle_get_login_token = gsd_smartcard_service_handle_get_login_token;
+ interface->handle_get_inserted_tokens = gsd_smartcard_service_handle_get_inserted_tokens;
+}
+
+static void
+gsd_smartcard_service_init (GsdSmartcardService *self)
+{
+ self->tokens = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ NULL);
+}
+
+static void
+gsd_smartcard_service_dispose (GObject *object)
+{
+ GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (object);
+
+ g_clear_object (&self->bus_connection);
+ g_clear_object (&self->object_manager_server);
+ g_clear_object (&self->smartcard_manager);
+
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+ g_clear_pointer (&self->tokens, g_hash_table_unref);
+
+ G_OBJECT_CLASS (gsd_smartcard_service_parent_class)->dispose (object);
+}
+
+static void
+gsd_smartcard_service_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *param_spec)
+{
+ GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (object);
+
+ switch (property_id) {
+ case PROP_MANAGER:
+ self->smartcard_manager = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
+ break;
+ }
+}
+
+static void
+gsd_smartcard_service_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *param_spec)
+{
+ GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (object);
+
+ switch (property_id) {
+ case PROP_MANAGER:
+ g_value_set_object (value, self->smartcard_manager);
+ break;
+ case PROP_BUS_CONNECTION:
+ g_value_set_object (value, self->bus_connection);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec);
+ break;
+ }
+}
+
+static void
+gsd_smartcard_service_class_init (GsdSmartcardServiceClass *service_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (service_class);
+ GParamSpec *param_spec;
+
+ object_class->dispose = gsd_smartcard_service_dispose;
+ object_class->set_property = gsd_smartcard_service_set_property;
+ object_class->get_property = gsd_smartcard_service_get_property;
+
+ param_spec = g_param_spec_object ("manager",
+ "Smartcard Manager",
+ "Smartcard Manager",
+ GSD_TYPE_SMARTCARD_MANAGER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_MANAGER, param_spec);
+ param_spec = g_param_spec_object ("bus-connection",
+ "Bus Connection",
+ "bus connection",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_BUS_CONNECTION, param_spec);
+}
+
+static void
+on_new_async_finished (GObject *source_object,
+ GAsyncResult *result,
+ GTask *task)
+{
+ GError *error = NULL;
+ GObject *object;
+
+ object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+ result,
+ &error);
+
+ if (object == NULL) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ g_assert (GSD_IS_SMARTCARD_SERVICE (object));
+
+ g_task_return_pointer (task, object, g_object_unref);
+out:
+ g_object_unref (task);
+ return;
+}
+
+void
+gsd_smartcard_service_new_async (GsdSmartcardManager *manager,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (NULL, cancellable, callback, user_data);
+
+ g_async_initable_new_async (GSD_TYPE_SMARTCARD_SERVICE,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ (GAsyncReadyCallback) on_new_async_finished,
+ task,
+ "manager", manager,
+ NULL);
+}
+
+GsdSmartcardService *
+gsd_smartcard_service_new_finish (GAsyncResult *result,
+ GError **error)
+{
+ GTask *task;
+ GsdSmartcardService *self = NULL;
+
+ task = G_TASK (result);
+
+ self = g_task_propagate_pointer (task, error);
+
+ if (self == NULL)
+ return self;
+
+ return g_object_ref (self);
+}
+
+static char *
+get_object_path_for_driver (GsdSmartcardService *self,
+ SECMODModule *driver)
+{
+ char *object_path;
+ char *escaped_library_path;
+
+ escaped_library_path = gsd_smartcard_utils_escape_object_path (driver->dllName);
+
+ object_path = g_build_path ("/",
+ GSD_SMARTCARD_MANAGER_DRIVERS_DBUS_PATH,
+ escaped_library_path, NULL);
+ g_free (escaped_library_path);
+
+ return object_path;
+}
+
+void
+gsd_smartcard_service_register_driver (GsdSmartcardService *self,
+ SECMODModule *driver)
+{
+ char *object_path;
+ GDBusObjectSkeleton *object;
+ GDBusInterfaceSkeleton *interface;
+
+ object_path = get_object_path_for_driver (self, driver);
+ object = G_DBUS_OBJECT_SKELETON (gsd_smartcard_service_object_skeleton_new (object_path));
+ g_free (object_path);
+
+ interface = G_DBUS_INTERFACE_SKELETON (gsd_smartcard_service_driver_skeleton_new ());
+ g_dbus_object_skeleton_add_interface (object, interface);
+ g_object_unref (interface);
+
+ g_object_set (G_OBJECT (interface),
+ "library", driver->dllName,
+ "description", driver->commonName,
+ NULL);
+ g_dbus_object_manager_server_export (self->object_manager_server,
+ object);
+ g_object_unref (object);
+}
+
+static void
+synchronize_token_now (GsdSmartcardService *self,
+ PK11SlotInfo *card_slot)
+{
+ GDBusInterfaceSkeleton *interface;
+ char *object_path;
+ const char *token_name;
+ gboolean is_present, is_login_card;
+
+ object_path = get_object_path_for_token (self, card_slot);
+
+ G_LOCK (gsd_smartcard_tokens);
+ interface = g_hash_table_lookup (self->tokens, object_path);
+ g_free (object_path);
+
+ if (interface == NULL)
+ goto out;
+
+ token_name = PK11_GetTokenName (card_slot);
+ is_present = PK11_IsPresent (card_slot);
+
+ if (g_strcmp0 (g_getenv ("PKCS11_LOGIN_TOKEN_NAME"), token_name) == 0)
+ is_login_card = TRUE;
+ else
+ is_login_card = FALSE;
+
+ g_debug ("===============================");
+ g_debug (" Token '%s'", token_name);
+ g_debug (" Inserted: %s", is_present? "yes" : "no");
+ g_debug (" Previously used to login: %s", is_login_card? "yes" : "no");
+ g_debug ("===============================\n");
+
+ if (!is_present && is_login_card) {
+ gboolean was_present;
+
+ g_object_get (G_OBJECT (interface),
+ "is-inserted", &was_present,
+ NULL);
+
+ if (was_present)
+ gsd_smartcard_manager_do_remove_action (self->smartcard_manager);
+ }
+
+ g_object_set (G_OBJECT (interface),
+ "used-to-login", is_login_card,
+ "is-inserted", is_present,
+ NULL);
+ g_object_get (G_OBJECT (interface),
+ "used-to-login", &is_login_card,
+ "is-inserted", &is_present,
+ NULL);
+
+ if (is_login_card && !self->login_token_bound) {
+ const char *login_token_path;
+ GDBusInterfaceSkeleton *login_token_interface;
+
+ login_token_path = get_login_token_object_path (self);
+ login_token_interface = g_hash_table_lookup (self->tokens, login_token_path);
+
+ if (login_token_interface != NULL) {
+ g_object_bind_property (interface, "driver",
+ login_token_interface, "driver",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (interface, "is-inserted",
+ login_token_interface, "is-inserted",
+ G_BINDING_SYNC_CREATE);
+ self->login_token_bound = TRUE;
+ }
+ }
+
+out:
+ G_UNLOCK (gsd_smartcard_tokens);
+}
+
+typedef struct
+{
+ PK11SlotInfo *card_slot;
+ char *object_path;
+ GSource *main_thread_source;
+} RegisterNewTokenOperation;
+
+static void
+destroy_register_new_token_operation (RegisterNewTokenOperation *operation)
+{
+ g_clear_pointer (&operation->main_thread_source,
+ g_source_destroy);
+ PK11_FreeSlot (operation->card_slot);
+ g_free (operation->object_path);
+ g_free (operation);
+}
+
+static gboolean
+on_main_thread_to_register_new_token (GTask *task)
+{
+ GsdSmartcardService *self;
+ GDBusObjectSkeleton *object;
+ GDBusInterfaceSkeleton *interface;
+ RegisterNewTokenOperation *operation;
+ SECMODModule *driver;
+ char *driver_object_path;
+ const char *token_name;
+
+ self = g_task_get_source_object (task);
+
+ operation = g_task_get_task_data (task);
+ operation->main_thread_source = NULL;
+
+ object = G_DBUS_OBJECT_SKELETON (gsd_smartcard_service_object_skeleton_new (operation->object_path));
+ interface = G_DBUS_INTERFACE_SKELETON (gsd_smartcard_service_token_skeleton_new ());
+
+ g_dbus_object_skeleton_add_interface (object, interface);
+ g_object_unref (interface);
+
+ driver = PK11_GetModule (operation->card_slot);
+ driver_object_path = get_object_path_for_driver (self, driver);
+
+ token_name = PK11_GetTokenName (operation->card_slot);
+
+ g_object_set (G_OBJECT (interface),
+ "driver", driver_object_path,
+ "name", token_name,
+ NULL);
+ g_free (driver_object_path);
+
+ g_dbus_object_manager_server_export (self->object_manager_server,
+ object);
+
+ G_LOCK (gsd_smartcard_tokens);
+ g_hash_table_insert (self->tokens, g_strdup (operation->object_path), interface);
+ G_UNLOCK (gsd_smartcard_tokens);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+create_main_thread_source (GSourceFunc callback,
+ gpointer user_data,
+ GSource **source_out)
+{
+ GSource *source;
+
+ source = g_idle_source_new ();
+ g_source_set_callback (source, callback, user_data, NULL);
+
+ *source_out = source;
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+}
+
+static void
+register_new_token_in_main_thread (GsdSmartcardService *self,
+ PK11SlotInfo *card_slot,
+ char *object_path,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ RegisterNewTokenOperation *operation;
+ GTask *task;
+
+ operation = g_new0 (RegisterNewTokenOperation, 1);
+ operation->card_slot = PK11_ReferenceSlot (card_slot);
+ operation->object_path = g_strdup (object_path);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ g_task_set_task_data (task,
+ operation,
+ (GDestroyNotify) destroy_register_new_token_operation);
+
+ create_main_thread_source ((GSourceFunc) on_main_thread_to_register_new_token,
+ task,
+ &operation->main_thread_source);
+}
+
+static gboolean
+register_new_token_in_main_thread_finish (GsdSmartcardService *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+on_token_registered (GsdSmartcardService *self,
+ GAsyncResult *result,
+ PK11SlotInfo *card_slot)
+{
+ gboolean registered;
+ GError *error = NULL;
+
+ registered = register_new_token_in_main_thread_finish (self, result, &error);
+
+ if (!registered) {
+ g_debug ("Couldn't register token: %s",
+ error->message);
+ goto out;
+ }
+
+ synchronize_token_now (self, card_slot);
+
+out:
+ PK11_FreeSlot (card_slot);
+}
+
+typedef struct
+{
+ PK11SlotInfo *card_slot;
+ GSource *main_thread_source;
+} SynchronizeTokenOperation;
+
+static void
+destroy_synchronize_token_operation (SynchronizeTokenOperation *operation)
+{
+ g_clear_pointer (&operation->main_thread_source,
+ g_source_destroy);
+ PK11_FreeSlot (operation->card_slot);
+ g_free (operation);
+}
+
+static gboolean
+on_main_thread_to_synchronize_token (GTask *task)
+{
+ GsdSmartcardService *self;
+ SynchronizeTokenOperation *operation;
+
+ self = g_task_get_source_object (task);
+
+ operation = g_task_get_task_data (task);
+ operation->main_thread_source = NULL;
+
+ synchronize_token_now (self, operation->card_slot);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+synchronize_token_in_main_thread_finish (GsdSmartcardService *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+synchronize_token_in_main_thread (GsdSmartcardService *self,
+ PK11SlotInfo *card_slot,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SynchronizeTokenOperation *operation;
+ GTask *task;
+
+ operation = g_new0 (SynchronizeTokenOperation, 1);
+ operation->card_slot = PK11_ReferenceSlot (card_slot);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ g_task_set_task_data (task,
+ operation,
+ (GDestroyNotify)
+ destroy_synchronize_token_operation);
+
+ create_main_thread_source ((GSourceFunc)
+ on_main_thread_to_synchronize_token,
+ task,
+ &operation->main_thread_source);
+}
+
+static void
+on_token_synchronized (GsdSmartcardService *self,
+ GAsyncResult *result,
+ PK11SlotInfo *card_slot)
+{
+ gboolean synchronized;
+ GError *error = NULL;
+
+ synchronized = synchronize_token_in_main_thread_finish (self, result, &error);
+
+ if (!synchronized)
+ g_debug ("Couldn't synchronize token: %s", error->message);
+
+ PK11_FreeSlot (card_slot);
+}
+
+void
+gsd_smartcard_service_sync_token (GsdSmartcardService *self,
+ PK11SlotInfo *card_slot,
+ GCancellable *cancellable)
+{
+ char *object_path;
+ GDBusInterfaceSkeleton *interface;
+
+ object_path = get_object_path_for_token (self, card_slot);
+
+ G_LOCK (gsd_smartcard_tokens);
+ interface = g_hash_table_lookup (self->tokens, object_path);
+ G_UNLOCK (gsd_smartcard_tokens);
+
+ if (interface == NULL)
+ register_new_token_in_main_thread (self,
+ card_slot,
+ object_path,
+ cancellable,
+ (GAsyncReadyCallback)
+ on_token_registered,
+ PK11_ReferenceSlot (card_slot));
+
+ else
+ synchronize_token_in_main_thread (self,
+ card_slot,
+ cancellable,
+ (GAsyncReadyCallback)
+ on_token_synchronized,
+ PK11_ReferenceSlot (card_slot));
+
+ g_free (object_path);
+}