Branch data Line data Source code
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 : 327 : 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 : 40 : compute_checksum (guint8 *digest,
97 : : gconstpointer contents,
98 : : gsize length)
99 : : {
100 : : GChecksum *checksum;
101 : 40 : gsize len = 32;
102 : :
103 : 40 : checksum = g_checksum_new (G_CHECKSUM_SHA256);
104 : 40 : g_checksum_update (checksum, contents, length);
105 : 40 : g_checksum_get_digest (checksum, digest, &len);
106 : 40 : g_checksum_free (checksum);
107 : 40 : g_assert (len == 32);
108 : 40 : }
109 : :
110 : : static gboolean
111 : 16 : g_keyfile_settings_backend_keyfile_write (GKeyfileSettingsBackend *kfsb,
112 : : GError **error)
113 : : {
114 : : gchar *contents;
115 : : gsize length;
116 : : gboolean success;
117 : :
118 : 16 : contents = g_key_file_to_data (kfsb->keyfile, &length, NULL);
119 : 16 : 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 : 16 : compute_checksum (kfsb->digest, contents, length);
125 : 16 : g_free (contents);
126 : :
127 : 16 : return success;
128 : : }
129 : :
130 : : static gboolean
131 : 0 : 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 : 0 : for (i = 0; prefix[i]; i++)
142 : 0 : if (prefix[i] != group_name[i])
143 : 0 : return FALSE;
144 : :
145 : 0 : return group_name[i] == '\0' || group_name[i] == '/';
146 : : }
147 : :
148 : : static gboolean
149 : 52 : convert_path (GKeyfileSettingsBackend *kfsb,
150 : : const gchar *key,
151 : : gchar **group,
152 : : gchar **basename)
153 : : {
154 : 52 : gsize key_len = strlen (key);
155 : : const gchar *last_slash;
156 : :
157 : 52 : if (key_len < kfsb->prefix_len ||
158 : 52 : memcmp (key, kfsb->prefix, kfsb->prefix_len) != 0)
159 : 1 : return FALSE;
160 : :
161 : 51 : key_len -= kfsb->prefix_len;
162 : 51 : key += kfsb->prefix_len;
163 : :
164 : 51 : last_slash = strrchr (key, '/');
165 : :
166 : : /* Disallow empty group names or key names */
167 : 51 : if (key_len == 0 ||
168 : 40 : (last_slash != NULL &&
169 : 40 : (*(last_slash + 1) == '\0' ||
170 : : last_slash == key)))
171 : 10 : return FALSE;
172 : :
173 : 41 : 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 : 39 : if (last_slash != NULL && last_slash - key >= 0 &&
179 : 30 : (gsize) (last_slash - key) == kfsb->root_group_len &&
180 : 1 : memcmp (key, kfsb->root_group, last_slash - key) == 0)
181 : 1 : return FALSE;
182 : : }
183 : : else
184 : : {
185 : : /* if no root_group was given, ensure that the user gave a path */
186 : 2 : if (last_slash == NULL)
187 : 1 : return FALSE;
188 : : }
189 : :
190 : 39 : if (group)
191 : : {
192 : 26 : if (last_slash != NULL)
193 : : {
194 : 20 : *group = g_memdup2 (key, (last_slash - key) + 1);
195 : 20 : (*group)[(last_slash - key)] = '\0';
196 : : }
197 : : else
198 : 12 : *group = g_strdup (kfsb->root_group);
199 : : }
200 : :
201 : 39 : if (basename)
202 : : {
203 : 26 : if (last_slash != NULL)
204 : 20 : *basename = g_memdup2 (last_slash + 1, key_len - (last_slash - key));
205 : : else
206 : 6 : *basename = g_strdup (key);
207 : : }
208 : :
209 : 39 : return TRUE;
210 : : }
211 : :
212 : : static gboolean
213 : 26 : path_is_valid (GKeyfileSettingsBackend *kfsb,
214 : : const gchar *path)
215 : : {
216 : 26 : return convert_path (kfsb, path, NULL, NULL);
217 : : }
218 : :
219 : : static GVariant *
220 : 10 : get_from_keyfile (GKeyfileSettingsBackend *kfsb,
221 : : const GVariantType *type,
222 : : const gchar *key)
223 : : {
224 : 10 : GVariant *return_value = NULL;
225 : : gchar *group, *name;
226 : :
227 : 10 : if (convert_path (kfsb, key, &group, &name))
228 : : {
229 : : gchar *str;
230 : : gchar *sysstr;
231 : :
232 : 10 : g_assert (*name);
233 : :
234 : 10 : sysstr = g_key_file_get_value (kfsb->system_keyfile, group, name, NULL);
235 : 10 : str = g_key_file_get_value (kfsb->keyfile, group, name, NULL);
236 : 10 : if (sysstr &&
237 : 0 : (g_hash_table_contains (kfsb->system_locks, key) ||
238 : : str == NULL))
239 : : {
240 : 0 : g_free (str);
241 : 0 : str = g_steal_pointer (&sysstr);
242 : : }
243 : :
244 : 10 : if (str)
245 : : {
246 : 6 : 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 : 7 : if (return_value == NULL &&
252 : 1 : g_variant_type_equal (type, G_VARIANT_TYPE_STRING) &&
253 : 1 : str[0] != '\"')
254 : : {
255 : 1 : GString *s = g_string_sized_new (strlen (str) + 2);
256 : 1 : char *p = str;
257 : :
258 : : g_string_append_c (s, '\"');
259 : 12 : while (*p)
260 : : {
261 : 11 : if (*p == '\"')
262 : : g_string_append_c (s, '\\');
263 : 11 : g_string_append_c (s, *p);
264 : 11 : p++;
265 : : }
266 : : g_string_append_c (s, '\"');
267 : 1 : return_value = g_variant_parse (type, s->str, NULL, NULL, NULL);
268 : 1 : g_string_free (s, TRUE);
269 : : }
270 : 6 : g_free (str);
271 : : }
272 : :
273 : 10 : g_free (sysstr);
274 : :
275 : 10 : g_free (group);
276 : 10 : g_free (name);
277 : : }
278 : :
279 : 10 : return return_value;
280 : : }
281 : :
282 : : static gboolean
283 : 16 : set_to_keyfile (GKeyfileSettingsBackend *kfsb,
284 : : const gchar *key,
285 : : GVariant *value)
286 : : {
287 : : gchar *group, *name;
288 : :
289 : 16 : if (g_hash_table_contains (kfsb->system_locks, key))
290 : 0 : return FALSE;
291 : :
292 : 16 : if (convert_path (kfsb, key, &group, &name))
293 : : {
294 : 16 : if (value)
295 : : {
296 : 8 : gchar *str = g_variant_print (value, FALSE);
297 : 8 : g_key_file_set_value (kfsb->keyfile, group, name, str);
298 : 8 : g_variant_unref (g_variant_ref_sink (value));
299 : 8 : g_free (str);
300 : : }
301 : : else
302 : : {
303 : 8 : if (*name == '\0')
304 : : {
305 : : gchar **groups;
306 : : gint i;
307 : :
308 : 0 : groups = g_key_file_get_groups (kfsb->keyfile, NULL);
309 : :
310 : 0 : for (i = 0; groups[i]; i++)
311 : 0 : if (group_name_matches (groups[i], group))
312 : 0 : g_key_file_remove_group (kfsb->keyfile, groups[i], NULL);
313 : :
314 : 0 : g_strfreev (groups);
315 : : }
316 : : else
317 : 8 : g_key_file_remove_key (kfsb->keyfile, group, name, NULL);
318 : : }
319 : :
320 : 16 : g_free (group);
321 : 16 : g_free (name);
322 : :
323 : 16 : return TRUE;
324 : : }
325 : :
326 : 0 : return FALSE;
327 : : }
328 : :
329 : : static GVariant *
330 : 10 : g_keyfile_settings_backend_read (GSettingsBackend *backend,
331 : : const gchar *key,
332 : : const GVariantType *expected_type,
333 : : gboolean default_value)
334 : : {
335 : 10 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
336 : :
337 : 10 : if (default_value)
338 : 0 : return NULL;
339 : :
340 : 10 : 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 : 8 : g_keyfile_settings_backend_write_one (gpointer key,
351 : : gpointer value,
352 : : gpointer user_data)
353 : : {
354 : 8 : WriteManyData *data = user_data;
355 : : gboolean success G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
356 : :
357 : 8 : success = set_to_keyfile (data->kfsb, key, value);
358 : 8 : g_assert (success);
359 : :
360 : 8 : return FALSE;
361 : : }
362 : :
363 : : static gboolean
364 : 8 : g_keyfile_settings_backend_check_one (gpointer key,
365 : : gpointer value,
366 : : gpointer user_data)
367 : : {
368 : 8 : WriteManyData *data = user_data;
369 : :
370 : 16 : return data->failed = g_hash_table_contains (data->kfsb->system_locks, key) ||
371 : 8 : !path_is_valid (data->kfsb, key);
372 : : }
373 : :
374 : : static gboolean
375 : 8 : g_keyfile_settings_backend_write_tree (GSettingsBackend *backend,
376 : : GTree *tree,
377 : : gpointer origin_tag)
378 : : {
379 : 8 : WriteManyData data = { G_KEYFILE_SETTINGS_BACKEND (backend), 0 };
380 : : gboolean success;
381 : 8 : GError *error = NULL;
382 : :
383 : 8 : if (!data.kfsb->writable)
384 : 0 : return FALSE;
385 : :
386 : 8 : g_tree_foreach (tree, g_keyfile_settings_backend_check_one, &data);
387 : :
388 : 8 : if (data.failed)
389 : 0 : return FALSE;
390 : :
391 : 8 : g_tree_foreach (tree, g_keyfile_settings_backend_write_one, &data);
392 : 8 : success = g_keyfile_settings_backend_keyfile_write (data.kfsb, &error);
393 : 8 : if (error)
394 : : {
395 : 0 : g_warning ("Failed to write keyfile to %s: %s", g_file_peek_path (data.kfsb->file), error->message);
396 : 0 : g_error_free (error);
397 : : }
398 : :
399 : 8 : g_settings_backend_changed_tree (backend, tree, origin_tag);
400 : :
401 : 8 : return success;
402 : : }
403 : :
404 : : static gboolean
405 : 4 : g_keyfile_settings_backend_write (GSettingsBackend *backend,
406 : : const gchar *key,
407 : : GVariant *value,
408 : : gpointer origin_tag)
409 : : {
410 : 4 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
411 : : gboolean success;
412 : 4 : GError *error = NULL;
413 : :
414 : 4 : if (!kfsb->writable)
415 : 0 : return FALSE;
416 : :
417 : 4 : success = set_to_keyfile (kfsb, key, value);
418 : :
419 : 4 : if (success)
420 : : {
421 : 4 : g_settings_backend_changed (backend, key, origin_tag);
422 : 4 : success = g_keyfile_settings_backend_keyfile_write (kfsb, &error);
423 : 4 : if (error)
424 : : {
425 : 0 : g_warning ("Failed to write keyfile to %s: %s", g_file_peek_path (kfsb->file), error->message);
426 : 0 : g_error_free (error);
427 : : }
428 : : }
429 : :
430 : 4 : return success;
431 : : }
432 : :
433 : : static void
434 : 4 : g_keyfile_settings_backend_reset (GSettingsBackend *backend,
435 : : const gchar *key,
436 : : gpointer origin_tag)
437 : : {
438 : 4 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
439 : 4 : GError *error = NULL;
440 : :
441 : 4 : if (set_to_keyfile (kfsb, key, NULL))
442 : : {
443 : 4 : g_keyfile_settings_backend_keyfile_write (kfsb, &error);
444 : 4 : if (error)
445 : : {
446 : 0 : g_warning ("Failed to write keyfile to %s: %s", g_file_peek_path (kfsb->file), error->message);
447 : 0 : g_error_free (error);
448 : : }
449 : : }
450 : :
451 : 4 : g_settings_backend_changed (backend, key, origin_tag);
452 : 4 : }
453 : :
454 : : static gboolean
455 : 20 : g_keyfile_settings_backend_get_writable (GSettingsBackend *backend,
456 : : const gchar *name)
457 : : {
458 : 20 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
459 : :
460 : 18 : return kfsb->writable &&
461 : 38 : !g_hash_table_contains (kfsb->system_locks, name) &&
462 : 18 : path_is_valid (kfsb, name);
463 : : }
464 : :
465 : : static GPermission *
466 : 0 : g_keyfile_settings_backend_get_permission (GSettingsBackend *backend,
467 : : const gchar *path)
468 : : {
469 : 0 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
470 : :
471 : 0 : return g_object_ref (kfsb->permission);
472 : : }
473 : :
474 : : static void
475 : 4 : keyfile_to_tree (GKeyfileSettingsBackend *kfsb,
476 : : GTree *tree,
477 : : GKeyFile *keyfile,
478 : : gboolean dup_check)
479 : : {
480 : : gchar **groups;
481 : : gint i;
482 : :
483 : 4 : groups = g_key_file_get_groups (keyfile, NULL);
484 : 8 : for (i = 0; groups[i]; i++)
485 : : {
486 : : gboolean is_root_group;
487 : : gchar **keys;
488 : : gint j;
489 : :
490 : 4 : is_root_group = g_strcmp0 (kfsb->root_group, groups[i]) == 0;
491 : :
492 : : /* reject group names that will form invalid key names */
493 : 4 : if (!is_root_group &&
494 : 4 : (g_str_has_prefix (groups[i], "/") ||
495 : 4 : g_str_has_suffix (groups[i], "/") || strstr (groups[i], "//")))
496 : 0 : continue;
497 : :
498 : 4 : keys = g_key_file_get_keys (keyfile, groups[i], NULL, NULL);
499 : 4 : g_assert (keys != NULL);
500 : :
501 : 11 : for (j = 0; keys[j]; j++)
502 : : {
503 : : gchar *path, *value;
504 : :
505 : : /* reject key names with slashes in them */
506 : 7 : if (strchr (keys[j], '/'))
507 : 0 : continue;
508 : :
509 : 7 : if (is_root_group)
510 : 0 : path = g_strdup_printf ("%s%s", kfsb->prefix, keys[j]);
511 : : else
512 : 7 : path = g_strdup_printf ("%s%s/%s", kfsb->prefix, groups[i], keys[j]);
513 : :
514 : 7 : value = g_key_file_get_value (keyfile, groups[i], keys[j], NULL);
515 : :
516 : 7 : if (dup_check && g_strcmp0 (g_tree_lookup (tree, path), value) == 0)
517 : : {
518 : 2 : g_tree_remove (tree, path);
519 : 2 : g_free (value);
520 : 2 : g_free (path);
521 : : }
522 : : else
523 : 5 : g_tree_insert (tree, path, value);
524 : : }
525 : :
526 : 4 : g_strfreev (keys);
527 : : }
528 : 4 : g_strfreev (groups);
529 : 4 : }
530 : :
531 : : static void
532 : 18 : g_keyfile_settings_backend_keyfile_reload (GKeyfileSettingsBackend *kfsb)
533 : : {
534 : : guint8 digest[32];
535 : : gchar *contents;
536 : : gsize length;
537 : :
538 : 18 : contents = NULL;
539 : 18 : length = 0;
540 : :
541 : 18 : g_file_load_contents (kfsb->file, NULL, &contents, &length, NULL, NULL);
542 : 18 : compute_checksum (digest, contents, length);
543 : :
544 : 18 : if (memcmp (kfsb->digest, digest, sizeof digest) != 0)
545 : : {
546 : : GKeyFile *keyfiles[2];
547 : : GTree *tree;
548 : :
549 : 2 : tree = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
550 : : g_free, g_free);
551 : :
552 : 2 : keyfiles[0] = kfsb->keyfile;
553 : 2 : keyfiles[1] = g_key_file_new ();
554 : :
555 : 2 : if (length > 0)
556 : 2 : 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 : 2 : keyfile_to_tree (kfsb, tree, keyfiles[0], FALSE);
561 : 2 : keyfile_to_tree (kfsb, tree, keyfiles[1], TRUE);
562 : 2 : g_key_file_free (keyfiles[0]);
563 : 2 : kfsb->keyfile = keyfiles[1];
564 : :
565 : 2 : if (g_tree_nnodes (tree) > 0)
566 : 2 : g_settings_backend_changed_tree (&kfsb->parent_instance, tree, NULL);
567 : :
568 : 2 : g_tree_unref (tree);
569 : :
570 : 2 : memcpy (kfsb->digest, digest, sizeof digest);
571 : : }
572 : :
573 : 18 : g_free (contents);
574 : 18 : }
575 : :
576 : : static void
577 : 40 : g_keyfile_settings_backend_keyfile_writable (GKeyfileSettingsBackend *kfsb)
578 : : {
579 : : GFileInfo *fileinfo;
580 : : gboolean writable;
581 : :
582 : 40 : fileinfo = g_file_query_info (kfsb->dir, "access::*", 0, NULL, NULL);
583 : :
584 : 40 : if (fileinfo)
585 : : {
586 : 40 : writable =
587 : 79 : g_file_info_get_attribute_boolean (fileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE) &&
588 : 39 : g_file_info_get_attribute_boolean (fileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
589 : 40 : g_object_unref (fileinfo);
590 : : }
591 : : else
592 : 0 : writable = FALSE;
593 : :
594 : 40 : if (writable != kfsb->writable)
595 : : {
596 : 7 : kfsb->writable = writable;
597 : 7 : g_settings_backend_path_writable_changed (&kfsb->parent_instance, "/");
598 : : }
599 : 40 : }
600 : :
601 : : static void
602 : 6 : g_keyfile_settings_backend_finalize (GObject *object)
603 : : {
604 : 6 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
605 : :
606 : 6 : g_key_file_free (kfsb->keyfile);
607 : 6 : g_object_unref (kfsb->permission);
608 : 6 : g_key_file_unref (kfsb->system_keyfile);
609 : 6 : g_hash_table_unref (kfsb->system_locks);
610 : 6 : g_free (kfsb->defaults_dir);
611 : :
612 : 6 : if (kfsb->file_monitor)
613 : : {
614 : 6 : g_file_monitor_cancel (kfsb->file_monitor);
615 : 6 : g_object_unref (kfsb->file_monitor);
616 : : }
617 : 6 : g_object_unref (kfsb->file);
618 : :
619 : 6 : if (kfsb->dir_monitor)
620 : : {
621 : 6 : g_file_monitor_cancel (kfsb->dir_monitor);
622 : 6 : g_object_unref (kfsb->dir_monitor);
623 : : }
624 : 6 : g_object_unref (kfsb->dir);
625 : :
626 : 6 : g_free (kfsb->root_group);
627 : 6 : g_free (kfsb->prefix);
628 : :
629 : 6 : G_OBJECT_CLASS (g_keyfile_settings_backend_parent_class)->finalize (object);
630 : 6 : }
631 : :
632 : : static void
633 : 6 : g_keyfile_settings_backend_init (GKeyfileSettingsBackend *kfsb)
634 : : {
635 : 6 : }
636 : :
637 : : static void
638 : 17 : file_changed (GFileMonitor *monitor,
639 : : GFile *file,
640 : : GFile *other_file,
641 : : GFileMonitorEvent event_type,
642 : : gpointer user_data)
643 : : {
644 : 17 : GKeyfileSettingsBackend *kfsb = user_data;
645 : :
646 : : /* Ignore file deletions, let the GKeyFile content remain in tact. */
647 : 17 : if (event_type != G_FILE_MONITOR_EVENT_DELETED)
648 : 12 : g_keyfile_settings_backend_keyfile_reload (kfsb);
649 : 17 : }
650 : :
651 : : static void
652 : 34 : dir_changed (GFileMonitor *monitor,
653 : : GFile *file,
654 : : GFile *other_file,
655 : : GFileMonitorEvent event_type,
656 : : gpointer user_data)
657 : : {
658 : 34 : GKeyfileSettingsBackend *kfsb = user_data;
659 : :
660 : 34 : g_keyfile_settings_backend_keyfile_writable (kfsb);
661 : 34 : }
662 : :
663 : : static void
664 : 6 : load_system_settings (GKeyfileSettingsBackend *kfsb)
665 : : {
666 : 6 : GError *error = NULL;
667 : 6 : const char *dir = "/etc/glib-2.0/settings";
668 : : char *path;
669 : : char *contents;
670 : :
671 : 6 : kfsb->system_keyfile = g_key_file_new ();
672 : 6 : kfsb->system_locks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
673 : :
674 : 6 : if (kfsb->defaults_dir)
675 : 0 : dir = kfsb->defaults_dir;
676 : :
677 : 6 : 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 : 6 : if (!g_key_file_load_from_file (kfsb->system_keyfile, path, G_KEY_FILE_NONE, &error))
683 : : {
684 : 6 : if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
685 : 0 : g_warning ("Failed to read %s: %s", path, error->message);
686 : 6 : g_clear_error (&error);
687 : : }
688 : : else
689 : 0 : g_debug ("Loading default settings from %s", path);
690 : :
691 : 6 : g_free (path);
692 : :
693 : 6 : 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 : 6 : if (!g_file_get_contents (path, &contents, NULL, &error))
699 : : {
700 : 6 : if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
701 : 0 : g_warning ("Failed to read %s: %s", path, error->message);
702 : 6 : g_clear_error (&error);
703 : : }
704 : : else
705 : : {
706 : : char **lines;
707 : : gsize i;
708 : :
709 : 0 : g_debug ("Loading locks from %s", path);
710 : :
711 : 0 : lines = g_strsplit (contents, "\n", 0);
712 : 0 : for (i = 0; lines[i]; i++)
713 : : {
714 : 0 : char *line = lines[i];
715 : 0 : if (line[0] == '#' || line[0] == '\0')
716 : : {
717 : 0 : g_free (line);
718 : 0 : continue;
719 : : }
720 : :
721 : 0 : g_debug ("Locking key %s", line);
722 : 0 : g_hash_table_add (kfsb->system_locks, g_steal_pointer (&line));
723 : : }
724 : :
725 : 0 : g_free (lines);
726 : : }
727 : 6 : g_free (contents);
728 : :
729 : 6 : g_free (path);
730 : 6 : }
731 : :
732 : : static void
733 : 6 : g_keyfile_settings_backend_constructed (GObject *object)
734 : : {
735 : 6 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
736 : 6 : GError *error = NULL;
737 : : const char *path;
738 : :
739 : 6 : if (kfsb->file == NULL)
740 : : {
741 : 0 : char *filename = g_build_filename (g_get_user_config_dir (),
742 : : "glib-2.0", "settings", "keyfile",
743 : : NULL);
744 : 0 : kfsb->file = g_file_new_for_path (filename);
745 : 0 : g_free (filename);
746 : : }
747 : :
748 : 6 : if (kfsb->prefix == NULL)
749 : : {
750 : 0 : kfsb->prefix = g_strdup ("/");
751 : 0 : kfsb->prefix_len = 1;
752 : : }
753 : :
754 : 6 : kfsb->keyfile = g_key_file_new ();
755 : 6 : kfsb->permission = g_simple_permission_new (TRUE);
756 : :
757 : 6 : kfsb->dir = g_file_get_parent (kfsb->file);
758 : 6 : path = g_file_peek_path (kfsb->dir);
759 : 6 : if (g_mkdir_with_parents (path, 0700) == -1)
760 : 0 : g_warning ("Failed to create %s: %s", path, g_strerror (errno));
761 : :
762 : 6 : kfsb->file_monitor = g_file_monitor (kfsb->file, G_FILE_MONITOR_NONE, NULL, &error);
763 : 6 : if (!kfsb->file_monitor)
764 : : {
765 : 0 : g_warning ("Failed to create file monitor for %s: %s", g_file_peek_path (kfsb->file), error->message);
766 : 0 : g_clear_error (&error);
767 : : }
768 : : else
769 : : {
770 : 6 : g_signal_connect (kfsb->file_monitor, "changed",
771 : : G_CALLBACK (file_changed), kfsb);
772 : : }
773 : :
774 : 6 : kfsb->dir_monitor = g_file_monitor (kfsb->dir, G_FILE_MONITOR_NONE, NULL, &error);
775 : 6 : if (!kfsb->dir_monitor)
776 : : {
777 : 0 : g_warning ("Failed to create file monitor for %s: %s", g_file_peek_path (kfsb->file), error->message);
778 : 0 : g_clear_error (&error);
779 : : }
780 : : else
781 : : {
782 : 6 : g_signal_connect (kfsb->dir_monitor, "changed",
783 : : G_CALLBACK (dir_changed), kfsb);
784 : : }
785 : :
786 : 6 : compute_checksum (kfsb->digest, NULL, 0);
787 : :
788 : 6 : g_keyfile_settings_backend_keyfile_writable (kfsb);
789 : 6 : g_keyfile_settings_backend_keyfile_reload (kfsb);
790 : :
791 : 6 : load_system_settings (kfsb);
792 : 6 : }
793 : :
794 : : static void
795 : 24 : g_keyfile_settings_backend_set_property (GObject *object,
796 : : guint prop_id,
797 : : const GValue *value,
798 : : GParamSpec *pspec)
799 : : {
800 : 24 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
801 : :
802 : 24 : switch ((GKeyfileSettingsBackendProperty)prop_id)
803 : : {
804 : 6 : case PROP_FILENAME:
805 : : /* Construct only. */
806 : 6 : g_assert (kfsb->file == NULL);
807 : 6 : if (g_value_get_string (value))
808 : 6 : kfsb->file = g_file_new_for_path (g_value_get_string (value));
809 : 6 : break;
810 : :
811 : 6 : case PROP_ROOT_PATH:
812 : : /* Construct only. */
813 : 6 : g_assert (kfsb->prefix == NULL);
814 : 6 : kfsb->prefix = g_value_dup_string (value);
815 : 6 : if (kfsb->prefix)
816 : 6 : kfsb->prefix_len = strlen (kfsb->prefix);
817 : 6 : break;
818 : :
819 : 6 : case PROP_ROOT_GROUP:
820 : : /* Construct only. */
821 : 6 : g_assert (kfsb->root_group == NULL);
822 : 6 : kfsb->root_group = g_value_dup_string (value);
823 : 6 : if (kfsb->root_group)
824 : 5 : kfsb->root_group_len = strlen (kfsb->root_group);
825 : 6 : break;
826 : :
827 : 6 : case PROP_DEFAULTS_DIR:
828 : : /* Construct only. */
829 : 6 : g_assert (kfsb->defaults_dir == NULL);
830 : 6 : kfsb->defaults_dir = g_value_dup_string (value);
831 : 6 : break;
832 : :
833 : 0 : default:
834 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
835 : 0 : break;
836 : : }
837 : 24 : }
838 : :
839 : : static void
840 : 0 : g_keyfile_settings_backend_get_property (GObject *object,
841 : : guint prop_id,
842 : : GValue *value,
843 : : GParamSpec *pspec)
844 : : {
845 : 0 : GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
846 : :
847 : 0 : switch ((GKeyfileSettingsBackendProperty)prop_id)
848 : : {
849 : 0 : case PROP_FILENAME:
850 : 0 : g_value_set_string (value, g_file_peek_path (kfsb->file));
851 : 0 : break;
852 : :
853 : 0 : case PROP_ROOT_PATH:
854 : 0 : g_value_set_string (value, kfsb->prefix);
855 : 0 : break;
856 : :
857 : 0 : case PROP_ROOT_GROUP:
858 : 0 : g_value_set_string (value, kfsb->root_group);
859 : 0 : break;
860 : :
861 : 0 : case PROP_DEFAULTS_DIR:
862 : 0 : g_value_set_string (value, kfsb->defaults_dir);
863 : 0 : break;
864 : :
865 : 0 : default:
866 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
867 : 0 : break;
868 : : }
869 : 0 : }
870 : :
871 : : static void
872 : 1 : g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class)
873 : : {
874 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (class);
875 : :
876 : 1 : object_class->finalize = g_keyfile_settings_backend_finalize;
877 : 1 : object_class->constructed = g_keyfile_settings_backend_constructed;
878 : 1 : object_class->get_property = g_keyfile_settings_backend_get_property;
879 : 1 : object_class->set_property = g_keyfile_settings_backend_set_property;
880 : :
881 : 1 : class->read = g_keyfile_settings_backend_read;
882 : 1 : class->write = g_keyfile_settings_backend_write;
883 : 1 : class->write_tree = g_keyfile_settings_backend_write_tree;
884 : 1 : class->reset = g_keyfile_settings_backend_reset;
885 : 1 : class->get_writable = g_keyfile_settings_backend_get_writable;
886 : 1 : 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 : 1 : 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 : 1 : 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 : 1 : 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 : 1 : 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 : 1 : }
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 : 6 : g_keyfile_settings_backend_new (const gchar *filename,
1013 : : const gchar *root_path,
1014 : : const gchar *root_group)
1015 : : {
1016 : 6 : g_return_val_if_fail (filename != NULL, NULL);
1017 : 6 : g_return_val_if_fail (root_path != NULL, NULL);
1018 : 6 : g_return_val_if_fail (g_str_has_prefix (root_path, "/"), NULL);
1019 : 6 : g_return_val_if_fail (g_str_has_suffix (root_path, "/"), NULL);
1020 : 6 : g_return_val_if_fail (strstr (root_path, "//") == NULL, NULL);
1021 : :
1022 : 6 : 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 : : }
|