GCC Code Coverage Report


Directory: ./
File: panels/system/users/cc-fingerprint-manager.c
Date: 2024-05-03 09:46:52
Exec Total Coverage
Lines: 0 240 0.0%
Functions: 0 28 0.0%
Branches: 0 154 0.0%

Line Branch Exec Source
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2020 Canonical Ltd.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 *
20 * Authors: Marco Trevisan <marco.trevisan@canonical.com>
21 */
22
23 #include "cc-fingerprint-manager.h"
24
25 #include "cc-fprintd-generated.h"
26 #include "cc-user-accounts-enum-types.h"
27
28 #define CC_FPRINTD_NAME "net.reactivated.Fprint"
29 #define CC_FPRINTD_MANAGER_PATH "/net/reactivated/Fprint/Manager"
30
31 struct _CcFingerprintManager
32 {
33 GObject parent_instance;
34 };
35
36 typedef struct
37 {
38 ActUser *user;
39 GTask *current_task;
40 CcFingerprintState state;
41 GList *cached_devices;
42 } CcFingerprintManagerPrivate;
43
44 G_DEFINE_TYPE_WITH_PRIVATE (CcFingerprintManager, cc_fingerprint_manager, G_TYPE_OBJECT)
45
46 enum {
47 PROP_0,
48 PROP_USER,
49 PROP_STATE,
50 N_PROPS
51 };
52
53 static GParamSpec *properties[N_PROPS];
54
55 static void cleanup_cached_devices (CcFingerprintManager *self);
56
57 CcFingerprintManager *
58 cc_fingerprint_manager_new (ActUser *user)
59 {
60 return g_object_new (CC_TYPE_FINGERPRINT_MANAGER, "user", user, NULL);
61 }
62
63 static void
64 cc_fingerprint_manager_dispose (GObject *object)
65 {
66 CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
67 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
68
69 if (priv->current_task)
70 {
71 g_cancellable_cancel (g_task_get_cancellable (priv->current_task));
72 priv->current_task = NULL;
73 }
74
75 g_clear_object (&priv->user);
76 cleanup_cached_devices (self);
77
78 G_OBJECT_CLASS (cc_fingerprint_manager_parent_class)->dispose (object);
79 }
80
81 static void
82 cc_fingerprint_manager_get_property (GObject *object,
83 guint prop_id,
84 GValue *value,
85 GParamSpec *pspec)
86 {
87 CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
88 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
89
90 switch (prop_id)
91 {
92 case PROP_STATE:
93 g_value_set_enum (value, priv->state);
94 break;
95
96 case PROP_USER:
97 g_value_set_object (value, priv->user);
98 break;
99
100 default:
101 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
102 }
103 }
104
105 static void
106 cc_fingerprint_manager_set_property (GObject *object,
107 guint prop_id,
108 const GValue *value,
109 GParamSpec *pspec)
110 {
111 CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
112 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
113
114 switch (prop_id)
115 {
116 case PROP_USER:
117 g_set_object (&priv->user, g_value_get_object (value));
118 break;
119
120 default:
121 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
122 }
123 }
124
125 static void
126 cc_fingerprint_manager_constructed (GObject *object)
127 {
128 cc_fingerprint_manager_update_state (CC_FINGERPRINT_MANAGER (object), NULL, NULL);
129 }
130
131 static void
132 cc_fingerprint_manager_class_init (CcFingerprintManagerClass *klass)
133 {
134 GObjectClass *object_class = G_OBJECT_CLASS (klass);
135
136 object_class->constructed = cc_fingerprint_manager_constructed;
137 object_class->dispose = cc_fingerprint_manager_dispose;
138 object_class->get_property = cc_fingerprint_manager_get_property;
139 object_class->set_property = cc_fingerprint_manager_set_property;
140
141 properties[PROP_USER] =
142 g_param_spec_object ("user",
143 "User",
144 "The user account we manage the fingerprint for",
145 ACT_TYPE_USER,
146 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
147
148 properties[PROP_STATE] =
149 g_param_spec_enum ("state",
150 "State",
151 "The state of the fingerprint for the user",
152 CC_TYPE_FINGERPRINT_STATE, CC_FINGERPRINT_STATE_NONE,
153 G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
154
155 g_object_class_install_properties (object_class, N_PROPS, properties);
156 }
157
158 static void
159 cc_fingerprint_manager_init (CcFingerprintManager *self)
160 {
161 }
162
163 typedef struct
164 {
165 guint waiting_devices;
166 GList *devices;
167 } DeviceListData;
168
169 static void
170 object_list_destroy_notify (gpointer data)
171 {
172 GList *list = data;
173 g_list_free_full (list, g_object_unref);
174 }
175
176 static void
177 on_device_owner_changed (CcFingerprintManager *self,
178 GParamSpec *spec,
179 CcFprintdDevice *device)
180 {
181 g_autofree char *name_owner = NULL;
182
183 name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (device));
184
185 if (!name_owner)
186 {
187 g_debug ("Fprintd daemon disappeared, cleaning cache...");
188 cleanup_cached_devices (self);
189 }
190 }
191
192 static void
193 cleanup_cached_devices (CcFingerprintManager *self)
194 {
195 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
196 CcFprintdDevice *target_device;
197
198 if (!priv->cached_devices)
199 return;
200
201 g_return_if_fail (CC_FPRINTD_IS_DEVICE (priv->cached_devices->data));
202
203 target_device = CC_FPRINTD_DEVICE (priv->cached_devices->data);
204
205 g_signal_handlers_disconnect_by_func (target_device, on_device_owner_changed, self);
206 g_list_free_full (g_steal_pointer (&priv->cached_devices), g_object_unref);
207 }
208
209 static void
210 cache_devices (CcFingerprintManager *self,
211 GList *devices)
212 {
213 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
214 CcFprintdDevice *target_device;
215
216 g_return_if_fail (devices && CC_FPRINTD_IS_DEVICE (devices->data));
217
218 cleanup_cached_devices (self);
219 priv->cached_devices = g_list_copy_deep (devices, (GCopyFunc) g_object_ref, NULL);
220
221 /* We can monitor just the first device name, as the owner is just the same */
222 target_device = CC_FPRINTD_DEVICE (priv->cached_devices->data);
223
224 g_signal_connect_object (target_device, "notify::g-name-owner",
225 G_CALLBACK (on_device_owner_changed), self,
226 G_CONNECT_SWAPPED);
227 }
228
229 static void
230 on_device_proxy (GObject *object, GAsyncResult *res, gpointer user_data)
231 {
232 g_autoptr(CcFprintdDevice) fprintd_device = NULL;
233 g_autoptr(GTask) task = G_TASK (user_data);
234 g_autoptr(GError) error = NULL;
235 CcFingerprintManager *self = g_task_get_source_object (task);
236 DeviceListData *list_data = g_task_get_task_data (task);
237
238 fprintd_device = cc_fprintd_device_proxy_new_for_bus_finish (res, &error);
239 list_data->waiting_devices--;
240
241 if (error)
242 {
243 if (list_data->waiting_devices == 0)
244 g_task_return_error (task, g_steal_pointer (&error));
245 else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
246 g_warning ("Impossible to ge the device proxy: %s", error->message);
247
248 return;
249 }
250
251 g_debug ("Got fingerprint device %s", cc_fprintd_device_get_name (fprintd_device));
252
253 list_data->devices = g_list_append (list_data->devices, g_steal_pointer (&fprintd_device));
254
255 if (list_data->waiting_devices == 0)
256 {
257 cache_devices (self, list_data->devices);
258 g_task_return_pointer (task, g_steal_pointer (&list_data->devices), object_list_destroy_notify);
259 }
260 }
261
262 static void
263 on_devices_list (GObject *object, GAsyncResult *res, gpointer user_data)
264 {
265 CcFprintdManager *fprintd_manager = CC_FPRINTD_MANAGER (object);
266 g_autoptr(GTask) task = G_TASK (user_data);
267 g_autoptr(GError) error = NULL;
268 g_auto(GStrv) devices_list = NULL;
269 DeviceListData *list_data;
270 guint i;
271
272 cc_fprintd_manager_call_get_devices_finish (fprintd_manager, &devices_list, res, &error);
273
274 if (error)
275 {
276 g_task_return_error (task, g_steal_pointer (&error));
277 return;
278 }
279
280 if (!devices_list || !devices_list[0])
281 {
282 g_task_return_pointer (task, NULL, NULL);
283 return;
284 }
285
286 list_data = g_new0 (DeviceListData, 1);
287 g_task_set_task_data (task, list_data, g_free);
288
289 g_debug ("Fprintd replied with %u device(s)", g_strv_length (devices_list));
290
291 for (i = 0; devices_list[i] != NULL; ++i)
292 {
293 const char *device_path = devices_list[i];
294
295 list_data->waiting_devices++;
296
297 cc_fprintd_device_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
298 G_DBUS_PROXY_FLAGS_NONE,
299 CC_FPRINTD_NAME,
300 device_path,
301 g_task_get_cancellable (task),
302 on_device_proxy,
303 g_object_ref (task));
304 }
305 }
306
307 static void
308 on_manager_proxy (GObject *object, GAsyncResult *res, gpointer user_data)
309 {
310 g_autoptr(GTask) task = G_TASK (user_data);
311 g_autoptr(CcFprintdManager) fprintd_manager = NULL;
312 g_autoptr(GError) error = NULL;
313
314 fprintd_manager = cc_fprintd_manager_proxy_new_for_bus_finish (res, &error);
315
316 if (error)
317 {
318 g_task_return_error (task, g_steal_pointer (&error));
319 return;
320 }
321
322 g_debug ("Fprintd manager connected");
323
324 cc_fprintd_manager_call_get_devices (fprintd_manager,
325 g_task_get_cancellable (task),
326 on_devices_list,
327 g_object_ref (task));
328 }
329
330 static void
331 fprintd_manager_connect (CcFingerprintManager *self,
332 GAsyncReadyCallback callback,
333 GTask *task)
334 {
335 g_assert (G_IS_TASK (task));
336
337 cc_fprintd_manager_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE,
338 CC_FPRINTD_NAME, CC_FPRINTD_MANAGER_PATH,
339 g_task_get_cancellable (task),
340 callback,
341 task);
342 }
343
344 void
345 cc_fingerprint_manager_get_devices (CcFingerprintManager *self,
346 GCancellable *cancellable,
347 GAsyncReadyCallback callback,
348 gpointer user_data)
349 {
350 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
351 g_autoptr(GTask) task = NULL;
352
353 task = g_task_new (self, cancellable, callback, user_data);
354 g_task_set_source_tag (task, cc_fingerprint_manager_get_devices);
355
356 if (priv->cached_devices)
357 {
358 GList *devices;
359
360 devices = g_list_copy_deep (priv->cached_devices, (GCopyFunc) g_object_ref, NULL);
361 g_task_return_pointer (task, devices, object_list_destroy_notify);
362 return;
363 }
364
365 fprintd_manager_connect (self, on_manager_proxy, g_steal_pointer (&task));
366 }
367
368 /**
369 * cc_fingerprint_manager_get_devices_finish:
370 * @self: The #CcFingerprintManager
371 * @result: A #GAsyncResult
372 * @error: Return location for errors, or %NULL to ignore
373 *
374 * Finish an asynchronous operation to list all devices.
375 *
376 * Returns: (element-type CcFprintdDevice) (transfer full): List of prints or %NULL on error
377 */
378 GList *
379 cc_fingerprint_manager_get_devices_finish (CcFingerprintManager *self,
380 GAsyncResult *res,
381 GError **error)
382 {
383 g_return_val_if_fail (g_task_is_valid (res, self), NULL);
384
385 return g_task_propagate_pointer (G_TASK (res), error);
386 }
387
388 static void
389 set_state (CcFingerprintManager *self,
390 CcFingerprintState state)
391 {
392 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
393
394 if (priv->state == state)
395 return;
396
397 g_debug ("Fingerprint manager state changed to %d", state);
398
399 priv->state = state;
400 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATE]);
401 }
402
403 typedef struct
404 {
405 guint waiting_devices;
406 CcFingerprintStateUpdated callback;
407 gpointer user_data;
408 } UpdateStateData;
409
410 static void
411 update_state_callback (GObject *object,
412 GAsyncResult *res,
413 gpointer user_data)
414 {
415 CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
416 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
417 g_autoptr(GError) error = NULL;
418 CcFingerprintState state;
419 UpdateStateData *data;
420 GTask *task;
421
422 g_return_if_fail (g_task_is_valid (res, self));
423
424 task = G_TASK (res);
425 g_assert (g_steal_pointer (&priv->current_task) == task);
426
427 state = g_task_propagate_int (task, &error);
428 data = g_task_get_task_data (task);
429
430 if (error)
431 {
432 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
433 return;
434
435 g_warning ("Impossible to update fingerprint manager state: %s",
436 error->message);
437
438 state = CC_FINGERPRINT_STATE_NONE;
439 }
440
441 set_state (self, state);
442
443 if (data->callback)
444 data->callback (self, state, data->user_data, error);
445 }
446
447 static void
448 on_device_list_enrolled (GObject *object,
449 GAsyncResult *res,
450 gpointer user_data)
451 {
452 CcFprintdDevice *fprintd_device = CC_FPRINTD_DEVICE (object);
453 g_autoptr(GTask) task = G_TASK (user_data);
454 g_autoptr(GError) error = NULL;
455 g_auto(GStrv) enrolled_fingers = NULL;
456 UpdateStateData *data = g_task_get_task_data (task);
457 guint num_enrolled_fingers;
458
459 cc_fprintd_device_call_list_enrolled_fingers_finish (fprintd_device,
460 &enrolled_fingers,
461 res, &error);
462
463 if (data->waiting_devices == 0)
464 return;
465
466 data->waiting_devices--;
467
468 if (error)
469 {
470 g_autofree char *dbus_error = g_dbus_error_get_remote_error (error);
471
472 if (!g_str_equal (dbus_error, CC_FPRINTD_NAME ".Error.NoEnrolledPrints"))
473 {
474 if (data->waiting_devices == 0)
475 g_task_return_error (task, g_steal_pointer (&error));
476 else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
477 g_warning ("Impossible to list enrolled fingers: %s", error->message);
478
479 return;
480 }
481 }
482
483 num_enrolled_fingers = enrolled_fingers ? g_strv_length (enrolled_fingers) : 0;
484
485 g_debug ("Device %s has %u enrolled fingers",
486 cc_fprintd_device_get_name (fprintd_device),
487 num_enrolled_fingers);
488
489 if (num_enrolled_fingers > 0)
490 {
491 data->waiting_devices = 0;
492 g_task_return_int (task, CC_FINGERPRINT_STATE_ENABLED);
493 }
494 else if (data->waiting_devices == 0)
495 {
496 g_task_return_int (task, CC_FINGERPRINT_STATE_DISABLED);
497 }
498 }
499
500 static void
501 on_manager_devices_list (GObject *object,
502 GAsyncResult *res,
503 gpointer user_data)
504 {
505 CcFingerprintManager *self = CC_FINGERPRINT_MANAGER (object);
506 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
507 g_autolist(CcFprintdDevice) fprintd_devices = NULL;
508 g_autoptr(GTask) task = G_TASK (user_data);
509 g_autoptr(GError) error = NULL;
510 UpdateStateData *data = g_task_get_task_data (task);
511 const char *user_name;
512 GList *l;
513
514 fprintd_devices = cc_fingerprint_manager_get_devices_finish (self, res, &error);
515
516 if (error)
517 {
518 g_task_return_error (task, g_steal_pointer (&error));
519 return;
520 }
521
522 if (fprintd_devices == NULL)
523 {
524 g_debug ("No fingerprint devices found");
525 g_task_return_int (task, CC_FINGERPRINT_STATE_NONE);
526 return;
527 }
528
529 user_name = act_user_get_user_name (priv->user);
530
531 for (l = fprintd_devices; l; l = l->next)
532 {
533 CcFprintdDevice *device = l->data;
534
535 g_debug ("Connected to device %s, looking for enrolled fingers",
536 cc_fprintd_device_get_name (device));
537
538 data->waiting_devices++;
539 cc_fprintd_device_call_list_enrolled_fingers (device, user_name,
540 g_task_get_cancellable (task),
541 on_device_list_enrolled,
542 g_object_ref (task));
543 }
544 }
545
546 void
547 cc_fingerprint_manager_update_state (CcFingerprintManager *self,
548 CcFingerprintStateUpdated callback,
549 gpointer user_data)
550 {
551 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
552 g_autoptr(GCancellable) cancellable = NULL;
553 UpdateStateData *data;
554
555 g_return_if_fail (priv->current_task == NULL);
556
557 if (act_user_get_uid (priv->user) != getuid () ||
558 !act_user_is_local_account (priv->user))
559 {
560 set_state (self, CC_FINGERPRINT_STATE_NONE);
561 return;
562 }
563
564 cancellable = g_cancellable_new ();
565 data = g_new0 (UpdateStateData, 1);
566 data->callback = callback;
567 data->user_data = user_data;
568
569 priv->current_task = g_task_new (self, cancellable, update_state_callback, NULL);
570 g_task_set_source_tag (priv->current_task, cc_fingerprint_manager_update_state);
571 g_task_set_task_data (priv->current_task, data, g_free);
572
573 set_state (self, CC_FINGERPRINT_STATE_UPDATING);
574
575 cc_fingerprint_manager_get_devices (self, cancellable, on_manager_devices_list,
576 priv->current_task);
577 }
578
579 CcFingerprintState
580 cc_fingerprint_manager_get_state (CcFingerprintManager *self)
581 {
582 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
583
584 g_return_val_if_fail (CC_IS_FINGERPRINT_MANAGER (self), CC_FINGERPRINT_STATE_NONE);
585
586 return priv->state;
587 }
588
589 ActUser *
590 cc_fingerprint_manager_get_user (CcFingerprintManager *self)
591 {
592 CcFingerprintManagerPrivate *priv = cc_fingerprint_manager_get_instance_private (self);
593
594 g_return_val_if_fail (CC_IS_FINGERPRINT_MANAGER (self), NULL);
595
596 return priv->user;
597 }
598