Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright 2025 GNOME Foundation, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this library; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : : *
21 : : * Authors:
22 : : * - Philip Withnall <pwithnall@gnome.org>
23 : : */
24 : :
25 : : #include "config.h"
26 : :
27 : : #include <act/act.h>
28 : : #include <glib.h>
29 : : #include <glib-object.h>
30 : : #include <glib/gi18n-lib.h>
31 : : #include <gio/gio.h>
32 : : #include <libmalcontent/user-manager.h>
33 : :
34 : : #include "user-private.h"
35 : :
36 : :
37 : : /**
38 : : * MctUserManager:
39 : : *
40 : : * Gives access to the system user database.
41 : : *
42 : : * Before it can be used, the user data needs to be loaded by calling
43 : : * [method@Malcontent.UserManager.load_async]. After then, changes to the user
44 : : * database are signalled using [signal@Malcontent.UserManager::user-added] and
45 : : * [signal@Malcontent.UserManager::user-removed]. Changes to the properties of
46 : : * individual users are signalled using [signal@GObject.Object::notify] on the
47 : : * user objects.
48 : : *
49 : : * As well as exposing the (non-system) users in the database, the user manager
50 : : * can describe family relationships between the users. Currently, there is only
51 : : * one family supported on the system, containing all the normal and
52 : : * administrator user accounts.
53 : : * [In the future](https://gitlab.gnome.org/Teams/Design/app-mockups/-/issues/118),
54 : : * this may extend to supporting more families, with more roles for members
55 : : * within a family.
56 : : *
57 : : * Since: 0.14.0
58 : : */
59 : : struct _MctUserManager
60 : : {
61 : : GObject parent_instance;
62 : :
63 : : GDBusConnection *connection; /* (owned) */
64 : :
65 : : ActUserManager *user_manager; /* (owned) (nullable) */
66 : : unsigned long user_manager_notify_is_loaded_id;
67 : : unsigned long user_added_id;
68 : : unsigned long user_removed_id;
69 : : gboolean is_loaded;
70 : : };
71 : :
72 [ + + + - : 22 : G_DEFINE_TYPE (MctUserManager, mct_user_manager, G_TYPE_OBJECT)
+ + ]
73 : :
74 : : typedef enum
75 : : {
76 : : PROP_CONNECTION = 1,
77 : : PROP_IS_LOADED,
78 : : } MctUserManagerProperty;
79 : :
80 : : static GParamSpec *props[PROP_IS_LOADED + 1] = { NULL, };
81 : :
82 : : typedef enum
83 : : {
84 : : SIGNAL_USER_ADDED,
85 : : SIGNAL_USER_REMOVED,
86 : : } MctUserManagerSignal;
87 : :
88 : : static unsigned int signals[SIGNAL_USER_REMOVED + 1] = { 0, };
89 : :
90 : : static void
91 : 2 : mct_user_manager_init (MctUserManager *self)
92 : : {
93 : : /* Nothing to do here. */
94 : 2 : }
95 : :
96 : : static void
97 : 0 : mct_user_manager_get_property (GObject *object,
98 : : guint property_id,
99 : : GValue *value,
100 : : GParamSpec *spec)
101 : : {
102 : 0 : MctUserManager *self = MCT_USER_MANAGER (object);
103 : :
104 [ # # # ]: 0 : switch ((MctUserManagerProperty) property_id)
105 : : {
106 : 0 : case PROP_CONNECTION:
107 : 0 : g_value_set_object (value, self->connection);
108 : 0 : break;
109 : 0 : case PROP_IS_LOADED:
110 : 0 : g_value_set_boolean (value, self->is_loaded);
111 : 0 : break;
112 : 0 : default:
113 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
114 : 0 : break;
115 : : }
116 : 0 : }
117 : :
118 : : static void
119 : 2 : mct_user_manager_set_property (GObject *object,
120 : : guint property_id,
121 : : const GValue *value,
122 : : GParamSpec *spec)
123 : : {
124 : 2 : MctUserManager *self = MCT_USER_MANAGER (object);
125 : :
126 [ + - - ]: 2 : switch ((MctUserManagerProperty) property_id)
127 : : {
128 : 2 : case PROP_CONNECTION:
129 : : /* Construct-only. May not be %NULL. */
130 : 2 : g_assert (self->connection == NULL);
131 : 2 : self->connection = g_value_dup_object (value);
132 : 2 : g_assert (self->connection != NULL);
133 : 2 : break;
134 : 0 : case PROP_IS_LOADED:
135 : : /* Read only. */
136 : : g_assert_not_reached ();
137 : 0 : default:
138 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
139 : 0 : break;
140 : : }
141 : 2 : }
142 : :
143 : : static void
144 : 0 : mct_user_manager_dispose (GObject *object)
145 : : {
146 : 0 : MctUserManager *self = MCT_USER_MANAGER (object);
147 : :
148 : : /* MctUserManager can’t be disposed while load_async() is still in flight */
149 : 0 : g_assert (self->user_manager_notify_is_loaded_id == 0);
150 : :
151 [ # # ]: 0 : g_clear_signal_handler (&self->user_added_id, self->user_manager);
152 [ # # ]: 0 : g_clear_signal_handler (&self->user_removed_id, self->user_manager);
153 [ # # ]: 0 : g_clear_object (&self->user_manager);
154 : :
155 [ # # ]: 0 : g_clear_object (&self->connection);
156 : :
157 : 0 : G_OBJECT_CLASS (mct_user_manager_parent_class)->dispose (object);
158 : 0 : }
159 : :
160 : : static void
161 : 3 : mct_user_manager_class_init (MctUserManagerClass *klass)
162 : : {
163 : 3 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
164 : :
165 : 3 : object_class->dispose = mct_user_manager_dispose;
166 : 3 : object_class->get_property = mct_user_manager_get_property;
167 : 3 : object_class->set_property = mct_user_manager_set_property;
168 : :
169 : : /**
170 : : * MctUserManager:connection: (not nullable)
171 : : *
172 : : * A connection to the system bus, where accounts-service runs.
173 : : *
174 : : * It’s provided mostly for testing purposes, or to allow an existing
175 : : * connection to be re-used.
176 : : *
177 : : * Since: 0.14.0
178 : : */
179 : 3 : props[PROP_CONNECTION] = g_param_spec_object ("connection",
180 : : NULL, NULL,
181 : : G_TYPE_DBUS_CONNECTION,
182 : : G_PARAM_READWRITE |
183 : : G_PARAM_CONSTRUCT_ONLY |
184 : : G_PARAM_STATIC_STRINGS);
185 : :
186 : : /**
187 : : * MctUserManager:is-loaded:
188 : : *
189 : : * Whether the user manager has finished loading yet.
190 : : *
191 : : * Since: 0.14.0
192 : : */
193 : 3 : props[PROP_IS_LOADED] = g_param_spec_boolean ("is-loaded",
194 : : NULL, NULL,
195 : : FALSE,
196 : : G_PARAM_READABLE |
197 : : G_PARAM_STATIC_STRINGS |
198 : : G_PARAM_EXPLICIT_NOTIFY);
199 : :
200 : 3 : g_object_class_install_properties (object_class,
201 : : G_N_ELEMENTS (props),
202 : : props);
203 : :
204 : : /**
205 : : * MctUserManager::user-added:
206 : : * @self: a user manager
207 : : * @user: (not nullable): the new user
208 : : *
209 : : * Emitted when a new user is added on the system.
210 : : *
211 : : * Since: 0.14.0
212 : : */
213 : 3 : signals[SIGNAL_USER_ADDED] = g_signal_new ("user-added",
214 : : G_TYPE_FROM_CLASS (klass),
215 : : G_SIGNAL_RUN_LAST,
216 : : 0, NULL, NULL, NULL,
217 : : G_TYPE_NONE, 1,
218 : : MCT_TYPE_USER);
219 : :
220 : : /**
221 : : * MctUserManager::user-removed:
222 : : * @self: a user manager
223 : : * @user: (not nullable): the old user
224 : : *
225 : : * Emitted when a user is removed from the system.
226 : : *
227 : : * Since: 0.14.0
228 : : */
229 : 3 : signals[SIGNAL_USER_REMOVED] = g_signal_new ("user-removed",
230 : : G_TYPE_FROM_CLASS (klass),
231 : : G_SIGNAL_RUN_LAST,
232 : : 0, NULL, NULL, NULL,
233 : : G_TYPE_NONE, 1,
234 : : MCT_TYPE_USER);
235 : 3 : }
236 : :
237 : : /**
238 : : * mct_user_manager_new:
239 : : * @connection: (transfer none): a D-Bus system bus connection to use
240 : : *
241 : : * Create a new user manager.
242 : : *
243 : : * Returns: (transfer full): a new user manager
244 : : * Since: 0.14.0
245 : : */
246 : : MctUserManager *
247 : 2 : mct_user_manager_new (GDBusConnection *connection)
248 : : {
249 : 2 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
250 : :
251 : 2 : return g_object_new (MCT_TYPE_USER_MANAGER,
252 : : "connection", connection,
253 : : NULL);
254 : : }
255 : :
256 : : static void
257 : 0 : user_added_cb (ActUserManager *user_manager,
258 : : ActUser *act_user,
259 : : void *user_data)
260 : : {
261 : 0 : MctUserManager *self = MCT_USER_MANAGER (user_data);
262 : 0 : g_autoptr(MctUser) user = mct_user_new_from_act_user (act_user);
263 : :
264 : 0 : g_signal_emit (self, signals[SIGNAL_USER_ADDED], 0, user);
265 : 0 : }
266 : :
267 : : static void
268 : 0 : user_removed_cb (ActUserManager *user_manager,
269 : : ActUser *act_user,
270 : : void *user_data)
271 : : {
272 : 0 : MctUserManager *self = MCT_USER_MANAGER (user_data);
273 : 0 : g_autoptr(MctUser) user = mct_user_new_from_act_user (act_user);
274 : :
275 : 0 : g_signal_emit (self, signals[SIGNAL_USER_REMOVED], 0, user);
276 : 0 : }
277 : :
278 : : static void user_manager_notify_is_loaded_cb (GObject *object,
279 : : GParamSpec *pspec,
280 : : void *user_data);
281 : :
282 : : static void
283 : 2 : object_unref_closure (void *data,
284 : : GClosure *closure)
285 : : {
286 : 2 : g_object_unref (data);
287 : 2 : }
288 : :
289 : : /**
290 : : * mct_user_manager_load_async:
291 : : * @self: a user manager
292 : : * @cancellable: a [class@Gio.Cancellable], or `NULL`,
293 : : * @callback: callback for when the operation is complete
294 : : * @user_data: data to pass to @callback
295 : : *
296 : : * Load the user data from the system.
297 : : *
298 : : * This must be called before the [class@Malcontent.UserManager] can be used.
299 : : *
300 : : * It does not currently support being called more than once.
301 : : *
302 : : * Since: 0.14.0
303 : : */
304 : : void
305 : 2 : mct_user_manager_load_async (MctUserManager *self,
306 : : GCancellable *cancellable,
307 : : GAsyncReadyCallback callback,
308 : : void *user_data)
309 : : {
310 [ + - ]: 2 : g_autoptr(GTask) task = NULL;
311 : 2 : gboolean is_loaded = FALSE;
312 : :
313 : 2 : g_return_if_fail (MCT_IS_USER_MANAGER (self));
314 : 2 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
315 : :
316 : : /* Doesn’t currently support being called in parallel */
317 : 2 : g_return_if_fail (self->user_manager == NULL);
318 : :
319 : 2 : task = g_task_new (self, cancellable, callback, user_data);
320 [ + - ]: 2 : g_task_set_source_tag (task, mct_user_manager_load_async);
321 : :
322 : : /* FIXME: Ideally we would not be using a singleton ActUserManager, and it
323 : : * would use the GDBusConnection in ->connection. Then this class becomes
324 : : * testable by pointing it to a private GDBusConnection. However,
325 : : * libaccountsservice doesn’t currently support that. */
326 : 2 : self->user_manager = g_object_ref (act_user_manager_get_default ());
327 : 2 : self->user_manager_notify_is_loaded_id =
328 : 2 : g_signal_connect_data (self->user_manager, "notify::is-loaded",
329 : : G_CALLBACK (user_manager_notify_is_loaded_cb),
330 : : g_object_ref (task), object_unref_closure,
331 : : G_CONNECT_DEFAULT);
332 : :
333 : 2 : g_object_get (self->user_manager, "is-loaded", &is_loaded, NULL);
334 [ - + ]: 2 : if (is_loaded)
335 : 0 : user_manager_notify_is_loaded_cb (G_OBJECT (self->user_manager), NULL, task);
336 : : /* otherwise wait for the notify::is-loaded signal */
337 : : }
338 : :
339 : : static void
340 : 2 : user_manager_notify_is_loaded_cb (GObject *object,
341 : : GParamSpec *pspec,
342 : : void *user_data)
343 : : {
344 [ + - ]: 4 : g_autoptr(GTask) task = g_object_ref (G_TASK (user_data));
345 : 2 : MctUserManager *self = g_task_get_source_object (task);
346 : 2 : gboolean is_loaded = FALSE;
347 : :
348 : 2 : g_object_get (self->user_manager, "is-loaded", &is_loaded, NULL);
349 [ - + ]: 2 : if (!is_loaded)
350 : 0 : return;
351 : :
352 [ + - ]: 2 : g_clear_signal_handler (&self->user_manager_notify_is_loaded_id, self->user_manager);
353 : :
354 : : /* Connect to notifications from AccountsService. */
355 : 2 : self->user_added_id = g_signal_connect (self->user_manager, "user-added",
356 : : G_CALLBACK (user_added_cb),
357 : : self);
358 : 2 : self->user_removed_id = g_signal_connect (self->user_manager, "user-removed",
359 : : G_CALLBACK (user_removed_cb),
360 : : self);
361 : :
362 : 2 : self->is_loaded = TRUE;
363 : 2 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_IS_LOADED]);
364 : :
365 : : /* Is the service available? */
366 [ - + ]: 2 : if (act_user_manager_no_service (self->user_manager))
367 : 0 : g_task_return_new_error_literal (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
368 : 0 : _("Failed to load user data from the system"));
369 : : else
370 : 2 : g_task_return_boolean (task, TRUE);
371 : : }
372 : :
373 : : typedef struct
374 : : {
375 : : GPtrArray *users; /* (owned) (element-type MctUser) */
376 : : unsigned long *is_loaded_ids; /* (owned) (array length=users->len) */
377 : : size_t n_remaining_not_loaded;
378 : :
379 : : GCancellable *cancellable; /* (nullable) (owned) */
380 : : unsigned long cancelled_id;
381 : : } EnsureUsersLoadedData;
382 : :
383 : : static void
384 : 0 : ensure_users_loaded_data_clear (EnsureUsersLoadedData *data)
385 : : {
386 [ # # ]: 0 : g_clear_signal_handler (&data->cancelled_id, data->cancellable);
387 [ # # ]: 0 : g_clear_object (&data->cancellable);
388 : :
389 [ # # # # ]: 0 : for (unsigned int i = 0; data->users != NULL && i < data->users->len; i++)
390 [ # # ]: 0 : g_clear_signal_handler (&data->is_loaded_ids[i], data->users->pdata[i]);
391 : :
392 [ # # ]: 0 : g_clear_pointer (&data->users, g_ptr_array_unref);
393 [ # # ]: 0 : g_clear_pointer (&data->is_loaded_ids, g_free);
394 : 0 : }
395 : :
396 : : static void
397 : 0 : ensure_users_loaded_data_free (EnsureUsersLoadedData *data)
398 : : {
399 : 0 : ensure_users_loaded_data_clear (data);
400 : 0 : g_free (data);
401 : 0 : }
402 : :
403 [ # # ]: 0 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (EnsureUsersLoadedData, ensure_users_loaded_data_free)
404 : :
405 : : static void
406 : 0 : ensure_users_loaded_maybe_finish (GTask *task)
407 : : {
408 : 0 : EnsureUsersLoadedData *data = g_task_get_task_data (task);
409 : 0 : g_autoptr(GError) local_error = NULL;
410 : :
411 [ # # ]: 0 : if (g_cancellable_set_error_if_cancelled (data->cancellable, &local_error))
412 : : {
413 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
414 : 0 : ensure_users_loaded_data_clear (data);
415 : : }
416 [ # # ]: 0 : else if (data->n_remaining_not_loaded == 0)
417 : : {
418 : 0 : g_task_return_pointer (task, g_ptr_array_ref (data->users), (GDestroyNotify) g_ptr_array_unref);
419 : 0 : ensure_users_loaded_data_clear (data);
420 : : }
421 : 0 : }
422 : :
423 : : static void ensure_users_loaded_cb (GObject *object,
424 : : GParamSpec *pspec,
425 : : void *user_data);
426 : : static void ensure_users_loaded_cancelled_cb (GCancellable *cancellable,
427 : : void *user_data);
428 : :
429 : : static void
430 : 0 : ensure_users_loaded_async (GPtrArray *users /* (element-type MctUser) */,
431 : : GCancellable *cancellable,
432 : : GAsyncReadyCallback callback,
433 : : void *user_data)
434 : : {
435 : 0 : g_autoptr(GTask) task = NULL;
436 : 0 : g_autoptr(EnsureUsersLoadedData) data_owned = NULL;
437 : : EnsureUsersLoadedData *data;
438 : :
439 : 0 : task = g_task_new (NULL, cancellable, callback, user_data);
440 [ # # ]: 0 : g_task_set_source_tag (task, ensure_users_loaded_async);
441 : :
442 : 0 : data = data_owned = g_new0 (EnsureUsersLoadedData, 1);
443 : 0 : data->users = g_ptr_array_ref (users);
444 : 0 : data->is_loaded_ids = g_new0 (unsigned long, users->len);
445 [ # # ]: 0 : data->cancellable = (cancellable != NULL) ? g_object_ref (cancellable) : NULL;
446 : 0 : data->cancelled_id = g_cancellable_connect (cancellable, G_CALLBACK (ensure_users_loaded_cancelled_cb),
447 : : g_object_ref (task), (GDestroyNotify) g_object_unref);
448 : :
449 : 0 : g_task_set_task_data (task, g_steal_pointer (&data_owned), (GDestroyNotify) ensure_users_loaded_data_free);
450 : :
451 [ # # ]: 0 : for (unsigned int i = 0; i < users->len; i++)
452 : : {
453 : 0 : MctUser *user_i = MCT_USER (users->pdata[i]);
454 : 0 : gboolean is_loaded = FALSE;
455 : :
456 : 0 : g_object_get (mct_user_get_act_user (user_i), "is-loaded", &is_loaded, NULL);
457 [ # # ]: 0 : if (!is_loaded)
458 : : {
459 : 0 : data->n_remaining_not_loaded++;
460 : 0 : data->is_loaded_ids[i] =
461 : 0 : g_signal_connect_data (mct_user_get_act_user (user_i), "notify::is-loaded",
462 : : G_CALLBACK (ensure_users_loaded_cb),
463 : : g_object_ref (task), object_unref_closure,
464 : : G_CONNECT_DEFAULT);
465 : : }
466 : : }
467 : :
468 : 0 : ensure_users_loaded_maybe_finish (task);
469 : 0 : }
470 : :
471 : : static gboolean
472 : 0 : user_wrapper_equal (const void *a,
473 : : const void *b)
474 : : {
475 : 0 : MctUser *mct_user = MCT_USER ((void *) a);
476 : 0 : ActUser *act_user = ACT_USER ((void *) b);
477 : :
478 : 0 : return (mct_user_get_act_user (mct_user) == act_user);
479 : : }
480 : :
481 : : static void
482 : 0 : ensure_users_loaded_cb (GObject *object,
483 : : GParamSpec *pspec,
484 : : void *user_data)
485 : : {
486 : 0 : ActUser *user = ACT_USER (object);
487 [ # # ]: 0 : g_autoptr(GTask) task = g_object_ref (G_TASK (user_data));
488 : 0 : EnsureUsersLoadedData *data = g_task_get_task_data (task);
489 : : gboolean found;
490 : 0 : unsigned int user_index = 0;
491 : 0 : gboolean is_loaded = FALSE;
492 : :
493 : 0 : g_object_get (user, "is-loaded", &is_loaded, NULL);
494 [ # # ]: 0 : if (!is_loaded)
495 : 0 : return;
496 : :
497 : 0 : found = g_ptr_array_find_with_equal_func (data->users, user, user_wrapper_equal, &user_index);
498 : 0 : g_assert (found);
499 [ # # ]: 0 : g_clear_signal_handler (&data->is_loaded_ids[user_index], user);
500 : :
501 : 0 : g_assert (data->n_remaining_not_loaded > 0);
502 : 0 : data->n_remaining_not_loaded--;
503 : :
504 : 0 : ensure_users_loaded_maybe_finish (task);
505 : : }
506 : :
507 : : static void
508 : 0 : ensure_users_loaded_cancelled_cb (GCancellable *cancellable,
509 : : void *user_data)
510 : : {
511 : 0 : g_autoptr(GTask) task = g_object_ref (G_TASK (user_data));
512 : :
513 : 0 : ensure_users_loaded_maybe_finish (task);
514 : 0 : }
515 : :
516 : : static MctUser **
517 : 0 : ensure_users_loaded_finish (GAsyncResult *result,
518 : : size_t *out_len,
519 : : GError **error)
520 : : {
521 : 0 : g_autoptr(GPtrArray) users = NULL;
522 : :
523 : 0 : g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
524 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
525 : :
526 : 0 : users = g_task_propagate_pointer (G_TASK (result), error);
527 : :
528 [ # # ]: 0 : if (users == NULL)
529 : 0 : return NULL;
530 : :
531 [ # # ]: 0 : if (out_len != NULL)
532 : 0 : *out_len = users->len;
533 : :
534 : 0 : return (MctUser **) g_ptr_array_free (g_steal_pointer (&users), FALSE);
535 : : }
536 : :
537 : : static GPtrArray * /* (element-type MctUser) */
538 : 0 : ensure_users_loaded_finish_array (GAsyncResult *result,
539 : : GError **error)
540 : : {
541 : 0 : g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
542 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
543 : :
544 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
545 : : }
546 : :
547 : : /**
548 : : * mct_user_manager_load_finish:
549 : : * @self: a user manager
550 : : * @result: result of the asynchronous operation
551 : : * @error: return location for a [type@GLib.Error], or `NULL`
552 : : *
553 : : * Finish an asynchronous load operation started with
554 : : * [method@Malcontent.UserManager.load_async].
555 : : *
556 : : * Returns: true on success, false otherwise
557 : : * Since: 0.14.0
558 : : */
559 : : gboolean
560 : 2 : mct_user_manager_load_finish (MctUserManager *self,
561 : : GAsyncResult *result,
562 : : GError **error)
563 : : {
564 : 2 : g_return_val_if_fail (MCT_IS_USER_MANAGER (self), FALSE);
565 : 2 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
566 : 2 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
567 : :
568 : 2 : return g_task_propagate_boolean (G_TASK (result), error);
569 : : }
570 : :
571 : : /**
572 : : * mct_user_manager_get_is_loaded:
573 : : * @self: a user manager
574 : : *
575 : : * Get the value of [property@Malcontent.UserManager:is-loaded].
576 : : *
577 : : * It is safe to call this before [method@Malcontent.UserManager.load_async] is
578 : : * finished.
579 : : *
580 : : * Returns: true if the user manager is loaded, false otherwise
581 : : * Since: 0.14.0
582 : : */
583 : : gboolean
584 : 0 : mct_user_manager_get_is_loaded (MctUserManager *self)
585 : : {
586 : 0 : g_return_val_if_fail (MCT_IS_USER_MANAGER (self), FALSE);
587 : :
588 : 0 : return self->is_loaded;
589 : : }
590 : :
591 : : static void get_user_cb (GObject *object,
592 : : GAsyncResult *result,
593 : : void *user_data);
594 : :
595 : : /**
596 : : * mct_user_manager_get_user_by_uid_async:
597 : : * @self: a user manager
598 : : * @uid: user ID to fetch
599 : : * @cancellable: a [class@Gio.Cancellable], or `NULL`
600 : : * @callback: callback for when the operation is complete
601 : : * @user_data: data to pass to @callback
602 : : *
603 : : * Get the user with the given @uid.
604 : : *
605 : : * It is not safe to call this before [method@Malcontent.UserManager.load_async]
606 : : * is finished.
607 : : *
608 : : * Since: 0.14.0
609 : : */
610 : : void
611 : 0 : mct_user_manager_get_user_by_uid_async (MctUserManager *self,
612 : : uid_t uid,
613 : : GCancellable *cancellable,
614 : : GAsyncReadyCallback callback,
615 : : void *user_data)
616 : : {
617 [ # # ]: 0 : g_autoptr(GTask) task = NULL;
618 : : ActUser *user;
619 [ # # ]: 0 : g_autoptr(GPtrArray) users = NULL;
620 : :
621 : 0 : g_return_if_fail (MCT_IS_USER_MANAGER (self));
622 : 0 : g_return_if_fail (uid != (uid_t) -1);
623 : 0 : g_return_if_fail (self->is_loaded);
624 : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
625 : :
626 : 0 : task = g_task_new (self, cancellable, callback, user_data);
627 [ # # ]: 0 : g_task_set_source_tag (task, mct_user_manager_get_user_by_uid_async);
628 : :
629 : 0 : user = act_user_manager_get_user_by_id (self->user_manager, uid);
630 : 0 : users = g_ptr_array_new_null_terminated (1, g_object_unref, TRUE);
631 : 0 : g_ptr_array_add (users, mct_user_new_from_act_user (user));
632 : 0 : ensure_users_loaded_async (users, cancellable, get_user_cb, g_steal_pointer (&task));
633 : : }
634 : :
635 : : static void
636 : 0 : get_user_cb (GObject *object,
637 : : GAsyncResult *result,
638 : : void *user_data)
639 : : {
640 : 0 : g_autoptr(GTask) task = g_steal_pointer (&user_data);
641 : 0 : g_autoptr(GError) local_error = NULL;
642 : 0 : g_autoptr(MctUserArray) users = NULL;
643 : 0 : size_t n_users = 0;
644 : :
645 : 0 : users = ensure_users_loaded_finish (result, &n_users, &local_error);
646 : 0 : g_assert (local_error != NULL || n_users == 1);
647 : :
648 [ # # ]: 0 : if (local_error == NULL)
649 : 0 : g_task_return_pointer (task, g_object_ref (users[0]), g_object_unref);
650 : : else
651 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
652 : 0 : }
653 : :
654 : : /**
655 : : * mct_user_manager_get_user_by_uid_finish:
656 : : * @self: a user manager
657 : : * @result: result of the asynchronous operation
658 : : * @error: return location for a [type@GLib.Error], or `NULL`
659 : : *
660 : : * Finish an asynchronous query operation started with
661 : : * [method@Malcontent.UserManager.get_user_by_uid_async].
662 : : *
663 : : * Returns: (transfer full) (nullable): the user, or `NULL` if not found
664 : : * Since: 0.14.0
665 : : */
666 : : MctUser *
667 : 0 : mct_user_manager_get_user_by_uid_finish (MctUserManager *self,
668 : : GAsyncResult *result,
669 : : GError **error)
670 : : {
671 : 0 : g_return_val_if_fail (MCT_IS_USER_MANAGER (self), NULL);
672 : 0 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
673 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
674 : :
675 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
676 : : }
677 : :
678 : : /**
679 : : * mct_user_manager_get_user_by_username_async:
680 : : * @self: a user manager
681 : : * @username: username to fetch
682 : : * @cancellable: a [class@Gio.Cancellable], or `NULL`
683 : : * @callback: callback for when the operation is complete
684 : : * @user_data: data to pass to @callback
685 : : *
686 : : * Get the user with the given @username.
687 : : *
688 : : * It is not safe to call this before [method@Malcontent.UserManager.load_async]
689 : : * is finished.
690 : : *
691 : : * Since: 0.14.0
692 : : */
693 : : void
694 : 0 : mct_user_manager_get_user_by_username_async (MctUserManager *self,
695 : : const char *username,
696 : : GCancellable *cancellable,
697 : : GAsyncReadyCallback callback,
698 : : void *user_data)
699 : : {
700 [ # # ]: 0 : g_autoptr(GTask) task = NULL;
701 : : ActUser *user;
702 [ # # ]: 0 : g_autoptr(GPtrArray) users = NULL;
703 : :
704 : 0 : g_return_if_fail (MCT_IS_USER_MANAGER (self));
705 : 0 : g_return_if_fail (username != NULL);
706 : 0 : g_return_if_fail (self->is_loaded);
707 : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
708 : :
709 : 0 : task = g_task_new (self, cancellable, callback, user_data);
710 [ # # ]: 0 : g_task_set_source_tag (task, mct_user_manager_get_user_by_username_async);
711 : :
712 : 0 : user = act_user_manager_get_user (self->user_manager, username);
713 : 0 : users = g_ptr_array_new_null_terminated (1, g_object_unref, TRUE);
714 : 0 : g_ptr_array_add (users, mct_user_new_from_act_user (user));
715 : 0 : ensure_users_loaded_async (users, cancellable, get_user_cb, g_steal_pointer (&task));
716 : : }
717 : :
718 : : /**
719 : : * mct_user_manager_get_user_by_username_finish:
720 : : * @self: a user manager
721 : : * @result: result of the asynchronous operation
722 : : * @error: return location for a [type@GLib.Error], or `NULL`
723 : : *
724 : : * Finish an asynchronous query operation started with
725 : : * [method@Malcontent.UserManager.get_user_by_username_async].
726 : : *
727 : : * Returns: (transfer full) (nullable): the user, or `NULL` if not found
728 : : * Since: 0.14.0
729 : : */
730 : : MctUser *
731 : 0 : mct_user_manager_get_user_by_username_finish (MctUserManager *self,
732 : : GAsyncResult *result,
733 : : GError **error)
734 : : {
735 : 0 : g_return_val_if_fail (MCT_IS_USER_MANAGER (self), NULL);
736 : 0 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
737 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
738 : :
739 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
740 : : }
741 : :
742 : : static void get_users_cb (GObject *object,
743 : : GAsyncResult *result,
744 : : void *user_data);
745 : :
746 : : /**
747 : : * mct_user_manager_get_family_members_for_user_async:
748 : : * @self: a user manager
749 : : * @user: (transfer none) (not nullable): user to get the family members for
750 : : * @cancellable: a [class@Gio.Cancellable], or `NULL`
751 : : * @callback: callback for when the operation is complete
752 : : * @user_data: data to pass to @callback
753 : : *
754 : : * Get the members of the family containing @user.
755 : : *
756 : : * @user is not returned in the result set.
757 : : *
758 : : * Since: 0.14.0
759 : : */
760 : : void
761 : 0 : mct_user_manager_get_family_members_for_user_async (MctUserManager *self,
762 : : MctUser *user,
763 : : GCancellable *cancellable,
764 : : GAsyncReadyCallback callback,
765 : : void *user_data)
766 : : {
767 [ # # ]: 0 : g_autoptr(GSList) users = NULL;
768 [ # # ]: 0 : g_autoptr(GPtrArray) family = NULL; /* (element-type MctUser) */
769 [ # # ]: 0 : g_autoptr(GTask) task = NULL;
770 : :
771 : 0 : g_return_if_fail (MCT_IS_USER_MANAGER (self));
772 : 0 : g_return_if_fail (MCT_IS_USER (user));
773 : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
774 : 0 : g_return_if_fail (self->is_loaded);
775 : :
776 : 0 : task = g_task_new (self, cancellable, callback, user_data);
777 [ # # ]: 0 : g_task_set_source_tag (task, mct_user_manager_get_family_members_for_user_async);
778 : :
779 : : /* Build the family list */
780 : 0 : users = act_user_manager_list_users (self->user_manager);
781 : 0 : family = g_ptr_array_new_null_terminated (g_slist_length (users), g_object_unref, TRUE);
782 : :
783 [ # # ]: 0 : for (GSList *l = users; l != NULL; l = l->next)
784 : : {
785 : 0 : g_autoptr(MctUser) l_user = mct_user_new_from_act_user (l->data);
786 : :
787 [ # # # # ]: 0 : if (!mct_user_equal (l_user, user) &&
788 : 0 : mct_user_is_in_same_family (l_user, user))
789 : 0 : g_ptr_array_add (family, g_steal_pointer (&l_user));
790 : : }
791 : :
792 : : /* Ensure they’re all loaded */
793 : 0 : ensure_users_loaded_async (family, cancellable, get_users_cb, g_steal_pointer (&task));
794 : : }
795 : :
796 : : static void
797 : 0 : get_users_cb (GObject *object,
798 : : GAsyncResult *result,
799 : : void *user_data)
800 : : {
801 : 0 : g_autoptr(GTask) task = g_steal_pointer (&user_data);
802 : 0 : g_autoptr(GError) local_error = NULL;
803 : 0 : g_autoptr(GPtrArray) users = NULL; /* (element-type MctUser) */
804 : :
805 : 0 : users = ensure_users_loaded_finish_array (result, &local_error);
806 : :
807 [ # # ]: 0 : if (local_error == NULL)
808 : 0 : g_task_return_pointer (task, g_steal_pointer (&users), (GDestroyNotify) g_ptr_array_unref);
809 : : else
810 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
811 : 0 : }
812 : :
813 : : /**
814 : : * mct_user_manager_get_family_members_for_user_finish:
815 : : * @self: a user manager
816 : : * @result: result of the asynchronous operation
817 : : * @out_len: (out) (optional): return location for the array length of the
818 : : * return value
819 : : * @error: return location for a [type@GLib.Error], or `NULL`
820 : : *
821 : : * Finish an asynchronous query operation started with
822 : : * [method@Malcontent.UserManager.get_family_members_for_user_async].
823 : : *
824 : : * Returns: (array length=out_len) (transfer full) (not nullable): array of
825 : : * family members in an undefined order, may be empty
826 : : * Since: 0.14.0
827 : : */
828 : : MctUser **
829 : 0 : mct_user_manager_get_family_members_for_user_finish (MctUserManager *self,
830 : : GAsyncResult *result,
831 : : size_t *out_len,
832 : : GError **error)
833 : : {
834 : 0 : g_autoptr(GPtrArray) family = NULL;
835 : :
836 : 0 : g_return_val_if_fail (MCT_IS_USER_MANAGER (self), NULL);
837 : 0 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
838 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
839 : :
840 : 0 : family = g_task_propagate_pointer (G_TASK (result), error);
841 : :
842 [ # # ]: 0 : if (family == NULL)
843 : 0 : return NULL;
844 : :
845 [ # # ]: 0 : if (out_len != NULL)
846 : 0 : *out_len = family->len;
847 : :
848 : 0 : return (MctUser **) g_ptr_array_free (g_steal_pointer (&family), FALSE);
849 : : }
850 : :
851 : : /**
852 : : * mct_user_manager_get_all_users_async:
853 : : * @self: a user manager
854 : : * @cancellable: a [class@Gio.Cancellable], or `NULL`
855 : : * @callback: callback for when the operation is complete
856 : : * @user_data: data to pass to @callback
857 : : *
858 : : * Get all the non-system users on the system.
859 : : *
860 : : * Since: 0.14.0
861 : : */
862 : : void
863 : 0 : mct_user_manager_get_all_users_async (MctUserManager *self,
864 : : GCancellable *cancellable,
865 : : GAsyncReadyCallback callback,
866 : : void *user_data)
867 : : {
868 [ # # ]: 0 : g_autoptr(GSList) users = NULL;
869 [ # # ]: 0 : g_autoptr(GPtrArray) users_array = NULL; /* (element-type MctUser) */
870 [ # # ]: 0 : g_autoptr(GTask) task = NULL;
871 : :
872 : 0 : g_return_if_fail (MCT_IS_USER_MANAGER (self));
873 : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
874 : 0 : g_return_if_fail (self->is_loaded);
875 : :
876 : 0 : task = g_task_new (self, cancellable, callback, user_data);
877 [ # # ]: 0 : g_task_set_source_tag (task, mct_user_manager_get_all_users_async);
878 : :
879 : : /* Build the user list */
880 : 0 : users = act_user_manager_list_users (self->user_manager);
881 : 0 : users_array = g_ptr_array_new_null_terminated (g_slist_length (users), g_object_unref, TRUE);
882 : :
883 [ # # ]: 0 : for (GSList *l = users; l != NULL; l = l->next)
884 : 0 : g_ptr_array_add (users_array, mct_user_new_from_act_user (l->data));
885 : :
886 : : /* Ensure they’re all loaded */
887 : 0 : ensure_users_loaded_async (users_array, cancellable, get_users_cb, g_steal_pointer (&task));
888 : : }
889 : :
890 : : /**
891 : : * mct_user_manager_get_all_users_finish:
892 : : * @self: a user manager
893 : : * @result: result of the asynchronous operation
894 : : * @out_len: (out) (optional): return location for the array length of the
895 : : * return value
896 : : * @error: return location for a [type@GLib.Error], or `NULL`
897 : : *
898 : : * Finish an asynchronous query operation started with
899 : : * [method@Malcontent.UserManager.get_all_users_async].
900 : : *
901 : : * Returns: (array length=out_len) (transfer full) (not nullable): array of
902 : : * users in an undefined order, may be empty
903 : : * Since: 0.14.0
904 : : */
905 : : MctUser **
906 : 0 : mct_user_manager_get_all_users_finish (MctUserManager *self,
907 : : GAsyncResult *result,
908 : : size_t *out_len,
909 : : GError **error)
910 : : {
911 : 0 : g_autoptr(GPtrArray) users = NULL;
912 : :
913 : 0 : g_return_val_if_fail (MCT_IS_USER_MANAGER (self), NULL);
914 : 0 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
915 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
916 : :
917 : 0 : users = g_task_propagate_pointer (G_TASK (result), error);
918 : :
919 [ # # ]: 0 : if (users == NULL)
920 : 0 : return NULL;
921 : :
922 [ # # ]: 0 : if (out_len != NULL)
923 : 0 : *out_len = users->len;
924 : :
925 : 0 : return (MctUser **) g_ptr_array_free (g_steal_pointer (&users), FALSE);
926 : : }
|