Blob Blame Raw
From 14bb1237f71e38749558c74963032a0387eecec0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 15 Aug 2018 16:04:42 -0400
Subject: [PATCH 2/2] user: export new Saved property

accountsservice maintains a state file for some users, if those users
have selected a specific session or language.

There's no good way, at the moment, for an application to check if a
specific user has saved state.

This commit exports the Saved property on the User object.
---
 data/org.freedesktop.Accounts.User.xml | 10 ++++++++++
 src/daemon.c                           |  3 +++
 src/libaccountsservice/act-user.c      | 19 +++++++++++++++++++
 src/libaccountsservice/act-user.h      |  1 +
 src/user.c                             | 10 ++++++++++
 src/user.h                             |  2 ++
 6 files changed, 45 insertions(+)

diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml
index 7fc3c61..8d3fe1c 100644
--- a/data/org.freedesktop.Accounts.User.xml
+++ b/data/org.freedesktop.Accounts.User.xml
@@ -811,60 +811,70 @@
     </doc:doc>
   </property>
 
   <property name="LoginHistory" type="a(xxa{sv})" access="read">
     <doc:doc>
       <doc:description>
         <doc:para>
           The login history for this user.
           Each entry in the array represents a login session. The first two
           members are the login time and logout time, as timestamps (seconds since the epoch). If the session is still running, the logout time
           is 0.
         </doc:para>
         <doc:para>
           The a{sv} member is a dictionary containing additional information
           about the session. Possible members include 'type' (with values like ':0', 'tty0', 'pts/0' etc).
         </doc:para>
       </doc:description>
     </doc:doc>
   </property>
 
   <property name="IconFile" type="s" access="read">
     <doc:doc>
       <doc:description>
         <doc:para>
            The filename of a png file containing the users icon.
         </doc:para>
       </doc:description>
     </doc:doc>
   </property>
 
+  <property name="Saved" type="b" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           Whether the users account has retained state
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
   <property name="Locked" type="b" access="read">
     <doc:doc>
       <doc:description>
         <doc:para>
            Whether the users account is locked.
         </doc:para>
       </doc:description>
     </doc:doc>
   </property>
 
   <property name="PasswordMode" type="i" access="read">
     <doc:doc>
       <doc:description>
         <doc:para>
           The password mode for the user account, encoded as an integer:
           <doc:list>
             <doc:item>
               <doc:term>0</doc:term>
               <doc:definition>Regular password</doc:definition>
             </doc:item>
             <doc:item>
               <doc:term>1</doc:term>
               <doc:definition>Password must be set at next login</doc:definition>
             </doc:item>
             <doc:item>
               <doc:term>2</doc:term>
               <doc:definition>No password</doc:definition>
             </doc:item>
           </doc:list>
         </doc:para>
diff --git a/src/daemon.c b/src/daemon.c
index 2851ed6..2587b8a 100644
--- a/src/daemon.c
+++ b/src/daemon.c
@@ -1187,60 +1187,61 @@ daemon_cache_user (AccountsAccounts      *accounts,
                                  context,
                                  g_strdup (user_name),
                                  g_free);
 
         return TRUE;
 }
 
 static void
 daemon_uncache_user_authorized_cb (Daemon                *daemon,
                                    User                  *dummy,
                                    GDBusMethodInvocation *context,
                                    gpointer               data)
 {
         const gchar *user_name = data;
         User        *user;
 
         sys_log (context, "uncache user '%s'", user_name);
 
         user = daemon_local_find_user_by_name (daemon, user_name);
         if (user == NULL) {
                 throw_error (context, ERROR_USER_DOES_NOT_EXIST,
                              "No user with the name %s found", user_name);
                 return;
         }
 
         /* Always use the canonical user name looked up */
         user_name = user_get_user_name (user);
 
         remove_cache_files (user_name);
 
+        user_set_saved (user, FALSE);
         user_set_cached (user, FALSE);
 
         accounts_accounts_complete_uncache_user (NULL, context);
 
         queue_reload_users (daemon);
 }
 
 static gboolean
 daemon_uncache_user (AccountsAccounts      *accounts,
                      GDBusMethodInvocation *context,
                      const gchar           *user_name)
 {
         Daemon *daemon = (Daemon*)accounts;
 
         daemon_local_check_auth (daemon,
                                  NULL,
                                  "org.freedesktop.accounts.user-administration",
                                  TRUE,
                                  daemon_uncache_user_authorized_cb,
                                  context,
                                  g_strdup (user_name),
                                  g_free);
 
         return TRUE;
 }
 
 typedef struct {
         uid_t uid;
         gboolean remove_files;
 } DeleteUserData;
@@ -1252,60 +1253,62 @@ daemon_delete_user_authorized_cb (Daemon                *daemon,
                                   gpointer               data)
 
 {
         DeleteUserData *ud = data;
         g_autoptr(GError) error = NULL;
         struct passwd *pwent;
         const gchar *argv[6];
         User *user;
 
         pwent = getpwuid (ud->uid);
 
         if (pwent == NULL) {
                 throw_error (context, ERROR_USER_DOES_NOT_EXIST, "No user with uid %d found", ud->uid);
                 return;
         }
 
         sys_log (context, "delete user '%s' (%d)", pwent->pw_name, ud->uid);
 
         user = daemon_local_find_user_by_id (daemon, ud->uid);
 
         if (user != NULL) {
                 user_set_cached (user, FALSE);
 
                 if (daemon->priv->autologin == user) {
                         daemon_local_set_automatic_login (daemon, user, FALSE, NULL);
                 }
         }
 
         remove_cache_files (pwent->pw_name);
 
+        user_set_saved (user, FALSE);
+
         argv[0] = "/usr/sbin/userdel";
         if (ud->remove_files) {
                 argv[1] = "-f";
                 argv[2] = "-r";
                 argv[3] = "--";
                 argv[4] = pwent->pw_name;
                 argv[5] = NULL;
         }
         else {
                 argv[1] = "-f";
                 argv[2] = "--";
                 argv[3] = pwent->pw_name;
                 argv[4] = NULL;
         }
 
         if (!spawn_with_login_uid (context, argv, &error)) {
                 throw_error (context, ERROR_FAILED, "running '%s' failed: %s", argv[0], error->message);
                 return;
         }
 
         accounts_accounts_complete_delete_user (NULL, context);
 }
 
 
 static gboolean
 daemon_delete_user (AccountsAccounts      *accounts,
                     GDBusMethodInvocation *context,
                     gint64                 uid,
                     gboolean               remove_files)
 {
diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c
index dd8f81b..540ffe7 100644
--- a/src/libaccountsservice/act-user.c
+++ b/src/libaccountsservice/act-user.c
@@ -849,60 +849,79 @@ act_user_collate (ActUser *user1,
  *
  * Returns whether or not #ActUser is currently graphically logged in
  * on the same seat as the seat of the session of the calling process.
  *
  * Returns: %TRUE or %FALSE
  */
 gboolean
 act_user_is_logged_in (ActUser *user)
 {
         return user->our_sessions != NULL;
 }
 
 /**
  * act_user_is_logged_in_anywhere:
  * @user: a #ActUser
  *
  * Returns whether or not #ActUser is currently logged in in any way
  * whatsoever.  See also act_user_is_logged_in().
  *
  * (Currently, this function is only implemented for systemd-logind.
  * For ConsoleKit, it is equivalent to act_user_is_logged_in.)
  *
  * Returns: %TRUE or %FALSE
  */
 gboolean
 act_user_is_logged_in_anywhere (ActUser *user)
 {
         return user->our_sessions != NULL || user->other_sessions != NULL;
 }
 
+/**
+ * act_user_get_saved:
+ * @user: a #ActUser
+ *
+ * Returns whether or not the #ActUser account has retained state in accountsservice.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean
+act_user_get_saved (ActUser *user)
+{
+        g_return_val_if_fail (ACT_IS_USER (user), TRUE);
+
+        if (user->accounts_proxy == NULL)
+                return FALSE;
+
+        return accounts_user_get_saved (user->accounts_proxy);
+}
+
 /**
  * act_user_get_locked:
  * @user: a #ActUser
  *
  * Returns whether or not the #ActUser account is locked.
  *
  * Returns: %TRUE or %FALSE
  */
 gboolean
 act_user_get_locked (ActUser *user)
 {
         g_return_val_if_fail (ACT_IS_USER (user), TRUE);
 
         if (user->accounts_proxy == NULL)
                 return TRUE;
 
         return accounts_user_get_locked (user->accounts_proxy);
 }
 
 /**
  * act_user_get_automatic_login:
  * @user: a #ActUser
  *
  * Returns whether or not #ActUser is automatically logged in at boot time.
  *
  * Returns: %TRUE or %FALSE
  */
 gboolean
 act_user_get_automatic_login (ActUser *user)
 {
diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h
index 2ef13b1..34d7fe3 100644
--- a/src/libaccountsservice/act-user.h
+++ b/src/libaccountsservice/act-user.h
@@ -43,60 +43,61 @@ typedef enum {
 typedef enum {
         ACT_USER_PASSWORD_MODE_REGULAR,
         ACT_USER_PASSWORD_MODE_SET_AT_LOGIN,
         ACT_USER_PASSWORD_MODE_NONE,
 } ActUserPasswordMode;
 
 typedef struct _ActUser ActUser;
 typedef struct _ActUserClass ActUserClass;
 
 GType          act_user_get_type                  (void) G_GNUC_CONST;
 
 const char    *act_user_get_object_path           (ActUser *user);
 
 uid_t          act_user_get_uid                   (ActUser   *user);
 const char    *act_user_get_user_name             (ActUser   *user);
 const char    *act_user_get_real_name             (ActUser   *user);
 ActUserAccountType act_user_get_account_type      (ActUser   *user);
 ActUserPasswordMode act_user_get_password_mode    (ActUser   *user);
 const char    *act_user_get_password_hint         (ActUser   *user);
 const char    *act_user_get_home_dir              (ActUser   *user);
 const char    *act_user_get_shell                 (ActUser   *user);
 const char    *act_user_get_email                 (ActUser   *user);
 const char    *act_user_get_location              (ActUser   *user);
 guint          act_user_get_num_sessions          (ActUser   *user);
 guint          act_user_get_num_sessions_anywhere (ActUser   *user);
 gboolean       act_user_is_logged_in              (ActUser   *user);
 gboolean       act_user_is_logged_in_anywhere     (ActUser   *user);
 int            act_user_get_login_frequency       (ActUser   *user);
 gint64         act_user_get_login_time            (ActUser   *user);
 const GVariant*act_user_get_login_history         (ActUser   *user);
+gboolean       act_user_get_saved                 (ActUser   *user);
 gboolean       act_user_get_locked                (ActUser   *user);
 gboolean       act_user_get_automatic_login       (ActUser   *user);
 gboolean       act_user_is_system_account         (ActUser   *user);
 gboolean       act_user_is_local_account          (ActUser   *user);
 gboolean       act_user_is_nonexistent            (ActUser   *user);
 const char    *act_user_get_icon_file             (ActUser   *user);
 const char    *act_user_get_language              (ActUser   *user);
 const char    *act_user_get_x_session             (ActUser   *user);
 const char    *act_user_get_session               (ActUser   *user);
 const char    *act_user_get_session_type          (ActUser   *user);
 const char    *act_user_get_primary_session_id    (ActUser   *user);
 
 gint           act_user_collate                   (ActUser   *user1,
                                                    ActUser   *user2);
 gboolean       act_user_is_loaded                 (ActUser   *user);
 
 void           act_user_get_password_expiration_policy (ActUser   *user,
                                                         gint64    *expiration_time,
                                                         gint64    *last_change_time,
                                                         gint64    *min_days_between_changes,
                                                         gint64    *max_days_between_changes,
                                                         gint64    *days_to_warn,
                                                         gint64    *days_after_expiration_until_lock);
 
 void           act_user_set_email                 (ActUser    *user,
                                                    const char *email);
 void           act_user_set_language              (ActUser    *user,
                                                    const char *language);
 void           act_user_set_x_session             (ActUser    *user,
                                                    const char *x_session);
diff --git a/src/user.c b/src/user.c
index 94c0244..93afadc 100644
--- a/src/user.c
+++ b/src/user.c
@@ -284,60 +284,61 @@ user_update_from_keyfile (User     *user,
         }
 
         s = g_key_file_get_string (keyfile, "User", "Location", NULL);
         if (s != NULL) {
                 accounts_user_set_location (ACCOUNTS_USER (user), s);
                 g_clear_pointer (&s, g_free);
         }
 
         s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL);
         if (s != NULL) {
                 accounts_user_set_password_hint (ACCOUNTS_USER (user), s);
                 g_clear_pointer (&s, g_free);
         }
 
         s = g_key_file_get_string (keyfile, "User", "Icon", NULL);
         if (s != NULL) {
                 accounts_user_set_icon_file (ACCOUNTS_USER (user), s);
                 g_clear_pointer (&s, g_free);
         }
 
         if (g_key_file_has_key (keyfile, "User", "SystemAccount", NULL)) {
             gboolean system_account;
 
             system_account = g_key_file_get_boolean (keyfile, "User", "SystemAccount", NULL);
             accounts_user_set_system_account (ACCOUNTS_USER (user), system_account);
         }
 
         g_clear_pointer (&user->keyfile, g_key_file_unref);
         user->keyfile = g_key_file_ref (keyfile);
         user_set_cached (user, TRUE);
+        user_set_saved (user, TRUE);
 
         g_object_thaw_notify (G_OBJECT (user));
 }
 
 void
 user_update_local_account_property (User          *user,
                                     gboolean       local)
 {
         accounts_user_set_local_account (ACCOUNTS_USER (user), local);
 }
 
 void
 user_update_system_account_property (User          *user,
                                      gboolean       system)
 {
         accounts_user_set_system_account (ACCOUNTS_USER (user), system);
 }
 
 static void
 user_save_to_keyfile (User     *user,
                       GKeyFile *keyfile)
 {
         g_key_file_remove_group (keyfile, "User", NULL);
 
         if (accounts_user_get_email (ACCOUNTS_USER (user)))
                 g_key_file_set_string (keyfile, "User", "Email", accounts_user_get_email (ACCOUNTS_USER (user)));
 
         if (accounts_user_get_language (ACCOUNTS_USER (user)))
                 g_key_file_set_string (keyfile, "User", "Language", accounts_user_get_language (ACCOUNTS_USER (user)));
 
@@ -357,60 +358,62 @@ user_save_to_keyfile (User     *user,
                 g_key_file_set_string (keyfile, "User", "PasswordHint", accounts_user_get_password_hint (ACCOUNTS_USER (user)));
 
         if (accounts_user_get_icon_file (ACCOUNTS_USER (user)))
                 g_key_file_set_string (keyfile, "User", "Icon", accounts_user_get_icon_file (ACCOUNTS_USER (user)));
 
         g_key_file_set_boolean (keyfile, "User", "SystemAccount", accounts_user_get_system_account (ACCOUNTS_USER (user)));
 
         user_set_cached (user, TRUE);
 }
 
 static void
 save_extra_data (User *user)
 {
         g_autofree gchar *data = NULL;
         g_autofree gchar *filename = NULL;
         g_autoptr(GError) error = NULL;
 
         user_save_to_keyfile (user, user->keyfile);
 
         data = g_key_file_to_data (user->keyfile, NULL, &error);
         if (data == NULL) {
                 g_warning ("Saving data for user %s failed: %s",
                            accounts_user_get_user_name (ACCOUNTS_USER (user)), error->message);
                 return;
         }
 
         filename = g_build_filename (USERDIR,
                                      accounts_user_get_user_name (ACCOUNTS_USER (user)),
                                      NULL);
         g_file_set_contents (filename, data, -1, &error);
+
+        user_set_saved (user, TRUE);
 }
 
 static void
 move_extra_data (const gchar *old_name,
                  const gchar *new_name)
 {
         g_autofree gchar *old_filename = NULL;
         g_autofree gchar *new_filename = NULL;
 
         old_filename = g_build_filename (USERDIR,
                                          old_name, NULL);
         new_filename = g_build_filename (USERDIR,
                                          new_name, NULL);
 
         g_rename (old_filename, new_filename);
 }
 
 static GVariant *
 user_extension_get_value (User                    *user,
                           GDBusInterfaceInfo      *interface,
                           const GDBusPropertyInfo *property)
 {
         const GVariantType *type = G_VARIANT_TYPE (property->signature);
         GVariant *value;
         g_autofree gchar *printed = NULL;
         gint i;
 
         /* First, try to get the value from the keyfile */
         printed = g_key_file_get_value (user->keyfile, interface->name, property->name, NULL);
         if (printed) {
@@ -773,60 +776,67 @@ const gchar *
 user_get_object_path (User *user)
 {
         return g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (user));
 }
 
 uid_t
 user_get_uid (User *user)
 {
         return accounts_user_get_uid (ACCOUNTS_USER (user));
 }
 
 const gchar *
 user_get_shell(User *user)
 {
 	return accounts_user_get_shell (ACCOUNTS_USER (user));
 }
 
 gboolean
 user_get_cached (User *user)
 {
         return user->cached;
 }
 
 void
 user_set_cached (User     *user,
                  gboolean  cached)
 {
         user->cached = cached;
 }
 
+void
+user_set_saved (User     *user,
+                gboolean  saved)
+{
+        accounts_user_set_saved (ACCOUNTS_USER (user), saved);
+}
+
 static void
 throw_error (GDBusMethodInvocation *context,
              gint                   error_code,
              const gchar           *format,
              ...)
 {
         va_list args;
         g_autofree gchar *message = NULL;
 
         va_start (args, format);
         message = g_strdup_vprintf (format, args);
         va_end (args);
 
         g_dbus_method_invocation_return_error (context, ERROR, error_code, "%s", message);
 }
 
 static void
 user_change_real_name_authorized_cb (Daemon                *daemon,
                                      User                  *user,
                                      GDBusMethodInvocation *context,
                                      gpointer               data)
 
 {
         gchar *name = data;
         g_autoptr(GError) error = NULL;
         const gchar *argv[6];
 
         if (g_strcmp0 (accounts_user_get_real_name (ACCOUNTS_USER (user)), name) != 0) {
                 sys_log (context,
                          "change real name of user '%s' (%d) to '%s'",
diff --git a/src/user.h b/src/user.h
index 39c6f13..b3b3380 100644
--- a/src/user.h
+++ b/src/user.h
@@ -39,47 +39,49 @@ typedef enum {
         ACCOUNT_TYPE_STANDARD,
         ACCOUNT_TYPE_ADMINISTRATOR,
 #define ACCOUNT_TYPE_LAST ACCOUNT_TYPE_ADMINISTRATOR
 } AccountType;
 
 typedef enum {
         PASSWORD_MODE_REGULAR,
         PASSWORD_MODE_SET_AT_LOGIN,
         PASSWORD_MODE_NONE,
 #define PASSWORD_MODE_LAST PASSWORD_MODE_NONE
 } PasswordMode;
 
 /* local methods */
 
 GType          user_get_type                (void) G_GNUC_CONST;
 User *         user_new                     (Daemon        *daemon,
                                              uid_t          uid);
 
 void           user_update_from_pwent       (User          *user,
                                              struct passwd *pwent,
                                              struct spwd   *spent);
 void           user_update_from_keyfile     (User          *user,
                                              GKeyFile      *keyfile);
 void           user_update_local_account_property (User          *user,
                                                    gboolean       local);
 void           user_update_system_account_property (User          *user,
                                                     gboolean       system);
 gboolean       user_get_cached              (User          *user);
 void           user_set_cached              (User          *user,
                                              gboolean       cached);
+void           user_set_saved               (User          *user,
+                                             gboolean       saved);
 
 void           user_register                (User          *user);
 void           user_unregister              (User          *user);
 void           user_changed                 (User          *user);
 
 void           user_save                    (User          *user);
 
 const gchar *  user_get_user_name           (User          *user);
 gboolean       user_get_system_account      (User          *user);
 gboolean       user_get_local_account       (User          *user);
 const gchar *  user_get_object_path         (User          *user);
 uid_t          user_get_uid                 (User          *user);
 const gchar *  user_get_shell               (User          *user);
 
 G_END_DECLS
 
 #endif
-- 
2.17.1