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