From eebb604999d40ee4d3151536e6e4086735556378 Mon Sep 17 00:00:00 2001 From: William Hua Date: Fri, 26 Jul 2013 10:10:11 -0400 Subject: [PATCH] Store input sources in user objects. --- data/org.freedesktop.Accounts.User.xml | 45 +++++++ src/libaccountsservice/act-user.c | 82 +++++++++++++ src/libaccountsservice/act-user.h | 3 + src/user.c | 212 +++++++++++++++++++++++++++++++++ 4 files changed, 342 insertions(+) diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml index 88198d8..76aa8c9 100644 --- a/data/org.freedesktop.Accounts.User.xml +++ b/data/org.freedesktop.Accounts.User.xml @@ -150,6 +150,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 + + + + @@ -623,6 +658,16 @@ + + + + + The user's input sources. + + + + + diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c index 9de689e..bc2110d 100644 --- a/src/libaccountsservice/act-user.c +++ b/src/libaccountsservice/act-user.c @@ -93,6 +93,7 @@ enum { PROP_LOGIN_HISTORY, PROP_ICON_FILE, PROP_LANGUAGE, + PROP_INPUT_SOURCES, PROP_X_SESSION, PROP_IS_LOADED }; @@ -122,6 +123,7 @@ struct _ActUser { char *location; char *icon_file; char *language; + GVariant *input_sources; char *x_session; GList *our_sessions; GList *other_sessions; @@ -308,6 +310,9 @@ act_user_get_property (GObject *object, case PROP_LANGUAGE: g_value_set_string (value, user->language); 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; @@ -464,6 +469,14 @@ act_user_class_init (ActUserClass *class) 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", "X session", @@ -581,6 +594,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); @@ -1052,6 +1067,22 @@ act_user_get_language (ActUser *user) } /** + * 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 * @@ -1279,6 +1310,19 @@ collect_props (const gchar *key, user->language = g_strdup (new_language); g_object_notify (G_OBJECT (user), "language"); } + } 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; @@ -1535,6 +1579,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"); @@ -1635,6 +1688,35 @@ act_user_set_language (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. * @x_session: an x session (e.g. gnome) diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h index d188db4..66cf4c4 100644 --- a/src/libaccountsservice/act-user.h +++ b/src/libaccountsservice/act-user.h @@ -77,6 +77,7 @@ 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); +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); @@ -88,6 +89,8 @@ void act_user_set_email (ActUser *user, const char *email); void act_user_set_language (ActUser *user, const char *language); +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 85203f0..fdaadda 100644 --- a/src/user.c +++ b/src/user.c @@ -57,6 +57,7 @@ enum { PROP_SHELL, PROP_EMAIL, PROP_LANGUAGE, + PROP_INPUT_SOURCES, PROP_X_SESSION, PROP_LOCATION, PROP_LOGIN_FREQUENCY, @@ -92,6 +93,7 @@ struct User { gchar *shell; gchar *email; gchar *language; + GVariant *input_sources; gchar *x_session; gchar *location; guint64 login_frequency; @@ -305,6 +307,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) @@ -321,6 +392,11 @@ user_update_from_keyfile (User *user, g_object_notify (G_OBJECT (user), "language"); } + 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); @@ -393,6 +469,51 @@ user_update_system_account_property (User *user, } 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) { @@ -404,6 +525,9 @@ user_save_to_keyfile (User *user, if (user->language) g_key_file_set_string (keyfile, "User", "Language", user->language); + 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); @@ -1105,6 +1229,67 @@ user_set_language (AccountsUser *auser, } 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, GDBusMethodInvocation *context, @@ -2018,6 +2203,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) { @@ -2109,6 +2300,8 @@ user_finalize (GObject *object) g_free (user->location); g_free (user->password_hint); + if (user->input_sources) + g_variant_unref (user->input_sources); if (user->login_history) g_variant_unref (user->login_history); @@ -2123,6 +2316,7 @@ user_set_property (GObject *object, GParamSpec *pspec) { User *user = USER (object); + GVariant *variant; switch (param_id) { case PROP_ACCOUNT_TYPE: @@ -2131,6 +2325,18 @@ user_set_property (GObject *object, case PROP_LANGUAGE: user->language = 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); break; @@ -2197,6 +2403,9 @@ user_get_property (GObject *object, case PROP_LANGUAGE: g_value_set_string (value, user->language); 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; @@ -2263,6 +2472,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; @@ -2279,6 +2489,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; @@ -2306,6 +2517,7 @@ user_init (User *user) user->default_icon_file = NULL; user->email = NULL; user->language = NULL; + user->input_sources = NULL; user->x_session = NULL; user->location = NULL; user->password_mode = PASSWORD_MODE_REGULAR; -- 1.8.3.2