bump product version to 7.2.5.1
[LibreOffice.git] / configmgr / source / dconf.cxx
blob1c0711ff192725e8cf97a78a1111433f0e3508e0
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 <vector>
19 extern "C" {
20 //TODO: <https://bugzilla.gnome.org/show_bug.cgi?id=754245>
21 // "common/dconf-changeset.h etc. lack extern "C" wrapper for C++", fixed on current dconf
22 // master (towards 0.40?) now with
23 // <https://gitlab.gnome.org/GNOME/dconf/-/commit/db3d4df6d1a763698f27b013dc42da8d4ae02639>
24 // "Merge branch 'wip/issue-23' into 'master'"
25 #include <dconf/dconf.h>
28 #include <com/sun/star/uno/Sequence.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <rtl/ustrbuf.hxx>
31 #include <sal/log.hxx>
33 #include "data.hxx"
34 #include "dconf.hxx"
35 #include "groupnode.hxx"
36 #include "localizedpropertynode.hxx"
37 #include "localizedvaluenode.hxx"
38 #include "nodemap.hxx"
39 #include "propertynode.hxx"
40 #include "setnode.hxx"
42 // component-data is encoded in dconf as follows:
44 // * The node hierarchy (starting at component nodes with names like
45 // "org.openoffice.Setup") maps to dconf paths underneath
46 // "/org/libreoffice/registry/".
48 // * Component, group, set, and localized property nodes map to dconf dirs,
49 // while property and localized value nodes map to dconf keys.
51 // * The names of nodes that are not set elements are used directly as dconf
52 // path segments. (The syntax for node names is any non-empty sequences of
53 // any Unicode scalar values except U+0000--0008, U+000B--000C, U+000E--001F,
54 // U+002F SOLIDUS, and U+FFFE--FFFF. TODO: "<aruiz> sberg, in general I think
55 // it'd be nice if you used path separators instead of dots though, they have
56 // meaning in dconf/gvdb world :-)"?)
58 // * The names of set element nodes are encoded as dconf path segments as
59 // follows: each occurrence of U+0000 NULL is replace by the three characters
60 // "\00", each occurrence of U+002F SOLIDUS is replaced by the three
61 // characters "\2F", and each occurrence of U+005C REVERSE SOLIDUS is replaced
62 // by the three characters "\5C".
64 // * Set elements (which must themselves be either sets or groups) map to
65 // "indirection" dconf dirs as follows:
67 // ** The dir must contain a key named "op" of string type, with a value of
68 // "fuse", "replace", or "remove".
70 // ** If "op" is "fuse" or "replace", the dir must contain exactly the following
71 // further keys and dirs:
73 // *** The dir must contain a key named "template" of string type, containing
74 // the full template name, encoded as follows: each occurrence of U+0000
75 // NULL is replace by the three characters "\00" and each occurrence of
76 // U+005C REVERSE SOLIDUS is replaced by the three characters "\5C".
78 // *** The dir must contain a dir named "content" that contains the set
79 // element's (i.e., set or group node's) real content.
81 // ** If "op" is "remove", the dir must contain no further keys or dirs.
83 // * Property and localized property value "fuse" operations map to GVariant
84 // instances as follows:
86 // ** Non-nillable boolean values map to GVariant boolean instances.
88 // ** Non-nillable short values map to GVariant int16 instances.
90 // ** Non-nillable int values map to GVariant int32 instances.
92 // ** Non-nillable long values map to GVariant int64 instances.
94 // ** Non-nillable double values map to GVariant double instances.
96 // ** Non-nillable string values map to GVariant string instances, with the
97 // following encoding: each occurrence of U+0000 NULL is replace by the three
98 // characters "\00" and each occurrence of U+005C REVERSE SOLIDUS is replaced
99 // by the three characters "\5C".
101 // ** Non-nillable hexbinary values map to GVariant byte array instances.
103 // ** Non-nillable list values recursively map to GVariant array instances.
105 // ** Nillable values recursively map to GVariant maybe instances.
107 // * Property "remove" operations map to GVariant instances of empty tuple type.
109 // Finalization: The component-update.dtd allows for finalization of
110 // oor:component-data, node, and prop elements, while dconf allows for locking
111 // of individual keys. That does not match, but just mark the individual Node
112 // instances that correspond to individual dconf keys as finalized for
113 // non-writable dconf keys.
115 // TODO: support "mandatory" and "external"?
117 namespace configmgr::dconf {
119 namespace {
121 template<typename T> class GObjectHolder {
122 public:
123 explicit GObjectHolder(T * object): object_(object) {}
125 ~GObjectHolder() {
126 if (object_ != nullptr) {
127 g_object_unref(object_);
131 T * get() const { return object_; }
133 private:
134 GObjectHolder(GObjectHolder const &) = delete;
135 GObjectHolder& operator =(GObjectHolder const &) = delete;
137 T * object_;
140 class GVariantHolder {
141 public:
142 explicit GVariantHolder(GVariant * variant = nullptr): variant_(variant) {}
144 ~GVariantHolder() { unref(); }
146 void reset(GVariant * variant) {
147 unref();
148 variant_ = variant;
151 void release() { variant_ = nullptr; }
153 GVariant * get() const { return variant_; }
155 private:
156 GVariantHolder(GVariantHolder const &) = delete;
157 GVariantHolder& operator =(GVariantHolder const &) = delete;
159 void unref() {
160 if (variant_ != nullptr) {
161 g_variant_unref(variant_);
165 GVariant * variant_;
168 class GVariantTypeHolder {
169 public:
170 explicit GVariantTypeHolder(GVariantType * type): type_(type) {}
172 ~GVariantTypeHolder() {
173 if (type_ != nullptr) {
174 g_variant_type_free(type_);
178 GVariantType * get() const { return type_; }
180 private:
181 GVariantTypeHolder(GVariantTypeHolder const &) = delete;
182 GVariantTypeHolder& operator =(GVariantTypeHolder const &) = delete;
184 GVariantType * type_;
187 class StringArrayHolder {
188 public:
189 explicit StringArrayHolder(gchar ** array): array_(array) {}
191 ~StringArrayHolder() { g_strfreev(array_); }
193 gchar ** get() const { return array_; }
195 private:
196 StringArrayHolder(StringArrayHolder const &) = delete;
197 StringArrayHolder& operator =(StringArrayHolder const &) = delete;
199 gchar ** array_;
202 class ChangesetHolder {
203 public:
204 explicit ChangesetHolder(DConfChangeset * changeset):
205 changeset_(changeset)
208 ~ChangesetHolder() {
209 if (changeset_ != nullptr) {
210 dconf_changeset_unref(changeset_);
214 DConfChangeset * get() const { return changeset_; }
216 private:
217 ChangesetHolder(ChangesetHolder const &) = delete;
218 ChangesetHolder& operator =(ChangesetHolder const &) = delete;
220 DConfChangeset * changeset_;
223 OString getRoot() {
224 return "/org/libreoffice/registry";
227 bool decode(OUString * string, bool slash) {
228 for (sal_Int32 i = 0;; ++i) {
229 i = string->indexOf('\\', i);
230 if (i == -1) {
231 return true;
233 if (string->match("00", i + 1)) {
234 *string = string->replaceAt(i, 3, OUString(u'\0'));
235 } else if (slash && string->match("2F", i + 1)) {
236 *string = string->replaceAt(i, 3, "/");
237 } else if (string->match("5C", i + 1)) {
238 *string = string->replaceAt(i + 1, 2, "");
239 } else {
240 SAL_WARN("configmgr.dconf", "bad escape in " << *string);
241 return false;
246 bool getBoolean(
247 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
249 assert(value != nullptr);
250 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_BOOLEAN)) {
251 SAL_WARN(
252 "configmgr.dconf",
253 "bad key " << key << " does not match boolean property");
254 return false;
256 *value <<= bool(g_variant_get_boolean(variant.get()));
257 return true;
260 bool getShort(
261 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
263 assert(value != nullptr);
264 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT16)) {
265 SAL_WARN(
266 "configmgr.dconf",
267 "bad key " << key << " does not match short property");
268 return false;
270 *value <<= sal_Int16(g_variant_get_int16(variant.get()));
271 return true;
274 bool getInt(
275 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
277 assert(value != nullptr);
278 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT32)) {
279 SAL_WARN(
280 "configmgr.dconf",
281 "bad key " << key << " does not match int property");
282 return false;
284 *value <<= sal_Int32(g_variant_get_int32(variant.get()));
285 return true;
288 bool getLong(
289 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
291 assert(value != nullptr);
292 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT64)) {
293 SAL_WARN(
294 "configmgr.dconf",
295 "bad key " << key << " does not match long property");
296 return false;
298 *value <<= sal_Int64(g_variant_get_int64(variant.get()));
299 return true;
302 bool getDouble(
303 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
305 assert(value != nullptr);
306 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_DOUBLE)) {
307 SAL_WARN(
308 "configmgr.dconf",
309 "bad key " << key << " does not match double property");
310 return false;
312 *value <<= double(g_variant_get_double(variant.get()));
313 return true;
316 bool getStringValue(
317 OString const & key, GVariantHolder const & variant, OUString * value)
319 assert(value != nullptr);
320 if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_STRING)) {
321 SAL_WARN(
322 "configmgr.dconf",
323 "bad key " << key << " does not match string property");
324 return false;
326 gsize n;
327 char const * p = g_variant_get_string(variant.get(), &n);
328 if (n > o3tl::make_unsigned(
329 std::numeric_limits<sal_Int32>::max()))
331 SAL_WARN("configmgr.dconf", "too long string value for key " << key);
332 return false;
334 if (!rtl_convertStringToUString(
335 &value->pData, p, static_cast<sal_Int32>(n), RTL_TEXTENCODING_UTF8,
336 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
337 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
338 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
340 SAL_WARN("configmgr.dconf", "non--UTF-8 string value for key " << key);
341 return false;
343 return decode(value, false);
346 bool getString(
347 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
349 assert(value != nullptr);
350 OUString v;
351 if (!getStringValue(key, variant, &v)) {
352 return false;
354 *value <<= v;
355 return true;
358 bool getHexbinaryValue(
359 OString const & key, GVariantHolder const & variant,
360 css::uno::Sequence<sal_Int8> * value)
362 assert(value != nullptr);
363 if (std::strcmp(g_variant_get_type_string(variant.get()), "ay") != 0) {
364 SAL_WARN(
365 "configmgr.dconf",
366 "bad key " << key << " does not match hexbinary property");
367 return false;
369 gsize n;
370 gconstpointer p = g_variant_get_fixed_array(
371 variant.get(), &n, sizeof (guchar));
372 if (n > o3tl::make_unsigned(
373 std::numeric_limits<sal_Int32>::max()))
375 SAL_WARN("configmgr.dconf", "too long hexbinary value for key " << key);
376 return false;
378 value->realloc(static_cast<sal_Int32>(n));
379 static_assert(sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
380 std::memcpy(value->getArray(), p, n * sizeof (guchar));
381 // assuming that n * sizeof (guchar) is small enough for std::size_t
382 return true;
385 bool getHexbinary(
386 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
388 assert(value != nullptr);
389 css::uno::Sequence<sal_Int8> v;
390 if (!getHexbinaryValue(key, variant, &v)) {
391 return false;
393 *value <<= v;
394 return true;
397 bool getBooleanList(
398 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
400 assert(value != nullptr);
401 if (std::strcmp(g_variant_get_type_string(variant.get()), "ab") != 0) {
402 SAL_WARN(
403 "configmgr.dconf",
404 "bad key " << key << " does not match boolean list property");
405 return false;
407 gsize n;
408 gconstpointer p = g_variant_get_fixed_array(
409 variant.get(), &n, sizeof (guchar));
410 if (n > o3tl::make_unsigned(
411 std::numeric_limits<sal_Int32>::max()))
413 SAL_WARN("configmgr.dconf", "too long boolean list for key " << key);
414 return false;
416 css::uno::Sequence<sal_Bool> v(static_cast<sal_Int32>(n));
417 static_assert(sizeof (sal_Bool) == sizeof (guchar), "size mismatch");
418 std::memcpy(v.getArray(), p, n * sizeof (guchar));
419 // assuming that n * sizeof (guchar) is small enough for std::size_t
420 *value <<= v;
421 return true;
424 bool getShortList(
425 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
427 assert(value != nullptr);
428 if (std::strcmp(g_variant_get_type_string(variant.get()), "an") != 0) {
429 SAL_WARN(
430 "configmgr.dconf",
431 "bad key " << key << " does not match short list property");
432 return false;
434 gsize n;
435 gconstpointer p = g_variant_get_fixed_array(
436 variant.get(), &n, sizeof (gint16));
437 if (n > o3tl::make_unsigned(
438 std::numeric_limits<sal_Int32>::max()))
440 SAL_WARN("configmgr.dconf", "too long short list for key " << key);
441 return false;
443 css::uno::Sequence<sal_Int16> v(static_cast<sal_Int32>(n));
444 static_assert(sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
445 std::memcpy(v.getArray(), p, n * sizeof (gint16));
446 // assuming that n * sizeof (gint16) is small enough for std::size_t
447 *value <<= v;
448 return true;
451 bool getIntList(
452 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
454 assert(value != nullptr);
455 if (std::strcmp(g_variant_get_type_string(variant.get()), "ai") != 0) {
456 SAL_WARN(
457 "configmgr.dconf",
458 "bad key " << key << " does not match int list property");
459 return false;
461 gsize n;
462 gconstpointer p = g_variant_get_fixed_array(
463 variant.get(), &n, sizeof (gint32));
464 if (n > o3tl::make_unsigned(
465 std::numeric_limits<sal_Int32>::max()))
467 SAL_WARN("configmgr.dconf", "too long int list for key " << key);
468 return false;
470 css::uno::Sequence<sal_Int32> v(static_cast<sal_Int32>(n));
471 static_assert(sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
472 std::memcpy(v.getArray(), p, n * sizeof (gint32));
473 // assuming that n * sizeof (gint32) is small enough for std::size_t
474 *value <<= v;
475 return true;
478 bool getLongList(
479 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
481 assert(value != nullptr);
482 if (std::strcmp(g_variant_get_type_string(variant.get()), "ax") != 0) {
483 SAL_WARN(
484 "configmgr.dconf",
485 "bad key " << key << " does not match long list property");
486 return false;
488 gsize n;
489 gconstpointer p = g_variant_get_fixed_array(
490 variant.get(), &n, sizeof (gint64));
491 if (n > o3tl::make_unsigned(
492 std::numeric_limits<sal_Int32>::max()))
494 SAL_WARN("configmgr.dconf", "too long long list for key " << key);
495 return false;
497 css::uno::Sequence<sal_Int64> v(static_cast<sal_Int32>(n));
498 static_assert(sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
499 std::memcpy(v.getArray(), p, n * sizeof (gint64));
500 // assuming that n * sizeof (gint64) is small enough for std::size_t
501 *value <<= v;
502 return true;
505 bool getDoubleList(
506 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
508 assert(value != nullptr);
509 if (std::strcmp(g_variant_get_type_string(variant.get()), "ad") != 0) {
510 SAL_WARN(
511 "configmgr.dconf",
512 "bad key " << key << " does not match double list property");
513 return false;
515 gsize n;
516 gconstpointer p = g_variant_get_fixed_array(
517 variant.get(), &n, sizeof (gdouble));
518 if (n > o3tl::make_unsigned(
519 std::numeric_limits<sal_Int32>::max()))
521 SAL_WARN("configmgr.dconf", "too long double list for key " << key);
522 return false;
524 css::uno::Sequence<double> v(static_cast<sal_Int32>(n));
525 static_assert(std::is_same<double, gdouble>::value, "type mismatch");
526 std::memcpy(v.getArray(), p, n * sizeof (gdouble));
527 // assuming that n * sizeof (gdouble) is small enough for std::size_t
528 *value <<= v;
529 return true;
532 bool getStringList(
533 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
535 assert(value != nullptr);
536 if (std::strcmp(g_variant_get_type_string(variant.get()), "as") != 0) {
537 SAL_WARN(
538 "configmgr.dconf",
539 "bad key " << key << " does not match string list property");
540 return false;
542 gsize n = g_variant_n_children(variant.get());
543 if (n > o3tl::make_unsigned(
544 std::numeric_limits<sal_Int32>::max()))
546 SAL_WARN("configmgr.dconf", "too long string list for key " << key);
547 return false;
549 css::uno::Sequence<OUString> v(static_cast<sal_Int32>(n));
550 for (gsize i = 0; i != n; ++i) {
551 GVariantHolder c(g_variant_get_child_value(variant.get(), i));
552 if (!getStringValue(key, c, v.getArray() + i)) {
553 return false;
556 *value <<= v;
557 return true;
560 bool getHexbinaryList(
561 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
563 assert(value != nullptr);
564 if (std::strcmp(g_variant_get_type_string(variant.get()), "aay") != 0) {
565 SAL_WARN(
566 "configmgr.dconf",
567 "bad key " << key << " does not match hexbinary list property");
568 return false;
570 gsize n = g_variant_n_children(variant.get());
571 if (n > o3tl::make_unsigned(
572 std::numeric_limits<sal_Int32>::max()))
574 SAL_WARN("configmgr.dconf", "too long hexbinary list for key " << key);
575 return false;
577 css::uno::Sequence<css::uno::Sequence<sal_Int8>> v(
578 static_cast<sal_Int32>(n));
579 for (gsize i = 0; i != n; ++i) {
580 GVariantHolder c(g_variant_get_child_value(variant.get(), i));
581 if (!getHexbinaryValue(key, c, v.getArray() + i)) {
582 return false;
585 *value <<= v;
586 return true;
589 bool getAny(
590 OString const & key, GVariantHolder const & variant, css::uno::Any * value)
592 char const * t = g_variant_get_type_string(variant.get());
593 if (std::strcmp(t, "b") == 0) {
594 return getBoolean(key, variant, value);
596 if (std::strcmp(t, "n") == 0) {
597 return getShort(key, variant, value);
599 if (std::strcmp(t, "i") == 0) {
600 return getInt(key, variant, value);
602 if (std::strcmp(t, "x") == 0) {
603 return getLong(key, variant, value);
605 if (std::strcmp(t, "d") == 0) {
606 return getDouble(key, variant, value);
608 if (std::strcmp(t, "s") == 0) {
609 return getString(key, variant, value);
611 if (std::strcmp(t, "ay") == 0) {
612 return getHexbinary(key, variant, value);
614 if (std::strcmp(t, "ab") == 0) {
615 return getBooleanList(key, variant, value);
617 if (std::strcmp(t, "an") == 0) {
618 return getShortList(key, variant, value);
620 if (std::strcmp(t, "ai") == 0) {
621 return getIntList(key, variant, value);
623 if (std::strcmp(t, "ax") == 0) {
624 return getLongList(key, variant, value);
626 if (std::strcmp(t, "ad") == 0) {
627 return getDoubleList(key, variant, value);
629 if (std::strcmp(t, "as") == 0) {
630 return getStringList(key, variant, value);
632 if (std::strcmp(t, "aay") == 0) {
633 return getHexbinaryList(key, variant, value);
635 SAL_WARN(
636 "configmgr.dconf", "bad key " << key << " does not match any property");
637 return false;
640 enum class ReadValue { Error, Value, Remove };
642 ReadValue readValue(
643 GObjectHolder<DConfClient> const & client, OString const & path, Type type,
644 bool nillable, bool removable, css::uno::Any * value)
646 assert(value != nullptr);
647 assert(!value->hasValue());
648 assert(!path.endsWith("/"));
649 GVariantHolder v(dconf_client_read(client.get(), path.getStr()));
650 if (v.get() == nullptr) {
651 SAL_WARN("configmgr.dconf", "cannot read key " << path);
652 return ReadValue::Error;
654 if (removable && std::strcmp(g_variant_get_type_string(v.get()), "()") == 0)
656 return ReadValue::Remove;
658 bool nil;
659 if (nillable) {
660 if (g_variant_classify(v.get()) != G_VARIANT_CLASS_MAYBE) {
661 SAL_WARN(
662 "configmgr.dconf",
663 "bad key " << path << " does not match nillable property");
665 v.reset(g_variant_get_maybe(v.get()));
666 nil = v.get() == nullptr;
667 } else {
668 nil = false;
670 if (!nil) {
671 switch (type) {
672 case TYPE_ANY:
673 if (!getAny(path, v, value)) {
674 return ReadValue::Error;
676 break;
677 case TYPE_BOOLEAN:
678 if (!getBoolean(path, v, value)) {
679 return ReadValue::Error;
681 break;
682 case TYPE_SHORT:
683 if (!getShort(path, v, value)) {
684 return ReadValue::Error;
686 break;
687 case TYPE_INT:
688 if (!getInt(path, v, value)) {
689 return ReadValue::Error;
691 break;
692 case TYPE_LONG:
693 if (!getLong(path, v, value)) {
694 return ReadValue::Error;
696 break;
697 case TYPE_DOUBLE:
698 if (!getDouble(path, v, value)) {
699 return ReadValue::Error;
701 break;
702 case TYPE_STRING:
703 if (!getString(path, v, value)) {
704 return ReadValue::Error;
706 break;
707 case TYPE_HEXBINARY:
708 if (!getHexbinary(path, v, value)) {
709 return ReadValue::Error;
711 break;
712 case TYPE_BOOLEAN_LIST:
713 if (!getBooleanList(path, v, value)) {
714 return ReadValue::Error;
716 break;
717 case TYPE_SHORT_LIST:
718 if (!getShortList(path, v, value)) {
719 return ReadValue::Error;
721 break;
722 case TYPE_INT_LIST:
723 if (!getIntList(path, v, value)) {
724 return ReadValue::Error;
726 break;
727 case TYPE_LONG_LIST:
728 if (!getLongList(path, v, value)) {
729 return ReadValue::Error;
731 break;
732 case TYPE_DOUBLE_LIST:
733 if (!getDoubleList(path, v, value)) {
734 return ReadValue::Error;
736 break;
737 case TYPE_STRING_LIST:
738 if (!getStringList(path, v, value)) {
739 return ReadValue::Error;
741 break;
742 case TYPE_HEXBINARY_LIST:
743 if (!getHexbinaryList(path, v, value)) {
744 return ReadValue::Error;
746 break;
747 default:
748 assert(false); // cannot happen
751 return ReadValue::Value;
754 void finalize(
755 GObjectHolder<DConfClient> const & client, OString const & path,
756 rtl::Reference<Node> const & node, int layer)
758 if (!dconf_client_is_writable(client.get(), path.getStr())) {
759 node->setFinalized(layer);
763 void readDir(
764 Data & data, int layer, rtl::Reference<Node> const & node,
765 NodeMap & members, GObjectHolder<DConfClient> const & client,
766 OString const & dir)
768 StringArrayHolder a(dconf_client_list(client.get(), dir.getStr(), nullptr));
769 for (char const * const * p = a.get(); *p != nullptr; ++p) {
770 std::size_t n = std::strlen(*p);
771 if (n > o3tl::make_unsigned(
772 std::numeric_limits<sal_Int32>::max()))
774 SAL_WARN("configmgr.dconf", "too long dir/key in dir " << dir);
775 continue;
777 OString s(*p, static_cast<sal_Int32>(n));
778 OString path(dir + s);
779 OUString name;
780 if (!rtl_convertStringToUString(
781 &name.pData, s.getStr(), s.getLength(), RTL_TEXTENCODING_UTF8,
782 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
783 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
784 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
786 SAL_WARN("configmgr.dconf", "non--UTF-8 dir/key in dir " << dir);
787 continue;
789 bool isDir = name.endsWith("/", &name);
790 OUString templ;
791 bool remove;
792 bool replace;
793 if (node.is() && node->kind() == Node::KIND_SET) {
794 if (!isDir) {
795 SAL_WARN(
796 "configmgr.dconf",
797 "bad key " << path << " does not match set element");
798 continue;
800 if (!decode(&name, true)) {
801 continue;
803 enum class Op { None, Fuse, Replace, Remove };
804 Op op = Op::None;
805 bool content = false;
806 bool bad = false;
807 StringArrayHolder a2(
808 dconf_client_list(client.get(), path.getStr(), nullptr));
809 for (char const * const * p2 = a2.get(); *p2 != nullptr; ++p2) {
810 if (std::strcmp(*p2, "op") == 0) {
811 OString path2(path + "op");
812 GVariantHolder v(
813 dconf_client_read(client.get(), path2.getStr()));
814 if (v.get() == nullptr) {
815 SAL_WARN(
816 "configmgr.dconf", "cannot read key " << path2);
817 bad = true;
818 break;
820 OUString ops;
821 if (!getStringValue(path2, v, &ops)) {
822 bad = true;
823 break;
825 if (ops == "fuse") {
826 op = Op::Fuse;
827 } else if (ops == "replace") {
828 op = Op::Replace;
829 } else if (ops == "remove") {
830 op = Op::Remove;
831 } else {
832 SAL_WARN(
833 "configmgr.dconf",
834 "bad key " << path2 << " value " << ops);
835 bad = true;
836 break;
838 } else if (std::strcmp(*p2, "template") == 0) {
839 OString path2(path + "template");
840 GVariantHolder v(
841 dconf_client_read(client.get(), path2.getStr()));
842 if (v.get() == nullptr) {
843 SAL_WARN(
844 "configmgr.dconf", "cannot read key " << path2);
845 bad = true;
846 break;
848 if (!getStringValue(path2, v, &templ)) {
849 bad = true;
850 break;
852 if (!static_cast<SetNode *>(node.get())->
853 isValidTemplate(templ))
855 SAL_WARN(
856 "configmgr.dconf",
857 "bad key " << path2 << " value " << templ
858 << " denotes unsupported set element template");
859 bad = true;
860 break;
862 } else if (std::strcmp(*p2, "content/") == 0) {
863 content = true;
864 } else {
865 SAL_WARN(
866 "configmgr.dconf",
867 "bad dir/key " << p2
868 << " in set element indirection dir " << path);
869 bad = true;
870 break;
873 if (bad) {
874 continue;
876 switch (op) {
877 default: // case Op::None:
878 SAL_WARN(
879 "configmgr.dconf",
880 "bad set element indirection dir " << path
881 << " missing \"op\" key");
882 continue;
883 case Op::Fuse:
884 case Op::Replace:
885 if (templ.isEmpty() || !content) {
886 SAL_WARN(
887 "configmgr.dconf",
888 "missing \"content\" and/or \"template\" dir/key in "
889 "\"op\" = \"fuse\"/\"remove\" set element"
890 " indirection dir " << path);
891 continue;
893 path += "content/";
894 remove = false;
895 replace = op == Op::Replace;
896 break;
897 case Op::Remove:
898 if (!templ.isEmpty() || content) {
899 SAL_WARN(
900 "configmgr.dconf",
901 "bad \"content\" and/or \"template\" dir/key in \"op\" "
902 "= \"remove\" set element indirection dir "
903 << path);
904 continue;
906 remove = true;
907 replace = false;
908 break;
910 } else {
911 remove = false;
912 replace = false;
914 rtl::Reference<Node> member(members.findNode(layer, name));
915 bool insert = !member.is();
916 if (!remove) {
917 if (replace || insert) {
918 if (!node.is()) {
919 SAL_WARN("configmgr.dconf", "bad unmatched " << path);
920 continue;
922 switch (node->kind()) {
923 case Node::KIND_LOCALIZED_PROPERTY:
924 member.set(new LocalizedValueNode(layer));
925 break;
926 case Node::KIND_GROUP:
927 if (!static_cast<GroupNode *>(node.get())->isExtensible()) {
928 SAL_WARN("configmgr.dconf", "bad unmatched " << path);
929 continue;
931 member.set(
932 new PropertyNode(
933 layer, TYPE_ANY, true, css::uno::Any(), true));
934 break;
935 case Node::KIND_SET:
936 assert(!templ.isEmpty());
937 member = data.getTemplate(layer, templ);
938 if (!member.is()) {
939 SAL_WARN(
940 "configmgr.dconf",
941 "bad " << path << " denoting undefined template "
942 << templ);
943 continue;
945 member = member->clone(true);
946 break;
947 default:
948 assert(false); // cannot happen
950 } else if (!templ.isEmpty() && templ != member->getTemplateName()) {
951 SAL_WARN(
952 "configmgr.dconf",
953 "bad " << path
954 << " denoting set element of non-matching template "
955 << member->getTemplateName());
956 continue;
959 if (member.is()) {
960 if (member->getFinalized() < layer) {
961 continue;
963 switch (member->kind()) {
964 case Node::KIND_PROPERTY:
966 if (isDir) {
967 SAL_WARN(
968 "configmgr.dconf",
969 "bad dir " << path << " does not match property");
970 continue;
972 rtl::Reference<PropertyNode> prop(
973 static_cast<PropertyNode *>(member.get()));
974 css::uno::Any value;
975 switch (readValue(
976 client, path, prop->getStaticType(),
977 prop->isNillable(), prop->isExtension(),
978 &value))
980 case ReadValue::Error:
981 continue;
982 case ReadValue::Value:
983 prop->setValue(layer, value);
984 finalize(client, path, member, layer);
985 break;
986 case ReadValue::Remove:
987 remove = true;
988 break;
990 break;
992 case Node::KIND_LOCALIZED_VALUE:
994 if (isDir) {
995 SAL_WARN(
996 "configmgr.dconf",
997 "bad dir " << path
998 << " does not match localized value");
999 continue;
1001 assert(
1002 node.is()
1003 && node->kind() == Node::KIND_LOCALIZED_PROPERTY);
1004 rtl::Reference<LocalizedPropertyNode> locProp(
1005 static_cast<LocalizedPropertyNode *>(node.get()));
1006 css::uno::Any value;
1007 if (readValue(
1008 client, path, locProp->getStaticType(),
1009 locProp->isNillable(), false, &value)
1010 == ReadValue::Error)
1012 continue;
1014 static_cast<LocalizedValueNode *>(member.get())->setValue(
1015 layer, value);
1016 finalize(client, path, member, layer);
1017 break;
1019 case Node::KIND_LOCALIZED_PROPERTY:
1020 case Node::KIND_GROUP:
1021 case Node::KIND_SET:
1022 if (!isDir) {
1023 SAL_WARN(
1024 "configmgr.dconf",
1025 "bad key " << path
1026 << " does not match localized property, group, or"
1027 " set, respectively");
1028 continue;
1030 assert(path.endsWith("/"));
1031 readDir(
1032 data, layer, member, member->getMembers(), client, path);
1033 break;
1034 default:
1035 assert(false); // cannot happen
1038 if (remove) {
1039 if (!(member.is() && member->getMandatory())) {
1040 members.erase(name);
1042 } else if (replace) {
1043 members.erase(name);
1044 members.insert(NodeMap::value_type(name, member));
1045 } else if (insert) {
1046 members.insert(NodeMap::value_type(name, member));
1051 OString encodeSegment(OUString const & name, bool setElement) {
1052 if (!setElement) {
1053 return name.toUtf8();
1055 OUStringBuffer buf;
1056 for (sal_Int32 i = 0; i != name.getLength(); ++i) {
1057 sal_Unicode c = name[i];
1058 switch (c) {
1059 case '\0':
1060 buf.append("\\00");
1061 break;
1062 case '/':
1063 buf.append("\\2F");
1064 break;
1065 case '\\':
1066 buf.append("\\5C");
1067 break;
1068 default:
1069 buf.append(c);
1072 return buf.makeStringAndClear().toUtf8();
1075 OString encodeString(OUString const & value) {
1076 OUStringBuffer buf;
1077 for (sal_Int32 i = 0; i != value.getLength(); ++i) {
1078 sal_Unicode c = value[i];
1079 switch (c) {
1080 case '\0':
1081 buf.append("\\00");
1082 break;
1083 case '\\':
1084 buf.append("\\5C");
1085 break;
1086 default:
1087 buf.append(c);
1090 return buf.makeStringAndClear().toUtf8();
1093 bool addProperty(
1094 ChangesetHolder const & changeset, OString const & pathRepresentation,
1095 Type type, bool nillable, css::uno::Any const & value)
1097 Type dynType = getDynamicType(value);
1098 assert(dynType != TYPE_ERROR);
1099 if (type == TYPE_ANY) {
1100 type = dynType;
1102 GVariantHolder v;
1103 std::forward_list<GVariantHolder> children;
1104 if (dynType == TYPE_NIL) {
1105 switch (type) {
1106 case TYPE_BOOLEAN:
1107 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN, nullptr));
1108 break;
1109 case TYPE_SHORT:
1110 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16, nullptr));
1111 break;
1112 case TYPE_INT:
1113 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32, nullptr));
1114 break;
1115 case TYPE_LONG:
1116 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64, nullptr));
1117 break;
1118 case TYPE_DOUBLE:
1119 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE, nullptr));
1120 break;
1121 case TYPE_STRING:
1122 v.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING, nullptr));
1123 break;
1124 case TYPE_HEXBINARY:
1125 case TYPE_BOOLEAN_LIST:
1126 case TYPE_SHORT_LIST:
1127 case TYPE_INT_LIST:
1128 case TYPE_LONG_LIST:
1129 case TYPE_DOUBLE_LIST:
1130 case TYPE_STRING_LIST:
1131 case TYPE_HEXBINARY_LIST:
1133 static char const * const typeString[
1134 TYPE_HEXBINARY_LIST - TYPE_HEXBINARY + 1]
1135 = { "ay", "ab", "an", "ai", "ax", "ad", "as", "aay" };
1136 GVariantTypeHolder ty(
1137 g_variant_type_new(typeString[type - TYPE_HEXBINARY]));
1138 if (ty.get() == nullptr) {
1139 SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
1140 return false;
1142 v.reset(g_variant_new_maybe(ty.get(), nullptr));
1143 break;
1145 default:
1146 assert(false); // this cannot happen
1147 break;
1149 if (v.get() == nullptr) {
1150 SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1151 return false;
1153 } else {
1154 switch (type) {
1155 case TYPE_BOOLEAN:
1156 v.reset(g_variant_new_boolean(value.get<bool>()));
1157 break;
1158 case TYPE_SHORT:
1159 v.reset(g_variant_new_int16(value.get<sal_Int16>()));
1160 break;
1161 case TYPE_INT:
1162 v.reset(g_variant_new_int32(value.get<sal_Int32>()));
1163 break;
1164 case TYPE_LONG:
1165 v.reset(g_variant_new_int64(value.get<sal_Int64>()));
1166 break;
1167 case TYPE_DOUBLE:
1168 v.reset(g_variant_new_double(value.get<double>()));
1169 break;
1170 case TYPE_STRING:
1171 v.reset(
1172 g_variant_new_string(
1173 encodeString(value.get<OUString>()).getStr()));
1174 break;
1175 case TYPE_HEXBINARY:
1177 css::uno::Sequence<sal_Int8> seq(
1178 value.get<css::uno::Sequence<sal_Int8>>());
1179 static_assert(
1180 sizeof(sal_Int32) <= sizeof(gsize),
1181 "G_MAXSIZE too small");
1182 static_assert(
1183 sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
1184 v.reset(
1185 g_variant_new_fixed_array(
1186 G_VARIANT_TYPE_BYTE, seq.getConstArray(),
1187 seq.getLength(), sizeof (sal_Int8)));
1188 break;
1190 case TYPE_BOOLEAN_LIST:
1192 css::uno::Sequence<sal_Bool> seq(
1193 value.get<css::uno::Sequence<sal_Bool>>());
1194 static_assert(
1195 sizeof(sal_Int32) <= sizeof(gsize),
1196 "G_MAXSIZE too small");
1197 static_assert(sizeof (sal_Bool) == 1, "size mismatch");
1198 v.reset(
1199 g_variant_new_fixed_array(
1200 G_VARIANT_TYPE_BOOLEAN, seq.getConstArray(),
1201 seq.getLength(), sizeof (sal_Bool)));
1202 break;
1204 case TYPE_SHORT_LIST:
1206 css::uno::Sequence<sal_Int16> seq(
1207 value.get<css::uno::Sequence<sal_Int16>>());
1208 static_assert(
1209 sizeof(sal_Int32) <= sizeof(gsize),
1210 "G_MAXSIZE too small");
1211 static_assert(
1212 sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
1213 v.reset(
1214 g_variant_new_fixed_array(
1215 G_VARIANT_TYPE_INT16, seq.getConstArray(),
1216 seq.getLength(), sizeof (sal_Int16)));
1217 //TODO: endian-ness?
1218 break;
1220 case TYPE_INT_LIST:
1222 css::uno::Sequence<sal_Int32> seq(
1223 value.get<css::uno::Sequence<sal_Int32>>());
1224 static_assert(
1225 sizeof(sal_Int32) <= sizeof(gsize),
1226 "G_MAXSIZE too small");
1227 static_assert(
1228 sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
1229 v.reset(
1230 g_variant_new_fixed_array(
1231 G_VARIANT_TYPE_INT32, seq.getConstArray(),
1232 seq.getLength(), sizeof (sal_Int32)));
1233 //TODO: endian-ness?
1234 break;
1236 case TYPE_LONG_LIST:
1238 css::uno::Sequence<sal_Int64> seq(
1239 value.get<css::uno::Sequence<sal_Int64>>());
1240 static_assert(
1241 sizeof(sal_Int32) <= sizeof(gsize),
1242 "G_MAXSIZE too small");
1243 static_assert(
1244 sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
1245 v.reset(
1246 g_variant_new_fixed_array(
1247 G_VARIANT_TYPE_INT64, seq.getConstArray(),
1248 seq.getLength(), sizeof (sal_Int64)));
1249 //TODO: endian-ness?
1250 break;
1252 case TYPE_DOUBLE_LIST:
1254 css::uno::Sequence<double> seq(
1255 value.get<css::uno::Sequence<double>>());
1256 static_assert(
1257 sizeof(sal_Int32) <= sizeof(gsize),
1258 "G_MAXSIZE too small");
1259 static_assert(
1260 sizeof (double) == sizeof (gdouble), "size mismatch");
1261 v.reset(
1262 g_variant_new_fixed_array(
1263 G_VARIANT_TYPE_DOUBLE, seq.getConstArray(),
1264 seq.getLength(), sizeof (double)));
1265 //TODO: endian-ness?
1266 break;
1268 case TYPE_STRING_LIST:
1270 const css::uno::Sequence<OUString> seq(
1271 value.get<css::uno::Sequence<OUString>>());
1272 std::vector<GVariant *> vs;
1273 for (OUString const & s : seq) {
1274 children.emplace_front(
1275 g_variant_new_string(encodeString(s).getStr()));
1276 if (children.front().get() == nullptr) {
1277 SAL_WARN(
1278 "configmgr.dconf", "g_variant_new_string failed");
1279 return false;
1281 vs.push_back(children.front().get());
1283 static_assert(
1284 sizeof(sal_Int32) <= sizeof(gsize),
1285 "G_MAXSIZE too small");
1286 v.reset(
1287 g_variant_new_array(
1288 G_VARIANT_TYPE_STRING, vs.data(), seq.getLength()));
1289 break;
1291 case TYPE_HEXBINARY_LIST:
1293 const css::uno::Sequence<css::uno::Sequence<sal_Int8>> seqSeq(
1294 value.get<
1295 css::uno::Sequence<css::uno::Sequence<sal_Int8>>>());
1296 std::vector<GVariant *> vs;
1297 for (css::uno::Sequence<sal_Int8> const & seq : seqSeq) {
1298 static_assert(
1299 sizeof(sal_Int32) <= sizeof(gsize),
1300 "G_MAXSIZE too small");
1301 static_assert(
1302 sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
1303 children.emplace_front(
1304 g_variant_new_fixed_array(
1305 G_VARIANT_TYPE_BYTE, seq.getConstArray(),
1306 seq.getLength(), sizeof (sal_Int8)));
1307 if (children.front().get() == nullptr) {
1308 SAL_WARN(
1309 "configmgr.dconf",
1310 "g_variant_new_fixed_array failed");
1311 return false;
1313 vs.push_back(children.front().get());
1315 GVariantTypeHolder ty(g_variant_type_new("aay"));
1316 if (ty.get() == nullptr) {
1317 SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
1318 return false;
1320 static_assert(
1321 sizeof(sal_Int32) <= sizeof(gsize),
1322 "G_MAXSIZE too small");
1323 v.reset(
1324 g_variant_new_array(ty.get(), vs.data(), seqSeq.getLength()));
1325 break;
1327 default:
1328 assert(false); // this cannot happen
1329 break;
1331 if (v.get() == nullptr) {
1332 SAL_WARN("configmgr.dconf", "GVariant creation failed");
1333 return false;
1335 if (nillable) {
1336 GVariantHolder v1(g_variant_new_maybe(nullptr, v.get()));
1337 if (v1.get() == nullptr) {
1338 SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1339 return false;
1341 v.release();
1342 v.reset(v1.get());
1343 v1.release();
1346 dconf_changeset_set(
1347 changeset.get(), pathRepresentation.getStr(), v.get());
1348 for (auto & i: children) {
1349 i.release();
1351 v.release();
1352 return true;
1355 bool addNode(
1356 Components & components, ChangesetHolder const & changeset,
1357 rtl::Reference<Node> const & parent, OString const & pathRepresentation,
1358 rtl::Reference<Node> const & node)
1360 switch (node->kind()) {
1361 case Node::KIND_PROPERTY:
1363 PropertyNode * prop = static_cast<PropertyNode *>(node.get());
1364 if (!addProperty(
1365 changeset, pathRepresentation, prop->getStaticType(),
1366 prop->isNillable(), prop->getValue(components)))
1368 return false;
1370 break;
1372 case Node::KIND_LOCALIZED_VALUE:
1374 //TODO: name.isEmpty()?
1375 LocalizedPropertyNode * locprop
1376 = static_cast<LocalizedPropertyNode *>(parent.get());
1377 if (!addProperty(
1378 changeset, pathRepresentation,
1379 locprop->getStaticType(), locprop->isNillable(),
1380 static_cast<LocalizedValueNode *>(node.get())->getValue()))
1382 return false;
1384 break;
1386 case Node::KIND_LOCALIZED_PROPERTY:
1387 case Node::KIND_GROUP:
1388 case Node::KIND_SET:
1389 for (auto const & i: node->getMembers()) {
1390 OUString templ(i.second->getTemplateName());
1391 OString path(
1392 pathRepresentation + "/"
1393 + encodeSegment(i.first, !templ.isEmpty()));
1394 if (!templ.isEmpty()) {
1395 path += "/";
1396 GVariantHolder v(g_variant_new_string("replace"));
1397 if (v.get() == nullptr) {
1398 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1399 return false;
1401 dconf_changeset_set(
1402 changeset.get(), OString(path + "op").getStr(), v.get());
1403 v.release();
1404 v.reset(g_variant_new_string(encodeString(templ).getStr()));
1405 if (v.get() == nullptr) {
1406 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1407 return false;
1409 dconf_changeset_set(
1410 changeset.get(), OString(path + "template").getStr(),
1411 v.get());
1412 v.release();
1413 path += "content";
1415 if (!addNode(components, changeset, parent, path, i.second)) {
1416 return false;
1419 break;
1420 case Node::KIND_ROOT:
1421 assert(false); // this cannot happen
1422 break;
1424 return true;
1427 bool addModifications(
1428 Components & components, ChangesetHolder const & changeset,
1429 OString const & parentPathRepresentation,
1430 rtl::Reference<Node> const & parent, OUString const & nodeName,
1431 rtl::Reference<Node> const & node,
1432 Modifications::Node const & modifications)
1434 // It is never necessary to write oor:finalized or oor:mandatory attributes,
1435 // as they cannot be set via the UNO API.
1436 if (modifications.children.empty()) {
1437 assert(parent.is());
1438 // components themselves have no parent but must have children
1439 if (node.is()) {
1440 OUString templ(node->getTemplateName());
1441 OString path(
1442 parentPathRepresentation + "/"
1443 + encodeSegment(nodeName, !templ.isEmpty()));
1444 if (!templ.isEmpty()) {
1445 path += "/";
1446 GVariantHolder v(g_variant_new_string("replace"));
1447 if (v.get() == nullptr) {
1448 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1449 return false;
1451 dconf_changeset_set(
1452 changeset.get(), OString(path + "op").getStr(), v.get());
1453 v.release();
1454 v.reset(g_variant_new_string(encodeString(templ).getStr()));
1455 if (v.get() == nullptr) {
1456 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1457 return false;
1459 dconf_changeset_set(
1460 changeset.get(), OString(path + "template").getStr(),
1461 v.get());
1462 v.release();
1463 path += "content";
1465 if (!addNode(components, changeset, parent, path, node)) {
1466 return false;
1468 } else {
1469 switch (parent->kind()) {
1470 case Node::KIND_LOCALIZED_PROPERTY:
1471 case Node::KIND_GROUP:
1473 GVariantHolder v(g_variant_new_tuple(nullptr, 0));
1474 if (v.get() == nullptr) {
1475 SAL_WARN(
1476 "configmgr.dconf", "g_variant_new_tuple failed");
1477 return false;
1479 OString path(parentPathRepresentation);
1480 if (!nodeName.isEmpty()) { // KIND_LOCALIZED_PROPERTY
1481 path += "/" + encodeSegment(nodeName, false);
1483 dconf_changeset_set(
1484 changeset.get(), path.getStr(), v.get());
1485 v.release();
1486 break;
1488 case Node::KIND_SET:
1490 OString path(
1491 parentPathRepresentation + "/"
1492 + encodeSegment(nodeName, true) + "/");
1493 GVariantHolder v(g_variant_new_string("remove"));
1494 if (v.get() == nullptr) {
1495 SAL_WARN(
1496 "configmgr.dconf", "g_variant_new_string failed");
1497 return false;
1499 dconf_changeset_set(
1500 changeset.get(), OString(path + "op").getStr(),
1501 v.get());
1502 v.release();
1503 dconf_changeset_set(
1504 changeset.get(), OString(path + "template").getStr(),
1505 nullptr);
1506 dconf_changeset_set(
1507 changeset.get(), OString(path + "content/").getStr(),
1508 nullptr);
1509 break;
1511 default:
1512 assert(false); // this cannot happen
1513 break;
1516 } else {
1517 assert(node.is());
1518 OUString templ(node->getTemplateName());
1519 OString path(
1520 parentPathRepresentation + "/"
1521 + encodeSegment(nodeName, !templ.isEmpty()));
1522 if (!templ.isEmpty()) {
1523 path += "/";
1524 GVariantHolder v(g_variant_new_string("fuse"));
1525 if (v.get() == nullptr) {
1526 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1527 return false;
1529 dconf_changeset_set(
1530 changeset.get(), OString(path + "op").getStr(), v.get());
1531 v.release();
1532 v.reset(g_variant_new_string(encodeString(templ).getStr()));
1533 if (v.get() == nullptr) {
1534 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1535 return false;
1537 dconf_changeset_set(
1538 changeset.get(), OString(path + "template").getStr(), v.get());
1539 v.release();
1540 path += "content";
1542 for (auto const & i: modifications.children) {
1543 if (!addModifications(
1544 components, changeset, path, node, i.first,
1545 node->getMember(i.first), i.second))
1547 return false;
1551 return true;
1556 void readLayer(Data & data, int layer) {
1557 GObjectHolder<DConfClient> client(dconf_client_new());
1558 if (client.get() == nullptr) {
1559 SAL_WARN("configmgr.dconf", "dconf_client_new failed");
1560 return;
1562 readDir(
1563 data, layer, rtl::Reference<Node>(), data.getComponents(), client,
1564 getRoot() + "/");
1567 void writeModifications(Components & components, Data & data) {
1568 GObjectHolder<DConfClient> client(dconf_client_new());
1569 if (client.get() == nullptr) {
1570 SAL_WARN("configmgr.dconf", "dconf_client_new failed");
1572 ChangesetHolder cs(dconf_changeset_new());
1573 if (cs.get() == nullptr) {
1574 SAL_WARN("configmgr.dconf", "dconf_changeset_new failed");
1575 return;
1577 for (auto const & i: data.modifications.getRoot().children) {
1578 if (!addModifications(
1579 components, cs, getRoot(), rtl::Reference<Node>(), i.first,
1580 data.getComponents().findNode(Data::NO_LAYER, i.first),
1581 i.second))
1583 return;
1586 if (!dconf_client_change_sync(
1587 client.get(), cs.get(), nullptr, nullptr, nullptr))
1589 //TODO: GError
1590 SAL_WARN("configmgr.dconf", "dconf_client_change_sync failed");
1591 return;
1593 data.modifications.clear();
1598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */