LCOV - code coverage report
Current view: top level - libmalcontent - user-manager.c (source / functions) Coverage Total Hit
Test: 2 coverage DB files Lines: 20.4 % 294 60
Test Date: 2025-09-15 13:55:46 Functions: 29.7 % 37 11
Branches: 10.8 % 120 13

             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                 :             : }
        

Generated by: LCOV version 2.0-1