From b31422ba68a3f39bfa036764f8e5a2b003ab20ae Mon Sep 17 00:00:00 2001
From: Michael Terry <michael.terry@ubuntu.com>
Date: Thu, 21 Dec 2017 15:11:19 +1300
Subject: [PATCH 08/12] Add XKeyboardLayouts property to report a user's
 keyboard layouts, largely for LightDM's benefit.

---
 data/org.freedesktop.Accounts.User.xml | 45 ++++++++++++++++++++
 src/libaccountsservice/act-user.c      | 57 ++++++++++++++++++++++++++
 src/libaccountsservice/act-user.h      |  3 ++
 src/user.c                             | 54 ++++++++++++++++++++++++
 4 files changed, 159 insertions(+)

diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml
index 787facd..91ac609 100644
--- a/data/org.freedesktop.Accounts.User.xml
+++ b/data/org.freedesktop.Accounts.User.xml
@@ -455,6 +455,41 @@
     </doc:doc>
   </method>
 
+  <method name="SetXKeyboardLayouts">
+    <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    <arg name="layouts" direction="in" type="as">
+      <doc:doc>
+        <doc:summary>
+          The name of the keyboard layouts to use as the user's keyboard layout.  The first is the default.
+        </doc:summary>
+      </doc:doc>
+    </arg>
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+          Sets the user's keyboard layouts.
+        </doc:para>
+      </doc:description>
+      <doc:permission>
+        The caller needs one of the following PolicyKit authorizations:
+        <doc:list>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+            <doc:definition>To change his or her own keyboard layouts</doc:definition>
+          </doc:item>
+          <doc:item>
+            <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+            <doc:definition>To change the keyboard layouts of another user</doc:definition>
+          </doc:item>
+        </doc:list>
+      </doc:permission>
+      <doc:errors>
+        <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+        <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+      </doc:errors>
+    </doc:doc>
+  </method>
+
   <method name="SetBackgroundFile">
     <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
     <arg name="filename" direction="in" type="s">
@@ -973,6 +1008,16 @@
     </doc:doc>
   </property>
 
+  <property name="XKeyboardLayouts" type="as" access="read">
+    <doc:doc>
+      <doc:description>
+        <doc:para>
+           The names of the user's keyboard layouts.
+        </doc:para>
+      </doc:description>
+    </doc:doc>
+  </property>
+
   <property name="BackgroundFile" type="s" access="read">
     <doc:doc>
       <doc:description>
diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c
index 97332ff..749c7b5 100644
--- a/src/libaccountsservice/act-user.c
+++ b/src/libaccountsservice/act-user.c
@@ -90,6 +90,7 @@ enum
         PROP_LOGIN_FREQUENCY,
         PROP_LOGIN_TIME,
         PROP_LOGIN_HISTORY,
+        PROP_X_KEYBOARD_LAYOUTS,
         PROP_BACKGROUND_FILE,
         PROP_ICON_FILE,
         PROP_LANGUAGE,
@@ -370,6 +371,13 @@ act_user_class_init (ActUserClass *class)
                                                                G_VARIANT_TYPE ("a(xxa{sv})"),
                                                                NULL,
                                                                G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+        g_object_class_install_property (gobject_class,
+                                         PROP_X_KEYBOARD_LAYOUTS,
+                                         g_param_spec_boxed ("xkeyboard-layouts",
+                                                             "Keyboard layouts",
+                                                             "The name of keyboard layouts for this user.",
+                                                             G_TYPE_STRV,
+                                                             G_PARAM_READABLE));
         g_object_class_install_property (gobject_class,
                                          PROP_BACKGROUND_FILE,
                                          g_param_spec_string ("background-file",
@@ -1014,6 +1022,25 @@ act_user_is_nonexistent (ActUser *user)
         return user->nonexistent;
 }
 
+/**
+ * act_user_get_x_keyboard_layouts:
+ * @user: a #ActUser
+ *
+ * Returns the name of the account keyboard layouts belonging to @user.
+ *
+ * Returns: (transfer none): names of keyboard layouts
+ */
+const char * const *
+act_user_get_x_keyboard_layouts (ActUser *user)
+{
+        g_return_val_if_fail (ACT_IS_USER (user), NULL);
+
+        if (user->accounts_proxy == NULL)
+                return NULL;
+
+        return accounts_user_get_xkeyboard_layouts (user->accounts_proxy);
+}
+
 /**
  * act_user_get_background_file:
  * @user: a #ActUser
@@ -1467,6 +1494,36 @@ act_user_set_password_expiration_policy (ActUser *user,
         }
 }
 
+/**
+ * act_user_set_x_keyboard_layouts:
+ * @user: the user object to alter.
+ * @keyboard_layouts: names of keyboard layouts
+ *
+ * Assigns a new set of keyboard layouts for @user.
+ *
+ * Note this function is synchronous and ignores errors.
+ **/
+void
+act_user_set_x_keyboard_layouts (ActUser    *user,
+                                 const char * const *keyboard_layouts)
+{
+        g_autoptr(GError) error = NULL;
+
+        g_return_if_fail (ACT_IS_USER (user));
+        g_return_if_fail (keyboard_layouts != NULL);
+        g_return_if_fail (ACCOUNTS_IS_USER (user->accounts_proxy));
+
+        if (!accounts_user_call_set_xkeyboard_layouts_sync (user->accounts_proxy,
+                                                            keyboard_layouts,
+                                                            G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
+                                                           -1,
+                                                            NULL,
+                                                            &error)) {
+                g_warning ("SetXKeyboardLayouts call failed: %s", error->message);
+                return;
+        }
+}
+
 /**
  * act_user_set_formats_locale:
  * @user: the user object to alter.
diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h
index c4eabb9..c39a66a 100644
--- a/src/libaccountsservice/act-user.h
+++ b/src/libaccountsservice/act-user.h
@@ -68,6 +68,7 @@ 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);
+const char * const *act_user_get_x_keyboard_layouts (ActUser *user);
 gboolean       act_user_is_nonexistent (ActUser *user);
 const char *act_user_get_background_file (ActUser *user);
 const char *act_user_get_icon_file (ActUser *user);
@@ -105,6 +106,8 @@ void           act_user_set_language (ActUser    *user,
 void           act_user_set_languages (ActUser            *user,
                                        const char * const *languages);
 
+void           act_user_set_x_keyboard_layouts (ActUser    *user,
+                                                const char * const *keyboard_layouts);
 void           act_user_set_formats_locale (ActUser    *user,
                                             const char *formats_locale);
 void           act_user_set_background_file (ActUser    *user,
diff --git a/src/user.c b/src/user.c
index ac8e626..83e243c 100644
--- a/src/user.c
+++ b/src/user.c
@@ -521,6 +521,7 @@ user_update_from_keyfile (User     *user,
                           GKeyFile *keyfile)
 {
         gchar *s, **sl;
+        gchar **layouts;
 
         s = g_key_file_get_string (keyfile, "User", "Language", NULL);
         sl = g_key_file_get_string_list (keyfile, "User", "Languages", NULL, NULL);
@@ -586,6 +587,12 @@ user_update_from_keyfile (User     *user,
                 g_clear_pointer (&s, g_free);
         }
 
+        layouts = g_key_file_get_string_list (keyfile, "User", "XKeyboardLayouts", NULL, NULL);
+        if (layouts != NULL) {
+                accounts_user_set_xkeyboard_layouts (ACCOUNTS_USER (user), (const gchar * const *) layouts);
+                g_strfreev (layouts);
+        }
+
         s = g_key_file_get_string (keyfile, "User", "Background", NULL);
         if (s != NULL) {
                 accounts_user_set_background_file (ACCOUNTS_USER (user), s);
@@ -691,6 +698,9 @@ user_save_to_keyfile (User     *user,
         if (accounts_user_get_password_hint (ACCOUNTS_USER (user)))
                 g_key_file_set_string (keyfile, "User", "PasswordHint", accounts_user_get_password_hint (ACCOUNTS_USER (user)));
 
+        if (accounts_user_get_xkeyboard_layouts (ACCOUNTS_USER (user)))
+                g_key_file_set_string_list (keyfile, "User", "XKeyboardLayouts", (const gchar * const *) accounts_user_get_xkeyboard_layouts (ACCOUNTS_USER (user)), g_strv_length ((gchar **) accounts_user_get_xkeyboard_layouts (ACCOUNTS_USER (user))));
+
         if (accounts_user_get_background_file (ACCOUNTS_USER (user)))
                 g_key_file_set_string (keyfile, "User", "Background", accounts_user_get_background_file (ACCOUNTS_USER (user)));
 
@@ -2092,6 +2102,49 @@ become_user (gpointer data)
         }
 }
 
+static void
+user_change_x_keyboard_layouts_authorized_cb (Daemon                *daemon,
+                                              User                  *user,
+                                              GDBusMethodInvocation *context,
+                                              gpointer               data)
+{
+        accounts_user_set_xkeyboard_layouts (ACCOUNTS_USER (user), data);
+
+        save_extra_data (user);
+
+        accounts_user_complete_set_xkeyboard_layouts (ACCOUNTS_USER (user), context);
+}
+
+static gboolean
+user_set_xkeyboard_layouts (AccountsUser          *auser,
+                            GDBusMethodInvocation *context,
+                            const gchar * const   *layouts)
+{
+        User *user = (User*)auser;
+        int uid;
+        const gchar *action_id;
+
+        if (!get_caller_uid (context, &uid)) {
+                throw_error (context, ERROR_FAILED, "identifying caller failed");
+                return FALSE;
+        }
+
+        if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid)
+                action_id = "org.freedesktop.accounts.change-own-user-data";
+        else
+                action_id = "org.freedesktop.accounts.user-administration";
+
+        daemon_local_check_auth (user->daemon,
+                                 user,
+                                 action_id,
+                                 user_change_x_keyboard_layouts_authorized_cb,
+                                 context,
+                                 g_strdupv ((gchar **)layouts),
+                                 (GDestroyNotify)g_strfreev);
+
+        return TRUE;
+}
+
 static void
 user_change_background_file_authorized_cb (Daemon                *daemon,
                                            User                  *user,
@@ -2873,6 +2926,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface)
         iface->handle_set_real_name = user_set_real_name;
         iface->handle_set_shell = user_set_shell;
         iface->handle_set_user_name = user_set_user_name;
+        iface->handle_set_xkeyboard_layouts = user_set_xkeyboard_layouts;
         iface->handle_set_xsession = user_set_x_session;
         iface->handle_set_session = user_set_session;
         iface->handle_set_session_type = user_set_session_type;
-- 
2.39.3

