2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2010 Novell, Inc.
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.
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.
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/>.
18 * Authors: Vincent Untz <vuntz@gnome.org>
19 * Ryan Lortie <desrt@desrt.ca>
28 #include "gfileinfo.h"
29 #include "gfilemonitor.h"
30 #include "gsimplepermission.h"
31 #include "gsettingsbackend.h"
34 #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ())
35 #define G_KEYFILE_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
36 G_TYPE_KEYFILE_SETTINGS_BACKEND, \
37 GKeyfileSettingsBackend))
38 #define G_IS_KEYFILE_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
39 G_TYPE_KEYFILE_SETTINGS_BACKEND))
42 typedef GSettingsBackendClass GKeyfileSettingsBackendClass
;
46 GSettingsBackend parent_instance
;
49 GPermission
*permission
;
58 GFileMonitor
*file_monitor
;
61 GFileMonitor
*dir_monitor
;
62 } GKeyfileSettingsBackend
;
64 static GType
g_keyfile_settings_backend_get_type (void);
65 G_DEFINE_TYPE (GKeyfileSettingsBackend
,
66 g_keyfile_settings_backend
,
67 G_TYPE_SETTINGS_BACKEND
)
70 compute_checksum (guint8
*digest
,
71 gconstpointer contents
,
77 checksum
= g_checksum_new (G_CHECKSUM_SHA256
);
78 g_checksum_update (checksum
, contents
, length
);
79 g_checksum_get_digest (checksum
, digest
, &len
);
80 g_checksum_free (checksum
);
85 g_keyfile_settings_backend_keyfile_write (GKeyfileSettingsBackend
*kfsb
)
90 contents
= g_key_file_to_data (kfsb
->keyfile
, &length
, NULL
);
91 g_file_replace_contents (kfsb
->file
, contents
, length
, NULL
, FALSE
,
92 G_FILE_CREATE_REPLACE_DESTINATION
,
95 compute_checksum (kfsb
->digest
, contents
, length
);
100 group_name_matches (const gchar
*group_name
,
103 /* sort of like g_str_has_prefix() except that it must be an exact
104 * match or the prefix followed by '/'.
106 * for example 'a' is a prefix of 'a' and 'a/b' but not 'ab'.
110 for (i
= 0; prefix
[i
]; i
++)
111 if (prefix
[i
] != group_name
[i
])
114 return group_name
[i
] == '\0' || group_name
[i
] == '/';
118 convert_path (GKeyfileSettingsBackend
*kfsb
,
123 gint key_len
= strlen (key
);
126 if (key_len
< kfsb
->prefix_len
||
127 memcmp (key
, kfsb
->prefix
, kfsb
->prefix_len
) != 0)
130 key_len
-= kfsb
->prefix_len
;
131 key
+= kfsb
->prefix_len
;
133 for (i
= key_len
; i
>= 0; i
--)
137 if (kfsb
->root_group
)
139 /* if a root_group was specified, make sure the user hasn't given
140 * a path that ghosts that group name
142 if (i
== kfsb
->root_group_len
&& memcmp (key
, kfsb
->root_group
, i
) == 0)
147 /* if no root_group was given, ensure that the user gave a path */
156 *group
= g_memdup (key
, i
+ 1);
160 *group
= g_strdup (kfsb
->root_group
);
164 *basename
= g_memdup (key
+ i
+ 1, key_len
- i
);
170 path_is_valid (GKeyfileSettingsBackend
*kfsb
,
173 return convert_path (kfsb
, path
, NULL
, NULL
);
177 get_from_keyfile (GKeyfileSettingsBackend
*kfsb
,
178 const GVariantType
*type
,
181 GVariant
*return_value
= NULL
;
184 if (convert_path (kfsb
, key
, &group
, &name
))
190 str
= g_key_file_get_value (kfsb
->keyfile
, group
, name
, NULL
);
194 return_value
= g_variant_parse (type
, str
, NULL
, NULL
, NULL
);
206 set_to_keyfile (GKeyfileSettingsBackend
*kfsb
,
212 if (convert_path (kfsb
, key
, &group
, &name
))
216 gchar
*str
= g_variant_print (value
, FALSE
);
217 g_key_file_set_value (kfsb
->keyfile
, group
, name
, str
);
218 g_variant_unref (g_variant_ref_sink (value
));
228 groups
= g_key_file_get_groups (kfsb
->keyfile
, NULL
);
230 for (i
= 0; groups
[i
]; i
++)
231 if (group_name_matches (groups
[i
], group
))
232 g_key_file_remove_group (kfsb
->keyfile
, groups
[i
], NULL
);
237 g_key_file_remove_key (kfsb
->keyfile
, group
, name
, NULL
);
250 g_keyfile_settings_backend_read (GSettingsBackend
*backend
,
252 const GVariantType
*expected_type
,
253 gboolean default_value
)
255 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
260 return get_from_keyfile (kfsb
, expected_type
, key
);
265 GKeyfileSettingsBackend
*kfsb
;
270 g_keyfile_settings_backend_write_one (gpointer key
,
274 WriteManyData
*data
= user_data
;
277 success
= set_to_keyfile (data
->kfsb
, key
, value
);
284 g_keyfile_settings_backend_check_one (gpointer key
,
288 WriteManyData
*data
= user_data
;
290 return data
->failed
= !path_is_valid (data
->kfsb
, key
);
294 g_keyfile_settings_backend_write_tree (GSettingsBackend
*backend
,
298 WriteManyData data
= { G_KEYFILE_SETTINGS_BACKEND (backend
) };
300 if (!data
.kfsb
->writable
)
303 g_tree_foreach (tree
, g_keyfile_settings_backend_check_one
, &data
);
308 g_tree_foreach (tree
, g_keyfile_settings_backend_write_one
, &data
);
309 g_keyfile_settings_backend_keyfile_write (data
.kfsb
);
311 g_settings_backend_changed_tree (backend
, tree
, origin_tag
);
317 g_keyfile_settings_backend_write (GSettingsBackend
*backend
,
322 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
328 success
= set_to_keyfile (kfsb
, key
, value
);
332 g_settings_backend_changed (backend
, key
, origin_tag
);
333 g_keyfile_settings_backend_keyfile_write (kfsb
);
340 g_keyfile_settings_backend_reset (GSettingsBackend
*backend
,
344 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
346 if (set_to_keyfile (kfsb
, key
, NULL
))
347 g_keyfile_settings_backend_keyfile_write (kfsb
);
349 g_settings_backend_changed (backend
, key
, origin_tag
);
353 g_keyfile_settings_backend_get_writable (GSettingsBackend
*backend
,
356 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
358 return kfsb
->writable
&& path_is_valid (kfsb
, name
);
362 g_keyfile_settings_backend_get_permission (GSettingsBackend
*backend
,
365 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
367 return g_object_ref (kfsb
->permission
);
371 keyfile_to_tree (GKeyfileSettingsBackend
*kfsb
,
379 groups
= g_key_file_get_groups (keyfile
, NULL
);
380 for (i
= 0; groups
[i
]; i
++)
382 gboolean is_root_group
;
386 is_root_group
= g_strcmp0 (kfsb
->root_group
, groups
[i
]) == 0;
388 /* reject group names that will form invalid key names */
389 if (!is_root_group
&&
390 (g_str_has_prefix (groups
[i
], "/") ||
391 g_str_has_suffix (groups
[i
], "/") || strstr (groups
[i
], "//")))
394 keys
= g_key_file_get_keys (keyfile
, groups
[i
], NULL
, NULL
);
395 g_assert (keys
!= NULL
);
397 for (j
= 0; keys
[j
]; j
++)
401 /* reject key names with slashes in them */
402 if (strchr (keys
[j
], '/'))
406 path
= g_strdup_printf ("%s%s", kfsb
->prefix
, keys
[j
]);
408 path
= g_strdup_printf ("%s%s/%s", kfsb
->prefix
, groups
[i
], keys
[j
]);
410 value
= g_key_file_get_value (keyfile
, groups
[i
], keys
[j
], NULL
);
412 if (dup_check
&& g_strcmp0 (g_tree_lookup (tree
, path
), value
) == 0)
414 g_tree_remove (tree
, path
);
419 g_tree_insert (tree
, path
, value
);
428 g_keyfile_settings_backend_keyfile_reload (GKeyfileSettingsBackend
*kfsb
)
437 g_file_load_contents (kfsb
->file
, NULL
, &contents
, &length
, NULL
, NULL
);
438 compute_checksum (digest
, contents
, length
);
440 if (memcmp (kfsb
->digest
, digest
, sizeof digest
) != 0)
442 GKeyFile
*keyfiles
[2];
445 tree
= g_tree_new_full ((GCompareDataFunc
) strcmp
, NULL
,
448 keyfiles
[0] = kfsb
->keyfile
;
449 keyfiles
[1] = g_key_file_new ();
452 g_key_file_load_from_data (keyfiles
[1], contents
, length
,
453 G_KEY_FILE_KEEP_COMMENTS
|
454 G_KEY_FILE_KEEP_TRANSLATIONS
, NULL
);
456 keyfile_to_tree (kfsb
, tree
, keyfiles
[0], FALSE
);
457 keyfile_to_tree (kfsb
, tree
, keyfiles
[1], TRUE
);
458 g_key_file_free (keyfiles
[0]);
459 kfsb
->keyfile
= keyfiles
[1];
461 if (g_tree_nnodes (tree
) > 0)
462 g_settings_backend_changed_tree (&kfsb
->parent_instance
, tree
, NULL
);
466 memcpy (kfsb
->digest
, digest
, sizeof digest
);
473 g_keyfile_settings_backend_keyfile_writable (GKeyfileSettingsBackend
*kfsb
)
478 fileinfo
= g_file_query_info (kfsb
->dir
, "access::*", 0, NULL
, NULL
);
483 g_file_info_get_attribute_boolean (fileinfo
, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE
) &&
484 g_file_info_get_attribute_boolean (fileinfo
, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE
);
485 g_object_unref (fileinfo
);
490 if (writable
!= kfsb
->writable
)
492 kfsb
->writable
= writable
;
493 g_settings_backend_path_writable_changed (&kfsb
->parent_instance
, "/");
498 g_keyfile_settings_backend_finalize (GObject
*object
)
500 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (object
);
502 g_key_file_free (kfsb
->keyfile
);
503 g_object_unref (kfsb
->permission
);
505 g_file_monitor_cancel (kfsb
->file_monitor
);
506 g_object_unref (kfsb
->file_monitor
);
507 g_object_unref (kfsb
->file
);
509 g_file_monitor_cancel (kfsb
->dir_monitor
);
510 g_object_unref (kfsb
->dir_monitor
);
511 g_object_unref (kfsb
->dir
);
513 g_free (kfsb
->root_group
);
514 g_free (kfsb
->prefix
);
516 G_OBJECT_CLASS (g_keyfile_settings_backend_parent_class
)
521 g_keyfile_settings_backend_init (GKeyfileSettingsBackend
*kfsb
)
526 g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass
*class)
528 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
530 object_class
->finalize
= g_keyfile_settings_backend_finalize
;
532 class->read
= g_keyfile_settings_backend_read
;
533 class->write
= g_keyfile_settings_backend_write
;
534 class->write_tree
= g_keyfile_settings_backend_write_tree
;
535 class->reset
= g_keyfile_settings_backend_reset
;
536 class->get_writable
= g_keyfile_settings_backend_get_writable
;
537 class->get_permission
= g_keyfile_settings_backend_get_permission
;
538 /* No need to implement subscribed/unsubscribe: the only point would be to
539 * stop monitoring the file when there's no GSettings anymore, which is no
545 file_changed (GFileMonitor
*monitor
,
548 GFileMonitorEvent event_type
,
551 GKeyfileSettingsBackend
*kfsb
= user_data
;
553 /* Ignore file deletions, let the GKeyFile content remain in tact. */
554 if (event_type
!= G_FILE_MONITOR_EVENT_DELETED
)
555 g_keyfile_settings_backend_keyfile_reload (kfsb
);
559 dir_changed (GFileMonitor
*monitor
,
562 GFileMonitorEvent event_type
,
565 GKeyfileSettingsBackend
*kfsb
= user_data
;
567 g_keyfile_settings_backend_keyfile_writable (kfsb
);
571 * g_keyfile_settings_backend_new:
572 * @filename: the filename of the keyfile
573 * @root_path: the path under which all settings keys appear
574 * @root_group: (nullable): the group name corresponding to
575 * @root_path, or %NULL
577 * Creates a keyfile-backed #GSettingsBackend.
579 * The filename of the keyfile to use is given by @filename.
581 * All settings read to or written from the backend must fall under the
582 * path given in @root_path (which must start and end with a slash and
583 * not contain two consecutive slashes). @root_path may be "/".
585 * If @root_group is non-%NULL then it specifies the name of the keyfile
586 * group used for keys that are written directly below @root_path. For
587 * example, if @root_path is "/apps/example/" and @root_group is
588 * "toplevel", then settings the key "/apps/example/enabled" to a value
589 * of %TRUE will cause the following to appear in the keyfile:
596 * If @root_group is %NULL then it is not permitted to store keys
597 * directly below the @root_path.
599 * For keys not stored directly below @root_path (ie: in a sub-path),
600 * the name of the subpath (with the final slash stripped) is used as
601 * the name of the keyfile group. To continue the example, if
602 * "/apps/example/profiles/default/font-size" were set to
603 * 12 then the following would appear in the keyfile:
610 * The backend will refuse writes (and return writability as being
611 * %FALSE) for keys outside of @root_path and, in the event that
612 * @root_group is %NULL, also for keys directly under @root_path.
613 * Writes will also be refused if the backend detects that it has the
614 * inability to rewrite the keyfile (ie: the containing directory is not
617 * There is no checking done for your key namespace clashing with the
618 * syntax of the key file format. For example, if you have '[' or ']'
619 * characters in your path names or '=' in your key names you may be in
622 * Returns: (transfer full): a keyfile-backed #GSettingsBackend
625 g_keyfile_settings_backend_new (const gchar
*filename
,
626 const gchar
*root_path
,
627 const gchar
*root_group
)
629 GKeyfileSettingsBackend
*kfsb
;
631 g_return_val_if_fail (filename
!= NULL
, NULL
);
632 g_return_val_if_fail (root_path
!= NULL
, NULL
);
633 g_return_val_if_fail (g_str_has_prefix (root_path
, "/"), NULL
);
634 g_return_val_if_fail (g_str_has_suffix (root_path
, "/"), NULL
);
635 g_return_val_if_fail (strstr (root_path
, "//") == NULL
, NULL
);
637 kfsb
= g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND
, NULL
);
638 kfsb
->keyfile
= g_key_file_new ();
639 kfsb
->permission
= g_simple_permission_new (TRUE
);
641 kfsb
->file
= g_file_new_for_path (filename
);
642 kfsb
->dir
= g_file_get_parent (kfsb
->file
);
643 g_file_make_directory_with_parents (kfsb
->dir
, NULL
, NULL
);
645 kfsb
->file_monitor
= g_file_monitor (kfsb
->file
, 0, NULL
, NULL
);
646 kfsb
->dir_monitor
= g_file_monitor (kfsb
->dir
, 0, NULL
, NULL
);
648 kfsb
->prefix_len
= strlen (root_path
);
649 kfsb
->prefix
= g_strdup (root_path
);
653 kfsb
->root_group_len
= strlen (root_group
);
654 kfsb
->root_group
= g_strdup (root_group
);
657 compute_checksum (kfsb
->digest
, NULL
, 0);
659 g_signal_connect (kfsb
->file_monitor
, "changed",
660 G_CALLBACK (file_changed
), kfsb
);
661 g_signal_connect (kfsb
->dir_monitor
, "changed",
662 G_CALLBACK (dir_changed
), kfsb
);
664 g_keyfile_settings_backend_keyfile_writable (kfsb
);
665 g_keyfile_settings_backend_keyfile_reload (kfsb
);
667 return G_SETTINGS_BACKEND (kfsb
);