Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / configmgr / source / dconf.cxx
blobda351bd917a01c9945a6e98bfb41feb6e05dd326
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/config.h>
12 #include <cassert>
13 #include <cstddef>
14 #include <cstring>
15 #include <forward_list>
16 #include <limits>
17 #include <type_traits>
18 #include <vector>
20 extern "C" {
21 // <https://bugzilla.gnome.org/show_bug.cgi?id=754245>
22 // "common/dconf-changeset.h etc. lack extern "C" wrapper for C++"
23 #include <dconf/dconf.h>
26 #include <com/sun/star/uno/Sequence.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <sal/log.hxx>
30 #include "data.hxx"
31 #include "dconf.hxx"
32 #include "groupnode.hxx"
33 #include "localizedpropertynode.hxx"
34 #include "localizedvaluenode.hxx"
35 #include "nodemap.hxx"
36 #include "propertynode.hxx"
37 #include "setnode.hxx"
39 // component-data is encoded in dconf as follows:
41 // * The node hierarchy (starting at component nodes with names like
42 // "org.openoffice.Setup") maps to dconf paths underneath
43 // "/org/libreoffice/registry/".
45 // * Component, group, set, and localized property nodes map to dconf dirs,
46 // while property and localized value nodes map to dconf keys.
48 // * The names of nodes that are not set elements are used directly as dconf
49 // path segments. (The syntax for node names is any non-empty sequences of
50 // any Unicode scalar values except U+0000--0008, U+000B--000C, U+000E--001F,
51 // U+002F SOLIDUS, and U+FFFE--FFFF. TODO: "<aruiz> sberg, in general I think
52 // it'd be nice if you used path separators instead of dots though, they have
53 // meaning in dconf/gvdb world :-)"?)
55 // * The names of set element nodes are encoded as dconf path segments as
56 // follows: each occurrence of U+0000 NULL is replace by the three characters
57 // "\00", each occurrence of U+002F SOLIDUS is replaced by the three
58 // characters "\2F", and each occurrence of U+005C REVERSE SOLIDUS is replaced
59 // by the three characters "\5C".
61 // * Set elements (which must themselves be either sets or groups) map to
62 // "indirection" dconf dirs as follows:
64 // ** The dir must contain a key named "op" of string type, with a value of
65 // "fuse", "replace", or "remove".
67 // ** If "op" is "fuse" or "replace", the dir must contain exactly the following
68 // further keys and dirs:
70 // *** The dir must contain a key named "template" of string type, containing
71 // the full template name, encoded as follows: each occurrence of U+0000
72 // NULL is replace by the three characters "\00" and each occurrence of
73 // U+005C REVERSE SOLIDUS is replaced by the three characters "\5C".
75 // *** The dir must contain a dir named "content" that contains the set
76 // element's (i.e., set or group node's) real content.
78 // ** If "op" is "remove", the dir must contain no further keys or dirs.
80 // * Property and localized property value "fuse" operations map to GVariant
81 // instances as follows:
83 // ** Non-nillable boolean values map to GVariant boolean instances.
85 // ** Non-nillable short values map to GVariant int16 instances.
87 // ** Non-nillable int values map to GVariant int32 instances.
89 // ** Non-nillable long values map to GVariant int64 instances.
91 // ** Non-nillable double values map to GVariant double instances.
93 // ** Non-nillable string values map to GVariant string instances, with the
94 // following encoding: each occurrence of U+0000 NULL is replace by the three
95 // characters "\00" and each occurrence of U+005C REVERSE SOLIDUS is replaced
96 // by the three characters "\5C".
98 // ** Non-nillable hexbinary values map to GVariant byte array instances.
100 // ** Non-nillable list values recursively map to GVariant array instances.
102 // ** Nillable values recursively map to GVariant maybe instances.
104 // * Property "remove" operations map to GVariant instances of empty tuple type.
106 // Finalization: The component-update.dtd allows for finalization of
107 // oor:component-data, node, and prop elements, while dconf allows for locking
108 // of individual keys. That does not match, but just mark the individual Node
109 // instances that correspond to individual dconf keys as finalized for
110 // non-writable dconf keys.
112 // TODO: support "mandatory" and "external"?
114 namespace configmgr { namespace dconf {
116 namespace {
118 template<typename T> class GObjectHolder {
119 public:
120 explicit GObjectHolder(T * object): object_(object) {}
122 ~GObjectHolder() {
123 if (object_ != nullptr) {
124 g_object_unref(object_);
128 T * get() const { return object_; }
130 private:
131 GObjectHolder(GObjectHolder const &) = delete;
132 GObjectHolder& operator =(GObjectHolder const &) = delete;
134 T * object_;
137 class GVariantHolder {
138 public:
139 explicit GVariantHolder(GVariant * variant = nullptr): variant_(variant) {}
141 ~GVariantHolder() { unref(); }
143 void reset(GVariant * variant) {
144 unref();
145 variant_ = variant;
148 void release() { variant_ = nullptr; }
150 GVariant * get() const { return variant_; }
152 private:
153 GVariantHolder(GVariantHolder const &) = delete;
154 GVariantHolder& operator =(GVariantHolder const &) = delete;
156 void unref() {
157 if (variant_ != nullptr) {
158 g_variant_unref(variant_);
162 GVariant * variant_;
165 class GVariantTypeHolder {
166 public:
167 explicit GVariantTypeHolder(GVariantType * type): type_(type) {}
169 ~GVariantTypeHolder() {
170 if (type_ != nullptr) {
171 g_variant_type_free(type_);
175 GVariantType * get() const { return type_; }
177 private:
178 GVariantTypeHolder(GVariantTypeHolder const &) = delete;
179 GVariantTypeHolder& operator =(GVariantTypeHolder const &) = delete;
181 GVariantType * type_;
184 class StringArrayHolder {
185 public:
186 explicit StringArrayHolder(gchar ** array): array_(array) {}
188 ~StringArrayHolder() { g_strfreev(array_); }
190 gchar ** get() const { return array_; }
192 private:
193 StringArrayHolder(StringArrayHolder const &) = delete;
194 StringArrayHolder& operator =(StringArrayHolder const &) = delete;
196 gchar ** array_;
199 class ChangesetHolder {
200 public:
201 explicit ChangesetHolder(DConfChangeset * changeset):
202 changeset_(changeset)
205 ~ChangesetHolder() {
206 if (changeset_ != nullptr) {
207 dconf_changeset_unref(changeset_);
211 DConfChangeset * get() const { return changeset_; }
213 private:
214 ChangesetHolder(ChangesetHolder const &) = delete;
215 ChangesetHolder& operator =(ChangesetHolder const &) = delete;
217 DConfChangeset * changeset_;
220 OString getRoot() {
221 return "/org/libreoffice/registry";
224 bool decode(OUString * string, bool slash) {
225 for (sal_Int32 i = 0;; ++i) {
226 i = string->indexOf('\\', i);
227 if (i == -1) {
228 return true;
230 if (string->match("00", i + 1)) {
231 *string = string->replaceAt(i, 3, OUString(u'\0'));
232 } else if (slash && string->match("2F", i + 1)) {
233 *string = string->replaceAt(i, 3, "/");
234 } else if (string->match("5C", i + 1)) {
235 *string = string->replaceAt(i + 1, 2, "");
236 } else {
237 SAL_WARN("configmgr.dconf", "bad escape in " << *string);
238 return false;
243 bool getBoolean(
244 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
246 assert(value != nullptr);
247 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_BOOLEAN)) {
248 SAL_WARN(
249 "configmgr.dconf",
250 "bad key " << key << " does not match boolean property");
251 return false;
253 *value <<= bool(g_variant_get_boolean(variant.get()));
254 return true;
257 bool getShort(
258 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
260 assert(value != nullptr);
261 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT16)) {
262 SAL_WARN(
263 "configmgr.dconf",
264 "bad key " << key << " does not match short property");
265 return false;
267 *value <<= sal_Int16(g_variant_get_int16(variant.get()));
268 return true;
271 bool getInt(
272 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
274 assert(value != nullptr);
275 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT32)) {
276 SAL_WARN(
277 "configmgr.dconf",
278 "bad key " << key << " does not match int property");
279 return false;
281 *value <<= sal_Int32(g_variant_get_int32(variant.get()));
282 return true;
285 bool getLong(
286 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
288 assert(value != nullptr);
289 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT64)) {
290 SAL_WARN(
291 "configmgr.dconf",
292 "bad key " << key << " does not match long property");
293 return false;
295 *value <<= sal_Int64(g_variant_get_int64(variant.get()));
296 return true;
299 bool getDouble(
300 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
302 assert(value != nullptr);
303 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_DOUBLE)) {
304 SAL_WARN(
305 "configmgr.dconf",
306 "bad key " << key << " does not match double property");
307 return false;
309 *value <<= double(g_variant_get_double(variant.get()));
310 return true;
313 bool getStringValue(
314 OString const & key, GVariantHolder const & variant, OUString * value)
316 assert(value != nullptr);
317 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_STRING)) {
318 SAL_WARN(
319 "configmgr.dconf",
320 "bad key " << key << " does not match string property");
321 return false;
323 gsize n;
324 char const * p = g_variant_get_string(variant.get(), &n);
325 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
326 std::numeric_limits<sal_Int32>::max()))
328 SAL_WARN("configmgr.dconf", "too long string value for key " << key);
329 return false;
331 if (!rtl_convertStringToUString(
332 &value->pData, p, static_cast<sal_Int32>(n), RTL_TEXTENCODING_UTF8,
333 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
334 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
335 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
337 SAL_WARN("configmgr.dconf", "non--UTF-8 string value for key " << key);
338 return false;
340 return decode(value, false);
343 bool getString(
344 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
346 assert(value != nullptr);
347 OUString v;
348 if (!getStringValue(key, variant, &v)) {
349 return false;
351 *value <<= v;
352 return true;
355 bool getHexbinaryValue(
356 OString const & key, GVariantHolder const & variant,
357 css::uno::Sequence<sal_Int8> * value)
359 assert(value != nullptr);
360 if (std::strcmp(g_variant_get_type_string(variant.get()), "ay") != 0) {
361 SAL_WARN(
362 "configmgr.dconf",
363 "bad key " << key << " does not match hexbinary property");
364 return false;
366 gsize n;
367 gconstpointer p = g_variant_get_fixed_array(
368 variant.get(), &n, sizeof (guchar));
369 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
370 std::numeric_limits<sal_Int32>::max()))
372 SAL_WARN("configmgr.dconf", "too long hexbinary value for key " << key);
373 return false;
375 value->realloc(static_cast<sal_Int32>(n));
376 static_assert(sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
377 std::memcpy(value->getArray(), p, n * sizeof (guchar));
378 // assuming that n * sizeof (guchar) is small enough for std::size_t
379 return true;
382 bool getHexbinary(
383 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
385 assert(value != nullptr);
386 css::uno::Sequence<sal_Int8> v;
387 if (!getHexbinaryValue(key, variant, &v)) {
388 return false;
390 *value <<= v;
391 return true;
394 bool getBooleanList(
395 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
397 assert(value != nullptr);
398 if (std::strcmp(g_variant_get_type_string(variant.get()), "ab") != 0) {
399 SAL_WARN(
400 "configmgr.dconf",
401 "bad key " << key << " does not match boolean list property");
402 return false;
404 gsize n;
405 gconstpointer p = g_variant_get_fixed_array(
406 variant.get(), &n, sizeof (guchar));
407 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
408 std::numeric_limits<sal_Int32>::max()))
410 SAL_WARN("configmgr.dconf", "too long boolean list for key " << key);
411 return false;
413 css::uno::Sequence<sal_Bool> v(static_cast<sal_Int32>(n));
414 static_assert(sizeof (sal_Bool) == sizeof (guchar), "size mismatch");
415 std::memcpy(v.getArray(), p, n * sizeof (guchar));
416 // assuming that n * sizeof (guchar) is small enough for std::size_t
417 *value <<= v;
418 return true;
421 bool getShortList(
422 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
424 assert(value != nullptr);
425 if (std::strcmp(g_variant_get_type_string(variant.get()), "an") != 0) {
426 SAL_WARN(
427 "configmgr.dconf",
428 "bad key " << key << " does not match short list property");
429 return false;
431 gsize n;
432 gconstpointer p = g_variant_get_fixed_array(
433 variant.get(), &n, sizeof (gint16));
434 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
435 std::numeric_limits<sal_Int32>::max()))
437 SAL_WARN("configmgr.dconf", "too long short list for key " << key);
438 return false;
440 css::uno::Sequence<sal_Int16> v(static_cast<sal_Int32>(n));
441 static_assert(sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
442 std::memcpy(v.getArray(), p, n * sizeof (gint16));
443 // assuming that n * sizeof (gint16) is small enough for std::size_t
444 *value <<= v;
445 return true;
448 bool getIntList(
449 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
451 assert(value != nullptr);
452 if (std::strcmp(g_variant_get_type_string(variant.get()), "ai") != 0) {
453 SAL_WARN(
454 "configmgr.dconf",
455 "bad key " << key << " does not match int list property");
456 return false;
458 gsize n;
459 gconstpointer p = g_variant_get_fixed_array(
460 variant.get(), &n, sizeof (gint32));
461 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
462 std::numeric_limits<sal_Int32>::max()))
464 SAL_WARN("configmgr.dconf", "too long int list for key " << key);
465 return false;
467 css::uno::Sequence<sal_Int32> v(static_cast<sal_Int32>(n));
468 static_assert(sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
469 std::memcpy(v.getArray(), p, n * sizeof (gint32));
470 // assuming that n * sizeof (gint32) is small enough for std::size_t
471 *value <<= v;
472 return true;
475 bool getLongList(
476 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
478 assert(value != nullptr);
479 if (std::strcmp(g_variant_get_type_string(variant.get()), "ax") != 0) {
480 SAL_WARN(
481 "configmgr.dconf",
482 "bad key " << key << " does not match long list property");
483 return false;
485 gsize n;
486 gconstpointer p = g_variant_get_fixed_array(
487 variant.get(), &n, sizeof (gint64));
488 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
489 std::numeric_limits<sal_Int32>::max()))
491 SAL_WARN("configmgr.dconf", "too long long list for key " << key);
492 return false;
494 css::uno::Sequence<sal_Int64> v(static_cast<sal_Int32>(n));
495 static_assert(sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
496 std::memcpy(v.getArray(), p, n * sizeof (gint64));
497 // assuming that n * sizeof (gint64) is small enough for std::size_t
498 *value <<= v;
499 return true;
502 bool getDoubleList(
503 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
505 assert(value != nullptr);
506 if (std::strcmp(g_variant_get_type_string(variant.get()), "ad") != 0) {
507 SAL_WARN(
508 "configmgr.dconf",
509 "bad key " << key << " does not match double list property");
510 return false;
512 gsize n;
513 gconstpointer p = g_variant_get_fixed_array(
514 variant.get(), &n, sizeof (gdouble));
515 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
516 std::numeric_limits<sal_Int32>::max()))
518 SAL_WARN("configmgr.dconf", "too long double list for key " << key);
519 return false;
521 css::uno::Sequence<double> v(static_cast<sal_Int32>(n));
522 static_assert(std::is_same<double, gdouble>::value, "type mismatch");
523 std::memcpy(v.getArray(), p, n * sizeof (gdouble));
524 // assuming that n * sizeof (gdouble) is small enough for std::size_t
525 *value <<= v;
526 return true;
529 bool getStringList(
530 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
532 assert(value != nullptr);
533 if (std::strcmp(g_variant_get_type_string(variant.get()), "as") != 0) {
534 SAL_WARN(
535 "configmgr.dconf",
536 "bad key " << key << " does not match string list property");
537 return false;
539 gsize n = g_variant_n_children(variant.get());
540 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
541 std::numeric_limits<sal_Int32>::max()))
543 SAL_WARN("configmgr.dconf", "too long string list for key " << key);
544 return false;
546 css::uno::Sequence<OUString> v(static_cast<sal_Int32>(n));
547 for (gsize i = 0; i != n; ++i) {
548 GVariantHolder c(g_variant_get_child_value(variant.get(), i));
549 if (!getStringValue(key, c, v.getArray() + i)) {
550 return false;
553 *value <<= v;
554 return true;
557 bool getHexbinaryList(
558 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
560 assert(value != nullptr);
561 if (std::strcmp(g_variant_get_type_string(variant.get()), "aay") != 0) {
562 SAL_WARN(
563 "configmgr.dconf",
564 "bad key " << key << " does not match hexbinary list property");
565 return false;
567 gsize n = g_variant_n_children(variant.get());
568 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
569 std::numeric_limits<sal_Int32>::max()))
571 SAL_WARN("configmgr.dconf", "too long hexbinary list for key " << key);
572 return false;
574 css::uno::Sequence<css::uno::Sequence<sal_Int8>> v(
575 static_cast<sal_Int32>(n));
576 for (gsize i = 0; i != n; ++i) {
577 GVariantHolder c(g_variant_get_child_value(variant.get(), i));
578 if (!getHexbinaryValue(key, c, v.getArray() + i)) {
579 return false;
582 *value <<= v;
583 return true;
586 bool getAny(
587 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
589 char const * t = g_variant_get_type_string(variant.get());
590 if (std::strcmp(t, "b") == 0) {
591 return getBoolean(key, variant, value);
593 if (std::strcmp(t, "n") == 0) {
594 return getShort(key, variant, value);
596 if (std::strcmp(t, "i") == 0) {
597 return getInt(key, variant, value);
599 if (std::strcmp(t, "x") == 0) {
600 return getLong(key, variant, value);
602 if (std::strcmp(t, "d") == 0) {
603 return getDouble(key, variant, value);
605 if (std::strcmp(t, "s") == 0) {
606 return getString(key, variant, value);
608 if (std::strcmp(t, "ay") == 0) {
609 return getHexbinary(key, variant, value);
611 if (std::strcmp(t, "ab") == 0) {
612 return getBooleanList(key, variant, value);
614 if (std::strcmp(t, "an") == 0) {
615 return getShortList(key, variant, value);
617 if (std::strcmp(t, "ai") == 0) {
618 return getIntList(key, variant, value);
620 if (std::strcmp(t, "ax") == 0) {
621 return getLongList(key, variant, value);
623 if (std::strcmp(t, "ad") == 0) {
624 return getDoubleList(key, variant, value);
626 if (std::strcmp(t, "as") == 0) {
627 return getStringList(key, variant, value);
629 if (std::strcmp(t, "aay") == 0) {
630 return getHexbinaryList(key, variant, value);
632 SAL_WARN(
633 "configmgr.dconf", "bad key " << key << " does not match any property");
634 return false;
637 enum class ReadValue { Error, Value, Remove };
639 ReadValue readValue(
640 GObjectHolder<DConfClient> const & client, OString const & path, Type type,
641 bool nillable, bool removable, css::uno::Any * value)
643 assert(value != nullptr);
644 assert(!value->hasValue());
645 assert(!path.endsWith("/"));
646 GVariantHolder v(dconf_client_read(client.get(), path.getStr()));
647 if (v.get() == nullptr) {
648 SAL_WARN("configmgr.dconf", "cannot read key " << path);
649 return ReadValue::Error;
651 if (removable && std::strcmp(g_variant_get_type_string(v.get()), "()") == 0)
653 return ReadValue::Remove;
655 bool nil;
656 if (nillable) {
657 if (g_variant_classify(v.get()) != G_VARIANT_CLASS_MAYBE) {
658 SAL_WARN(
659 "configmgr.dconf",
660 "bad key " << path << " does not match nillable property");
662 v.reset(g_variant_get_maybe(v.get()));
663 nil = v.get() == nullptr;
664 } else {
665 nil = false;
667 if (!nil) {
668 switch (type) {
669 case TYPE_ANY:
670 if (!getAny(path, v, value)) {
671 return ReadValue::Error;
673 break;
674 case TYPE_BOOLEAN:
675 if (!getBoolean(path, v, value)) {
676 return ReadValue::Error;
678 break;
679 case TYPE_SHORT:
680 if (!getShort(path, v, value)) {
681 return ReadValue::Error;
683 break;
684 case TYPE_INT:
685 if (!getInt(path, v, value)) {
686 return ReadValue::Error;
688 break;
689 case TYPE_LONG:
690 if (!getLong(path, v, value)) {
691 return ReadValue::Error;
693 break;
694 case TYPE_DOUBLE:
695 if (!getDouble(path, v, value)) {
696 return ReadValue::Error;
698 break;
699 case TYPE_STRING:
700 if (!getString(path, v, value)) {
701 return ReadValue::Error;
703 break;
704 case TYPE_HEXBINARY:
705 if (!getHexbinary(path, v, value)) {
706 return ReadValue::Error;
708 break;
709 case TYPE_BOOLEAN_LIST:
710 if (!getBooleanList(path, v, value)) {
711 return ReadValue::Error;
713 break;
714 case TYPE_SHORT_LIST:
715 if (!getShortList(path, v, value)) {
716 return ReadValue::Error;
718 break;
719 case TYPE_INT_LIST:
720 if (!getIntList(path, v, value)) {
721 return ReadValue::Error;
723 break;
724 case TYPE_LONG_LIST:
725 if (!getLongList(path, v, value)) {
726 return ReadValue::Error;
728 break;
729 case TYPE_DOUBLE_LIST:
730 if (!getDoubleList(path, v, value)) {
731 return ReadValue::Error;
733 break;
734 case TYPE_STRING_LIST:
735 if (!getStringList(path, v, value)) {
736 return ReadValue::Error;
738 break;
739 case TYPE_HEXBINARY_LIST:
740 if (!getHexbinaryList(path, v, value)) {
741 return ReadValue::Error;
743 break;
744 default:
745 assert(false); // cannot happen
748 return ReadValue::Value;
751 void finalize(
752 GObjectHolder<DConfClient> const & client, OString const & path,
753 rtl::Reference<Node> const & node, int layer)
755 if (!dconf_client_is_writable(client.get(), path.getStr())) {
756 node->setFinalized(layer);
760 void readDir(
761 Data & data, int layer, rtl::Reference<Node> const & node,
762 NodeMap & members, GObjectHolder<DConfClient> const & client,
763 OString const & dir)
765 StringArrayHolder a(dconf_client_list(client.get(), dir.getStr(), nullptr));
766 for (char const * const * p = a.get(); *p != nullptr; ++p) {
767 std::size_t n = std::strlen(*p);
768 if (n > static_cast<typename std::make_unsigned<sal_Int32>::type>(
769 std::numeric_limits<sal_Int32>::max()))
771 SAL_WARN("configmgr.dconf", "too long dir/key in dir " << dir);
772 continue;
774 OString s(*p, static_cast<sal_Int32>(n));
775 OString path(dir + s);
776 OUString name;
777 if (!rtl_convertStringToUString(
778 &name.pData, s.getStr(), s.getLength(), RTL_TEXTENCODING_UTF8,
779 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
780 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
781 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
783 SAL_WARN("configmgr.dconf", "non--UTF-8 dir/key in dir " << dir);
784 continue;
786 bool isDir = name.endsWith("/", &name);
787 OUString templ;
788 bool remove;
789 bool replace;
790 if (node.is() && node->kind() == Node::KIND_SET) {
791 if (!isDir) {
792 SAL_WARN(
793 "configmgr.dconf",
794 "bad key " << path << " does not match set element");
795 continue;
797 if (!decode(&name, true)) {
798 continue;
800 enum class Op { None, Fuse, Replace, Remove };
801 Op op = Op::None;
802 bool content = false;
803 bool bad = false;
804 StringArrayHolder a2(
805 dconf_client_list(client.get(), path.getStr(), nullptr));
806 for (char const * const * p2 = a2.get(); *p2 != nullptr; ++p2) {
807 if (std::strcmp(*p2, "op") == 0) {
808 OString path2(path + "op");
809 GVariantHolder v(
810 dconf_client_read(client.get(), path2.getStr()));
811 if (v.get() == nullptr) {
812 SAL_WARN(
813 "configmgr.dconf", "cannot read key " << path2);
814 bad = true;
815 break;
817 OUString ops;
818 if (!getStringValue(path2, v, &ops)) {
819 bad = true;
820 break;
822 if (ops == "fuse") {
823 op = Op::Fuse;
824 } else if (ops == "replace") {
825 op = Op::Replace;
826 } else if (ops == "remove") {
827 op = Op::Remove;
828 } else {
829 SAL_WARN(
830 "configmgr.dconf",
831 "bad key " << path2 << " value " << ops);
832 bad = true;
833 break;
835 } else if (std::strcmp(*p2, "template") == 0) {
836 OString path2(path + "template");
837 GVariantHolder v(
838 dconf_client_read(client.get(), path2.getStr()));
839 if (v.get() == nullptr) {
840 SAL_WARN(
841 "configmgr.dconf", "cannot read key " << path2);
842 bad = true;
843 break;
845 if (!getStringValue(path2, v, &templ)) {
846 bad = true;
847 break;
849 if (!static_cast<SetNode *>(node.get())->
850 isValidTemplate(templ))
852 SAL_WARN(
853 "configmgr.dconf",
854 "bad key " << path2 << " value " << templ
855 << " denotes unsupported set element template");
856 bad = true;
857 break;
859 } else if (std::strcmp(*p2, "content/") == 0) {
860 content = true;
861 } else {
862 SAL_WARN(
863 "configmgr.dconf",
864 "bad dir/key " << p2
865 << " in set element indirection dir " << path);
866 bad = true;
867 break;
870 if (bad) {
871 continue;
873 switch (op) {
874 default: // case Op::None:
875 SAL_WARN(
876 "configmgr.dconf",
877 "bad set element indirection dir " << path
878 << " missing \"op\" key");
879 continue;
880 case Op::Fuse:
881 case Op::Replace:
882 if (templ.isEmpty() || !content) {
883 SAL_WARN(
884 "configmgr.dconf",
885 "missing \"content\" and/or \"template\" dir/key in "
886 "\"op\" = \"fuse\"/\"remove\" set element"
887 " indirection dir " << path);
888 continue;
890 path += "content/";
891 remove = false;
892 replace = op == Op::Replace;
893 break;
894 case Op::Remove:
895 if (!templ.isEmpty() || content) {
896 SAL_WARN(
897 "configmgr.dconf",
898 "bad \"content\" and/or \"template\" dir/key in \"op\" "
899 "= \"remove\" set element indirection dir "
900 << path);
901 continue;
903 remove = true;
904 replace = false;
905 break;
907 } else {
908 remove = false;
909 replace = false;
911 rtl::Reference<Node> member(members.findNode(layer, name));
912 bool insert = !member.is();
913 if (!remove) {
914 if (replace || insert) {
915 if (!node.is()) {
916 SAL_WARN("configmgr.dconf", "bad unmatched " << path);
917 continue;
919 switch (node->kind()) {
920 case Node::KIND_LOCALIZED_PROPERTY:
921 member.set(new LocalizedValueNode(layer));
922 break;
923 case Node::KIND_GROUP:
924 if (!static_cast<GroupNode *>(node.get())->isExtensible()) {
925 SAL_WARN("configmgr.dconf", "bad unmatched " << path);
926 continue;
928 member.set(
929 new PropertyNode(
930 layer, TYPE_ANY, true, css::uno::Any(), true));
931 break;
932 case Node::KIND_SET:
933 assert(!templ.isEmpty());
934 member = data.getTemplate(layer, templ);
935 if (!member.is()) {
936 SAL_WARN(
937 "configmgr.dconf",
938 "bad " << path << " denoting undefined template "
939 << templ);
940 continue;
942 member = member->clone(true);
943 break;
944 default:
945 assert(false); // cannot happen
947 } else if (!templ.isEmpty() && templ != member->getTemplateName()) {
948 SAL_WARN(
949 "configmgr.dconf",
950 "bad " << path
951 << " denoting set element of non-matching template "
952 << member->getTemplateName());
953 continue;
956 if (member.is()) {
957 if (member->getFinalized() < layer) {
958 continue;
960 switch (member->kind()) {
961 case Node::KIND_PROPERTY:
963 if (isDir) {
964 SAL_WARN(
965 "configmgr.dconf",
966 "bad dir " << path << " does not match property");
967 continue;
969 rtl::Reference<PropertyNode> prop(
970 static_cast<PropertyNode *>(member.get()));
971 css::uno::Any value;
972 switch (readValue(
973 client, path, prop->getStaticType(),
974 prop->isNillable(), prop->isExtension(),
975 &value))
977 case ReadValue::Error:
978 continue;
979 case ReadValue::Value:
980 prop->setValue(layer, value);
981 finalize(client, path, member, layer);
982 break;
983 case ReadValue::Remove:
984 remove = true;
985 break;
987 break;
989 case Node::KIND_LOCALIZED_VALUE:
991 if (isDir) {
992 SAL_WARN(
993 "configmgr.dconf",
994 "bad dir " << path
995 << " does not match localized value");
996 continue;
998 assert(
999 node.is()
1000 && node->kind() == Node::KIND_LOCALIZED_PROPERTY);
1001 rtl::Reference<LocalizedPropertyNode> locProp(
1002 static_cast<LocalizedPropertyNode *>(node.get()));
1003 css::uno::Any value;
1004 if (readValue(
1005 client, path, locProp->getStaticType(),
1006 locProp->isNillable(), false, &value)
1007 == ReadValue::Error)
1009 continue;
1011 static_cast<LocalizedValueNode *>(member.get())->setValue(
1012 layer, value);
1013 finalize(client, path, member, layer);
1014 break;
1016 case Node::KIND_LOCALIZED_PROPERTY:
1017 case Node::KIND_GROUP:
1018 case Node::KIND_SET:
1019 if (!isDir) {
1020 SAL_WARN(
1021 "configmgr.dconf",
1022 "bad key " << path
1023 << " does not match localized property, group, or"
1024 " set, respectively");
1025 continue;
1027 assert(path.endsWith("/"));
1028 readDir(
1029 data, layer, member, member->getMembers(), client, path);
1030 break;
1031 default:
1032 assert(false); // cannot happen
1035 if (remove) {
1036 if (!(member.is() && member->getMandatory())) {
1037 members.erase(name);
1039 } else if (replace) {
1040 members.erase(name);
1041 members.insert(NodeMap::value_type(name, member));
1042 } else if (insert) {
1043 members.insert(NodeMap::value_type(name, member));
1048 OString encodeSegment(OUString const & name, bool setElement) {
1049 if (!setElement) {
1050 return name.toUtf8();
1052 OUStringBuffer buf;
1053 for (sal_Int32 i = 0; i != name.getLength(); ++i) {
1054 sal_Unicode c = name[i];
1055 switch (c) {
1056 case '\0':
1057 buf.append("\\00");
1058 break;
1059 case '/':
1060 buf.append("\\2F");
1061 break;
1062 case '\\':
1063 buf.append("\\5C");
1064 break;
1065 default:
1066 buf.append(c);
1069 return buf.makeStringAndClear().toUtf8();
1072 OString encodeString(OUString const & value) {
1073 OUStringBuffer buf;
1074 for (sal_Int32 i = 0; i != value.getLength(); ++i) {
1075 sal_Unicode c = value[i];
1076 switch (c) {
1077 case '\0':
1078 buf.append("\\00");
1079 break;
1080 case '\\':
1081 buf.append("\\5C");
1082 break;
1083 default:
1084 buf.append(c);
1087 return buf.makeStringAndClear().toUtf8();
1090 bool addProperty(
1091 ChangesetHolder const & changeset, OString const & pathRepresentation,
1092 Type type, bool nillable, css::uno::Any const & value)
1094 Type dynType = getDynamicType(value);
1095 assert(dynType != TYPE_ERROR);
1096 if (type == TYPE_ANY) {
1097 type = dynType;
1099 GVariantHolder v;
1100 std::forward_list<GVariantHolder> children;
1101 if (dynType == TYPE_NIL) {
1102 switch (type) {
1103 case TYPE_BOOLEAN:
1104 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN, nullptr));
1105 break;
1106 case TYPE_SHORT:
1107 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16, nullptr));
1108 break;
1109 case TYPE_INT:
1110 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32, nullptr));
1111 break;
1112 case TYPE_LONG:
1113 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64, nullptr));
1114 break;
1115 case TYPE_DOUBLE:
1116 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE, nullptr));
1117 break;
1118 case TYPE_STRING:
1119 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING, nullptr));
1120 break;
1121 case TYPE_HEXBINARY:
1122 case TYPE_BOOLEAN_LIST:
1123 case TYPE_SHORT_LIST:
1124 case TYPE_INT_LIST:
1125 case TYPE_LONG_LIST:
1126 case TYPE_DOUBLE_LIST:
1127 case TYPE_STRING_LIST:
1128 case TYPE_HEXBINARY_LIST:
1130 static char const * const typeString[
1131 TYPE_HEXBINARY_LIST - TYPE_HEXBINARY + 1]
1132 = { "ay", "ab", "an", "ai", "ax", "ad", "as", "aay" };
1133 GVariantTypeHolder ty(
1134 g_variant_type_new(typeString[type - TYPE_HEXBINARY]));
1135 if (ty.get() == nullptr) {
1136 SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
1137 return false;
1139 v.reset(g_variant_new_maybe(ty.get(), nullptr));
1140 break;
1142 default:
1143 assert(false); // this cannot happen
1144 break;
1146 if (v.get() == nullptr) {
1147 SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1148 return false;
1150 } else {
1151 switch (type) {
1152 case TYPE_BOOLEAN:
1153 v.reset(g_variant_new_boolean(value.get<bool>()));
1154 break;
1155 case TYPE_SHORT:
1156 v.reset(g_variant_new_int16(value.get<sal_Int16>()));
1157 break;
1158 case TYPE_INT:
1159 v.reset(g_variant_new_int32(value.get<sal_Int32>()));
1160 break;
1161 case TYPE_LONG:
1162 v.reset(g_variant_new_int64(value.get<sal_Int64>()));
1163 break;
1164 case TYPE_DOUBLE:
1165 v.reset(g_variant_new_double(value.get<double>()));
1166 break;
1167 case TYPE_STRING:
1168 v.reset(
1169 g_variant_new_string(
1170 encodeString(value.get<OUString>()).getStr()));
1171 break;
1172 case TYPE_HEXBINARY:
1174 css::uno::Sequence<sal_Int8> seq(
1175 value.get<css::uno::Sequence<sal_Int8>>());
1176 static_assert(
1177 sizeof(sal_Int32) <= sizeof(gsize),
1178 "G_MAXSIZE too small");
1179 static_assert(
1180 sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
1181 v.reset(
1182 g_variant_new_fixed_array(
1183 G_VARIANT_TYPE_BYTE, seq.getConstArray(),
1184 seq.getLength(), sizeof (sal_Int8)));
1185 break;
1187 case TYPE_BOOLEAN_LIST:
1189 css::uno::Sequence<sal_Bool> seq(
1190 value.get<css::uno::Sequence<sal_Bool>>());
1191 static_assert(
1192 sizeof(sal_Int32) <= sizeof(gsize),
1193 "G_MAXSIZE too small");
1194 static_assert(sizeof (sal_Bool) == 1, "size mismatch");
1195 v.reset(
1196 g_variant_new_fixed_array(
1197 G_VARIANT_TYPE_BOOLEAN, seq.getConstArray(),
1198 seq.getLength(), sizeof (sal_Bool)));
1199 break;
1201 case TYPE_SHORT_LIST:
1203 css::uno::Sequence<sal_Int16> seq(
1204 value.get<css::uno::Sequence<sal_Int16>>());
1205 static_assert(
1206 sizeof(sal_Int32) <= sizeof(gsize),
1207 "G_MAXSIZE too small");
1208 static_assert(
1209 sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
1210 v.reset(
1211 g_variant_new_fixed_array(
1212 G_VARIANT_TYPE_INT16, seq.getConstArray(),
1213 seq.getLength(), sizeof (sal_Int16)));
1214 //TODO: endian-ness?
1215 break;
1217 case TYPE_INT_LIST:
1219 css::uno::Sequence<sal_Int32> seq(
1220 value.get<css::uno::Sequence<sal_Int32>>());
1221 static_assert(
1222 sizeof(sal_Int32) <= sizeof(gsize),
1223 "G_MAXSIZE too small");
1224 static_assert(
1225 sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
1226 v.reset(
1227 g_variant_new_fixed_array(
1228 G_VARIANT_TYPE_INT32, seq.getConstArray(),
1229 seq.getLength(), sizeof (sal_Int32)));
1230 //TODO: endian-ness?
1231 break;
1233 case TYPE_LONG_LIST:
1235 css::uno::Sequence<sal_Int64> seq(
1236 value.get<css::uno::Sequence<sal_Int64>>());
1237 static_assert(
1238 sizeof(sal_Int32) <= sizeof(gsize),
1239 "G_MAXSIZE too small");
1240 static_assert(
1241 sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
1242 v.reset(
1243 g_variant_new_fixed_array(
1244 G_VARIANT_TYPE_INT64, seq.getConstArray(),
1245 seq.getLength(), sizeof (sal_Int64)));
1246 //TODO: endian-ness?
1247 break;
1249 case TYPE_DOUBLE_LIST:
1251 css::uno::Sequence<double> seq(
1252 value.get<css::uno::Sequence<double>>());
1253 static_assert(
1254 sizeof(sal_Int32) <= sizeof(gsize),
1255 "G_MAXSIZE too small");
1256 static_assert(
1257 sizeof (double) == sizeof (gdouble), "size mismatch");
1258 v.reset(
1259 g_variant_new_fixed_array(
1260 G_VARIANT_TYPE_DOUBLE, seq.getConstArray(),
1261 seq.getLength(), sizeof (double)));
1262 //TODO: endian-ness?
1263 break;
1265 case TYPE_STRING_LIST:
1267 css::uno::Sequence<OUString> seq(
1268 value.get<css::uno::Sequence<OUString>>());
1269 std::vector<GVariant *> vs;
1270 for (sal_Int32 i = 0; i != seq.getLength(); ++i) {
1271 children.emplace_front(
1272 g_variant_new_string(encodeString(seq[i]).getStr()));
1273 if (children.front().get() == nullptr) {
1274 SAL_WARN(
1275 "configmgr.dconf", "g_variant_new_string failed");
1276 return false;
1278 vs.push_back(children.front().get());
1280 static_assert(
1281 sizeof(sal_Int32) <= sizeof(gsize),
1282 "G_MAXSIZE too small");
1283 v.reset(
1284 g_variant_new_array(
1285 G_VARIANT_TYPE_STRING, vs.data(), seq.getLength()));
1286 break;
1288 case TYPE_HEXBINARY_LIST:
1290 css::uno::Sequence<css::uno::Sequence<sal_Int8>> seq(
1291 value.get<
1292 css::uno::Sequence<css::uno::Sequence<sal_Int8>>>());
1293 std::vector<GVariant *> vs;
1294 for (sal_Int32 i = 0; i != seq.getLength(); ++i) {
1295 static_assert(
1296 sizeof(sal_Int32) <= sizeof(gsize),
1297 "G_MAXSIZE too small");
1298 static_assert(
1299 sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
1300 children.emplace_front(
1301 g_variant_new_fixed_array(
1302 G_VARIANT_TYPE_BYTE, seq[i].getConstArray(),
1303 seq[i].getLength(), sizeof (sal_Int8)));
1304 if (children.front().get() == nullptr) {
1305 SAL_WARN(
1306 "configmgr.dconf",
1307 "g_variant_new_fixed_array failed");
1308 return false;
1310 vs.push_back(children.front().get());
1312 GVariantTypeHolder ty(g_variant_type_new("aay"));
1313 if (ty.get() == nullptr) {
1314 SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
1315 return false;
1317 static_assert(
1318 sizeof(sal_Int32) <= sizeof(gsize),
1319 "G_MAXSIZE too small");
1320 v.reset(
1321 g_variant_new_array(ty.get(), vs.data(), seq.getLength()));
1322 break;
1324 default:
1325 assert(false); // this cannot happen
1326 break;
1328 if (v.get() == nullptr) {
1329 SAL_WARN("configmgr.dconf", "GVariant creation failed");
1330 return false;
1332 if (nillable) {
1333 GVariantHolder v1(g_variant_new_maybe(nullptr, v.get()));
1334 if (v1.get() == nullptr) {
1335 SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1336 return false;
1338 v.release();
1339 v.reset(v1.get());
1340 v1.release();
1343 dconf_changeset_set(
1344 changeset.get(), pathRepresentation.getStr(), v.get());
1345 for (auto & i: children) {
1346 i.release();
1348 v.release();
1349 return true;
1352 bool addNode(
1353 Components & components, ChangesetHolder const & changeset,
1354 rtl::Reference<Node> const & parent, OString const & pathRepresentation,
1355 rtl::Reference<Node> const & node)
1357 switch (node->kind()) {
1358 case Node::KIND_PROPERTY:
1360 PropertyNode * prop = static_cast<PropertyNode *>(node.get());
1361 if (!addProperty(
1362 changeset, pathRepresentation, prop->getStaticType(),
1363 prop->isNillable(), prop->getValue(components)))
1365 return false;
1367 break;
1369 case Node::KIND_LOCALIZED_VALUE:
1371 //TODO: name.isEmpty()?
1372 LocalizedPropertyNode * locprop
1373 = static_cast<LocalizedPropertyNode *>(parent.get());
1374 if (!addProperty(
1375 changeset, pathRepresentation,
1376 locprop->getStaticType(), locprop->isNillable(),
1377 static_cast<LocalizedValueNode *>(node.get())->getValue()))
1379 return false;
1381 break;
1383 case Node::KIND_LOCALIZED_PROPERTY:
1384 case Node::KIND_GROUP:
1385 case Node::KIND_SET:
1386 for (auto const & i: node->getMembers()) {
1387 OUString templ(i.second->getTemplateName());
1388 OString path(
1389 pathRepresentation + "/"
1390 + encodeSegment(i.first, !templ.isEmpty()));
1391 if (!templ.isEmpty()) {
1392 path += "/";
1393 GVariantHolder v(g_variant_new_string("replace"));
1394 if (v.get() == nullptr) {
1395 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1396 return false;
1398 dconf_changeset_set(
1399 changeset.get(), OString(path + "op").getStr(), v.get());
1400 v.release();
1401 v.reset(g_variant_new_string(encodeString(templ).getStr()));
1402 if (v.get() == nullptr) {
1403 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1404 return false;
1406 dconf_changeset_set(
1407 changeset.get(), OString(path + "template").getStr(),
1408 v.get());
1409 v.release();
1410 path += "content";
1412 if (!addNode(components, changeset, parent, path, i.second)) {
1413 return false;
1416 break;
1417 case Node::KIND_ROOT:
1418 assert(false); // this cannot happen
1419 break;
1421 return true;
1424 bool addModifications(
1425 Components & components, ChangesetHolder const & changeset,
1426 OString const & parentPathRepresentation,
1427 rtl::Reference<Node> const & parent, OUString const & nodeName,
1428 rtl::Reference<Node> const & node,
1429 Modifications::Node const & modifications)
1431 // It is never necessary to write oor:finalized or oor:mandatory attributes,
1432 // as they cannot be set via the UNO API.
1433 if (modifications.children.empty()) {
1434 assert(parent.is());
1435 // components themselves have no parent but must have children
1436 if (node.is()) {
1437 OUString templ(node->getTemplateName());
1438 OString path(
1439 parentPathRepresentation + "/"
1440 + encodeSegment(nodeName, !templ.isEmpty()));
1441 if (!templ.isEmpty()) {
1442 path += "/";
1443 GVariantHolder v(g_variant_new_string("replace"));
1444 if (v.get() == nullptr) {
1445 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1446 return false;
1448 dconf_changeset_set(
1449 changeset.get(), OString(path + "op").getStr(), v.get());
1450 v.release();
1451 v.reset(g_variant_new_string(encodeString(templ).getStr()));
1452 if (v.get() == nullptr) {
1453 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1454 return false;
1456 dconf_changeset_set(
1457 changeset.get(), OString(path + "template").getStr(),
1458 v.get());
1459 v.release();
1460 path += "content";
1462 if (!addNode(components, changeset, parent, path, node)) {
1463 return false;
1465 } else {
1466 switch (parent->kind()) {
1467 case Node::KIND_LOCALIZED_PROPERTY:
1468 case Node::KIND_GROUP:
1470 GVariantHolder v(g_variant_new_tuple(nullptr, 0));
1471 if (v.get() == nullptr) {
1472 SAL_WARN(
1473 "configmgr.dconf", "g_variant_new_tuple failed");
1474 return false;
1476 OString path(parentPathRepresentation);
1477 if (!nodeName.isEmpty()) { // KIND_LOCALIZED_PROPERTY
1478 path += "/" + encodeSegment(nodeName, false);
1480 dconf_changeset_set(
1481 changeset.get(), path.getStr(), v.get());
1482 v.release();
1483 break;
1485 case Node::KIND_SET:
1487 OString path(
1488 parentPathRepresentation + "/"
1489 + encodeSegment(nodeName, true) + "/");
1490 GVariantHolder v(g_variant_new_string("remove"));
1491 if (v.get() == nullptr) {
1492 SAL_WARN(
1493 "configmgr.dconf", "g_variant_new_string failed");
1494 return false;
1496 dconf_changeset_set(
1497 changeset.get(), OString(path + "op").getStr(),
1498 v.get());
1499 v.release();
1500 dconf_changeset_set(
1501 changeset.get(), OString(path + "template").getStr(),
1502 nullptr);
1503 dconf_changeset_set(
1504 changeset.get(), OString(path + "content/").getStr(),
1505 nullptr);
1506 break;
1508 default:
1509 assert(false); // this cannot happen
1510 break;
1513 } else {
1514 assert(node.is());
1515 OUString templ(node->getTemplateName());
1516 OString path(
1517 parentPathRepresentation + "/"
1518 + encodeSegment(nodeName, !templ.isEmpty()));
1519 if (!templ.isEmpty()) {
1520 path += "/";
1521 GVariantHolder v(g_variant_new_string("fuse"));
1522 if (v.get() == nullptr) {
1523 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1524 return false;
1526 dconf_changeset_set(
1527 changeset.get(), OString(path + "op").getStr(), v.get());
1528 v.release();
1529 v.reset(g_variant_new_string(encodeString(templ).getStr()));
1530 if (v.get() == nullptr) {
1531 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1532 return false;
1534 dconf_changeset_set(
1535 changeset.get(), OString(path + "template").getStr(), v.get());
1536 v.release();
1537 path += "content";
1539 for (auto const & i: modifications.children) {
1540 if (!addModifications(
1541 components, changeset, path, node, i.first,
1542 node->getMember(i.first), i.second))
1544 return false;
1548 return true;
1553 void readLayer(Data & data, int layer) {
1554 GObjectHolder<DConfClient> client(dconf_client_new());
1555 if (client.get() == nullptr) {
1556 SAL_WARN("configmgr.dconf", "dconf_client_new failed");
1557 return;
1559 readDir(
1560 data, layer, rtl::Reference<Node>(), data.getComponents(), client,
1561 getRoot() + "/");
1564 void writeModifications(Components & components, Data & data) {
1565 GObjectHolder<DConfClient> client(dconf_client_new());
1566 if (client.get() == nullptr) {
1567 SAL_WARN("configmgr.dconf", "dconf_client_new failed");
1569 ChangesetHolder cs(dconf_changeset_new());
1570 if (cs.get() == nullptr) {
1571 SAL_WARN("configmgr.dconf", "dconf_changeset_new failed");
1572 return;
1574 for (auto const & i: data.modifications.getRoot().children) {
1575 if (!addModifications(
1576 components, cs, getRoot(), rtl::Reference<Node>(), i.first,
1577 data.getComponents().findNode(Data::NO_LAYER, i.first),
1578 i.second))
1580 return;
1583 if (!dconf_client_change_sync(
1584 client.get(), cs.get(), nullptr, nullptr, nullptr))
1586 //TODO: GError
1587 SAL_WARN("configmgr.dconf", "dconf_client_change_sync failed");
1588 return;
1590 data.modifications.clear();
1595 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */