From 6edc7e0cfc672fe4c2331ac8131ea1aa82881b63 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 19 Dec 2018 19:58:16 -0500 Subject: [PATCH 1/9] settings: Make the keyfile backend parameterless Make it possible to instantiate a keyfile settings backend without specifying parameters, by turning the arguments to the new() function into construct-only properties. If no filename is specified, default to $XDG_CONFIG_HOME/glib-2.0/settings/keyfile --- gio/gkeyfilesettingsbackend.c | 252 ++++++++++++++++++++++++++------- gio/gsettingsbackendinternal.h | 2 + 2 files changed, 199 insertions(+), 55 deletions(-) diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index a37978e83..e8e9f44a5 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -21,6 +21,9 @@ #include "config.h" +#include +#include + #include #include @@ -28,7 +31,8 @@ #include "gfileinfo.h" #include "gfilemonitor.h" #include "gsimplepermission.h" -#include "gsettingsbackend.h" +#include "gsettingsbackendinternal.h" +#include "giomodule.h" #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ()) @@ -41,6 +45,12 @@ typedef GSettingsBackendClass GKeyfileSettingsBackendClass; +enum { + PROP_FILENAME = 1, + PROP_ROOT_PATH, + PROP_ROOT_GROUP +}; + typedef struct { GSettingsBackend parent_instance; @@ -61,7 +71,6 @@ typedef struct GFileMonitor *dir_monitor; } GKeyfileSettingsBackend; -static GType g_keyfile_settings_backend_get_type (void); G_DEFINE_TYPE (GKeyfileSettingsBackend, g_keyfile_settings_backend, G_TYPE_SETTINGS_BACKEND) @@ -287,7 +296,8 @@ g_keyfile_settings_backend_check_one (gpointer key, { WriteManyData *data = user_data; - return data->failed = !path_is_valid (data->kfsb, key); + return data->failed = g_hash_table_contains (data->kfsb->system_locks, key) || + !path_is_valid (data->kfsb, key); } static gboolean @@ -522,25 +532,6 @@ g_keyfile_settings_backend_init (GKeyfileSettingsBackend *kfsb) { } -static void -g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - - object_class->finalize = g_keyfile_settings_backend_finalize; - - class->read = g_keyfile_settings_backend_read; - class->write = g_keyfile_settings_backend_write; - class->write_tree = g_keyfile_settings_backend_write_tree; - class->reset = g_keyfile_settings_backend_reset; - class->get_writable = g_keyfile_settings_backend_get_writable; - class->get_permission = g_keyfile_settings_backend_get_permission; - /* No need to implement subscribed/unsubscribe: the only point would be to - * stop monitoring the file when there's no GSettings anymore, which is no - * big win. - */ -} - static void file_changed (GFileMonitor *monitor, GFile *file, @@ -567,6 +558,185 @@ dir_changed (GFileMonitor *monitor, g_keyfile_settings_backend_keyfile_writable (kfsb); } +static void +g_keyfile_settings_backend_constructed (GObject *object) +{ + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); + + if (kfsb->file == NULL) + { + char *filename = g_build_filename (g_get_user_config_dir (), + "glib-2.0", "settings", "keyfile", + NULL); + kfsb->file = g_file_new_for_path (filename); + g_free (filename); + } + + if (kfsb->prefix == NULL) + { + kfsb->prefix = g_strdup ("/"); + kfsb->prefix_len = 1; + } + + kfsb->keyfile = g_key_file_new (); + kfsb->permission = g_simple_permission_new (TRUE); + + kfsb->dir = g_file_get_parent (kfsb->file); + g_file_make_directory_with_parents (kfsb->dir, NULL, NULL); + + kfsb->file_monitor = g_file_monitor (kfsb->file, G_FILE_MONITOR_NONE, NULL, NULL); + kfsb->dir_monitor = g_file_monitor (kfsb->dir, G_FILE_MONITOR_NONE, NULL, NULL); + + compute_checksum (kfsb->digest, NULL, 0); + + g_signal_connect (kfsb->file_monitor, "changed", + G_CALLBACK (file_changed), kfsb); + g_signal_connect (kfsb->dir_monitor, "changed", + G_CALLBACK (dir_changed), kfsb); + + g_keyfile_settings_backend_keyfile_writable (kfsb); + g_keyfile_settings_backend_keyfile_reload (kfsb); +} + +static void +g_keyfile_settings_backend_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); + + switch (prop_id) + { + case PROP_FILENAME: + /* Construct only. */ + g_assert (kfsb->file == NULL); + kfsb->file = g_file_new_for_path (g_value_get_string (value)); + break; + + case PROP_ROOT_PATH: + /* Construct only. */ + g_assert (kfsb->prefix == NULL); + kfsb->prefix = g_value_dup_string (value); + if (kfsb->prefix) + kfsb->prefix_len = strlen (kfsb->prefix); + break; + + case PROP_ROOT_GROUP: + /* Construct only. */ + g_assert (kfsb->root_group == NULL); + kfsb->root_group = g_value_dup_string (value); + if (kfsb->root_group) + kfsb->root_group_len = strlen (kfsb->root_group); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_keyfile_settings_backend_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); + + switch (prop_id) + { + case PROP_FILENAME: + g_value_set_string (value, g_file_peek_path (kfsb->file)); + break; + + case PROP_ROOT_PATH: + g_value_set_string (value, kfsb->prefix); + break; + + case PROP_ROOT_GROUP: + g_value_set_string (value, kfsb->root_group); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = g_keyfile_settings_backend_finalize; + object_class->constructed = g_keyfile_settings_backend_constructed; + object_class->get_property = g_keyfile_settings_backend_get_property; + object_class->set_property = g_keyfile_settings_backend_set_property; + + class->read = g_keyfile_settings_backend_read; + class->write = g_keyfile_settings_backend_write; + class->write_tree = g_keyfile_settings_backend_write_tree; + class->reset = g_keyfile_settings_backend_reset; + class->get_writable = g_keyfile_settings_backend_get_writable; + class->get_permission = g_keyfile_settings_backend_get_permission; + /* No need to implement subscribed/unsubscribe: the only point would be to + * stop monitoring the file when there's no GSettings anymore, which is no + * big win. + */ + + /** + * GKeyfileSettingsBackend:filename: + * + * The location where the settings are stored on disk. + * + * Defaults to `$XDG_CONFIG_HOME/glib-2.0/settings/keyfile`. + */ + g_object_class_install_property (object_class, + PROP_FILENAME, + g_param_spec_string ("filename", + P_("Filename"), + P_("The filename"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GKeyfileSettingsBackend:root-path: + * + * All settings read to or written from the backend must fall under the + * path given in @root_path (which must start and end with a slash and + * not contain two consecutive slashes). @root_path may be "/". + * + * Defaults to "/". + */ + g_object_class_install_property (object_class, + PROP_ROOT_PATH, + g_param_spec_string ("root-path", + P_("Root path"), + P_("The root path"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GKeyfileSettingsBackend:root-group: + * + * If @root_group is non-%NULL then it specifies the name of the keyfile + * group used for keys that are written directly below the root path. + * + * Defaults to NULL. + */ + g_object_class_install_property (object_class, + PROP_ROOT_GROUP, + g_param_spec_string ("root-group", + P_("Root group"), + P_("The root group"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + /** * g_keyfile_settings_backend_new: * @filename: the filename of the keyfile @@ -626,43 +796,15 @@ g_keyfile_settings_backend_new (const gchar *filename, const gchar *root_path, const gchar *root_group) { - GKeyfileSettingsBackend *kfsb; - g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (root_path != NULL, NULL); g_return_val_if_fail (g_str_has_prefix (root_path, "/"), NULL); g_return_val_if_fail (g_str_has_suffix (root_path, "/"), NULL); g_return_val_if_fail (strstr (root_path, "//") == NULL, NULL); - kfsb = g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND, NULL); - kfsb->keyfile = g_key_file_new (); - kfsb->permission = g_simple_permission_new (TRUE); - - kfsb->file = g_file_new_for_path (filename); - kfsb->dir = g_file_get_parent (kfsb->file); - g_file_make_directory_with_parents (kfsb->dir, NULL, NULL); - - kfsb->file_monitor = g_file_monitor (kfsb->file, 0, NULL, NULL); - kfsb->dir_monitor = g_file_monitor (kfsb->dir, 0, NULL, NULL); - - kfsb->prefix_len = strlen (root_path); - kfsb->prefix = g_strdup (root_path); - - if (root_group) - { - kfsb->root_group_len = strlen (root_group); - kfsb->root_group = g_strdup (root_group); - } - - compute_checksum (kfsb->digest, NULL, 0); - - g_signal_connect (kfsb->file_monitor, "changed", - G_CALLBACK (file_changed), kfsb); - g_signal_connect (kfsb->dir_monitor, "changed", - G_CALLBACK (dir_changed), kfsb); - - g_keyfile_settings_backend_keyfile_writable (kfsb); - g_keyfile_settings_backend_keyfile_reload (kfsb); - - return G_SETTINGS_BACKEND (kfsb); + return G_SETTINGS_BACKEND (g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND, + "filename", filename, + "root-path", root_path, + "root-group", root_group, + NULL)); } diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h index 2a76a80bc..9e1d51dba 100644 --- a/gio/gsettingsbackendinternal.h +++ b/gio/gsettingsbackendinternal.h @@ -87,6 +87,8 @@ GType g_null_settings_backend_get_type (void); GType g_memory_settings_backend_get_type (void); +GType g_keyfile_settings_backend_get_type (void); + #ifdef HAVE_COCOA GType g_nextstep_settings_backend_get_type (void); #endif -- 2.28.0 From cd448d51f610f1e6a701ac8a1146d3b2048427a0 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 19 Dec 2018 20:03:29 -0500 Subject: [PATCH 2/9] settings: Register the keyfile backend as extension This was not done previously because the backend could not be instantiated without parameters. --- gio/gkeyfilesettingsbackend.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index e8e9f44a5..0e9789cde 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -32,7 +32,7 @@ #include "gfilemonitor.h" #include "gsimplepermission.h" #include "gsettingsbackendinternal.h" -#include "giomodule.h" +#include "giomodule-priv.h" #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ()) @@ -71,9 +71,12 @@ typedef struct GFileMonitor *dir_monitor; } GKeyfileSettingsBackend; -G_DEFINE_TYPE (GKeyfileSettingsBackend, - g_keyfile_settings_backend, - G_TYPE_SETTINGS_BACKEND) +G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend, + g_keyfile_settings_backend, + G_TYPE_SETTINGS_BACKEND, + _g_io_modules_ensure_extension_points_registered (); + g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, + g_define_type_id, "keyfile", 10)) static void compute_checksum (guint8 *digest, -- 2.28.0 From 8cbdb7dd496f6c2361eb46ca4c0848d7210fe27a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 5 Nov 2018 16:07:55 -0500 Subject: [PATCH 3/9] settings: Add support for defaults to keyfile backend Stacked databases and locks are dconf features that allow management software like Fleet Commander to set system-wide defaults and overrides centrally for applications. This patch adds minimal support for the same to the keyfile backend. We look for a keyfile named 'defaults' and a lock-list named 'locks'. Suitable files can be produced from a dconf database with dconf dump and dconf list-locks, respectively. The default location for these files is /etc/glib-2.0/settings/. For test purposes, this can be overwritten with the GSETTINGS_DEFAULTS_DIR environment variable. Writes always go to the per-user keyfile. --- gio/gkeyfilesettingsbackend.c | 138 ++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 6 deletions(-) diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index 0e9789cde..0a4e81511 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -29,6 +29,7 @@ #include "gfile.h" #include "gfileinfo.h" +#include "gfileenumerator.h" #include "gfilemonitor.h" #include "gsimplepermission.h" #include "gsettingsbackendinternal.h" @@ -45,11 +46,12 @@ typedef GSettingsBackendClass GKeyfileSettingsBackendClass; -enum { +typedef enum { PROP_FILENAME = 1, PROP_ROOT_PATH, - PROP_ROOT_GROUP -}; + PROP_ROOT_GROUP, + PROP_DEFAULTS_DIR +} GKeyfileSettingsBackendProperty; typedef struct { @@ -58,6 +60,9 @@ typedef struct GKeyFile *keyfile; GPermission *permission; gboolean writable; + char *defaults_dir; + GKeyFile *system_keyfile; + GHashTable *system_locks; /* Used as a set, owning the strings it contains */ gchar *prefix; gint prefix_len; @@ -196,10 +201,19 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, if (convert_path (kfsb, key, &group, &name)) { gchar *str; + gchar *sysstr; g_assert (*name); + sysstr = g_key_file_get_value (kfsb->system_keyfile, group, name, NULL); str = g_key_file_get_value (kfsb->keyfile, group, name, NULL); + if (sysstr && + (g_hash_table_contains (kfsb->system_locks, key) || + str == NULL)) + { + g_free (str); + str = g_steal_pointer (&sysstr); + } if (str) { @@ -207,6 +221,8 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, g_free (str); } + g_free (sysstr); + g_free (group); g_free (name); } @@ -221,6 +237,9 @@ set_to_keyfile (GKeyfileSettingsBackend *kfsb, { gchar *group, *name; + if (g_hash_table_contains (kfsb->system_locks, key)) + return FALSE; + if (convert_path (kfsb, key, &group, &name)) { if (value) @@ -368,7 +387,9 @@ g_keyfile_settings_backend_get_writable (GSettingsBackend *backend, { GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend); - return kfsb->writable && path_is_valid (kfsb, name); + return kfsb->writable && + !g_hash_table_contains (kfsb->system_locks, name) && + path_is_valid (kfsb, name); } static GPermission * @@ -514,6 +535,9 @@ g_keyfile_settings_backend_finalize (GObject *object) g_key_file_free (kfsb->keyfile); g_object_unref (kfsb->permission); + g_key_file_unref (kfsb->system_keyfile); + g_hash_table_unref (kfsb->system_locks); + g_free (kfsb->defaults_dir); g_file_monitor_cancel (kfsb->file_monitor); g_object_unref (kfsb->file_monitor); @@ -561,6 +585,75 @@ dir_changed (GFileMonitor *monitor, g_keyfile_settings_backend_keyfile_writable (kfsb); } +static void +load_system_settings (GKeyfileSettingsBackend *kfsb) +{ + GError *error = NULL; + const char *dir = "/etc/glib-2.0/settings"; + char *path; + char *contents; + + kfsb->system_keyfile = g_key_file_new (); + kfsb->system_locks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + if (kfsb->defaults_dir) + dir = kfsb->defaults_dir; + + path = g_build_filename (dir, "defaults", NULL); + + /* The defaults are in the same keyfile format that we use for the settings. + * It can be produced from a dconf database using: dconf dump + */ + if (!g_key_file_load_from_file (kfsb->system_keyfile, path, G_KEY_FILE_NONE, &error)) + { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + g_warning ("Failed to read %s: %s", path, error->message); + g_clear_error (&error); + } + else + g_debug ("Loading default settings from %s", path); + + g_free (path); + + path = g_build_filename (dir, "locks", NULL); + + /* The locks file is a text file containing a list paths to lock, one per line. + * It can be produced from a dconf database using: dconf list-locks + */ + if (!g_file_get_contents (path, &contents, NULL, &error)) + { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + g_warning ("Failed to read %s: %s", path, error->message); + g_clear_error (&error); + } + else + { + char **lines; + gsize i; + + g_debug ("Loading locks from %s", path); + + lines = g_strsplit (contents, "\n", 0); + for (i = 0; lines[i]; i++) + { + char *line = lines[i]; + if (line[0] == '#' || line[0] == '\0') + { + g_free (line); + continue; + } + + g_debug ("Locking key %s", line); + g_hash_table_add (kfsb->system_locks, g_steal_pointer (&line)); + } + + g_free (lines); + } + g_free (contents); + + g_free (path); +} + static void g_keyfile_settings_backend_constructed (GObject *object) { @@ -599,6 +692,8 @@ g_keyfile_settings_backend_constructed (GObject *object) g_keyfile_settings_backend_keyfile_writable (kfsb); g_keyfile_settings_backend_keyfile_reload (kfsb); + + load_system_settings (kfsb); } static void @@ -609,7 +704,7 @@ g_keyfile_settings_backend_set_property (GObject *object, { GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); - switch (prop_id) + switch ((GKeyfileSettingsBackendProperty)prop_id) { case PROP_FILENAME: /* Construct only. */ @@ -633,6 +728,12 @@ g_keyfile_settings_backend_set_property (GObject *object, kfsb->root_group_len = strlen (kfsb->root_group); break; + case PROP_DEFAULTS_DIR: + /* Construct only. */ + g_assert (kfsb->defaults_dir == NULL); + kfsb->defaults_dir = g_value_dup_string (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -647,7 +748,7 @@ g_keyfile_settings_backend_get_property (GObject *object, { GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); - switch (prop_id) + switch ((GKeyfileSettingsBackendProperty)prop_id) { case PROP_FILENAME: g_value_set_string (value, g_file_peek_path (kfsb->file)); @@ -661,6 +762,10 @@ g_keyfile_settings_backend_get_property (GObject *object, g_value_set_string (value, kfsb->root_group); break; + case PROP_DEFAULTS_DIR: + g_value_set_string (value, kfsb->defaults_dir); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -738,6 +843,22 @@ g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * GKeyfileSettingsBackend:default-dir: + * + * The directory where the system defaults and locks are located. + * + * Defaults to `/etc/glib-2.0/settings`. + */ + g_object_class_install_property (object_class, + PROP_DEFAULTS_DIR, + g_param_spec_string ("defaults-dir", + P_("Default dir"), + P_("Defaults dir"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); } /** @@ -792,6 +913,11 @@ g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) * characters in your path names or '=' in your key names you may be in * trouble. * + * The backend reads default values from a keyfile called `defaults` in + * the directory specified by the #GKeyfileSettingsBackend:defaults-dir property, + * and a list of locked keys from a text file with the name `locks` in + * the same location. + * * Returns: (transfer full): a keyfile-backed #GSettingsBackend **/ GSettingsBackend * -- 2.28.0 From 93dceb39fe2de23aeffe40ff02c824393c749270 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 2 Nov 2018 23:00:49 -0400 Subject: [PATCH 4/9] settings: Prefer the keyfile backend when sandboxed When we are in a sandboxed situation, bump the priority of the keyfile settings backend above the dconf one, so we use a keyfile inside the sandbox instead of requiring holes in the sandbox for dconf. --- gio/gkeyfilesettingsbackend.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index 0a4e81511..398cb053a 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -34,6 +34,7 @@ #include "gsimplepermission.h" #include "gsettingsbackendinternal.h" #include "giomodule-priv.h" +#include "gportalsupport.h" #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ()) @@ -76,12 +77,18 @@ typedef struct GFileMonitor *dir_monitor; } GKeyfileSettingsBackend; +#ifdef G_OS_WIN32 +#define EXTENSION_PRIORITY 10 +#else +#define EXTENSION_PRIORITY (glib_should_use_portal () ? 110 : 10) +#endif + G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend, g_keyfile_settings_backend, G_TYPE_SETTINGS_BACKEND, _g_io_modules_ensure_extension_points_registered (); g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, - g_define_type_id, "keyfile", 10)) + g_define_type_id, "keyfile", EXTENSION_PRIORITY)) static void compute_checksum (guint8 *digest, -- 2.28.0 From 5d0d49750564aa7fd9e7d0d58c08a08847570921 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 21 Jan 2019 22:55:45 -0500 Subject: [PATCH 5/9] keyfile settings: Accept unquoted strings It is hard for users to remember that strings have to be explicitly quoted in the keyfile. Be lenient and accept strings that lack those quotes. --- gio/gkeyfilesettingsbackend.c | 19 +++++++++++++++++++ gio/tests/gsettings.c | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index 398cb053a..d5796b706 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -225,6 +225,25 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, if (str) { return_value = g_variant_parse (type, str, NULL, NULL, NULL); + if (return_value == NULL && + g_variant_type_equal (type, G_VARIANT_TYPE_STRING) && + str[0] != '\"') + { + GString *s = g_string_sized_new (strlen (str) + 2); + char *p = str; + + g_string_append_c (s, '\"'); + while (*p) + { + if (*p == '\"') + g_string_append_c (s, '\\'); + g_string_append_c (s, *p); + p++; + } + g_string_append_c (s, '\"'); + return_value = g_variant_parse (type, s->str, NULL, NULL, NULL); + g_string_free (s, TRUE); + } g_free (str); } diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c index 2be4122fe..6eb49f124 100644 --- a/gio/tests/gsettings.c +++ b/gio/tests/gsettings.c @@ -1716,6 +1716,23 @@ test_keyfile (void) g_assert_cmpstr (str, ==, "howdy"); g_free (str); + /* Now check setting a string without quotes */ + called = FALSE; + g_signal_connect (settings, "changed::greeting", G_CALLBACK (key_changed_cb), &called); + + g_key_file_set_string (keyfile, "tests", "greeting", "he\"l🤗uń"); + g_free (data); + data = g_key_file_to_data (keyfile, &len, NULL); + g_file_set_contents ("keyfile/gsettings.store", data, len, &error); + g_assert_no_error (error); + while (!called) + g_main_context_iteration (NULL, FALSE); + g_signal_handlers_disconnect_by_func (settings, key_changed_cb, &called); + + str = g_settings_get_string (settings, "greeting"); + g_assert_cmpstr (str, ==, "he\"l🤗uń"); + g_free (str); + g_settings_set (settings, "farewell", "s", "cheerio"); called = FALSE; -- 2.28.0 From 3765e73f9a2dbbc6863bcffdee85ffb18530c89b Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Wed, 23 Jan 2019 15:14:58 +0000 Subject: [PATCH 6/9] gkeyfilesettingsbackend: Add a code comment to clarify things Signed-off-by: Philip Withnall --- gio/gkeyfilesettingsbackend.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index d5796b706..5ea632305 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -225,6 +225,10 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, if (str) { return_value = g_variant_parse (type, str, NULL, NULL, NULL); + + /* As a special case, support values of type %G_VARIANT_TYPE_STRING + * not being quoted, since users keep forgetting to do it and then + * getting confused. */ if (return_value == NULL && g_variant_type_equal (type, G_VARIANT_TYPE_STRING) && str[0] != '\"') -- 2.28.0 From 1df628861afe027e8dce5e27f4249059bad6bc60 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 10 Jul 2019 11:14:03 -0400 Subject: [PATCH 7/9] key file: Handle filename being NULL This happens when we are default-constructed without explicit arguments. Closes: https://gitlab.gnome.org/GNOME/glib/issues/1825 --- gio/gkeyfilesettingsbackend.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index 5ea632305..a874af287 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -739,7 +739,8 @@ g_keyfile_settings_backend_set_property (GObject *object, case PROP_FILENAME: /* Construct only. */ g_assert (kfsb->file == NULL); - kfsb->file = g_file_new_for_path (g_value_get_string (value)); + if (g_value_get_string (value)) + kfsb->file = g_file_new_for_path (g_value_get_string (value)); break; case PROP_ROOT_PATH: -- 2.28.0 From 9c5d3a6081e5ff419db96d9651bddbfcdb8b1bc6 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 12 Jul 2019 11:30:30 -0400 Subject: [PATCH] portal: Add a getter for dconf access Add method to find whether the sandbox provides access to dconf. This will be used to tweak the priorities for the keyfile settings backend. --- gio/gportalsupport.c | 18 ++++++++++++++++++ gio/gportalsupport.h | 1 + 2 files changed, 19 insertions(+) diff --git a/gio/gportalsupport.c b/gio/gportalsupport.c index 2f1e82517..b0a94b360 100644 --- a/gio/gportalsupport.c +++ b/gio/gportalsupport.c @@ -23,6 +23,7 @@ static gboolean flatpak_info_read; static gboolean use_portal; static gboolean network_available; +static gboolean dconf_access; static void read_flatpak_info (void) @@ -40,11 +41,13 @@ read_flatpak_info (void) use_portal = TRUE; network_available = FALSE; + dconf_access = FALSE; keyfile = g_key_file_new (); if (g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { char **shared = NULL; + char *dconf_policy = NULL; shared = g_key_file_get_string_list (keyfile, "Context", "shared", NULL, NULL); if (shared) @@ -52,6 +55,14 @@ read_flatpak_info (void) network_available = g_strv_contains ((const char * const *)shared, "network"); g_strfreev (shared); } + + dconf_policy = g_key_file_get_string (keyfile, "Session Bus Policy", "ca.desrt.dconf", NULL); + if (dconf_policy) + { + if (strcmp (dconf_policy, "talk") == 0) + dconf_access = TRUE; + g_free (dconf_policy); + } } g_key_file_unref (keyfile); @@ -64,6 +75,7 @@ read_flatpak_info (void) if (var && var[0] == '1') use_portal = TRUE; network_available = TRUE; + dconf_access = TRUE; } } @@ -81,3 +93,9 @@ glib_network_available_in_sandbox (void) return network_available; } +gboolean +glib_has_dconf_access_in_sandbox (void) +{ + read_flatpak_info (); + return dconf_access; +} diff --git a/gio/gportalsupport.h b/gio/gportalsupport.h index a331f45d3..746f1fd6b 100644 --- a/gio/gportalsupport.h +++ b/gio/gportalsupport.h @@ -24,6 +24,7 @@ G_BEGIN_DECLS gboolean glib_should_use_portal (void); gboolean glib_network_available_in_sandbox (void); +gboolean glib_has_dconf_access_in_sandbox (void); G_END_DECLS -- GitLab From e6461f208931e27b52d82f7d3b5a7ce4fc26c9d7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 12 Jul 2019 11:31:37 -0400 Subject: [PATCH 8/9] settings: Tweak priorities for keyfile backend We want to use the keyfile backend in sandboxes, but we want to avoid people losing their existing settings that are stored in dconf. Flatpak does a migration from dconf to keyfile, but only if the app explictly requests it. From an app perspective, there are two steps to the dconf->keyfile migration: 1. Request that flatpak do the migration, by adding the migrate-path key to the metadata 2. Stop adding the 'dconf hole' to the sandbox To keep us from switching to the keyfile backend prematurely, look at whether the app has stopped requesting a 'dconf hole' in the sandbox. --- gio/gkeyfilesettingsbackend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c index a874af287..f5358818e 100644 --- a/gio/gkeyfilesettingsbackend.c +++ b/gio/gkeyfilesettingsbackend.c @@ -80,7 +80,7 @@ typedef struct #ifdef G_OS_WIN32 #define EXTENSION_PRIORITY 10 #else -#define EXTENSION_PRIORITY (glib_should_use_portal () ? 110 : 10) +#define EXTENSION_PRIORITY (glib_should_use_portal () && !glib_has_dconf_access_in_sandbox () ? 110 : 10) #endif G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend, -- 2.28.0 From 5f8d787815dc220fa2e288e03cd0cb9497727753 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 8 Jul 2019 10:31:51 -0400 Subject: [PATCH] Ensure that the keyfile settings backend exists We need to bring the type into existence. Closes: https://gitlab.gnome.org/GNOME/glib/issues/1822 --- gio/giomodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gio/giomodule.c b/gio/giomodule.c index 9bb28985a..1007abdbf 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -1211,6 +1211,7 @@ _g_io_modules_ensure_loaded (void) /* Initialize types from built-in "modules" */ g_type_ensure (g_null_settings_backend_get_type ()); g_type_ensure (g_memory_settings_backend_get_type ()); + g_type_ensure (g_keyfile_settings_backend_get_type ()); #if defined(HAVE_INOTIFY_INIT1) g_type_ensure (g_inotify_file_monitor_get_type ()); #endif -- GitLab