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 of the licence, 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, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Authors: Vincent Untz <vuntz@gnome.org>
21 * Ryan Lortie <desrt@desrt.ca>
30 #include "gfileinfo.h"
31 #include "gfilemonitor.h"
32 #include "gsimplepermission.h"
33 #include "gsettingsbackend.h"
36 #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ())
37 #define G_KEYFILE_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
38 G_TYPE_KEYFILE_SETTINGS_BACKEND, \
39 GKeyfileSettingsBackend))
40 #define G_IS_KEYFILE_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
41 G_TYPE_KEYFILE_SETTINGS_BACKEND))
44 typedef GSettingsBackendClass GKeyfileSettingsBackendClass
;
48 GSettingsBackend parent_instance
;
51 GPermission
*permission
;
60 GFileMonitor
*file_monitor
;
63 GFileMonitor
*dir_monitor
;
64 } GKeyfileSettingsBackend
;
66 static GType
g_keyfile_settings_backend_get_type (void);
67 G_DEFINE_TYPE (GKeyfileSettingsBackend
,
68 g_keyfile_settings_backend
,
69 G_TYPE_SETTINGS_BACKEND
)
72 compute_checksum (guint8
*digest
,
73 gconstpointer contents
,
79 checksum
= g_checksum_new (G_CHECKSUM_SHA256
);
80 g_checksum_update (checksum
, contents
, length
);
81 g_checksum_get_digest (checksum
, digest
, &len
);
82 g_checksum_free (checksum
);
87 g_keyfile_settings_backend_keyfile_write (GKeyfileSettingsBackend
*kfsb
)
92 contents
= g_key_file_to_data (kfsb
->keyfile
, &length
, NULL
);
93 g_file_replace_contents (kfsb
->file
, contents
, length
, NULL
, FALSE
,
94 G_FILE_CREATE_REPLACE_DESTINATION
,
97 compute_checksum (kfsb
->digest
, contents
, length
);
102 group_name_matches (const gchar
*group_name
,
105 /* sort of like g_str_has_prefix() except that it must be an exact
106 * match or the prefix followed by '/'.
108 * for example 'a' is a prefix of 'a' and 'a/b' but not 'ab'.
112 for (i
= 0; prefix
[i
]; i
++)
113 if (prefix
[i
] != group_name
[i
])
116 return group_name
[i
] == '\0' || group_name
[i
] == '/';
120 convert_path (GKeyfileSettingsBackend
*kfsb
,
125 gint key_len
= strlen (key
);
128 if (key_len
< kfsb
->prefix_len
||
129 memcmp (key
, kfsb
->prefix
, kfsb
->prefix_len
) != 0)
132 key_len
-= kfsb
->prefix_len
;
133 key
+= kfsb
->prefix_len
;
135 for (i
= key_len
; i
>= 0; i
--)
139 if (kfsb
->root_group
)
141 /* if a root_group was specified, make sure the user hasn't given
142 * a path that ghosts that group name
144 if (i
== kfsb
->root_group_len
&& memcmp (key
, kfsb
->root_group
, i
) == 0)
149 /* if no root_group was given, ensure that the user gave a path */
158 *group
= g_memdup (key
, i
+ 1);
162 *group
= g_strdup (kfsb
->root_group
);
166 *basename
= g_memdup (key
+ i
+ 1, key_len
- i
);
172 path_is_valid (GKeyfileSettingsBackend
*kfsb
,
175 return convert_path (kfsb
, path
, NULL
, NULL
);
179 get_from_keyfile (GKeyfileSettingsBackend
*kfsb
,
180 const GVariantType
*type
,
183 GVariant
*return_value
= NULL
;
186 if (convert_path (kfsb
, key
, &group
, &name
))
192 str
= g_key_file_get_value (kfsb
->keyfile
, group
, name
, NULL
);
196 return_value
= g_variant_parse (type
, str
, NULL
, NULL
, NULL
);
208 set_to_keyfile (GKeyfileSettingsBackend
*kfsb
,
214 if (convert_path (kfsb
, key
, &group
, &name
))
218 gchar
*str
= g_variant_print (value
, FALSE
);
219 g_key_file_set_value (kfsb
->keyfile
, group
, name
, str
);
220 g_variant_unref (g_variant_ref_sink (value
));
230 groups
= g_key_file_get_groups (kfsb
->keyfile
, NULL
);
232 for (i
= 0; groups
[i
]; i
++)
233 if (group_name_matches (groups
[i
], group
))
234 g_key_file_remove_group (kfsb
->keyfile
, groups
[i
], NULL
);
239 g_key_file_remove_key (kfsb
->keyfile
, group
, name
, NULL
);
252 g_keyfile_settings_backend_read (GSettingsBackend
*backend
,
254 const GVariantType
*expected_type
,
255 gboolean default_value
)
257 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
262 return get_from_keyfile (kfsb
, expected_type
, key
);
267 GKeyfileSettingsBackend
*kfsb
;
272 g_keyfile_settings_backend_write_one (gpointer key
,
276 WriteManyData
*data
= user_data
;
279 success
= set_to_keyfile (data
->kfsb
, key
, value
);
286 g_keyfile_settings_backend_check_one (gpointer key
,
290 WriteManyData
*data
= user_data
;
292 return data
->failed
= !path_is_valid (data
->kfsb
, key
);
296 g_keyfile_settings_backend_write_tree (GSettingsBackend
*backend
,
300 WriteManyData data
= { G_KEYFILE_SETTINGS_BACKEND (backend
) };
302 if (!data
.kfsb
->writable
)
305 g_tree_foreach (tree
, g_keyfile_settings_backend_check_one
, &data
);
310 g_tree_foreach (tree
, g_keyfile_settings_backend_write_one
, &data
);
311 g_keyfile_settings_backend_keyfile_write (data
.kfsb
);
313 g_settings_backend_changed_tree (backend
, tree
, origin_tag
);
319 g_keyfile_settings_backend_write (GSettingsBackend
*backend
,
324 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
330 success
= set_to_keyfile (kfsb
, key
, value
);
334 g_settings_backend_changed (backend
, key
, origin_tag
);
335 g_keyfile_settings_backend_keyfile_write (kfsb
);
342 g_keyfile_settings_backend_reset (GSettingsBackend
*backend
,
346 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
348 if (set_to_keyfile (kfsb
, key
, NULL
))
349 g_keyfile_settings_backend_keyfile_write (kfsb
);
351 g_settings_backend_changed (backend
, key
, origin_tag
);
355 g_keyfile_settings_backend_get_writable (GSettingsBackend
*backend
,
358 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
360 return kfsb
->writable
&& path_is_valid (kfsb
, name
);
364 g_keyfile_settings_backend_get_permission (GSettingsBackend
*backend
,
367 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (backend
);
369 return g_object_ref (kfsb
->permission
);
373 keyfile_to_tree (GKeyfileSettingsBackend
*kfsb
,
381 groups
= g_key_file_get_groups (keyfile
, NULL
);
382 for (i
= 0; groups
[i
]; i
++)
384 gboolean is_root_group
;
388 is_root_group
= g_strcmp0 (kfsb
->root_group
, groups
[i
]) == 0;
390 /* reject group names that will form invalid key names */
391 if (!is_root_group
&&
392 (g_str_has_prefix (groups
[i
], "/") ||
393 g_str_has_suffix (groups
[i
], "/") || strstr (groups
[i
], "//")))
396 keys
= g_key_file_get_keys (keyfile
, groups
[i
], NULL
, NULL
);
398 for (j
= 0; keys
[j
]; j
++)
402 /* reject key names with slashes in them */
403 if (strchr (keys
[j
], '/'))
407 path
= g_strdup_printf ("%s%s", kfsb
->prefix
, keys
[j
]);
409 path
= g_strdup_printf ("%s%s/%s", kfsb
->prefix
, groups
[i
], keys
[j
]);
411 value
= g_key_file_get_value (keyfile
, groups
[i
], keys
[j
], NULL
);
413 if (dup_check
&& g_strcmp0 (g_tree_lookup (tree
, path
), value
) == 0)
415 g_tree_remove (tree
, path
);
420 g_tree_insert (tree
, path
, value
);
429 g_keyfile_settings_backend_keyfile_reload (GKeyfileSettingsBackend
*kfsb
)
438 g_file_load_contents (kfsb
->file
, NULL
, &contents
, &length
, NULL
, NULL
);
439 compute_checksum (digest
, contents
, length
);
441 if (memcmp (kfsb
->digest
, digest
, sizeof digest
) != 0)
443 GKeyFile
*keyfiles
[2];
446 tree
= g_tree_new_full ((GCompareDataFunc
) strcmp
, NULL
,
449 keyfiles
[0] = kfsb
->keyfile
;
450 keyfiles
[1] = g_key_file_new ();
453 g_key_file_load_from_data (keyfiles
[1], contents
, length
,
454 G_KEY_FILE_KEEP_COMMENTS
|
455 G_KEY_FILE_KEEP_TRANSLATIONS
, NULL
);
457 keyfile_to_tree (kfsb
, tree
, keyfiles
[0], FALSE
);
458 keyfile_to_tree (kfsb
, tree
, keyfiles
[1], TRUE
);
459 g_key_file_free (keyfiles
[0]);
460 kfsb
->keyfile
= keyfiles
[1];
462 if (g_tree_nnodes (tree
) > 0)
463 g_settings_backend_changed_tree (&kfsb
->parent_instance
, tree
, NULL
);
467 memcpy (kfsb
->digest
, digest
, sizeof digest
);
474 g_keyfile_settings_backend_keyfile_writable (GKeyfileSettingsBackend
*kfsb
)
479 fileinfo
= g_file_query_info (kfsb
->dir
, "access::*", 0, NULL
, NULL
);
484 g_file_info_get_attribute_boolean (fileinfo
, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE
) &&
485 g_file_info_get_attribute_boolean (fileinfo
, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE
);
486 g_object_unref (fileinfo
);
491 if (writable
!= kfsb
->writable
)
493 kfsb
->writable
= writable
;
494 g_settings_backend_path_writable_changed (&kfsb
->parent_instance
, "/");
499 g_keyfile_settings_backend_finalize (GObject
*object
)
501 GKeyfileSettingsBackend
*kfsb
= G_KEYFILE_SETTINGS_BACKEND (object
);
503 g_key_file_free (kfsb
->keyfile
);
504 g_object_unref (kfsb
->permission
);
506 g_file_monitor_cancel (kfsb
->file_monitor
);
507 g_object_unref (kfsb
->file_monitor
);
508 g_object_unref (kfsb
->file
);
510 g_file_monitor_cancel (kfsb
->dir_monitor
);
511 g_object_unref (kfsb
->dir_monitor
);
512 g_object_unref (kfsb
->dir
);
514 g_free (kfsb
->root_group
);
515 g_free (kfsb
->prefix
);
517 G_OBJECT_CLASS (g_keyfile_settings_backend_parent_class
)
522 g_keyfile_settings_backend_init (GKeyfileSettingsBackend
*kfsb
)
527 g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass
*class)
529 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
531 object_class
->finalize
= g_keyfile_settings_backend_finalize
;
533 class->read
= g_keyfile_settings_backend_read
;
534 class->write
= g_keyfile_settings_backend_write
;
535 class->write_tree
= g_keyfile_settings_backend_write_tree
;
536 class->reset
= g_keyfile_settings_backend_reset
;
537 class->get_writable
= g_keyfile_settings_backend_get_writable
;
538 class->get_permission
= g_keyfile_settings_backend_get_permission
;
539 /* No need to implement subscribed/unsubscribe: the only point would be to
540 * 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 g_keyfile_settings_backend_keyfile_reload (kfsb
);
557 dir_changed (GFileMonitor
*monitor
,
560 GFileMonitorEvent event_type
,
563 GKeyfileSettingsBackend
*kfsb
= user_data
;
565 g_keyfile_settings_backend_keyfile_writable (kfsb
);
569 * g_keyfile_settings_backend_new:
570 * @filename: the filename of the keyfile
571 * @root_path: the path under which all settings keys appear
572 * @root_group: (allow-none): the group name corresponding to
573 * @root_path, or %NULL
575 * Creates a keyfile-backed #GSettingsBackend.
577 * The filename of the keyfile to use is given by @filename.
579 * All settings read to or written from the backend must fall under the
580 * path given in @root_path (which must start and end with a slash and
581 * not contain two consecutive slashes). @root_path may be "/".
583 * If @root_group is non-%NULL then it specifies the name of the keyfile
584 * group used for keys that are written directly below @root_path. For
585 * example, if @root_path is "/apps/example/" and @root_group is
586 * "toplevel", then settings the key "/apps/example/enabled" to a value
587 * of %TRUE will cause the following to appear in the keyfile:
594 * If @root_group is %NULL then it is not permitted to store keys
595 * directly below the @root_path.
597 * For keys not stored directly below @root_path (ie: in a sub-path),
598 * the name of the subpath (with the final slash stripped) is used as
599 * the name of the keyfile group. To continue the example, if
600 * "/apps/example/profiles/default/font-size" were set to
601 * 12 then the following would appear in the keyfile:
608 * The backend will refuse writes (and return writability as being
609 * %FALSE) for keys outside of @root_path and, in the event that
610 * @root_group is %NULL, also for keys directly under @root_path.
611 * Writes will also be refused if the backend detects that it has the
612 * inability to rewrite the keyfile (ie: the containing directory is not
615 * There is no checking done for your key namespace clashing with the
616 * syntax of the key file format. For example, if you have '[' or ']'
617 * characters in your path names or '=' in your key names you may be in
620 * Returns: (transfer full): a keyfile-backed #GSettingsBackend
623 g_keyfile_settings_backend_new (const gchar
*filename
,
624 const gchar
*root_path
,
625 const gchar
*root_group
)
627 GKeyfileSettingsBackend
*kfsb
;
629 g_return_val_if_fail (filename
!= NULL
, NULL
);
630 g_return_val_if_fail (root_path
!= NULL
, NULL
);
631 g_return_val_if_fail (g_str_has_prefix (root_path
, "/"), NULL
);
632 g_return_val_if_fail (g_str_has_suffix (root_path
, "/"), NULL
);
633 g_return_val_if_fail (strstr (root_path
, "//") == NULL
, NULL
);
635 kfsb
= g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND
, NULL
);
636 kfsb
->keyfile
= g_key_file_new ();
637 kfsb
->permission
= g_simple_permission_new (TRUE
);
639 kfsb
->file
= g_file_new_for_path (filename
);
640 kfsb
->dir
= g_file_get_parent (kfsb
->file
);
641 g_file_make_directory_with_parents (kfsb
->dir
, NULL
, NULL
);
643 kfsb
->file_monitor
= g_file_monitor_file (kfsb
->file
, 0, NULL
, NULL
);
644 kfsb
->dir_monitor
= g_file_monitor_file (kfsb
->dir
, 0, NULL
, NULL
);
646 kfsb
->prefix_len
= strlen (root_path
);
647 kfsb
->prefix
= g_strdup (root_path
);
651 kfsb
->root_group_len
= strlen (root_group
);
652 kfsb
->root_group
= g_strdup (root_group
);
655 compute_checksum (kfsb
->digest
, NULL
, 0);
657 g_signal_connect (kfsb
->file_monitor
, "changed",
658 G_CALLBACK (file_changed
), kfsb
);
659 g_signal_connect (kfsb
->dir_monitor
, "changed",
660 G_CALLBACK (dir_changed
), kfsb
);
662 g_keyfile_settings_backend_keyfile_writable (kfsb
);
663 g_keyfile_settings_backend_keyfile_reload (kfsb
);
665 return G_SETTINGS_BACKEND (kfsb
);