Blame SOURCES/0001-daemon-don-t-treat-explicitly-requested-users-as-cac.patch

15a511
From 59235d291c9aac5f68e17cc927f142cf5e532e46 Mon Sep 17 00:00:00 2001
889f5f
From: Ray Strode <rstrode@redhat.com>
889f5f
Date: Thu, 4 May 2017 12:04:05 -0400
889f5f
Subject: [PATCH] daemon: don't treat explicitly requested users as "cached"
889f5f
889f5f
The ListCachedUsers method currently returns users that have
889f5f
been explicitly requested by a client.  It's weird that merely
889f5f
querying a user can make it show up in login screen user lists.
889f5f
Furthermore, UncacheUser is broken since commit
889f5f
177509e9460b149ecbf85e75c930be2ea00b7d05 because the user has
889f5f
been explicitly requested in order to uncache it.  So trying
889f5f
to uncache a user inadvertently caches the user.
889f5f
889f5f
This commit fixes that.
889f5f
---
889f5f
 src/daemon.c | 71 +++++++++++++++++++++++++++++++++++++++---------------------
889f5f
 src/user.c   | 17 +++++++++++++++
889f5f
 src/user.h   |  3 +++
889f5f
 3 files changed, 66 insertions(+), 25 deletions(-)
889f5f
889f5f
diff --git a/src/daemon.c b/src/daemon.c
15a511
index 312394a..6e3e4b3 100644
889f5f
--- a/src/daemon.c
889f5f
+++ b/src/daemon.c
15a511
@@ -329,100 +329,108 @@ entry_generator_requested_users (Daemon       *daemon,
889f5f
                 while (node != NULL) {
889f5f
                         const char *name;
889f5f
 
889f5f
                         name = node->data;
889f5f
                         node = node->next;
889f5f
 
889f5f
                         *state = node;
889f5f
 
889f5f
                         if (!g_hash_table_lookup (users, name)) {
889f5f
                                 pwent = getpwnam (name);
889f5f
                                 if (pwent == NULL) {
889f5f
                                         g_debug ("user '%s' requested previously but not present on system", name);
889f5f
                                 } else {
889f5f
                                         *shadow_entry = getspnam (pwent->pw_name);
889f5f
 
889f5f
                                         return pwent;
889f5f
                                 }
889f5f
                         }
889f5f
                 }
889f5f
         }
889f5f
 
889f5f
         /* Last iteration */
889f5f
 
889f5f
         *state = NULL;
889f5f
         return NULL;
889f5f
 }
889f5f
 
889f5f
 static void
889f5f
 load_entries (Daemon             *daemon,
889f5f
               GHashTable         *users,
889f5f
-              gboolean            allow_system_users,
889f5f
+              gboolean            explicitly_requested,
889f5f
               EntryGeneratorFunc  entry_generator)
889f5f
 {
889f5f
         gpointer generator_state = NULL;
889f5f
         struct passwd *pwent;
889f5f
         struct spwd *spent = NULL;
889f5f
         User *user = NULL;
889f5f
 
889f5f
         g_assert (entry_generator != NULL);
889f5f
 
889f5f
         for (;;) {
889f5f
                 spent = NULL;
889f5f
                 pwent = entry_generator (daemon, users, &generator_state, &spent);
889f5f
                 if (pwent == NULL)
889f5f
                         break;
889f5f
 
889f5f
                 /* Skip system users... */
889f5f
-                if (!allow_system_users && !user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, spent? spent->sp_pwdp : NULL)) {
889f5f
+                if (!explicitly_requested && !user_classify_is_human (pwent->pw_uid, pwent->pw_name, pwent->pw_shell, spent? spent->sp_pwdp : NULL)) {
889f5f
                         g_debug ("skipping user: %s", pwent->pw_name);
889f5f
                         continue;
889f5f
                 }
889f5f
 
889f5f
-                /* ignore duplicate entries */
889f5f
-                if (g_hash_table_lookup (users, pwent->pw_name)) {
889f5f
-                        continue;
889f5f
-                }
889f5f
+                /* Only process users that haven't been processed yet.
889f5f
+                 * We do always make sure entries get promoted
889f5f
+                 * to "cached" status if they are supposed to be
889f5f
+                 */
889f5f
+
889f5f
+                user = g_hash_table_lookup (users, pwent->pw_name);
889f5f
 
889f5f
-                user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name);
889f5f
                 if (user == NULL) {
889f5f
-                        user = user_new (daemon, pwent->pw_uid);
889f5f
-                } else {
889f5f
-                        g_object_ref (user);
889f5f
-                }
889f5f
+                        user = g_hash_table_lookup (daemon->priv->users, pwent->pw_name);
889f5f
+                        if (user == NULL) {
889f5f
+                                user = user_new (daemon, pwent->pw_uid);
889f5f
+                        } else {
889f5f
+                                g_object_ref (user);
889f5f
+                        }
889f5f
 
889f5f
-                /* freeze & update users not already in the new list */
889f5f
-                g_object_freeze_notify (G_OBJECT (user));
889f5f
-                user_update_from_pwent (user, pwent, spent);
15a511
+                        /* freeze & update users not already in the new list */
15a511
+                        g_object_freeze_notify (G_OBJECT (user));
15a511
+                        user_update_from_pwent (user, pwent, spent);
889f5f
 
889f5f
-                g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user);
889f5f
-                g_debug ("loaded user: %s", user_get_user_name (user));
15a511
+                        g_hash_table_insert (users, g_strdup (user_get_user_name (user)), user);
15a511
+                        g_debug ("loaded user: %s", user_get_user_name (user));
15a511
+                }
15a511
+
889f5f
+                if (!explicitly_requested) {
889f5f
+                        user_set_cached (user, TRUE);
889f5f
+                }
889f5f
         }
889f5f
 
889f5f
         /* Generator should have cleaned up */
889f5f
         g_assert (generator_state == NULL);
889f5f
 }
889f5f
 
889f5f
 static GHashTable *
889f5f
 create_users_hash_table (void)
889f5f
 {
889f5f
         return g_hash_table_new_full (g_str_hash,
889f5f
                                       g_str_equal,
889f5f
                                       g_free,
889f5f
                                       g_object_unref);
889f5f
 }
889f5f
 
889f5f
 static void
889f5f
 reload_users (Daemon *daemon)
889f5f
 {
889f5f
         GHashTable *users;
889f5f
         GHashTable *old_users;
889f5f
         GHashTable *local;
889f5f
         GHashTableIter iter;
889f5f
         gpointer name;
889f5f
         User *user;
889f5f
 
889f5f
         /* Track the users that we saw during our (re)load */
889f5f
         users = create_users_hash_table ();
889f5f
 
889f5f
         /*
889f5f
          * NOTE: As we load data from all the sources, notifies are
15a511
@@ -432,71 +440,79 @@ reload_users (Daemon *daemon)
15a511
 
15a511
         /* Load the local users into our hash table */
889f5f
         load_entries (daemon, users, FALSE, entry_generator_fgetpwent);
889f5f
         local = g_hash_table_new (g_str_hash, g_str_equal);
889f5f
         g_hash_table_iter_init (&iter, users);
889f5f
         while (g_hash_table_iter_next (&iter, &name, NULL))
889f5f
                 g_hash_table_add (local, name);
889f5f
 
889f5f
         /* and add users to hash table that were explicitly requested  */
889f5f
         load_entries (daemon, users, TRUE, entry_generator_requested_users);
889f5f
 
889f5f
         /* Now add/update users from other sources, possibly non-local */
889f5f
         load_entries (daemon, users, FALSE, entry_generator_cachedir);
889f5f
 
15a511
         wtmp_helper_update_login_frequencies (users);
889f5f
 
889f5f
         /* Mark which users are local, which are not */
889f5f
         g_hash_table_iter_init (&iter, users);
889f5f
         while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user))
889f5f
                 user_update_local_account_property (user, g_hash_table_lookup (local, name) != NULL);
889f5f
 
889f5f
         g_hash_table_destroy (local);
889f5f
 
889f5f
         /* Swap out the users */
889f5f
         old_users = daemon->priv->users;
889f5f
         daemon->priv->users = users;
889f5f
 
889f5f
         /* Remove all the old users */
889f5f
         g_hash_table_iter_init (&iter, old_users);
889f5f
         while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) {
889f5f
-                if (!g_hash_table_lookup (users, name)) {
889f5f
+                User *refreshed_user;
889f5f
+
889f5f
+                refreshed_user = g_hash_table_lookup (users, name);
889f5f
+
889f5f
+                if (!refreshed_user || !user_get_cached (refreshed_user)) {
889f5f
                         user_unregister (user);
889f5f
                         accounts_accounts_emit_user_deleted (ACCOUNTS_ACCOUNTS (daemon),
889f5f
                                                              user_get_object_path (user));
889f5f
                 }
889f5f
         }
889f5f
 
889f5f
         /* Register all the new users */
889f5f
         g_hash_table_iter_init (&iter, users);
889f5f
         while (g_hash_table_iter_next (&iter, &name, (gpointer *)&user)) {
889f5f
-                if (!g_hash_table_lookup (old_users, name)) {
889f5f
+                User *stale_user;
889f5f
+
889f5f
+                stale_user = g_hash_table_lookup (old_users, name);
889f5f
+
889f5f
+                if (!stale_user || !user_get_cached (stale_user) && user_get_cached (user)) {
889f5f
                         user_register (user);
889f5f
                         accounts_accounts_emit_user_added (ACCOUNTS_ACCOUNTS (daemon),
889f5f
                                                            user_get_object_path (user));
889f5f
                 }
889f5f
                 g_object_thaw_notify (G_OBJECT (user));
889f5f
         }
889f5f
 
889f5f
         g_hash_table_destroy (old_users);
889f5f
 }
889f5f
 
889f5f
 static gboolean
889f5f
 reload_users_timeout (Daemon *daemon)
889f5f
 {
889f5f
         reload_users (daemon);
889f5f
         daemon->priv->reload_id = 0;
889f5f
 
889f5f
         return FALSE;
889f5f
 }
889f5f
 
889f5f
 static gboolean load_autologin (Daemon    *daemon,
889f5f
                                 gchar    **name,
889f5f
                                 gboolean  *enabled,
889f5f
                                 GError   **error);
889f5f
 
889f5f
 static gboolean
889f5f
 reload_autologin_timeout (Daemon *daemon)
889f5f
 {
889f5f
         gboolean enabled;
889f5f
         gchar *name = NULL;
889f5f
         GError *error = NULL;
15a511
@@ -911,60 +927,65 @@ list_user_data_new (Daemon                *daemon,
889f5f
 static void
889f5f
 list_user_data_free (ListUserData *data)
889f5f
 {
889f5f
         g_object_unref (data->daemon);
889f5f
         g_free (data);
889f5f
 }
889f5f
 
889f5f
 static gboolean
889f5f
 finish_list_cached_users (gpointer user_data)
889f5f
 {
889f5f
         ListUserData *data = user_data;
889f5f
         GPtrArray *object_paths;
889f5f
         GHashTableIter iter;
889f5f
         const gchar *name;
889f5f
         User *user;
889f5f
         uid_t uid;
889f5f
         const gchar *shell;
889f5f
 
889f5f
         object_paths = g_ptr_array_new ();
889f5f
 
889f5f
         g_hash_table_iter_init (&iter, data->daemon->priv->users);
889f5f
         while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&user)) {
889f5f
                 uid = user_get_uid (user);
889f5f
                 shell = user_get_shell (user);
889f5f
 
889f5f
                 if (!user_classify_is_human (uid, name, shell, NULL)) {
889f5f
                         g_debug ("user %s %ld excluded", name, (long) uid);
889f5f
                         continue;
889f5f
                 }
889f5f
 
889f5f
+                if (!user_get_cached (user)) {
889f5f
+                        g_debug ("user %s %ld not cached", name, (long) uid);
889f5f
+                        continue;
889f5f
+                }
889f5f
+
889f5f
                 g_debug ("user %s %ld not excluded", name, (long) uid);
889f5f
                 g_ptr_array_add (object_paths, (gpointer) user_get_object_path (user));
889f5f
         }
889f5f
         g_ptr_array_add (object_paths, NULL);
889f5f
 
889f5f
         accounts_accounts_complete_list_cached_users (NULL, data->context, (const gchar * const *) object_paths->pdata);
889f5f
 
889f5f
         g_ptr_array_free (object_paths, TRUE);
889f5f
 
889f5f
         list_user_data_free (data);
889f5f
 
889f5f
         return FALSE;
889f5f
 }
889f5f
 
889f5f
 static gboolean
889f5f
 daemon_list_cached_users (AccountsAccounts      *accounts,
889f5f
                           GDBusMethodInvocation *context)
889f5f
 {
889f5f
         Daemon *daemon = (Daemon*)accounts;
889f5f
         ListUserData *data;
889f5f
 
889f5f
         data = list_user_data_new (daemon, context);
889f5f
 
889f5f
         if (daemon->priv->reload_id > 0) {
889f5f
                 /* reload in progress, wait a bit */
889f5f
                 g_idle_add (finish_list_cached_users, data);
889f5f
         }
889f5f
         else {
889f5f
                 finish_list_cached_users (data);
889f5f
         }
15a511
@@ -1151,123 +1172,123 @@ daemon_cache_user (AccountsAccounts      *accounts,
889f5f
 static void
889f5f
 daemon_uncache_user_authorized_cb (Daemon                *daemon,
889f5f
                                    User                  *dummy,
889f5f
                                    GDBusMethodInvocation *context,
889f5f
                                    gpointer               data)
889f5f
 {
889f5f
         const gchar *user_name = data;
889f5f
         gchar       *filename;
889f5f
         User        *user;
889f5f
 
889f5f
         sys_log (context, "uncache user '%s'", user_name);
889f5f
 
889f5f
         user = daemon_local_find_user_by_name (daemon, user_name);
889f5f
         if (user == NULL) {
889f5f
                 throw_error (context, ERROR_USER_DOES_NOT_EXIST,
889f5f
                              "No user with the name %s found", user_name);
889f5f
                 return;
889f5f
         }
889f5f
 
889f5f
         /* Always use the canonical user name looked up */
889f5f
         user_name = user_get_user_name (user);
889f5f
 
889f5f
         filename = g_build_filename (USERDIR, user_name, NULL);
889f5f
         g_remove (filename);
889f5f
         g_free (filename);
889f5f
 
889f5f
         filename = g_build_filename (ICONDIR, user_name, NULL);
889f5f
         g_remove (filename);
889f5f
         g_free (filename);
889f5f
 
889f5f
+        user_set_cached (user, FALSE);
889f5f
+
889f5f
         accounts_accounts_complete_uncache_user (NULL, context);
889f5f
 
889f5f
         queue_reload_users (daemon);
889f5f
 }
889f5f
 
889f5f
 static gboolean
889f5f
 daemon_uncache_user (AccountsAccounts      *accounts,
889f5f
                      GDBusMethodInvocation *context,
889f5f
                      const gchar           *user_name)
889f5f
 {
889f5f
         Daemon *daemon = (Daemon*)accounts;
889f5f
 
889f5f
         daemon_local_check_auth (daemon,
889f5f
                                  NULL,
889f5f
                                  "org.freedesktop.accounts.user-administration",
889f5f
                                  TRUE,
889f5f
                                  daemon_uncache_user_authorized_cb,
889f5f
                                  context,
889f5f
                                  g_strdup (user_name),
889f5f
                                  g_free);
889f5f
 
889f5f
         return TRUE;
889f5f
 }
889f5f
 
889f5f
 typedef struct {
889f5f
         uid_t uid;
889f5f
         gboolean remove_files;
889f5f
 } DeleteUserData;
889f5f
 
889f5f
 static void
889f5f
 daemon_delete_user_authorized_cb (Daemon                *daemon,
889f5f
                                   User                  *dummy,
889f5f
                                   GDBusMethodInvocation *context,
889f5f
                                   gpointer               data)
889f5f
 
889f5f
 {
889f5f
         DeleteUserData *ud = data;
889f5f
         GError *error;
889f5f
         gchar *filename;
889f5f
         struct passwd *pwent;
889f5f
         const gchar *argv[6];
889f5f
+        User *user;
889f5f
 
889f5f
         pwent = getpwuid (ud->uid);
889f5f
 
889f5f
         if (pwent == NULL) {
889f5f
                 throw_error (context, ERROR_USER_DOES_NOT_EXIST, "No user with uid %d found", ud->uid);
889f5f
 
889f5f
                 return;
889f5f
         }
889f5f
 
889f5f
         sys_log (context, "delete user '%s' (%d)", pwent->pw_name, ud->uid);
889f5f
 
889f5f
-        if (daemon->priv->autologin != NULL) {
889f5f
-                User *user;
889f5f
+        user = daemon_local_find_user_by_id (daemon, ud->uid);
889f5f
 
889f5f
-                user = daemon_local_find_user_by_id (daemon, ud->uid);
889f5f
+        if (user != NULL) {
889f5f
+                user_set_cached (user, FALSE);
889f5f
 
889f5f
-                g_assert (user != NULL);
889f5f
-
889f5f
-                if (daemon->priv->autologin == user) {
889f5f
+                if (daemon->priv->autologin  == user) {
889f5f
                         daemon_local_set_automatic_login (daemon, user, FALSE, NULL);
889f5f
                 }
889f5f
-
889f5f
         }
889f5f
 
889f5f
         filename = g_build_filename (USERDIR, pwent->pw_name, NULL);
889f5f
         g_remove (filename);
889f5f
         g_free (filename);
889f5f
 
889f5f
         filename = g_build_filename (ICONDIR, pwent->pw_name, NULL);
889f5f
         g_remove (filename);
889f5f
         g_free (filename);
889f5f
 
889f5f
         argv[0] = "/usr/sbin/userdel";
889f5f
         if (ud->remove_files) {
889f5f
                 argv[1] = "-f";
889f5f
                 argv[2] = "-r";
889f5f
                 argv[3] = "--";
889f5f
                 argv[4] = pwent->pw_name;
889f5f
                 argv[5] = NULL;
889f5f
         }
889f5f
         else {
889f5f
                 argv[1] = "-f";
889f5f
                 argv[2] = "--";
889f5f
                 argv[3] = pwent->pw_name;
889f5f
                 argv[4] = NULL;
889f5f
         }
889f5f
 
889f5f
         error = NULL;
889f5f
         if (!spawn_with_login_uid (context, argv, &error)) {
889f5f
                 throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
889f5f
                 g_error_free (error);
889f5f
                 return;
889f5f
diff --git a/src/user.c b/src/user.c
15a511
index 802d07a..a83cfe4 100644
889f5f
--- a/src/user.c
889f5f
+++ b/src/user.c
15a511
@@ -83,60 +83,61 @@ struct User {
889f5f
         GKeyFile     *keyfile;
889f5f
 
889f5f
         uid_t         uid;
889f5f
         gid_t         gid;
889f5f
         gchar        *user_name;
889f5f
         gchar        *real_name;
889f5f
         AccountType   account_type;
889f5f
         PasswordMode  password_mode;
889f5f
         gchar        *password_hint;
889f5f
         gchar        *home_dir;
889f5f
         gchar        *shell;
889f5f
         gchar        *email;
889f5f
         gchar        *language;
889f5f
         gchar        *x_session;
889f5f
         gchar        *location;
889f5f
         guint64       login_frequency;
889f5f
         gint64        login_time;
15a511
         gint64        expiration_time;
15a511
         gint64        last_change_time;
15a511
         gint64        min_days_between_changes;
15a511
         gint64        max_days_between_changes;
15a511
         gint64        days_to_warn;
15a511
         gint64        days_after_expiration_until_lock;
889f5f
         GVariant     *login_history;
889f5f
         gchar        *icon_file;
889f5f
         gchar        *default_icon_file;
889f5f
         gboolean      locked;
889f5f
         gboolean      automatic_login;
889f5f
         gboolean      system_account;
889f5f
         gboolean      local_account;
889f5f
+        gboolean      cached;
15a511
 
15a511
         guint        *extension_ids;
15a511
         guint         n_extension_ids;
889f5f
 };
889f5f
 
889f5f
 typedef struct UserClass
889f5f
 {
889f5f
         AccountsUserSkeletonClass parent_class;
889f5f
 } UserClass;
889f5f
 
889f5f
 static void user_accounts_user_iface_init (AccountsUserIface *iface);
889f5f
 
889f5f
 G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init));
889f5f
 
889f5f
 static gint
889f5f
 account_type_from_pwent (struct passwd *pwent)
889f5f
 {
889f5f
         struct group *grp;
889f5f
         gint i;
889f5f
 
889f5f
         if (pwent->pw_uid == 0) {
889f5f
                 g_debug ("user is root so account type is administrator");
889f5f
                 return ACCOUNT_TYPE_ADMINISTRATOR;
889f5f
         }
889f5f
 
889f5f
         grp = getgrnam (ADMIN_GROUP);
889f5f
         if (grp == NULL) {
889f5f
                 g_debug (ADMIN_GROUP " group not found");
889f5f
                 return ACCOUNT_TYPE_STANDARD;
889f5f
         }
15a511
@@ -339,109 +340,112 @@ user_update_from_keyfile (User     *user,
889f5f
                 user->location = s;
889f5f
                 g_object_notify (G_OBJECT (user), "location");
889f5f
         }
889f5f
 
889f5f
         s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL);
889f5f
         if (s != NULL) {
889f5f
                 g_free (user->password_hint);
889f5f
                 user->password_hint = s;
889f5f
                 g_object_notify (G_OBJECT (user), "password-hint");
889f5f
         }
889f5f
 
889f5f
         s = g_key_file_get_string (keyfile, "User", "Icon", NULL);
889f5f
         if (s != NULL) {
889f5f
                 g_free (user->icon_file);
889f5f
                 user->icon_file = s;
889f5f
                 g_object_notify (G_OBJECT (user), "icon-file");
889f5f
         }
889f5f
 
889f5f
         if (g_key_file_has_key (keyfile, "User", "SystemAccount", NULL)) {
889f5f
             gboolean system_account;
889f5f
 
889f5f
             system_account = g_key_file_get_boolean (keyfile, "User", "SystemAccount", NULL);
889f5f
             if (system_account != user->system_account) {
889f5f
                     user->system_account = system_account;
889f5f
                     g_object_notify (G_OBJECT (user), "system-account");
889f5f
             }
889f5f
         }
889f5f
 
889f5f
         g_clear_pointer (&user->keyfile, g_key_file_unref);
889f5f
         user->keyfile = g_key_file_ref (keyfile);
889f5f
+        user_set_cached (user, TRUE);
889f5f
 
889f5f
         g_object_thaw_notify (G_OBJECT (user));
889f5f
 }
889f5f
 
889f5f
 void
889f5f
 user_update_local_account_property (User          *user,
889f5f
                                     gboolean       local)
889f5f
 {
889f5f
         if (local == user->local_account)
889f5f
                 return;
889f5f
         user->local_account = local;
889f5f
         g_object_notify (G_OBJECT (user), "local-account");
889f5f
 }
889f5f
 
889f5f
 void
889f5f
 user_update_system_account_property (User          *user,
889f5f
                                      gboolean       system)
889f5f
 {
889f5f
         if (system == user->system_account)
889f5f
                 return;
889f5f
         user->system_account = system;
889f5f
         g_object_notify (G_OBJECT (user), "system-account");
889f5f
 }
889f5f
 
889f5f
 static void
889f5f
 user_save_to_keyfile (User     *user,
889f5f
                       GKeyFile *keyfile)
889f5f
 {
889f5f
         g_key_file_remove_group (keyfile, "User", NULL);
889f5f
 
889f5f
         if (user->email)
889f5f
                 g_key_file_set_string (keyfile, "User", "Email", user->email);
889f5f
 
889f5f
         if (user->language)
889f5f
                 g_key_file_set_string (keyfile, "User", "Language", user->language);
889f5f
 
889f5f
         if (user->x_session)
889f5f
                 g_key_file_set_string (keyfile, "User", "XSession", user->x_session);
889f5f
 
889f5f
         if (user->location)
889f5f
                 g_key_file_set_string (keyfile, "User", "Location", user->location);
889f5f
 
889f5f
         if (user->password_hint)
889f5f
                 g_key_file_set_string (keyfile, "User", "PasswordHint", user->password_hint);
889f5f
 
889f5f
         if (user->icon_file)
889f5f
                 g_key_file_set_string (keyfile, "User", "Icon", user->icon_file);
889f5f
 
889f5f
         g_key_file_set_boolean (keyfile, "User", "SystemAccount", user->system_account);
889f5f
+
889f5f
+        user_set_cached (user, TRUE);
889f5f
 }
889f5f
 
889f5f
 static void
889f5f
 save_extra_data (User *user)
889f5f
 {
889f5f
         gchar *filename;
889f5f
         gchar *data;
889f5f
         GError *error;
889f5f
 
889f5f
         user_save_to_keyfile (user, user->keyfile);
889f5f
 
889f5f
         error = NULL;
889f5f
         data = g_key_file_to_data (user->keyfile, NULL, &error);
889f5f
         if (error == NULL) {
889f5f
                 filename = g_build_filename (USERDIR,
889f5f
                                              user->user_name,
889f5f
                                              NULL);
889f5f
                 g_file_set_contents (filename, data, -1, &error);
889f5f
                 g_free (filename);
889f5f
                 g_free (data);
889f5f
         }
889f5f
         if (error) {
889f5f
                 g_warning ("Saving data for user %s failed: %s",
889f5f
                            user->user_name, error->message);
889f5f
                 g_error_free (error);
889f5f
         }
889f5f
 }
889f5f
 
889f5f
 static void
889f5f
 move_extra_data (const gchar *old_name,
15a511
@@ -810,60 +814,73 @@ user_get_user_name (User *user)
889f5f
 gboolean
889f5f
 user_get_system_account (User *user)
889f5f
 {
889f5f
         return user->system_account;
889f5f
 }
889f5f
 
889f5f
 gboolean
889f5f
 user_get_local_account (User *user)
889f5f
 {
889f5f
         return user->local_account;
889f5f
 }
889f5f
 
889f5f
 const gchar *
889f5f
 user_get_object_path (User *user)
889f5f
 {
889f5f
         return user->object_path;
889f5f
 }
889f5f
 
889f5f
 uid_t
889f5f
 user_get_uid (User *user)
889f5f
 {
889f5f
         return user->uid;
889f5f
 }
889f5f
 
889f5f
 const gchar *
889f5f
 user_get_shell(User *user)
889f5f
 {
889f5f
 	return user->shell;
889f5f
 }
889f5f
 
889f5f
+gboolean
889f5f
+user_get_cached (User *user)
889f5f
+{
889f5f
+        return user->cached;
889f5f
+}
889f5f
+
889f5f
+void
889f5f
+user_set_cached (User     *user,
889f5f
+                 gboolean  cached)
889f5f
+{
889f5f
+        user->cached = cached;
889f5f
+}
889f5f
+
889f5f
 static void
889f5f
 throw_error (GDBusMethodInvocation *context,
889f5f
              gint                   error_code,
889f5f
              const gchar           *format,
889f5f
              ...)
889f5f
 {
889f5f
         va_list args;
889f5f
         gchar *message;
889f5f
 
889f5f
         va_start (args, format);
889f5f
         message = g_strdup_vprintf (format, args);
889f5f
         va_end (args);
889f5f
 
889f5f
         g_dbus_method_invocation_return_error (context, ERROR, error_code, "%s", message);
889f5f
 
889f5f
         g_free (message);
889f5f
 }
889f5f
 
889f5f
 static void
889f5f
 user_change_real_name_authorized_cb (Daemon                *daemon,
889f5f
                                      User                  *user,
889f5f
                                      GDBusMethodInvocation *context,
889f5f
                                      gpointer               data)
889f5f
 
889f5f
 {
889f5f
         gchar *name = data;
889f5f
         GError *error;
889f5f
         const gchar *argv[6];
889f5f
 
889f5f
         if (g_strcmp0 (user->real_name, name) != 0) {
889f5f
diff --git a/src/user.h b/src/user.h
889f5f
index 22548f9..39c6f13 100644
889f5f
--- a/src/user.h
889f5f
+++ b/src/user.h
889f5f
@@ -36,47 +36,50 @@ G_BEGIN_DECLS
889f5f
 #define IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_USER))
889f5f
 
889f5f
 typedef enum {
889f5f
         ACCOUNT_TYPE_STANDARD,
889f5f
         ACCOUNT_TYPE_ADMINISTRATOR,
889f5f
 #define ACCOUNT_TYPE_LAST ACCOUNT_TYPE_ADMINISTRATOR
889f5f
 } AccountType;
889f5f
 
889f5f
 typedef enum {
889f5f
         PASSWORD_MODE_REGULAR,
889f5f
         PASSWORD_MODE_SET_AT_LOGIN,
889f5f
         PASSWORD_MODE_NONE,
889f5f
 #define PASSWORD_MODE_LAST PASSWORD_MODE_NONE
889f5f
 } PasswordMode;
889f5f
 
889f5f
 /* local methods */
889f5f
 
889f5f
 GType          user_get_type                (void) G_GNUC_CONST;
889f5f
 User *         user_new                     (Daemon        *daemon,
889f5f
                                              uid_t          uid);
889f5f
 
889f5f
 void           user_update_from_pwent       (User          *user,
889f5f
                                              struct passwd *pwent,
889f5f
                                              struct spwd   *spent);
889f5f
 void           user_update_from_keyfile     (User          *user,
889f5f
                                              GKeyFile      *keyfile);
889f5f
 void           user_update_local_account_property (User          *user,
889f5f
                                                    gboolean       local);
889f5f
 void           user_update_system_account_property (User          *user,
889f5f
                                                     gboolean       system);
889f5f
+gboolean       user_get_cached              (User          *user);
889f5f
+void           user_set_cached              (User          *user,
889f5f
+                                             gboolean       cached);
889f5f
 
889f5f
 void           user_register                (User          *user);
889f5f
 void           user_unregister              (User          *user);
889f5f
 void           user_changed                 (User          *user);
889f5f
 
889f5f
 void           user_save                    (User          *user);
889f5f
 
889f5f
 const gchar *  user_get_user_name           (User          *user);
889f5f
 gboolean       user_get_system_account      (User          *user);
889f5f
 gboolean       user_get_local_account       (User          *user);
889f5f
 const gchar *  user_get_object_path         (User          *user);
889f5f
 uid_t          user_get_uid                 (User          *user);
889f5f
 const gchar *  user_get_shell               (User          *user);
889f5f
 
889f5f
 G_END_DECLS
889f5f
 
889f5f
 #endif
889f5f
-- 
889f5f
2.12.2
889f5f