From a1b09a0d0243ac3a7fbd26c176a274dd3716f391 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Thu, 27 Sep 2018 23:32:49 +0200 Subject: [PATCH 4/4] 0016-add-input-sources-support.patch --- data/org.freedesktop.Accounts.User.xml | 45 ++++++ src/libaccountsservice/act-user.c | 82 ++++++++++ src/libaccountsservice/act-user.h | 3 + src/user.c | 213 +++++++++++++++++++++++++ 4 files changed, 343 insertions(+) diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml index 103661c..ede2a19 100644 --- a/data/org.freedesktop.Accounts.User.xml +++ b/data/org.freedesktop.Accounts.User.xml @@ -185,6 +185,41 @@ + + + + + + A list of input sources. + + + + + + + Sets the user's input sources. + + + + The caller needs one of the following PolicyKit authorizations: + + + org.freedesktop.accounts.change-own-user-data + To change his own input sources + + + org.freedesktop.accounts.user-administration + To change the input sources of another user + + + + + if the caller lacks the appropriate PolicyKit authorization + if the operation failed + + + + @@ -748,6 +783,16 @@ + + + + + The user's input sources. + + + + + diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c index 36d1ade..43fc0ff 100644 --- a/src/libaccountsservice/act-user.c +++ b/src/libaccountsservice/act-user.c @@ -95,6 +95,7 @@ enum { PROP_ICON_FILE, PROP_LANGUAGE, PROP_FORMATS_LOCALE, + PROP_INPUT_SOURCES, PROP_X_SESSION, PROP_IS_LOADED }; @@ -126,6 +127,7 @@ struct _ActUser { char *icon_file; char *language; char *formats_locale; + GVariant *input_sources; char *x_session; GList *our_sessions; GList *other_sessions; @@ -318,6 +320,9 @@ act_user_get_property (GObject *object, case PROP_FORMATS_LOCALE: g_value_set_string (value, user->formats_locale); break; + case PROP_INPUT_SOURCES: + g_value_set_variant (value, user->input_sources); + break; case PROP_X_SESSION: g_value_set_string (value, user->x_session); break; @@ -487,6 +492,14 @@ act_user_class_init (ActUserClass *class) "User's regional formats.", NULL, G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, + PROP_INPUT_SOURCES, + g_param_spec_variant ("input-sources", + "Input sources", + "User's input sources.", + G_VARIANT_TYPE ("aa{ss}"), + NULL, + G_PARAM_READABLE)); g_object_class_install_property (gobject_class, PROP_X_SESSION, g_param_spec_string ("x-session", @@ -606,6 +619,8 @@ act_user_finalize (GObject *object) g_free (user->shell); g_free (user->email); g_free (user->location); + if (user->input_sources) + g_variant_unref (user->input_sources); if (user->login_history) g_variant_unref (user->login_history); g_free (user->formats_locale); @@ -1109,6 +1124,22 @@ act_user_get_formats_locale (ActUser *user) return user->formats_locale; } +/** + * act_user_get_input_sources: + * @user: a #ActUser + * + * Returns the input sources of @user. + * + * Returns: (transfer none): a list of input sources + */ +GVariant * +act_user_get_input_sources (ActUser *user) +{ + g_return_val_if_fail (ACT_IS_USER (user), NULL); + + return user->input_sources; +} + /** * act_user_get_x_session: * @user: a #ActUser @@ -1357,6 +1388,19 @@ collect_props (const gchar *key, g_object_notify (G_OBJECT (user), "formats_locale"); } + } else if (strcmp (key, "InputSources") == 0) { + GVariant *sources; + + g_variant_get (value, "@aa{ss}", &sources); + + if (!user->input_sources || !g_variant_equal (sources, user->input_sources)) { + if (user->input_sources) + g_variant_unref (user->input_sources); + user->input_sources = g_variant_ref (sources); + g_object_notify (G_OBJECT (user), "input-sources"); + } + + g_variant_unref (sources); } else if (strcmp (key, "XSession") == 0) { const char *new_x_session; @@ -1612,6 +1656,15 @@ _act_user_load_from_user (ActUser *user, user->language = g_strdup (user_to_copy->language); g_object_notify (G_OBJECT (user), "language"); + if (user_to_copy->input_sources != user->input_sources) { + if (user->input_sources) + g_variant_unref (user->input_sources); + user->input_sources = user_to_copy->input_sources; + if (user->input_sources) + g_variant_ref (user->input_sources); + g_object_notify (G_OBJECT (user), "input-sources"); + } + g_free (user->x_session); user->x_session = g_strdup (user_to_copy->x_session); g_object_notify (G_OBJECT (user), "x-session"); @@ -1812,6 +1865,35 @@ act_user_set_background_file (ActUser *user, } } +/** + * act_user_set_input_sources: + * @user: the user object to alter. + * @sources: a list of input sources + * + * Assigns new input sources for @user. + * + * Note this function is synchronous and ignores errors. + **/ +void +act_user_set_input_sources (ActUser *user, + GVariant *sources) +{ + GError *error = NULL; + + g_return_if_fail (ACT_IS_USER (user)); + g_return_if_fail (ACCOUNTS_IS_USER (user->accounts_proxy)); + g_return_if_fail (g_variant_is_of_type (sources, G_VARIANT_TYPE ("aa{ss}"))); + + if (!accounts_user_call_set_input_sources_sync (user->accounts_proxy, + sources, + NULL, + &error)) { + g_warning ("SetInputSources call failed: %s", error->message); + g_error_free (error); + return; + } +} + /** * act_user_set_x_session: * @user: the user object to alter. diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h index e026ce2..de9a5a9 100644 --- a/src/libaccountsservice/act-user.h +++ b/src/libaccountsservice/act-user.h @@ -78,6 +78,7 @@ 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_formats_locale (ActUser *user); +GVariant *act_user_get_input_sources (ActUser *user); const char *act_user_get_x_session (ActUser *user); const char *act_user_get_primary_session_id (ActUser *user); @@ -99,6 +100,8 @@ void act_user_set_language (ActUser *user, const char *language); void act_user_set_formats_locale (ActUser *user, const char *formats_locale); +void act_user_set_input_sources (ActUser *user, + GVariant *sources); void act_user_set_x_session (ActUser *user, const char *x_session); void act_user_set_location (ActUser *user, diff --git a/src/user.c b/src/user.c index c32971e..17772f8 100644 --- a/src/user.c +++ b/src/user.c @@ -59,6 +59,7 @@ enum { PROP_EMAIL, PROP_LANGUAGE, PROP_FORMATS_LOCALE, + PROP_INPUT_SOURCES, PROP_X_SESSION, PROP_LOCATION, PROP_LOGIN_FREQUENCY, @@ -96,6 +97,7 @@ struct User { gchar *email; gchar *language; gchar *formats_locale; + GVariant *input_sources; gchar *x_session; gchar *location; guint64 login_frequency; @@ -307,6 +309,75 @@ user_update_from_pwent (User *user, accounts_user_emit_changed (ACCOUNTS_USER (user)); } +static gint +intcmp (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b); +} + +static GVariant * +key_file_get_input_sources (GKeyFile *key_file) +{ + GVariantBuilder builder; + GSequence *indices; + GSequenceIter *indices_iter; + gchar **groups; + gchar **groups_iter; + + indices = g_sequence_new (NULL); + groups = g_key_file_get_groups (key_file, NULL); + + for (groups_iter = groups; *groups_iter; groups_iter++) { + if (g_str_has_prefix (*groups_iter, "InputSource") && (*groups_iter)[11]) { + gchar *end; + guint64 index; + + index = g_ascii_strtoull (*groups_iter + 11, &end, 0); + + if (!*end) + g_sequence_insert_sorted (indices, GINT_TO_POINTER (index), intcmp, NULL); + } + } + + g_strfreev (groups); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{ss}")); + + indices_iter = g_sequence_get_begin_iter (indices); + + while (!g_sequence_iter_is_end (indices_iter)) { + guint index; + gchar *group; + gchar **keys; + gchar **keys_iter; + + g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{ss}")); + + index = GPOINTER_TO_UINT (g_sequence_get (indices_iter)); + group = g_strdup_printf ("InputSource%u", index); + keys = g_key_file_get_keys (key_file, group, NULL, NULL); + + for (keys_iter = keys; *keys_iter; keys_iter++) { + gchar *value = g_key_file_get_string (key_file, group, *keys_iter, NULL); + g_variant_builder_add (&builder, "{ss}", *keys_iter, value); + g_free (value); + } + + g_strfreev (keys); + g_free (group); + + g_variant_builder_close (&builder); + + indices_iter = g_sequence_iter_next (indices_iter); + } + + g_sequence_free (indices); + + return g_variant_ref_sink (g_variant_builder_end (&builder)); +} + void user_update_from_keyfile (User *user, GKeyFile *keyfile) @@ -330,6 +401,11 @@ user_update_from_keyfile (User *user, g_object_notify (G_OBJECT (user), "formats-locale"); } + if (user->input_sources != NULL) + g_variant_unref (user->input_sources); + user->input_sources = key_file_get_input_sources (keyfile); + g_object_notify (G_OBJECT (user), "input-sources"); + s = g_key_file_get_string (keyfile, "User", "XSession", NULL); if (s != NULL) { g_free (user->x_session); @@ -408,6 +484,51 @@ user_update_system_account_property (User *user, g_object_notify (G_OBJECT (user), "system-account"); } +static void +key_file_set_input_sources (GKeyFile *key_file, + GVariant *input_sources) +{ + gchar **groups; + gchar **groups_iter; + GVariantIter sources; + GVariantIter *source; + guint i; + + /* Remove all groups matching regex "InputSource\d+". */ + + groups = g_key_file_get_groups (key_file, NULL); + + for (groups_iter = groups; *groups_iter; groups_iter++) { + if (g_str_has_prefix (*groups_iter, "InputSource")) { + for (i = 11; g_ascii_isdigit ((*groups_iter)[i]); i++); + + if (i > 11 && !(*groups_iter)[i]) + g_key_file_remove_group (key_file, *groups_iter, NULL); + } + } + + g_strfreev (groups); + + /* Write all input sources to key file. */ + + g_variant_iter_init (&sources, input_sources); + + for (i = 0; g_variant_iter_next (&sources, "a{ss}", &source); i++) { + gchar *group; + const gchar *key; + const gchar *value; + + group = g_strdup_printf ("InputSource%u", i); + + while (g_variant_iter_next (source, "{&s&s}", &key, &value)) + g_key_file_set_string (key_file, group, key, value); + + g_free (group); + + g_variant_iter_free (source); + } +} + static void user_save_to_keyfile (User *user, GKeyFile *keyfile) @@ -423,6 +544,9 @@ user_save_to_keyfile (User *user, if (user->formats_locale) g_key_file_set_string (keyfile, "User", "FormatsLocale", user->formats_locale); + if (user->input_sources) + key_file_set_input_sources (keyfile, user->input_sources); + if (user->x_session) g_key_file_set_string (keyfile, "User", "XSession", user->x_session); @@ -1182,6 +1306,67 @@ user_set_formats_locale (AccountsUser *auser, return TRUE; } +static void +user_change_input_sources_authorized_cb (Daemon *daemon, + User *user, + GDBusMethodInvocation *context, + gpointer data) + +{ + GVariant *sources = data; + + if (sources != user->input_sources && + (!sources || !user->input_sources || + !g_variant_equal (sources, user->input_sources))) { + if (user->input_sources) + g_variant_unref (user->input_sources); + + user->input_sources = sources; + + if (user->input_sources) + g_variant_ref (user->input_sources); + + save_extra_data (user); + + accounts_user_emit_changed (ACCOUNTS_USER (user)); + + g_object_notify (G_OBJECT (user), "input-sources"); + } + + accounts_user_complete_set_input_sources (ACCOUNTS_USER (user), context); +} + +static gboolean +user_set_input_sources (AccountsUser *auser, + GDBusMethodInvocation *context, + GVariant *sources) +{ + 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 (user->uid == (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, + TRUE, + user_change_input_sources_authorized_cb, + context, + sources ? g_variant_ref (sources) : NULL, + sources ? (GDestroyNotify) g_variant_unref : NULL); + + return TRUE; +} + static void user_change_x_session_authorized_cb (Daemon *daemon, User *user, @@ -2281,6 +2466,12 @@ user_real_get_language (AccountsUser *user) return USER (user)->language; } +static GVariant * +user_real_get_input_sources (AccountsUser *user) +{ + return USER (user)->input_sources; +} + static const gchar * user_real_get_xsession (AccountsUser *user) { @@ -2377,6 +2568,9 @@ user_finalize (GObject *object) if (user->login_history) g_variant_unref (user->login_history); + if (user->input_sources) + g_variant_unref (user->input_sources); + if (G_OBJECT_CLASS (user_parent_class)->finalize) (*G_OBJECT_CLASS (user_parent_class)->finalize) (object); } @@ -2388,6 +2582,7 @@ user_set_property (GObject *object, GParamSpec *pspec) { User *user = USER (object); + GVariant *variant; switch (param_id) { case PROP_ACCOUNT_TYPE: @@ -2398,6 +2593,18 @@ user_set_property (GObject *object, break; case PROP_FORMATS_LOCALE: user->formats_locale = g_value_dup_string (value); + break; + case PROP_INPUT_SOURCES: + variant = g_value_get_variant (value); + + if (variant != user->input_sources) { + if (user->input_sources) + g_variant_unref (user->input_sources); + user->input_sources = variant; + if (user->input_sources) + g_variant_ref (user->input_sources); + } + break; case PROP_X_SESSION: user->x_session = g_value_dup_string (value); @@ -2470,6 +2677,9 @@ user_get_property (GObject *object, case PROP_FORMATS_LOCALE: g_value_set_string (value, user->formats_locale); break; + case PROP_INPUT_SOURCES: + g_value_set_variant (value, user->input_sources); + break; case PROP_X_SESSION: g_value_set_string (value, user->x_session); break; @@ -2541,6 +2751,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface) iface->handle_set_home_directory = user_set_home_directory; iface->handle_set_icon_file = user_set_icon_file; iface->handle_set_language = user_set_language; + iface->handle_set_input_sources = user_set_input_sources; iface->handle_set_location = user_set_location; iface->handle_set_locked = user_set_locked; iface->handle_set_password = user_set_password; @@ -2559,6 +2770,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface) iface->get_shell = user_real_get_shell; iface->get_email = user_real_get_email; iface->get_language = user_real_get_language; + iface->get_input_sources = user_real_get_input_sources; iface->get_xsession = user_real_get_xsession; iface->get_location = user_real_get_location; iface->get_login_frequency = user_real_get_login_frequency; @@ -2588,6 +2800,7 @@ user_init (User *user) user->email = NULL; user->language = NULL; user->formats_locale = NULL; + user->input_sources = NULL; user->x_session = NULL; user->location = NULL; user->password_mode = PASSWORD_MODE_REGULAR; -- 2.20.1