summaryrefslogtreecommitdiffstats
path: root/panels/user-accounts/cc-fingerprint-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/user-accounts/cc-fingerprint-manager.c')
-rw-r--r--panels/user-accounts/cc-fingerprint-manager.c597
1 files changed, 597 insertions, 0 deletions
diff --git a/panels/user-accounts/cc-fingerprint-manager.c b/panels/user-accounts/cc-fingerprint-manager.c
new file mode 100644
index 0000000..07a50e7
--- /dev/null
+++ b/panels/user-accounts/cc-fingerprint-manager.c
@@ -0,0 +1,597 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2020 Canonical Ltd.
+ *
+ * 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * Authors: Marco Trevisan <marco.trevisan@canonical.com>
+ */
+
+#include "cc-fingerprint-manager.h"
+
+#include "cc-fprintd-generated.h"
+#include "cc-user-accounts-enum-types.h"
+
+#define CC_FPRINTD_NAME "net.reactivated.Fprint"
+#define CC_FPRINTD_MANAGER_PATH "/net/reactivated/Fprint/Manager"
+
+struct _CcFingerprintManager
+{
+ GObject parent_instance;
+};
+
+typedef struct
+{
+ ActUser *user;
+ GTask *current_task;
+ CcFingerprintState state;
+ GList *cached_devices;
+} CcFingerprintManagerPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (CcFingerprintManager, cc_fingerprint_manager, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_USER,
+ PROP_STATE,
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+static void cleanup_cached_devices (CcFingerprintManager *self);
+
+CcFingerprintManager *
+cc_fingerprint_manager_new (ActUser *user)
+{
+ return g_object_new (CC_TYPE_FINGERPRINT_MANAGER, "user", user, NULL);
+}
+
+static void
+cc_fingerprint_manager_dispose (GObject *object)
+{
+ CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+
+ if (priv->current_task)
+ {
+ g_cancellable_cancel (g_task_get_cancellable (priv->current_task));
+ priv->current_task = NULL;
+ }
+
+ g_clear_object (&priv->user);
+ cleanup_cached_devices (self);
+
+ G_OBJECT_CLASS (cc_fingerprint_manager_parent_class)->dispose (object);
+}
+
+static void
+cc_fingerprint_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_STATE:
+ g_value_set_enum (value, priv->state);
+ break;
+
+ case PROP_USER:
+ g_value_set_object (value, priv->user);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+cc_fingerprint_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_USER:
+ g_set_object (&priv->user, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+cc_fingerprint_manager_constructed (GObject *object)
+{
+ cc_fingerprint_manager_update_state (CC_FINGERPRINT_MANAGER (object), NULL, NULL);
+}
+
+static void
+cc_fingerprint_manager_class_init (CcFingerprintManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = cc_fingerprint_manager_constructed;
+ object_class->dispose = cc_fingerprint_manager_dispose;
+ object_class->get_property = cc_fingerprint_manager_get_property;
+ object_class->set_property = cc_fingerprint_manager_set_property;
+
+ properties[PROP_USER] =
+ g_param_spec_object ("user",
+ "User",
+ "The user account we manage the fingerprint for",
+ ACT_TYPE_USER,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_STATE] =
+ g_param_spec_enum ("state",
+ "State",
+ "The state of the fingerprint for the user",
+ CC_TYPE_FINGERPRINT_STATE, CC_FINGERPRINT_STATE_NONE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+cc_fingerprint_manager_init (CcFingerprintManager *self)
+{
+}
+
+typedef struct
+{
+ guint waiting_devices;
+ GList *devices;
+} DeviceListData;
+
+static void
+object_list_destroy_notify (gpointer data)
+{
+ GList *list = data;
+ g_list_free_full (list, g_object_unref);
+}
+
+static void
+on_device_owner_changed (CcFingerprintManager *self,
+ GParamSpec *spec,
+ CcFprintdDevice *device)
+{
+ g_autofree char *name_owner = NULL;
+
+ name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (device));
+
+ if (!name_owner)
+ {
+ g_debug ("Fprintd daemon disappeared, cleaning cache...");
+ cleanup_cached_devices (self);
+ }
+}
+
+static void
+cleanup_cached_devices (CcFingerprintManager *self)
+{
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+ CcFprintdDevice *target_device;
+
+ if (!priv->cached_devices)
+ return;
+
+ g_return_if_fail (CC_FPRINTD_IS_DEVICE (priv->cached_devices->data));
+
+ target_device = CC_FPRINTD_DEVICE (priv->cached_devices->data);
+
+ g_signal_handlers_disconnect_by_func (target_device, on_device_owner_changed, self);
+ g_list_free_full (g_steal_pointer (&priv->cached_devices), g_object_unref);
+}
+
+static void
+cache_devices (CcFingerprintManager *self,
+ GList *devices)
+{
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+ CcFprintdDevice *target_device;
+
+ g_return_if_fail (devices && CC_FPRINTD_IS_DEVICE (devices->data));
+
+ cleanup_cached_devices (self);
+ priv->cached_devices = g_list_copy_deep (devices, (GCopyFunc) g_object_ref, NULL);
+
+ /* We can monitor just the first device name, as the owner is just the same */
+ target_device = CC_FPRINTD_DEVICE (priv->cached_devices->data);
+
+ g_signal_connect_object (target_device, "notify::g-name-owner",
+ G_CALLBACK (on_device_owner_changed), self,
+ G_CONNECT_SWAPPED);
+}
+
+static void
+on_device_proxy (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ g_autoptr(CcFprintdDevice) fprintd_device = NULL;
+ g_autoptr(GTask) task = G_TASK (user_data);
+ g_autoptr(GError) error = NULL;
+ CcFingerprintManager *self = g_task_get_source_object (task);
+ DeviceListData *list_data = g_task_get_task_data (task);
+
+ fprintd_device = cc_fprintd_device_proxy_new_for_bus_finish (res, &error);
+ list_data->waiting_devices--;
+
+ if (error)
+ {
+ if (list_data->waiting_devices == 0)
+ g_task_return_error (task, g_steal_pointer (&error));
+ else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Impossible to ge the device proxy: %s", error->message);
+
+ return;
+ }
+
+ g_debug ("Got fingerprint device %s", cc_fprintd_device_get_name (fprintd_device));
+
+ list_data->devices = g_list_append (list_data->devices, g_steal_pointer (&fprintd_device));
+
+ if (list_data->waiting_devices == 0)
+ {
+ cache_devices (self, list_data->devices);
+ g_task_return_pointer (task, g_steal_pointer (&list_data->devices), object_list_destroy_notify);
+ }
+}
+
+static void
+on_devices_list (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ CcFprintdManager *fprintd_manager = CC_FPRINTD_MANAGER (object);
+ g_autoptr(GTask) task = G_TASK (user_data);
+ g_autoptr(GError) error = NULL;
+ g_auto(GStrv) devices_list = NULL;
+ DeviceListData *list_data;
+ guint i;
+
+ cc_fprintd_manager_call_get_devices_finish (fprintd_manager, &devices_list, res, &error);
+
+ if (error)
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ if (!devices_list || !devices_list[0])
+ {
+ g_task_return_pointer (task, NULL, NULL);
+ return;
+ }
+
+ list_data = g_new0 (DeviceListData, 1);
+ g_task_set_task_data (task, list_data, g_free);
+
+ g_debug ("Fprintd replied with %u device(s)", g_strv_length (devices_list));
+
+ for (i = 0; devices_list[i] != NULL; ++i)
+ {
+ const char *device_path = devices_list[i];
+
+ list_data->waiting_devices++;
+
+ cc_fprintd_device_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ CC_FPRINTD_NAME,
+ device_path,
+ g_task_get_cancellable (task),
+ on_device_proxy,
+ g_object_ref (task));
+ }
+}
+
+static void
+on_manager_proxy (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ g_autoptr(GTask) task = G_TASK (user_data);
+ g_autoptr(CcFprintdManager) fprintd_manager = NULL;
+ g_autoptr(GError) error = NULL;
+
+ fprintd_manager = cc_fprintd_manager_proxy_new_for_bus_finish (res, &error);
+
+ if (error)
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ g_debug ("Fprintd manager connected");
+
+ cc_fprintd_manager_call_get_devices (fprintd_manager,
+ g_task_get_cancellable (task),
+ on_devices_list,
+ g_object_ref (task));
+}
+
+static void
+fprintd_manager_connect (CcFingerprintManager *self,
+ GAsyncReadyCallback callback,
+ GTask *task)
+{
+ g_assert (G_IS_TASK (task));
+
+ cc_fprintd_manager_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE,
+ CC_FPRINTD_NAME, CC_FPRINTD_MANAGER_PATH,
+ g_task_get_cancellable (task),
+ callback,
+ task);
+}
+
+void
+cc_fingerprint_manager_get_devices (CcFingerprintManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, cc_fingerprint_manager_get_devices);
+
+ if (priv->cached_devices)
+ {
+ GList *devices;
+
+ devices = g_list_copy_deep (priv->cached_devices, (GCopyFunc) g_object_ref, NULL);
+ g_task_return_pointer (task, devices, object_list_destroy_notify);
+ return;
+ }
+
+ fprintd_manager_connect (self, on_manager_proxy, g_steal_pointer (&task));
+}
+
+/**
+ * cc_fingerprint_manager_get_devices_finish:
+ * @self: The #CcFingerprintManager
+ * @result: A #GAsyncResult
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to list all devices.
+ *
+ * Returns: (element-type CcFprintdDevice) (transfer full): List of prints or %NULL on error
+ */
+GList *
+cc_fingerprint_manager_get_devices_finish (CcFingerprintManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (res, self), NULL);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+set_state (CcFingerprintManager *self,
+ CcFingerprintState state)
+{
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+
+ if (priv->state == state)
+ return;
+
+ g_debug ("Fingerprint manager state changed to %d", state);
+
+ priv->state = state;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATE]);
+}
+
+typedef struct
+{
+ guint waiting_devices;
+ CcFingerprintStateUpdated callback;
+ gpointer user_data;
+} UpdateStateData;
+
+static void
+update_state_callback (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+ g_autoptr(GError) error = NULL;
+ CcFingerprintState state;
+ UpdateStateData *data;
+ GTask *task;
+
+ g_return_if_fail (g_task_is_valid (res, self));
+
+ task = G_TASK (res);
+ g_assert (g_steal_pointer (&priv->current_task) == task);
+
+ state = g_task_propagate_int (task, &error);
+ data = g_task_get_task_data (task);
+
+ if (error)
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ g_warning ("Impossible to update fingerprint manager state: %s",
+ error->message);
+
+ state = CC_FINGERPRINT_STATE_NONE;
+ }
+
+ set_state (self, state);
+
+ if (data->callback)
+ data->callback (self, state, data->user_data, error);
+}
+
+static void
+on_device_list_enrolled (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
+ g_autoptr(GTask) task = G_TASK (user_data);
+ g_autoptr(GError) error = NULL;
+ g_auto(GStrv) enrolled_fingers = NULL;
+ UpdateStateData *data = g_task_get_task_data (task);
+ guint num_enrolled_fingers;
+
+ cc_fprintd_device_call_list_enrolled_fingers_finish (fprintd_device,
+ &enrolled_fingers,
+ res, &error);
+
+ if (data->waiting_devices == 0)
+ return;
+
+ data->waiting_devices--;
+
+ if (error)
+ {
+ g_autofree char *dbus_error = g_dbus_error_get_remote_error (error);
+
+ if (!g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.NoEnrolledPrints"))
+ {
+ if (data->waiting_devices == 0)
+ g_task_return_error (task, g_steal_pointer (&error));
+ else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Impossible to list enrolled fingers: %s", error->message);
+
+ return;
+ }
+ }
+
+ num_enrolled_fingers = enrolled_fingers ? g_strv_length (enrolled_fingers) : 0;
+
+ g_debug ("Device %s has %u enrolled fingers",
+ cc_fprintd_device_get_name (fprintd_device),
+ num_enrolled_fingers);
+
+ if (num_enrolled_fingers > 0)
+ {
+ data->waiting_devices = 0;
+ g_task_return_int (task, CC_FINGERPRINT_STATE_ENABLED);
+ }
+ else if (data->waiting_devices == 0)
+ {
+ g_task_return_int (task, CC_FINGERPRINT_STATE_DISABLED);
+ }
+}
+
+static void
+on_manager_devices_list (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+ g_autolist(CcFprintdDevice) fprintd_devices = NULL;
+ g_autoptr(GTask) task = G_TASK (user_data);
+ g_autoptr(GError) error = NULL;
+ UpdateStateData *data = g_task_get_task_data (task);
+ const char *user_name;
+ GList *l;
+
+ fprintd_devices = cc_fingerprint_manager_get_devices_finish (self, res, &error);
+
+ if (error)
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ if (fprintd_devices == NULL)
+ {
+ g_debug ("No fingerprint devices found");
+ g_task_return_int (task, CC_FINGERPRINT_STATE_NONE);
+ return;
+ }
+
+ user_name = act_user_get_user_name (priv->user);
+
+ for (l = fprintd_devices; l; l = l->next)
+ {
+ CcFprintdDevice *device = l->data;
+
+ g_debug ("Connected to device %s, looking for enrolled fingers",
+ cc_fprintd_device_get_name (device));
+
+ data->waiting_devices++;
+ cc_fprintd_device_call_list_enrolled_fingers (device, user_name,
+ g_task_get_cancellable (task),
+ on_device_list_enrolled,
+ g_object_ref (task));
+ }
+}
+
+void
+cc_fingerprint_manager_update_state (CcFingerprintManager *self,
+ CcFingerprintStateUpdated callback,
+ gpointer user_data)
+{
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+ g_autoptr(GCancellable) cancellable = NULL;
+ UpdateStateData *data;
+
+ g_return_if_fail (priv->current_task == NULL);
+
+ if (act_user_get_uid (priv->user) != getuid () ||
+ !act_user_is_local_account (priv->user))
+ {
+ set_state (self, CC_FINGERPRINT_STATE_NONE);
+ return;
+ }
+
+ cancellable = g_cancellable_new ();
+ data = g_new0 (UpdateStateData, 1);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ priv->current_task = g_task_new (self, cancellable, update_state_callback, NULL);
+ g_task_set_source_tag (priv->current_task, cc_fingerprint_manager_update_state);
+ g_task_set_task_data (priv->current_task, data, g_free);
+
+ set_state (self, CC_FINGERPRINT_STATE_UPDATING);
+
+ cc_fingerprint_manager_get_devices (self, cancellable, on_manager_devices_list,
+ priv->current_task);
+}
+
+CcFingerprintState
+cc_fingerprint_manager_get_state (CcFingerprintManager *self)
+{
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+
+ g_return_val_if_fail (CC_IS_FINGERPRINT_MANAGER (self), CC_FINGERPRINT_STATE_NONE);
+
+ return priv->state;
+}
+
+ActUser *
+cc_fingerprint_manager_get_user (CcFingerprintManager *self)
+{
+ CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
+
+ g_return_val_if_fail (CC_IS_FINGERPRINT_MANAGER (self), NULL);
+
+ return priv->user;
+}