1/*
2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2010 Novell, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors: Vincent Untz <vuntz@gnome.org>
19 * Ryan Lortie <desrt@desrt.ca>
20 */
21
22#include "config.h"
23
24#include <glib.h>
25#include <glibintl.h>
26
27#include <stdio.h>
28#include <string.h>
29
30#include "gfile.h"
31#include "gfileinfo.h"
32#include "gfileenumerator.h"
33#include "gfilemonitor.h"
34#include "gsimplepermission.h"
35#include "gsettingsbackendinternal.h"
36#include "giomodule-priv.h"
37#include "gportalsupport.h"
38
39
40#define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ())
41#define G_KEYFILE_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
42 G_TYPE_KEYFILE_SETTINGS_BACKEND, \
43 GKeyfileSettingsBackend))
44#define G_IS_KEYFILE_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
45 G_TYPE_KEYFILE_SETTINGS_BACKEND))
46
47
48typedef GSettingsBackendClass GKeyfileSettingsBackendClass;
49
50typedef enum {
51 PROP_FILENAME = 1,
52 PROP_ROOT_PATH,
53 PROP_ROOT_GROUP,
54 PROP_DEFAULTS_DIR
55} GKeyfileSettingsBackendProperty;
56
57typedef struct
58{
59 GSettingsBackend parent_instance;
60
61 GKeyFile *keyfile;
62 GPermission *permission;
63 gboolean writable;
64 char *defaults_dir;
65 GKeyFile *system_keyfile;
66 GHashTable *system_locks; /* Used as a set, owning the strings it contains */
67
68 gchar *prefix;
69 gint prefix_len;
70 gchar *root_group;
71 gint root_group_len;
72
73 GFile *file;
74 GFileMonitor *file_monitor;
75 guint8 digest[32];
76 GFile *dir;
77 GFileMonitor *dir_monitor;
78} GKeyfileSettingsBackend;
79
80#ifdef G_OS_WIN32
81#define EXTENSION_PRIORITY 10
82#else
83#define EXTENSION_PRIORITY (glib_should_use_portal () && !glib_has_dconf_access_in_sandbox () ? 110 : 10)
84#endif
85
86G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend,
87 g_keyfile_settings_backend,
88 G_TYPE_SETTINGS_BACKEND,
89 _g_io_modules_ensure_extension_points_registered ();
90 g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
91 g_define_type_id, "keyfile", EXTENSION_PRIORITY))
92
93static void
94compute_checksum (guint8 *digest,
95 gconstpointer contents,
96 gsize length)
97{
98 GChecksum *checksum;
99 gsize len = 32;
100
101 checksum = g_checksum_new (checksum_type: G_CHECKSUM_SHA256);
102 g_checksum_update (checksum, data: contents, length);
103 g_checksum_get_digest (checksum, buffer: digest, digest_len: &len);
104 g_checksum_free (checksum);
105 g_assert (len == 32);
106}
107
108static gboolean
109g_keyfile_settings_backend_keyfile_write (GKeyfileSettingsBackend *kfsb,
110 GError **error)
111{
112 gchar *contents;
113 gsize length;
114 gboolean success;
115
116 contents = g_key_file_to_data (key_file: kfsb->keyfile, length: &length, NULL);
117 success = g_file_replace_contents (file: kfsb->file, contents, length, NULL, FALSE,
118 flags: G_FILE_CREATE_REPLACE_DESTINATION |
119 G_FILE_CREATE_PRIVATE,
120 NULL, NULL, error);
121
122 compute_checksum (digest: kfsb->digest, contents, length);
123 g_free (mem: contents);
124
125 return success;
126}
127
128static gboolean
129group_name_matches (const gchar *group_name,
130 const gchar *prefix)
131{
132 /* sort of like g_str_has_prefix() except that it must be an exact
133 * match or the prefix followed by '/'.
134 *
135 * for example 'a' is a prefix of 'a' and 'a/b' but not 'ab'.
136 */
137 gint i;
138
139 for (i = 0; prefix[i]; i++)
140 if (prefix[i] != group_name[i])
141 return FALSE;
142
143 return group_name[i] == '\0' || group_name[i] == '/';
144}
145
146static gboolean
147convert_path (GKeyfileSettingsBackend *kfsb,
148 const gchar *key,
149 gchar **group,
150 gchar **basename)
151{
152 gsize key_len = strlen (s: key);
153 const gchar *last_slash;
154
155 if (key_len < kfsb->prefix_len ||
156 memcmp (s1: key, s2: kfsb->prefix, n: kfsb->prefix_len) != 0)
157 return FALSE;
158
159 key_len -= kfsb->prefix_len;
160 key += kfsb->prefix_len;
161
162 last_slash = strrchr (s: key, c: '/');
163
164 /* Disallow empty group names or key names */
165 if (key_len == 0 ||
166 (last_slash != NULL &&
167 (*(last_slash + 1) == '\0' ||
168 last_slash == key)))
169 return FALSE;
170
171 if (kfsb->root_group)
172 {
173 /* if a root_group was specified, make sure the user hasn't given
174 * a path that ghosts that group name
175 */
176 if (last_slash != NULL && (last_slash - key) == kfsb->root_group_len && memcmp (s1: key, s2: kfsb->root_group, n: last_slash - key) == 0)
177 return FALSE;
178 }
179 else
180 {
181 /* if no root_group was given, ensure that the user gave a path */
182 if (last_slash == NULL)
183 return FALSE;
184 }
185
186 if (group)
187 {
188 if (last_slash != NULL)
189 {
190 *group = g_memdup2 (mem: key, byte_size: (last_slash - key) + 1);
191 (*group)[(last_slash - key)] = '\0';
192 }
193 else
194 *group = g_strdup (str: kfsb->root_group);
195 }
196
197 if (basename)
198 {
199 if (last_slash != NULL)
200 *basename = g_memdup2 (mem: last_slash + 1, byte_size: key_len - (last_slash - key));
201 else
202 *basename = g_strdup (str: key);
203 }
204
205 return TRUE;
206}
207
208static gboolean
209path_is_valid (GKeyfileSettingsBackend *kfsb,
210 const gchar *path)
211{
212 return convert_path (kfsb, key: path, NULL, NULL);
213}
214
215static GVariant *
216get_from_keyfile (GKeyfileSettingsBackend *kfsb,
217 const GVariantType *type,
218 const gchar *key)
219{
220 GVariant *return_value = NULL;
221 gchar *group, *name;
222
223 if (convert_path (kfsb, key, group: &group, basename: &name))
224 {
225 gchar *str;
226 gchar *sysstr;
227
228 g_assert (*name);
229
230 sysstr = g_key_file_get_value (key_file: kfsb->system_keyfile, group_name: group, key: name, NULL);
231 str = g_key_file_get_value (key_file: kfsb->keyfile, group_name: group, key: name, NULL);
232 if (sysstr &&
233 (g_hash_table_contains (hash_table: kfsb->system_locks, key) ||
234 str == NULL))
235 {
236 g_free (mem: str);
237 str = g_steal_pointer (&sysstr);
238 }
239
240 if (str)
241 {
242 return_value = g_variant_parse (type, text: str, NULL, NULL, NULL);
243
244 /* As a special case, support values of type %G_VARIANT_TYPE_STRING
245 * not being quoted, since users keep forgetting to do it and then
246 * getting confused. */
247 if (return_value == NULL &&
248 g_variant_type_equal (type1: type, G_VARIANT_TYPE_STRING) &&
249 str[0] != '\"')
250 {
251 GString *s = g_string_sized_new (dfl_size: strlen (s: str) + 2);
252 char *p = str;
253
254 g_string_append_c (s, '\"');
255 while (*p)
256 {
257 if (*p == '\"')
258 g_string_append_c (s, '\\');
259 g_string_append_c (s, *p);
260 p++;
261 }
262 g_string_append_c (s, '\"');
263 return_value = g_variant_parse (type, text: s->str, NULL, NULL, NULL);
264 g_string_free (string: s, TRUE);
265 }
266 g_free (mem: str);
267 }
268
269 g_free (mem: sysstr);
270
271 g_free (mem: group);
272 g_free (mem: name);
273 }
274
275 return return_value;
276}
277
278static gboolean
279set_to_keyfile (GKeyfileSettingsBackend *kfsb,
280 const gchar *key,
281 GVariant *value)
282{
283 gchar *group, *name;
284
285 if (g_hash_table_contains (hash_table: kfsb->system_locks, key))
286 return FALSE;
287
288 if (convert_path (kfsb, key, group: &group, basename: &name))
289 {
290 if (value)
291 {
292 gchar *str = g_variant_print (value, FALSE);
293 g_key_file_set_value (key_file: kfsb->keyfile, group_name: group, key: name, value: str);
294 g_variant_unref (value: g_variant_ref_sink (value));
295 g_free (mem: str);
296 }
297 else
298 {
299 if (*name == '\0')
300 {
301 gchar **groups;
302 gint i;
303
304 groups = g_key_file_get_groups (key_file: kfsb->keyfile, NULL);
305
306 for (i = 0; groups[i]; i++)
307 if (group_name_matches (group_name: groups[i], prefix: group))
308 g_key_file_remove_group (key_file: kfsb->keyfile, group_name: groups[i], NULL);
309
310 g_strfreev (str_array: groups);
311 }
312 else
313 g_key_file_remove_key (key_file: kfsb->keyfile, group_name: group, key: name, NULL);
314 }
315
316 g_free (mem: group);
317 g_free (mem: name);
318
319 return TRUE;
320 }
321
322 return FALSE;
323}
324
325static GVariant *
326g_keyfile_settings_backend_read (GSettingsBackend *backend,
327 const gchar *key,
328 const GVariantType *expected_type,
329 gboolean default_value)
330{
331 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
332
333 if (default_value)
334 return NULL;
335
336 return get_from_keyfile (kfsb, type: expected_type, key);
337}
338
339typedef struct
340{
341 GKeyfileSettingsBackend *kfsb;
342 gboolean failed;
343} WriteManyData;
344
345static gboolean
346g_keyfile_settings_backend_write_one (gpointer key,
347 gpointer value,
348 gpointer user_data)
349{
350 WriteManyData *data = user_data;
351 gboolean success G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
352
353 success = set_to_keyfile (kfsb: data->kfsb, key, value);
354 g_assert (success);
355
356 return FALSE;
357}
358
359static gboolean
360g_keyfile_settings_backend_check_one (gpointer key,
361 gpointer value,
362 gpointer user_data)
363{
364 WriteManyData *data = user_data;
365
366 return data->failed = g_hash_table_contains (hash_table: data->kfsb->system_locks, key) ||
367 !path_is_valid (kfsb: data->kfsb, path: key);
368}
369
370static gboolean
371g_keyfile_settings_backend_write_tree (GSettingsBackend *backend,
372 GTree *tree,
373 gpointer origin_tag)
374{
375 WriteManyData data = { G_KEYFILE_SETTINGS_BACKEND (backend), 0 };
376 gboolean success;
377 GError *error = NULL;
378
379 if (!data.kfsb->writable)
380 return FALSE;
381
382 g_tree_foreach (tree, func: g_keyfile_settings_backend_check_one, user_data: &data);
383
384 if (data.failed)
385 return FALSE;
386
387 g_tree_foreach (tree, func: g_keyfile_settings_backend_write_one, user_data: &data);
388 success = g_keyfile_settings_backend_keyfile_write (kfsb: data.kfsb, error: &error);
389 if (error)
390 {
391 g_warning ("Failed to write keyfile to %s: %s", g_file_peek_path (data.kfsb->file), error->message);
392 g_error_free (error);
393 }
394
395 g_settings_backend_changed_tree (backend, tree, origin_tag);
396
397 return success;
398}
399
400static gboolean
401g_keyfile_settings_backend_write (GSettingsBackend *backend,
402 const gchar *key,
403 GVariant *value,
404 gpointer origin_tag)
405{
406 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
407 gboolean success;
408 GError *error = NULL;
409
410 if (!kfsb->writable)
411 return FALSE;
412
413 success = set_to_keyfile (kfsb, key, value);
414
415 if (success)
416 {
417 g_settings_backend_changed (backend, key, origin_tag);
418 success = g_keyfile_settings_backend_keyfile_write (kfsb, error: &error);
419 if (error)
420 {
421 g_warning ("Failed to write keyfile to %s: %s", g_file_peek_path (kfsb->file), error->message);
422 g_error_free (error);
423 }
424 }
425
426 return success;
427}
428
429static void
430g_keyfile_settings_backend_reset (GSettingsBackend *backend,
431 const gchar *key,
432 gpointer origin_tag)
433{
434 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
435 GError *error = NULL;
436
437 if (set_to_keyfile (kfsb, key, NULL))
438 {
439 g_keyfile_settings_backend_keyfile_write (kfsb, error: &error);
440 if (error)
441 {
442 g_warning ("Failed to write keyfile to %s: %s", g_file_peek_path (kfsb->file), error->message);
443 g_error_free (error);
444 }
445 }
446
447 g_settings_backend_changed (backend, key, origin_tag);
448}
449
450static gboolean
451g_keyfile_settings_backend_get_writable (GSettingsBackend *backend,
452 const gchar *name)
453{
454 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
455
456 return kfsb->writable &&
457 !g_hash_table_contains (hash_table: kfsb->system_locks, key: name) &&
458 path_is_valid (kfsb, path: name);
459}
460
461static GPermission *
462g_keyfile_settings_backend_get_permission (GSettingsBackend *backend,
463 const gchar *path)
464{
465 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
466
467 return g_object_ref (kfsb->permission);
468}
469
470static void
471keyfile_to_tree (GKeyfileSettingsBackend *kfsb,
472 GTree *tree,
473 GKeyFile *keyfile,
474 gboolean dup_check)
475{
476 gchar **groups;
477 gint i;
478
479 groups = g_key_file_get_groups (key_file: keyfile, NULL);
480 for (i = 0; groups[i]; i++)
481 {
482 gboolean is_root_group;
483 gchar **keys;
484 gint j;
485
486 is_root_group = g_strcmp0 (str1: kfsb->root_group, str2: groups[i]) == 0;
487
488 /* reject group names that will form invalid key names */
489 if (!is_root_group &&
490 (g_str_has_prefix (str: groups[i], prefix: "/") ||
491 g_str_has_suffix (str: groups[i], suffix: "/") || strstr (haystack: groups[i], needle: "//")))
492 continue;
493
494 keys = g_key_file_get_keys (key_file: keyfile, group_name: groups[i], NULL, NULL);
495 g_assert (keys != NULL);
496
497 for (j = 0; keys[j]; j++)
498 {
499 gchar *path, *value;
500
501 /* reject key names with slashes in them */
502 if (strchr (s: keys[j], c: '/'))
503 continue;
504
505 if (is_root_group)
506 path = g_strdup_printf (format: "%s%s", kfsb->prefix, keys[j]);
507 else
508 path = g_strdup_printf (format: "%s%s/%s", kfsb->prefix, groups[i], keys[j]);
509
510 value = g_key_file_get_value (key_file: keyfile, group_name: groups[i], key: keys[j], NULL);
511
512 if (dup_check && g_strcmp0 (str1: g_tree_lookup (tree, key: path), str2: value) == 0)
513 {
514 g_tree_remove (tree, key: path);
515 g_free (mem: value);
516 g_free (mem: path);
517 }
518 else
519 g_tree_insert (tree, key: path, value);
520 }
521
522 g_strfreev (str_array: keys);
523 }
524 g_strfreev (str_array: groups);
525}
526
527static void
528g_keyfile_settings_backend_keyfile_reload (GKeyfileSettingsBackend *kfsb)
529{
530 guint8 digest[32];
531 gchar *contents;
532 gsize length;
533
534 contents = NULL;
535 length = 0;
536
537 g_file_load_contents (file: kfsb->file, NULL, contents: &contents, length: &length, NULL, NULL);
538 compute_checksum (digest, contents, length);
539
540 if (memcmp (s1: kfsb->digest, s2: digest, n: sizeof digest) != 0)
541 {
542 GKeyFile *keyfiles[2];
543 GTree *tree;
544
545 tree = g_tree_new_full (key_compare_func: (GCompareDataFunc) strcmp, NULL,
546 key_destroy_func: g_free, value_destroy_func: g_free);
547
548 keyfiles[0] = kfsb->keyfile;
549 keyfiles[1] = g_key_file_new ();
550
551 if (length > 0)
552 g_key_file_load_from_data (key_file: keyfiles[1], data: contents, length,
553 flags: G_KEY_FILE_KEEP_COMMENTS |
554 G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
555
556 keyfile_to_tree (kfsb, tree, keyfile: keyfiles[0], FALSE);
557 keyfile_to_tree (kfsb, tree, keyfile: keyfiles[1], TRUE);
558 g_key_file_free (key_file: keyfiles[0]);
559 kfsb->keyfile = keyfiles[1];
560
561 if (g_tree_nnodes (tree) > 0)
562 g_settings_backend_changed_tree (backend: &kfsb->parent_instance, tree, NULL);
563
564 g_tree_unref (tree);
565
566 memcpy (dest: kfsb->digest, src: digest, n: sizeof digest);
567 }
568
569 g_free (mem: contents);
570}
571
572static void
573g_keyfile_settings_backend_keyfile_writable (GKeyfileSettingsBackend *kfsb)
574{
575 GFileInfo *fileinfo;
576 gboolean writable;
577
578 fileinfo = g_file_query_info (file: kfsb->dir, attributes: "access::*", flags: 0, NULL, NULL);
579
580 if (fileinfo)
581 {
582 writable =
583 g_file_info_get_attribute_boolean (info: fileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE) &&
584 g_file_info_get_attribute_boolean (info: fileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
585 g_object_unref (object: fileinfo);
586 }
587 else
588 writable = FALSE;
589
590 if (writable != kfsb->writable)
591 {
592 kfsb->writable = writable;
593 g_settings_backend_path_writable_changed (backend: &kfsb->parent_instance, path: "/");
594 }
595}
596
597static void
598g_keyfile_settings_backend_finalize (GObject *object)
599{
600 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
601
602 g_key_file_free (key_file: kfsb->keyfile);
603 g_object_unref (object: kfsb->permission);
604 g_key_file_unref (key_file: kfsb->system_keyfile);
605 g_hash_table_unref (hash_table: kfsb->system_locks);
606 g_free (mem: kfsb->defaults_dir);
607
608 if (kfsb->file_monitor)
609 {
610 g_file_monitor_cancel (monitor: kfsb->file_monitor);
611 g_object_unref (object: kfsb->file_monitor);
612 }
613 g_object_unref (object: kfsb->file);
614
615 if (kfsb->dir_monitor)
616 {
617 g_file_monitor_cancel (monitor: kfsb->dir_monitor);
618 g_object_unref (object: kfsb->dir_monitor);
619 }
620 g_object_unref (object: kfsb->dir);
621
622 g_free (mem: kfsb->root_group);
623 g_free (mem: kfsb->prefix);
624
625 G_OBJECT_CLASS (g_keyfile_settings_backend_parent_class)->finalize (object);
626}
627
628static void
629g_keyfile_settings_backend_init (GKeyfileSettingsBackend *kfsb)
630{
631}
632
633static void
634file_changed (GFileMonitor *monitor,
635 GFile *file,
636 GFile *other_file,
637 GFileMonitorEvent event_type,
638 gpointer user_data)
639{
640 GKeyfileSettingsBackend *kfsb = user_data;
641
642 /* Ignore file deletions, let the GKeyFile content remain in tact. */
643 if (event_type != G_FILE_MONITOR_EVENT_DELETED)
644 g_keyfile_settings_backend_keyfile_reload (kfsb);
645}
646
647static void
648dir_changed (GFileMonitor *monitor,
649 GFile *file,
650 GFile *other_file,
651 GFileMonitorEvent event_type,
652 gpointer user_data)
653{
654 GKeyfileSettingsBackend *kfsb = user_data;
655
656 g_keyfile_settings_backend_keyfile_writable (kfsb);
657}
658
659static void
660load_system_settings (GKeyfileSettingsBackend *kfsb)
661{
662 GError *error = NULL;
663 const char *dir = "/etc/glib-2.0/settings";
664 char *path;
665 char *contents;
666
667 kfsb->system_keyfile = g_key_file_new ();
668 kfsb->system_locks = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, NULL);
669
670 if (kfsb->defaults_dir)
671 dir = kfsb->defaults_dir;
672
673 path = g_build_filename (first_element: dir, "defaults", NULL);
674
675 /* The defaults are in the same keyfile format that we use for the settings.
676 * It can be produced from a dconf database using: dconf dump
677 */
678 if (!g_key_file_load_from_file (key_file: kfsb->system_keyfile, file: path, flags: G_KEY_FILE_NONE, error: &error))
679 {
680 if (!g_error_matches (error, G_FILE_ERROR, code: G_FILE_ERROR_NOENT))
681 g_warning ("Failed to read %s: %s", path, error->message);
682 g_clear_error (err: &error);
683 }
684 else
685 g_debug ("Loading default settings from %s", path);
686
687 g_free (mem: path);
688
689 path = g_build_filename (first_element: dir, "locks", NULL);
690
691 /* The locks file is a text file containing a list paths to lock, one per line.
692 * It can be produced from a dconf database using: dconf list-locks
693 */
694 if (!g_file_get_contents (filename: path, contents: &contents, NULL, error: &error))
695 {
696 if (!g_error_matches (error, G_FILE_ERROR, code: G_FILE_ERROR_NOENT))
697 g_warning ("Failed to read %s: %s", path, error->message);
698 g_clear_error (err: &error);
699 }
700 else
701 {
702 char **lines;
703 gsize i;
704
705 g_debug ("Loading locks from %s", path);
706
707 lines = g_strsplit (string: contents, delimiter: "\n", max_tokens: 0);
708 for (i = 0; lines[i]; i++)
709 {
710 char *line = lines[i];
711 if (line[0] == '#' || line[0] == '\0')
712 {
713 g_free (mem: line);
714 continue;
715 }
716
717 g_debug ("Locking key %s", line);
718 g_hash_table_add (hash_table: kfsb->system_locks, g_steal_pointer (&line));
719 }
720
721 g_free (mem: lines);
722 }
723 g_free (mem: contents);
724
725 g_free (mem: path);
726}
727
728static void
729g_keyfile_settings_backend_constructed (GObject *object)
730{
731 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
732 GError *error = NULL;
733 const char *path;
734
735 if (kfsb->file == NULL)
736 {
737 char *filename = g_build_filename (first_element: g_get_user_config_dir (),
738 "glib-2.0", "settings", "keyfile",
739 NULL);
740 kfsb->file = g_file_new_for_path (path: filename);
741 g_free (mem: filename);
742 }
743
744 if (kfsb->prefix == NULL)
745 {
746 kfsb->prefix = g_strdup (str: "/");
747 kfsb->prefix_len = 1;
748 }
749
750 kfsb->keyfile = g_key_file_new ();
751 kfsb->permission = g_simple_permission_new (TRUE);
752
753 kfsb->dir = g_file_get_parent (file: kfsb->file);
754 path = g_file_peek_path (file: kfsb->dir);
755 if (g_mkdir_with_parents (pathname: path, mode: 0700) == -1)
756 g_warning ("Failed to create %s: %s", path, g_strerror (errno));
757
758 kfsb->file_monitor = g_file_monitor (file: kfsb->file, flags: G_FILE_MONITOR_NONE, NULL, error: &error);
759 if (!kfsb->file_monitor)
760 {
761 g_warning ("Failed to create file monitor for %s: %s", g_file_peek_path (kfsb->file), error->message);
762 g_clear_error (err: &error);
763 }
764 else
765 {
766 g_signal_connect (kfsb->file_monitor, "changed",
767 G_CALLBACK (file_changed), kfsb);
768 }
769
770 kfsb->dir_monitor = g_file_monitor (file: kfsb->dir, flags: G_FILE_MONITOR_NONE, NULL, error: &error);
771 if (!kfsb->dir_monitor)
772 {
773 g_warning ("Failed to create file monitor for %s: %s", g_file_peek_path (kfsb->file), error->message);
774 g_clear_error (err: &error);
775 }
776 else
777 {
778 g_signal_connect (kfsb->dir_monitor, "changed",
779 G_CALLBACK (dir_changed), kfsb);
780 }
781
782 compute_checksum (digest: kfsb->digest, NULL, length: 0);
783
784 g_keyfile_settings_backend_keyfile_writable (kfsb);
785 g_keyfile_settings_backend_keyfile_reload (kfsb);
786
787 load_system_settings (kfsb);
788}
789
790static void
791g_keyfile_settings_backend_set_property (GObject *object,
792 guint prop_id,
793 const GValue *value,
794 GParamSpec *pspec)
795{
796 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
797
798 switch ((GKeyfileSettingsBackendProperty)prop_id)
799 {
800 case PROP_FILENAME:
801 /* Construct only. */
802 g_assert (kfsb->file == NULL);
803 if (g_value_get_string (value))
804 kfsb->file = g_file_new_for_path (path: g_value_get_string (value));
805 break;
806
807 case PROP_ROOT_PATH:
808 /* Construct only. */
809 g_assert (kfsb->prefix == NULL);
810 kfsb->prefix = g_value_dup_string (value);
811 if (kfsb->prefix)
812 kfsb->prefix_len = strlen (s: kfsb->prefix);
813 break;
814
815 case PROP_ROOT_GROUP:
816 /* Construct only. */
817 g_assert (kfsb->root_group == NULL);
818 kfsb->root_group = g_value_dup_string (value);
819 if (kfsb->root_group)
820 kfsb->root_group_len = strlen (s: kfsb->root_group);
821 break;
822
823 case PROP_DEFAULTS_DIR:
824 /* Construct only. */
825 g_assert (kfsb->defaults_dir == NULL);
826 kfsb->defaults_dir = g_value_dup_string (value);
827 break;
828
829 default:
830 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
831 break;
832 }
833}
834
835static void
836g_keyfile_settings_backend_get_property (GObject *object,
837 guint prop_id,
838 GValue *value,
839 GParamSpec *pspec)
840{
841 GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
842
843 switch ((GKeyfileSettingsBackendProperty)prop_id)
844 {
845 case PROP_FILENAME:
846 g_value_set_string (value, v_string: g_file_peek_path (file: kfsb->file));
847 break;
848
849 case PROP_ROOT_PATH:
850 g_value_set_string (value, v_string: kfsb->prefix);
851 break;
852
853 case PROP_ROOT_GROUP:
854 g_value_set_string (value, v_string: kfsb->root_group);
855 break;
856
857 case PROP_DEFAULTS_DIR:
858 g_value_set_string (value, v_string: kfsb->defaults_dir);
859 break;
860
861 default:
862 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
863 break;
864 }
865}
866
867static void
868g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class)
869{
870 GObjectClass *object_class = G_OBJECT_CLASS (class);
871
872 object_class->finalize = g_keyfile_settings_backend_finalize;
873 object_class->constructed = g_keyfile_settings_backend_constructed;
874 object_class->get_property = g_keyfile_settings_backend_get_property;
875 object_class->set_property = g_keyfile_settings_backend_set_property;
876
877 class->read = g_keyfile_settings_backend_read;
878 class->write = g_keyfile_settings_backend_write;
879 class->write_tree = g_keyfile_settings_backend_write_tree;
880 class->reset = g_keyfile_settings_backend_reset;
881 class->get_writable = g_keyfile_settings_backend_get_writable;
882 class->get_permission = g_keyfile_settings_backend_get_permission;
883 /* No need to implement subscribed/unsubscribe: the only point would be to
884 * stop monitoring the file when there's no GSettings anymore, which is no
885 * big win.
886 */
887
888 /**
889 * GKeyfileSettingsBackend:filename:
890 *
891 * The location where the settings are stored on disk.
892 *
893 * Defaults to `$XDG_CONFIG_HOME/glib-2.0/settings/keyfile`.
894 */
895 g_object_class_install_property (oclass: object_class,
896 property_id: PROP_FILENAME,
897 pspec: g_param_spec_string (name: "filename",
898 P_("Filename"),
899 P_("The filename"),
900 NULL,
901 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
902 G_PARAM_STATIC_STRINGS));
903
904 /**
905 * GKeyfileSettingsBackend:root-path:
906 *
907 * All settings read to or written from the backend must fall under the
908 * path given in @root_path (which must start and end with a slash and
909 * not contain two consecutive slashes). @root_path may be "/".
910 *
911 * Defaults to "/".
912 */
913 g_object_class_install_property (oclass: object_class,
914 property_id: PROP_ROOT_PATH,
915 pspec: g_param_spec_string (name: "root-path",
916 P_("Root path"),
917 P_("The root path"),
918 NULL,
919 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
920 G_PARAM_STATIC_STRINGS));
921
922 /**
923 * GKeyfileSettingsBackend:root-group:
924 *
925 * If @root_group is non-%NULL then it specifies the name of the keyfile
926 * group used for keys that are written directly below the root path.
927 *
928 * Defaults to NULL.
929 */
930 g_object_class_install_property (oclass: object_class,
931 property_id: PROP_ROOT_GROUP,
932 pspec: g_param_spec_string (name: "root-group",
933 P_("Root group"),
934 P_("The root group"),
935 NULL,
936 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
937 G_PARAM_STATIC_STRINGS));
938
939 /**
940 * GKeyfileSettingsBackend:default-dir:
941 *
942 * The directory where the system defaults and locks are located.
943 *
944 * Defaults to `/etc/glib-2.0/settings`.
945 */
946 g_object_class_install_property (oclass: object_class,
947 property_id: PROP_DEFAULTS_DIR,
948 pspec: g_param_spec_string (name: "defaults-dir",
949 P_("Default dir"),
950 P_("Defaults dir"),
951 NULL,
952 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
953 G_PARAM_STATIC_STRINGS));
954}
955
956/**
957 * g_keyfile_settings_backend_new:
958 * @filename: the filename of the keyfile
959 * @root_path: the path under which all settings keys appear
960 * @root_group: (nullable): the group name corresponding to
961 * @root_path, or %NULL
962 *
963 * Creates a keyfile-backed #GSettingsBackend.
964 *
965 * The filename of the keyfile to use is given by @filename.
966 *
967 * All settings read to or written from the backend must fall under the
968 * path given in @root_path (which must start and end with a slash and
969 * not contain two consecutive slashes). @root_path may be "/".
970 *
971 * If @root_group is non-%NULL then it specifies the name of the keyfile
972 * group used for keys that are written directly below @root_path. For
973 * example, if @root_path is "/apps/example/" and @root_group is
974 * "toplevel", then settings the key "/apps/example/enabled" to a value
975 * of %TRUE will cause the following to appear in the keyfile:
976 *
977 * |[
978 * [toplevel]
979 * enabled=true
980 * ]|
981 *
982 * If @root_group is %NULL then it is not permitted to store keys
983 * directly below the @root_path.
984 *
985 * For keys not stored directly below @root_path (ie: in a sub-path),
986 * the name of the subpath (with the final slash stripped) is used as
987 * the name of the keyfile group. To continue the example, if
988 * "/apps/example/profiles/default/font-size" were set to
989 * 12 then the following would appear in the keyfile:
990 *
991 * |[
992 * [profiles/default]
993 * font-size=12
994 * ]|
995 *
996 * The backend will refuse writes (and return writability as being
997 * %FALSE) for keys outside of @root_path and, in the event that
998 * @root_group is %NULL, also for keys directly under @root_path.
999 * Writes will also be refused if the backend detects that it has the
1000 * inability to rewrite the keyfile (ie: the containing directory is not
1001 * writable).
1002 *
1003 * There is no checking done for your key namespace clashing with the
1004 * syntax of the key file format. For example, if you have '[' or ']'
1005 * characters in your path names or '=' in your key names you may be in
1006 * trouble.
1007 *
1008 * The backend reads default values from a keyfile called `defaults` in
1009 * the directory specified by the #GKeyfileSettingsBackend:defaults-dir property,
1010 * and a list of locked keys from a text file with the name `locks` in
1011 * the same location.
1012 *
1013 * Returns: (transfer full): a keyfile-backed #GSettingsBackend
1014 **/
1015GSettingsBackend *
1016g_keyfile_settings_backend_new (const gchar *filename,
1017 const gchar *root_path,
1018 const gchar *root_group)
1019{
1020 g_return_val_if_fail (filename != NULL, NULL);
1021 g_return_val_if_fail (root_path != NULL, NULL);
1022 g_return_val_if_fail (g_str_has_prefix (root_path, "/"), NULL);
1023 g_return_val_if_fail (g_str_has_suffix (root_path, "/"), NULL);
1024 g_return_val_if_fail (strstr (root_path, "//") == NULL, NULL);
1025
1026 return G_SETTINGS_BACKEND (g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND,
1027 "filename", filename,
1028 "root-path", root_path,
1029 "root-group", root_group,
1030 NULL));
1031}
1032

source code of gtk/subprojects/glib/gio/gkeyfilesettingsbackend.c