Coverage Report

Created: 2025-06-13 06:55

/src/glib/gio/gkeyfilesettingsbackend.c
Line
Count
Source (jump to first uncovered line)
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
0
#define G_KEYFILE_SETTINGS_BACKEND(inst)     (G_TYPE_CHECK_INSTANCE_CAST ((inst),      \
44
0
                                              G_TYPE_KEYFILE_SETTINGS_BACKEND,         \
45
0
                                              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
0
{
100
0
  GChecksum *checksum;
101
0
  gsize len = 32;
102
103
0
  checksum = g_checksum_new (G_CHECKSUM_SHA256);
104
0
  g_checksum_update (checksum, contents, length);
105
0
  g_checksum_get_digest (checksum, digest, &len);
106
0
  g_checksum_free (checksum);
107
0
  g_assert (len == 32);
108
0
}
109
110
static gboolean
111
g_keyfile_settings_backend_keyfile_write (GKeyfileSettingsBackend  *kfsb,
112
                                          GError                  **error)
113
0
{
114
0
  gchar *contents;
115
0
  gsize length;
116
0
  gboolean success;
117
118
0
  contents = g_key_file_to_data (kfsb->keyfile, &length, NULL);
119
0
  success = g_file_replace_contents (kfsb->file, contents, length, NULL, FALSE,
120
0
                                     G_FILE_CREATE_REPLACE_DESTINATION |
121
0
                                     G_FILE_CREATE_PRIVATE,
122
0
                                     NULL, NULL, error);
123
124
0
  compute_checksum (kfsb->digest, contents, length);
125
0
  g_free (contents);
126
127
0
  return success;
128
0
}
129
130
static gboolean
131
group_name_matches (const gchar *group_name,
132
                    const gchar *prefix)
133
0
{
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
0
  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
0
}
147
148
static gboolean
149
convert_path (GKeyfileSettingsBackend  *kfsb,
150
              const gchar              *key,
151
              gchar                   **group,
152
              gchar                   **basename)
153
0
{
154
0
  gsize key_len = strlen (key);
155
0
  const gchar *last_slash;
156
157
0
  if (key_len < kfsb->prefix_len ||
158
0
      memcmp (key, kfsb->prefix, kfsb->prefix_len) != 0)
159
0
    return FALSE;
160
161
0
  key_len -= kfsb->prefix_len;
162
0
  key += kfsb->prefix_len;
163
164
0
  last_slash = strrchr (key, '/');
165
166
  /* Disallow empty group names or key names */
167
0
  if (key_len == 0 ||
168
0
      (last_slash != NULL &&
169
0
       (*(last_slash + 1) == '\0' ||
170
0
        last_slash == key)))
171
0
    return FALSE;
172
173
0
  if (kfsb->root_group)
174
0
    {
175
      /* if a root_group was specified, make sure the user hasn't given
176
       * a path that ghosts that group name
177
       */
178
0
      if (last_slash != NULL && last_slash - key >= 0 &&
179
0
          (gsize) (last_slash - key) == kfsb->root_group_len &&
180
0
          memcmp (key, kfsb->root_group, last_slash - key) == 0)
181
0
        return FALSE;
182
0
    }
183
0
  else
184
0
    {
185
      /* if no root_group was given, ensure that the user gave a path */
186
0
      if (last_slash == NULL)
187
0
        return FALSE;
188
0
    }
189
190
0
  if (group)
191
0
    {
192
0
      if (last_slash != NULL)
193
0
        {
194
0
          *group = g_memdup2 (key, (last_slash - key) + 1);
195
0
          (*group)[(last_slash - key)] = '\0';
196
0
        }
197
0
      else
198
0
        *group = g_strdup (kfsb->root_group);
199
0
    }
200
201
0
  if (basename)
202
0
    {
203
0
      if (last_slash != NULL)
204
0
        *basename = g_memdup2 (last_slash + 1, key_len - (last_slash - key));
205
0
      else
206
0
        *basename = g_strdup (key);
207
0
    }
208
209
0
  return TRUE;
210
0
}
211
212
static gboolean
213
path_is_valid (GKeyfileSettingsBackend *kfsb,
214
               const gchar             *path)
215
0
{
216
0
  return convert_path (kfsb, path, NULL, NULL);
217
0
}
218
219
static GVariant *
220
get_from_keyfile (GKeyfileSettingsBackend *kfsb,
221
                  const GVariantType      *type,
222
                  const gchar             *key)
223
0
{
224
0
  GVariant *return_value = NULL;
225
0
  gchar *group, *name;
226
227
0
  if (convert_path (kfsb, key, &group, &name))
228
0
    {
229
0
      gchar *str;
230
0
      gchar *sysstr;
231
232
0
      g_assert (*name);
233
234
0
      sysstr = g_key_file_get_value (kfsb->system_keyfile, group, name, NULL);
235
0
      str = g_key_file_get_value (kfsb->keyfile, group, name, NULL);
236
0
      if (sysstr &&
237
0
          (g_hash_table_contains (kfsb->system_locks, key) ||
238
0
           str == NULL))
239
0
        {
240
0
          g_free (str);
241
0
          str = g_steal_pointer (&sysstr);
242
0
        }
243
244
0
      if (str)
245
0
        {
246
0
          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
0
          if (return_value == NULL &&
252
0
              g_variant_type_equal (type, G_VARIANT_TYPE_STRING) &&
253
0
              str[0] != '\"')
254
0
            {
255
0
              GString *s = g_string_sized_new (strlen (str) + 2);
256
0
              char *p = str;
257
258
0
              g_string_append_c (s, '\"');
259
0
              while (*p)
260
0
                {
261
0
                  if (*p == '\"')
262
0
                    g_string_append_c (s, '\\');
263
0
                  g_string_append_c (s, *p);
264
0
                  p++;
265
0
                }
266
0
              g_string_append_c (s, '\"');
267
0
              return_value = g_variant_parse (type, s->str, NULL, NULL, NULL);
268
0
              g_string_free (s, TRUE);
269
0
            }
270
0
          g_free (str);
271
0
        }
272
273
0
      g_free (sysstr);
274
275
0
      g_free (group);
276
0
      g_free (name);
277
0
    }
278
279
0
  return return_value;
280
0
}
281
282
static gboolean
283
set_to_keyfile (GKeyfileSettingsBackend *kfsb,
284
                const gchar             *key,
285
                GVariant                *value)
286
0
{
287
0
  gchar *group, *name;
288
289
0
  if (g_hash_table_contains (kfsb->system_locks, key))
290
0
    return FALSE;
291
292
0
  if (convert_path (kfsb, key, &group, &name))
293
0
    {
294
0
      if (value)
295
0
        {
296
0
          gchar *str = g_variant_print (value, FALSE);
297
0
          g_key_file_set_value (kfsb->keyfile, group, name, str);
298
0
          g_variant_unref (g_variant_ref_sink (value));
299
0
          g_free (str);
300
0
        }
301
0
      else
302
0
        {
303
0
          if (*name == '\0')
304
0
            {
305
0
              gchar **groups;
306
0
              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
0
            }
316
0
          else
317
0
            g_key_file_remove_key (kfsb->keyfile, group, name, NULL);
318
0
        }
319
320
0
      g_free (group);
321
0
      g_free (name);
322
323
0
      return TRUE;
324
0
    }
325
326
0
  return FALSE;
327
0
}
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
0
{
335
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
336
337
0
  if (default_value)
338
0
    return NULL;
339
340
0
  return get_from_keyfile (kfsb, expected_type, key);
341
0
}
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
0
{
354
0
  WriteManyData *data = user_data;
355
0
  gboolean success G_GNUC_UNUSED  /* when compiling with G_DISABLE_ASSERT */;
356
357
0
  success = set_to_keyfile (data->kfsb, key, value);
358
0
  g_assert (success);
359
360
0
  return FALSE;
361
0
}
362
363
static gboolean
364
g_keyfile_settings_backend_check_one (gpointer key,
365
                                      gpointer value,
366
                                      gpointer user_data)
367
0
{
368
0
  WriteManyData *data = user_data;
369
370
0
  return data->failed = g_hash_table_contains (data->kfsb->system_locks, key) ||
371
0
                        !path_is_valid (data->kfsb, key);
372
0
}
373
374
static gboolean
375
g_keyfile_settings_backend_write_tree (GSettingsBackend *backend,
376
                                       GTree            *tree,
377
                                       gpointer          origin_tag)
378
0
{
379
0
  WriteManyData data = { G_KEYFILE_SETTINGS_BACKEND (backend), 0 };
380
0
  gboolean success;
381
0
  GError *error = NULL;
382
383
0
  if (!data.kfsb->writable)
384
0
    return FALSE;
385
386
0
  g_tree_foreach (tree, g_keyfile_settings_backend_check_one, &data);
387
388
0
  if (data.failed)
389
0
    return FALSE;
390
391
0
  g_tree_foreach (tree, g_keyfile_settings_backend_write_one, &data);
392
0
  success = g_keyfile_settings_backend_keyfile_write (data.kfsb, &error);
393
0
  if (error)
394
0
    {
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
0
    }
398
399
0
  g_settings_backend_changed_tree (backend, tree, origin_tag);
400
401
0
  return success;
402
0
}
403
404
static gboolean
405
g_keyfile_settings_backend_write (GSettingsBackend *backend,
406
                                  const gchar      *key,
407
                                  GVariant         *value,
408
                                  gpointer          origin_tag)
409
0
{
410
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
411
0
  gboolean success;
412
0
  GError *error = NULL;
413
414
0
  if (!kfsb->writable)
415
0
    return FALSE;
416
417
0
  success = set_to_keyfile (kfsb, key, value);
418
419
0
  if (success)
420
0
    {
421
0
      g_settings_backend_changed (backend, key, origin_tag);
422
0
      success = g_keyfile_settings_backend_keyfile_write (kfsb, &error);
423
0
      if (error)
424
0
        {
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
0
        }
428
0
    }
429
430
0
  return success;
431
0
}
432
433
static void
434
g_keyfile_settings_backend_reset (GSettingsBackend *backend,
435
                                  const gchar      *key,
436
                                  gpointer          origin_tag)
437
0
{
438
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
439
0
  GError *error = NULL;
440
441
0
  if (set_to_keyfile (kfsb, key, NULL))
442
0
    {
443
0
      g_keyfile_settings_backend_keyfile_write (kfsb, &error);
444
0
      if (error)
445
0
        {
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
0
        }
449
0
    }
450
451
0
  g_settings_backend_changed (backend, key, origin_tag);
452
0
}
453
454
static gboolean
455
g_keyfile_settings_backend_get_writable (GSettingsBackend *backend,
456
                                         const gchar      *name)
457
0
{
458
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
459
460
0
  return kfsb->writable &&
461
0
         !g_hash_table_contains (kfsb->system_locks, name) &&
462
0
         path_is_valid (kfsb, name);
463
0
}
464
465
static GPermission *
466
g_keyfile_settings_backend_get_permission (GSettingsBackend *backend,
467
                                           const gchar      *path)
468
0
{
469
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
470
471
0
  return g_object_ref (kfsb->permission);
472
0
}
473
474
static void
475
keyfile_to_tree (GKeyfileSettingsBackend *kfsb,
476
                 GTree                   *tree,
477
                 GKeyFile                *keyfile,
478
                 gboolean                 dup_check)
479
0
{
480
0
  gchar **groups;
481
0
  gint i;
482
483
0
  groups = g_key_file_get_groups (keyfile, NULL);
484
0
  for (i = 0; groups[i]; i++)
485
0
    {
486
0
      gboolean is_root_group;
487
0
      gchar **keys;
488
0
      gint j;
489
490
0
      is_root_group = g_strcmp0 (kfsb->root_group, groups[i]) == 0;
491
492
      /* reject group names that will form invalid key names */
493
0
      if (!is_root_group &&
494
0
          (g_str_has_prefix (groups[i], "/") ||
495
0
           g_str_has_suffix (groups[i], "/") || strstr (groups[i], "//")))
496
0
        continue;
497
498
0
      keys = g_key_file_get_keys (keyfile, groups[i], NULL, NULL);
499
0
      g_assert (keys != NULL);
500
501
0
      for (j = 0; keys[j]; j++)
502
0
        {
503
0
          gchar *path, *value;
504
505
          /* reject key names with slashes in them */
506
0
          if (strchr (keys[j], '/'))
507
0
            continue;
508
509
0
          if (is_root_group)
510
0
            path = g_strdup_printf ("%s%s", kfsb->prefix, keys[j]);
511
0
          else
512
0
            path = g_strdup_printf ("%s%s/%s", kfsb->prefix, groups[i], keys[j]);
513
514
0
          value = g_key_file_get_value (keyfile, groups[i], keys[j], NULL);
515
516
0
          if (dup_check && g_strcmp0 (g_tree_lookup (tree, path), value) == 0)
517
0
            {
518
0
              g_tree_remove (tree, path);
519
0
              g_free (value);
520
0
              g_free (path);
521
0
            }
522
0
          else
523
0
            g_tree_insert (tree, path, value);
524
0
        }
525
526
0
      g_strfreev (keys);
527
0
    }
528
0
  g_strfreev (groups);
529
0
}
530
531
static void
532
g_keyfile_settings_backend_keyfile_reload (GKeyfileSettingsBackend *kfsb)
533
0
{
534
0
  guint8 digest[32];
535
0
  gchar *contents;
536
0
  gsize length;
537
538
0
  contents = NULL;
539
0
  length = 0;
540
541
0
  g_file_load_contents (kfsb->file, NULL, &contents, &length, NULL, NULL);
542
0
  compute_checksum (digest, contents, length);
543
544
0
  if (memcmp (kfsb->digest, digest, sizeof digest) != 0)
545
0
    {
546
0
      GKeyFile *keyfiles[2];
547
0
      GTree *tree;
548
549
0
      tree = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
550
0
                              g_free, g_free);
551
552
0
      keyfiles[0] = kfsb->keyfile;
553
0
      keyfiles[1] = g_key_file_new ();
554
555
0
      if (length > 0)
556
0
        g_key_file_load_from_data (keyfiles[1], contents, length,
557
0
                                   G_KEY_FILE_KEEP_COMMENTS |
558
0
                                   G_KEY_FILE_KEEP_TRANSLATIONS, NULL);
559
560
0
      keyfile_to_tree (kfsb, tree, keyfiles[0], FALSE);
561
0
      keyfile_to_tree (kfsb, tree, keyfiles[1], TRUE);
562
0
      g_key_file_free (keyfiles[0]);
563
0
      kfsb->keyfile = keyfiles[1];
564
565
0
      if (g_tree_nnodes (tree) > 0)
566
0
        g_settings_backend_changed_tree (&kfsb->parent_instance, tree, NULL);
567
568
0
      g_tree_unref (tree);
569
570
0
      memcpy (kfsb->digest, digest, sizeof digest);
571
0
    }
572
573
0
  g_free (contents);
574
0
}
575
576
static void
577
g_keyfile_settings_backend_keyfile_writable (GKeyfileSettingsBackend *kfsb)
578
0
{
579
0
  GFileInfo *fileinfo;
580
0
  gboolean writable;
581
582
0
  fileinfo = g_file_query_info (kfsb->dir, "access::*", 0, NULL, NULL);
583
584
0
  if (fileinfo)
585
0
    {
586
0
      writable =
587
0
        g_file_info_get_attribute_boolean (fileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE) &&
588
0
        g_file_info_get_attribute_boolean (fileinfo, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
589
0
      g_object_unref (fileinfo);
590
0
    }
591
0
  else
592
0
    writable = FALSE;
593
594
0
  if (writable != kfsb->writable)
595
0
    {
596
0
      kfsb->writable = writable;
597
0
      g_settings_backend_path_writable_changed (&kfsb->parent_instance, "/");
598
0
    }
599
0
}
600
601
static void
602
g_keyfile_settings_backend_finalize (GObject *object)
603
0
{
604
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
605
606
0
  g_key_file_free (kfsb->keyfile);
607
0
  g_object_unref (kfsb->permission);
608
0
  g_key_file_unref (kfsb->system_keyfile);
609
0
  g_hash_table_unref (kfsb->system_locks);
610
0
  g_free (kfsb->defaults_dir);
611
612
0
  if (kfsb->file_monitor)
613
0
    {
614
0
      g_file_monitor_cancel (kfsb->file_monitor);
615
0
      g_object_unref (kfsb->file_monitor);
616
0
    }
617
0
  g_object_unref (kfsb->file);
618
619
0
  if (kfsb->dir_monitor)
620
0
    {
621
0
      g_file_monitor_cancel (kfsb->dir_monitor);
622
0
      g_object_unref (kfsb->dir_monitor);
623
0
    }
624
0
  g_object_unref (kfsb->dir);
625
626
0
  g_free (kfsb->root_group);
627
0
  g_free (kfsb->prefix);
628
629
0
  G_OBJECT_CLASS (g_keyfile_settings_backend_parent_class)->finalize (object);
630
0
}
631
632
static void
633
g_keyfile_settings_backend_init (GKeyfileSettingsBackend *kfsb)
634
0
{
635
0
}
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
0
{
644
0
  GKeyfileSettingsBackend *kfsb = user_data;
645
646
  /* Ignore file deletions, let the GKeyFile content remain in tact. */
647
0
  if (event_type != G_FILE_MONITOR_EVENT_DELETED)
648
0
    g_keyfile_settings_backend_keyfile_reload (kfsb);
649
0
}
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
0
{
658
0
  GKeyfileSettingsBackend *kfsb = user_data;
659
660
0
  g_keyfile_settings_backend_keyfile_writable (kfsb);
661
0
}
662
663
static void
664
load_system_settings (GKeyfileSettingsBackend *kfsb)
665
0
{
666
0
  GError *error = NULL;
667
0
  const char *dir = "/etc/glib-2.0/settings";
668
0
  char *path;
669
0
  char *contents;
670
671
0
  kfsb->system_keyfile = g_key_file_new ();
672
0
  kfsb->system_locks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
673
674
0
  if (kfsb->defaults_dir)
675
0
    dir = kfsb->defaults_dir;
676
677
0
  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
0
  if (!g_key_file_load_from_file (kfsb->system_keyfile, path, G_KEY_FILE_NONE, &error))
683
0
    {
684
0
      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
0
      g_clear_error (&error);
687
0
    }
688
0
  else
689
0
    g_debug ("Loading default settings from %s", path);
690
691
0
  g_free (path);
692
693
0
  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
0
  if (!g_file_get_contents (path, &contents, NULL, &error))
699
0
    {
700
0
      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
0
      g_clear_error (&error);
703
0
    }
704
0
  else
705
0
    {
706
0
      char **lines;
707
0
      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
0
        {
714
0
          char *line = lines[i];
715
0
          if (line[0] == '#' || line[0] == '\0')
716
0
            {
717
0
              g_free (line);
718
0
              continue;
719
0
            }
720
721
0
          g_debug ("Locking key %s", line);
722
0
          g_hash_table_add (kfsb->system_locks, g_steal_pointer (&line));
723
0
        }
724
725
0
      g_free (lines);
726
0
    }
727
0
  g_free (contents);
728
729
0
  g_free (path);
730
0
}
731
732
static void
733
g_keyfile_settings_backend_constructed (GObject *object)
734
0
{
735
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
736
0
  GError *error = NULL;
737
0
  const char *path;
738
739
0
  if (kfsb->file == NULL)
740
0
    {
741
0
      char *filename = g_build_filename (g_get_user_config_dir (),
742
0
                                         "glib-2.0", "settings", "keyfile",
743
0
                                         NULL);
744
0
      kfsb->file = g_file_new_for_path (filename);
745
0
      g_free (filename);
746
0
    }
747
748
0
  if (kfsb->prefix == NULL)
749
0
    {
750
0
      kfsb->prefix = g_strdup ("/");
751
0
      kfsb->prefix_len = 1;
752
0
    }
753
  
754
0
  kfsb->keyfile = g_key_file_new ();
755
0
  kfsb->permission = g_simple_permission_new (TRUE);
756
757
0
  kfsb->dir = g_file_get_parent (kfsb->file);
758
0
  path = g_file_peek_path (kfsb->dir);
759
0
  if (g_mkdir_with_parents (path, 0700) == -1)
760
0
    g_warning ("Failed to create %s: %s", path, g_strerror (errno));
761
762
0
  kfsb->file_monitor = g_file_monitor (kfsb->file, G_FILE_MONITOR_NONE, NULL, &error);
763
0
  if (!kfsb->file_monitor)
764
0
    {
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
0
    }
768
0
  else
769
0
    {
770
0
      g_signal_connect (kfsb->file_monitor, "changed",
771
0
                        G_CALLBACK (file_changed), kfsb);
772
0
    }
773
774
0
  kfsb->dir_monitor = g_file_monitor (kfsb->dir, G_FILE_MONITOR_NONE, NULL, &error);
775
0
  if (!kfsb->dir_monitor)
776
0
    {
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
0
    }
780
0
  else
781
0
    {
782
0
      g_signal_connect (kfsb->dir_monitor, "changed",
783
0
                        G_CALLBACK (dir_changed), kfsb);
784
0
    }
785
786
0
  compute_checksum (kfsb->digest, NULL, 0);
787
788
0
  g_keyfile_settings_backend_keyfile_writable (kfsb);
789
0
  g_keyfile_settings_backend_keyfile_reload (kfsb);
790
791
0
  load_system_settings (kfsb);
792
0
}
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
0
{
800
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
801
802
0
  switch ((GKeyfileSettingsBackendProperty)prop_id)
803
0
    {
804
0
    case PROP_FILENAME:
805
      /* Construct only. */
806
0
      g_assert (kfsb->file == NULL);
807
0
      if (g_value_get_string (value))
808
0
        kfsb->file = g_file_new_for_path (g_value_get_string (value));
809
0
      break;
810
811
0
    case PROP_ROOT_PATH:
812
      /* Construct only. */
813
0
      g_assert (kfsb->prefix == NULL);
814
0
      kfsb->prefix = g_value_dup_string (value);
815
0
      if (kfsb->prefix)
816
0
        kfsb->prefix_len = strlen (kfsb->prefix);
817
0
      break;
818
819
0
    case PROP_ROOT_GROUP:
820
      /* Construct only. */
821
0
      g_assert (kfsb->root_group == NULL);
822
0
      kfsb->root_group = g_value_dup_string (value);
823
0
      if (kfsb->root_group)
824
0
        kfsb->root_group_len = strlen (kfsb->root_group);
825
0
      break;
826
827
0
    case PROP_DEFAULTS_DIR:
828
      /* Construct only. */
829
0
      g_assert (kfsb->defaults_dir == NULL);
830
0
      kfsb->defaults_dir = g_value_dup_string (value);
831
0
      break;
832
833
0
    default:
834
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
835
0
      break;
836
0
    }
837
0
}
838
839
static void
840
g_keyfile_settings_backend_get_property (GObject    *object,
841
                                         guint       prop_id,
842
                                         GValue     *value,
843
                                         GParamSpec *pspec)
844
0
{
845
0
  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
846
847
0
  switch ((GKeyfileSettingsBackendProperty)prop_id)
848
0
    {
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
0
    }
869
0
}
870
871
static void
872
g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class)
873
0
{
874
0
  GObjectClass *object_class = G_OBJECT_CLASS (class);
875
876
0
  object_class->finalize = g_keyfile_settings_backend_finalize;
877
0
  object_class->constructed = g_keyfile_settings_backend_constructed;
878
0
  object_class->get_property = g_keyfile_settings_backend_get_property;
879
0
  object_class->set_property = g_keyfile_settings_backend_set_property;
880
881
0
  class->read = g_keyfile_settings_backend_read;
882
0
  class->write = g_keyfile_settings_backend_write;
883
0
  class->write_tree = g_keyfile_settings_backend_write_tree;
884
0
  class->reset = g_keyfile_settings_backend_reset;
885
0
  class->get_writable = g_keyfile_settings_backend_get_writable;
886
0
  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
0
  g_object_class_install_property (object_class,
900
0
                                   PROP_FILENAME,
901
0
                                   g_param_spec_string ("filename",
902
0
                                                        P_("Filename"),
903
0
                                                        P_("The filename"),
904
0
                                                        NULL,
905
0
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
906
0
                                                        G_PARAM_STATIC_STRINGS));
907
908
  /**
909
   * GKeyfileSettingsBackend:root-path:
910
   *
911
   * All settings read to or written from the backend must fall under the
912
   * path given in @root_path (which must start and end with a slash and
913
   * not contain two consecutive slashes).  @root_path may be "/".
914
   * 
915
   * Defaults to "/".
916
   */
917
0
  g_object_class_install_property (object_class,
918
0
                                   PROP_ROOT_PATH,
919
0
                                   g_param_spec_string ("root-path",
920
0
                                                        P_("Root path"),
921
0
                                                        P_("The root path"),
922
0
                                                        NULL,
923
0
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
924
0
                                                        G_PARAM_STATIC_STRINGS));
925
926
  /**
927
   * GKeyfileSettingsBackend:root-group:
928
   *
929
   * If @root_group is non-%NULL then it specifies the name of the keyfile
930
   * group used for keys that are written directly below the root path.
931
   *
932
   * Defaults to NULL.
933
   */
934
0
  g_object_class_install_property (object_class,
935
0
                                   PROP_ROOT_GROUP,
936
0
                                   g_param_spec_string ("root-group",
937
0
                                                        P_("Root group"),
938
0
                                                        P_("The root group"),
939
0
                                                        NULL,
940
0
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
941
0
                                                        G_PARAM_STATIC_STRINGS));
942
943
  /**
944
   * GKeyfileSettingsBackend:default-dir:
945
   *
946
   * The directory where the system defaults and locks are located.
947
   *
948
   * Defaults to `/etc/glib-2.0/settings`.
949
   */
950
0
  g_object_class_install_property (object_class,
951
0
                                   PROP_DEFAULTS_DIR,
952
0
                                   g_param_spec_string ("defaults-dir",
953
0
                                                        P_("Default dir"),
954
0
                                                        P_("Defaults dir"),
955
0
                                                        NULL,
956
0
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
957
0
                                                        G_PARAM_STATIC_STRINGS));
958
0
}
959
960
/**
961
 * g_keyfile_settings_backend_new:
962
 * @filename: the filename of the keyfile
963
 * @root_path: the path under which all settings keys appear
964
 * @root_group: (nullable): the group name corresponding to
965
 *              @root_path, or %NULL
966
 *
967
 * Creates a keyfile-backed #GSettingsBackend.
968
 *
969
 * The filename of the keyfile to use is given by @filename.
970
 *
971
 * All settings read to or written from the backend must fall under the
972
 * path given in @root_path (which must start and end with a slash and
973
 * not contain two consecutive slashes).  @root_path may be "/".
974
 *
975
 * If @root_group is non-%NULL then it specifies the name of the keyfile
976
 * group used for keys that are written directly below @root_path.  For
977
 * example, if @root_path is "/apps/example/" and @root_group is
978
 * "toplevel", then settings the key "/apps/example/enabled" to a value
979
 * of %TRUE will cause the following to appear in the keyfile:
980
 *
981
 * |[
982
 *   [toplevel]
983
 *   enabled=true
984
 * ]|
985
 *
986
 * If @root_group is %NULL then it is not permitted to store keys
987
 * directly below the @root_path.
988
 *
989
 * For keys not stored directly below @root_path (ie: in a sub-path),
990
 * the name of the subpath (with the final slash stripped) is used as
991
 * the name of the keyfile group.  To continue the example, if
992
 * "/apps/example/profiles/default/font-size" were set to
993
 * 12 then the following would appear in the keyfile:
994
 *
995
 * |[
996
 *   [profiles/default]
997
 *   font-size=12
998
 * ]|
999
 *
1000
 * The backend will refuse writes (and return writability as being
1001
 * %FALSE) for keys outside of @root_path and, in the event that
1002
 * @root_group is %NULL, also for keys directly under @root_path.
1003
 * Writes will also be refused if the backend detects that it has the
1004
 * inability to rewrite the keyfile (ie: the containing directory is not
1005
 * writable).
1006
 *
1007
 * There is no checking done for your key namespace clashing with the
1008
 * syntax of the key file format.  For example, if you have '[' or ']'
1009
 * characters in your path names or '=' in your key names you may be in
1010
 * trouble.
1011
 *
1012
 * The backend reads default values from a keyfile called `defaults` in
1013
 * the directory specified by the #GKeyfileSettingsBackend:defaults-dir property,
1014
 * and a list of locked keys from a text file with the name `locks` in
1015
 * the same location.
1016
 *
1017
 * Returns: (transfer full): a keyfile-backed #GSettingsBackend
1018
 **/
1019
GSettingsBackend *
1020
g_keyfile_settings_backend_new (const gchar *filename,
1021
                                const gchar *root_path,
1022
                                const gchar *root_group)
1023
0
{
1024
0
  g_return_val_if_fail (filename != NULL, NULL);
1025
0
  g_return_val_if_fail (root_path != NULL, NULL);
1026
0
  g_return_val_if_fail (g_str_has_prefix (root_path, "/"), NULL);
1027
0
  g_return_val_if_fail (g_str_has_suffix (root_path, "/"), NULL);
1028
0
  g_return_val_if_fail (strstr (root_path, "//") == NULL, NULL);
1029
1030
0
  return G_SETTINGS_BACKEND (g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND,
1031
0
                                           "filename", filename,
1032
0
                                           "root-path", root_path,
1033
0
                                           "root-group", root_group,
1034
0
                                           NULL));
1035
0
}