1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include <sal/config.h>
15 #include <forward_list>
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>
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
{
121 template<typename T
> class GObjectHolder
{
123 explicit GObjectHolder(T
* object
): object_(object
) {}
126 if (object_
!= nullptr) {
127 g_object_unref(object_
);
131 T
* get() const { return object_
; }
134 GObjectHolder(GObjectHolder
const &) = delete;
135 GObjectHolder
& operator =(GObjectHolder
const &) = delete;
140 class GVariantHolder
{
142 explicit GVariantHolder(GVariant
* variant
= nullptr): variant_(variant
) {}
144 ~GVariantHolder() { unref(); }
146 void reset(GVariant
* variant
) {
151 void release() { variant_
= nullptr; }
153 GVariant
* get() const { return variant_
; }
156 GVariantHolder(GVariantHolder
const &) = delete;
157 GVariantHolder
& operator =(GVariantHolder
const &) = delete;
160 if (variant_
!= nullptr) {
161 g_variant_unref(variant_
);
168 class GVariantTypeHolder
{
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_
; }
181 GVariantTypeHolder(GVariantTypeHolder
const &) = delete;
182 GVariantTypeHolder
& operator =(GVariantTypeHolder
const &) = delete;
184 GVariantType
* type_
;
187 class StringArrayHolder
{
189 explicit StringArrayHolder(gchar
** array
): array_(array
) {}
191 ~StringArrayHolder() { g_strfreev(array_
); }
193 gchar
** get() const { return array_
; }
196 StringArrayHolder(StringArrayHolder
const &) = delete;
197 StringArrayHolder
& operator =(StringArrayHolder
const &) = delete;
202 class ChangesetHolder
{
204 explicit ChangesetHolder(DConfChangeset
* changeset
):
205 changeset_(changeset
)
209 if (changeset_
!= nullptr) {
210 dconf_changeset_unref(changeset_
);
214 DConfChangeset
* get() const { return changeset_
; }
217 ChangesetHolder(ChangesetHolder
const &) = delete;
218 ChangesetHolder
& operator =(ChangesetHolder
const &) = delete;
220 DConfChangeset
* changeset_
;
224 return "/org/libreoffice/registry";
227 bool decode(OUString
* string
, bool slash
) {
228 for (sal_Int32 i
= 0;; ++i
) {
229 i
= string
->indexOf('\\', i
);
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, "");
240 SAL_WARN("configmgr.dconf", "bad escape in " << *string
);
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
)) {
253 "bad key " << key
<< " does not match boolean property");
256 *value
<<= bool(g_variant_get_boolean(variant
.get()));
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
)) {
267 "bad key " << key
<< " does not match short property");
270 *value
<<= sal_Int16(g_variant_get_int16(variant
.get()));
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
)) {
281 "bad key " << key
<< " does not match int property");
284 *value
<<= sal_Int32(g_variant_get_int32(variant
.get()));
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
)) {
295 "bad key " << key
<< " does not match long property");
298 *value
<<= sal_Int64(g_variant_get_int64(variant
.get()));
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
)) {
309 "bad key " << key
<< " does not match double property");
312 *value
<<= double(g_variant_get_double(variant
.get()));
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
)) {
323 "bad key " << key
<< " does not match string property");
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
);
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
);
343 return decode(value
, false);
347 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
349 assert(value
!= nullptr);
351 if (!getStringValue(key
, variant
, &v
)) {
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) {
366 "bad key " << key
<< " does not match hexbinary property");
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
);
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
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
)) {
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) {
404 "bad key " << key
<< " does not match boolean list property");
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
);
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
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) {
431 "bad key " << key
<< " does not match short list property");
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
);
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
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) {
458 "bad key " << key
<< " does not match int list property");
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
);
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
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) {
485 "bad key " << key
<< " does not match long list property");
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
);
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
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) {
512 "bad key " << key
<< " does not match double list property");
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
);
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
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) {
539 "bad key " << key
<< " does not match string list property");
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
);
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
)) {
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) {
567 "bad key " << key
<< " does not match hexbinary list property");
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
);
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
)) {
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
);
636 "configmgr.dconf", "bad key " << key
<< " does not match any property");
640 enum class ReadValue
{ Error
, Value
, Remove
};
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
;
660 if (g_variant_classify(v
.get()) != G_VARIANT_CLASS_MAYBE
) {
663 "bad key " << path
<< " does not match nillable property");
665 v
.reset(g_variant_get_maybe(v
.get()));
666 nil
= v
.get() == nullptr;
673 if (!getAny(path
, v
, value
)) {
674 return ReadValue::Error
;
678 if (!getBoolean(path
, v
, value
)) {
679 return ReadValue::Error
;
683 if (!getShort(path
, v
, value
)) {
684 return ReadValue::Error
;
688 if (!getInt(path
, v
, value
)) {
689 return ReadValue::Error
;
693 if (!getLong(path
, v
, value
)) {
694 return ReadValue::Error
;
698 if (!getDouble(path
, v
, value
)) {
699 return ReadValue::Error
;
703 if (!getString(path
, v
, value
)) {
704 return ReadValue::Error
;
708 if (!getHexbinary(path
, v
, value
)) {
709 return ReadValue::Error
;
712 case TYPE_BOOLEAN_LIST
:
713 if (!getBooleanList(path
, v
, value
)) {
714 return ReadValue::Error
;
717 case TYPE_SHORT_LIST
:
718 if (!getShortList(path
, v
, value
)) {
719 return ReadValue::Error
;
723 if (!getIntList(path
, v
, value
)) {
724 return ReadValue::Error
;
728 if (!getLongList(path
, v
, value
)) {
729 return ReadValue::Error
;
732 case TYPE_DOUBLE_LIST
:
733 if (!getDoubleList(path
, v
, value
)) {
734 return ReadValue::Error
;
737 case TYPE_STRING_LIST
:
738 if (!getStringList(path
, v
, value
)) {
739 return ReadValue::Error
;
742 case TYPE_HEXBINARY_LIST
:
743 if (!getHexbinaryList(path
, v
, value
)) {
744 return ReadValue::Error
;
748 assert(false); // cannot happen
751 return ReadValue::Value
;
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
);
764 Data
& data
, int layer
, rtl::Reference
<Node
> const & node
,
765 NodeMap
& members
, GObjectHolder
<DConfClient
> const & client
,
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
);
777 OString
s(*p
, static_cast<sal_Int32
>(n
));
778 OString
path(dir
+ s
);
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
);
789 bool isDir
= name
.endsWith("/", &name
);
793 if (node
.is() && node
->kind() == Node::KIND_SET
) {
797 "bad key " << path
<< " does not match set element");
800 if (!decode(&name
, true)) {
803 enum class Op
{ None
, Fuse
, Replace
, Remove
};
805 bool content
= 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");
813 dconf_client_read(client
.get(), path2
.getStr()));
814 if (v
.get() == nullptr) {
816 "configmgr.dconf", "cannot read key " << path2
);
821 if (!getStringValue(path2
, v
, &ops
)) {
827 } else if (ops
== "replace") {
829 } else if (ops
== "remove") {
834 "bad key " << path2
<< " value " << ops
);
838 } else if (std::strcmp(*p2
, "template") == 0) {
839 OString
path2(path
+ "template");
841 dconf_client_read(client
.get(), path2
.getStr()));
842 if (v
.get() == nullptr) {
844 "configmgr.dconf", "cannot read key " << path2
);
848 if (!getStringValue(path2
, v
, &templ
)) {
852 if (!static_cast<SetNode
*>(node
.get())->
853 isValidTemplate(templ
))
857 "bad key " << path2
<< " value " << templ
858 << " denotes unsupported set element template");
862 } else if (std::strcmp(*p2
, "content/") == 0) {
868 << " in set element indirection dir " << path
);
877 default: // case Op::None:
880 "bad set element indirection dir " << path
881 << " missing \"op\" key");
885 if (templ
.isEmpty() || !content
) {
888 "missing \"content\" and/or \"template\" dir/key in "
889 "\"op\" = \"fuse\"/\"remove\" set element"
890 " indirection dir " << path
);
895 replace
= op
== Op::Replace
;
898 if (!templ
.isEmpty() || content
) {
901 "bad \"content\" and/or \"template\" dir/key in \"op\" "
902 "= \"remove\" set element indirection dir "
914 rtl::Reference
<Node
> member(members
.findNode(layer
, name
));
915 bool insert
= !member
.is();
917 if (replace
|| insert
) {
919 SAL_WARN("configmgr.dconf", "bad unmatched " << path
);
922 switch (node
->kind()) {
923 case Node::KIND_LOCALIZED_PROPERTY
:
924 member
.set(new LocalizedValueNode(layer
));
926 case Node::KIND_GROUP
:
927 if (!static_cast<GroupNode
*>(node
.get())->isExtensible()) {
928 SAL_WARN("configmgr.dconf", "bad unmatched " << path
);
933 layer
, TYPE_ANY
, true, css::uno::Any(), true));
936 assert(!templ
.isEmpty());
937 member
= data
.getTemplate(layer
, templ
);
941 "bad " << path
<< " denoting undefined template "
945 member
= member
->clone(true);
948 assert(false); // cannot happen
950 } else if (!templ
.isEmpty() && templ
!= member
->getTemplateName()) {
954 << " denoting set element of non-matching template "
955 << member
->getTemplateName());
960 if (member
->getFinalized() < layer
) {
963 switch (member
->kind()) {
964 case Node::KIND_PROPERTY
:
969 "bad dir " << path
<< " does not match property");
972 rtl::Reference
<PropertyNode
> prop(
973 static_cast<PropertyNode
*>(member
.get()));
976 client
, path
, prop
->getStaticType(),
977 prop
->isNillable(), prop
->isExtension(),
980 case ReadValue::Error
:
982 case ReadValue::Value
:
983 prop
->setValue(layer
, value
);
984 finalize(client
, path
, member
, layer
);
986 case ReadValue::Remove
:
992 case Node::KIND_LOCALIZED_VALUE
:
998 << " does not match localized value");
1003 && node
->kind() == Node::KIND_LOCALIZED_PROPERTY
);
1004 rtl::Reference
<LocalizedPropertyNode
> locProp(
1005 static_cast<LocalizedPropertyNode
*>(node
.get()));
1006 css::uno::Any value
;
1008 client
, path
, locProp
->getStaticType(),
1009 locProp
->isNillable(), false, &value
)
1010 == ReadValue::Error
)
1014 static_cast<LocalizedValueNode
*>(member
.get())->setValue(
1016 finalize(client
, path
, member
, layer
);
1019 case Node::KIND_LOCALIZED_PROPERTY
:
1020 case Node::KIND_GROUP
:
1021 case Node::KIND_SET
:
1026 << " does not match localized property, group, or"
1027 " set, respectively");
1030 assert(path
.endsWith("/"));
1032 data
, layer
, member
, member
->getMembers(), client
, path
);
1035 assert(false); // cannot happen
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
) {
1053 return name
.toUtf8();
1056 for (sal_Int32 i
= 0; i
!= name
.getLength(); ++i
) {
1057 sal_Unicode c
= name
[i
];
1072 return buf
.makeStringAndClear().toUtf8();
1075 OString
encodeString(OUString
const & value
) {
1077 for (sal_Int32 i
= 0; i
!= value
.getLength(); ++i
) {
1078 sal_Unicode c
= value
[i
];
1090 return buf
.makeStringAndClear().toUtf8();
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
) {
1103 std::forward_list
<GVariantHolder
> children
;
1104 if (dynType
== TYPE_NIL
) {
1107 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN
, nullptr));
1110 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16
, nullptr));
1113 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32
, nullptr));
1116 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64
, nullptr));
1119 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE
, nullptr));
1122 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING
, nullptr));
1124 case TYPE_HEXBINARY
:
1125 case TYPE_BOOLEAN_LIST
:
1126 case TYPE_SHORT_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");
1142 v
.reset(g_variant_new_maybe(ty
.get(), nullptr));
1146 assert(false); // this cannot happen
1149 if (v
.get() == nullptr) {
1150 SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1156 v
.reset(g_variant_new_boolean(value
.get
<bool>()));
1159 v
.reset(g_variant_new_int16(value
.get
<sal_Int16
>()));
1162 v
.reset(g_variant_new_int32(value
.get
<sal_Int32
>()));
1165 v
.reset(g_variant_new_int64(value
.get
<sal_Int64
>()));
1168 v
.reset(g_variant_new_double(value
.get
<double>()));
1172 g_variant_new_string(
1173 encodeString(value
.get
<OUString
>()).getStr()));
1175 case TYPE_HEXBINARY
:
1177 css::uno::Sequence
<sal_Int8
> seq(
1178 value
.get
<css::uno::Sequence
<sal_Int8
>>());
1180 sizeof(sal_Int32
) <= sizeof(gsize
),
1181 "G_MAXSIZE too small");
1183 sizeof (sal_Int8
) == sizeof (guchar
), "size mismatch");
1185 g_variant_new_fixed_array(
1186 G_VARIANT_TYPE_BYTE
, seq
.getConstArray(),
1187 seq
.getLength(), sizeof (sal_Int8
)));
1190 case TYPE_BOOLEAN_LIST
:
1192 css::uno::Sequence
<sal_Bool
> seq(
1193 value
.get
<css::uno::Sequence
<sal_Bool
>>());
1195 sizeof(sal_Int32
) <= sizeof(gsize
),
1196 "G_MAXSIZE too small");
1197 static_assert(sizeof (sal_Bool
) == 1, "size mismatch");
1199 g_variant_new_fixed_array(
1200 G_VARIANT_TYPE_BOOLEAN
, seq
.getConstArray(),
1201 seq
.getLength(), sizeof (sal_Bool
)));
1204 case TYPE_SHORT_LIST
:
1206 css::uno::Sequence
<sal_Int16
> seq(
1207 value
.get
<css::uno::Sequence
<sal_Int16
>>());
1209 sizeof(sal_Int32
) <= sizeof(gsize
),
1210 "G_MAXSIZE too small");
1212 sizeof (sal_Int16
) == sizeof (gint16
), "size mismatch");
1214 g_variant_new_fixed_array(
1215 G_VARIANT_TYPE_INT16
, seq
.getConstArray(),
1216 seq
.getLength(), sizeof (sal_Int16
)));
1217 //TODO: endian-ness?
1222 css::uno::Sequence
<sal_Int32
> seq(
1223 value
.get
<css::uno::Sequence
<sal_Int32
>>());
1225 sizeof(sal_Int32
) <= sizeof(gsize
),
1226 "G_MAXSIZE too small");
1228 sizeof (sal_Int32
) == sizeof (gint32
), "size mismatch");
1230 g_variant_new_fixed_array(
1231 G_VARIANT_TYPE_INT32
, seq
.getConstArray(),
1232 seq
.getLength(), sizeof (sal_Int32
)));
1233 //TODO: endian-ness?
1236 case TYPE_LONG_LIST
:
1238 css::uno::Sequence
<sal_Int64
> seq(
1239 value
.get
<css::uno::Sequence
<sal_Int64
>>());
1241 sizeof(sal_Int32
) <= sizeof(gsize
),
1242 "G_MAXSIZE too small");
1244 sizeof (sal_Int64
) == sizeof (gint64
), "size mismatch");
1246 g_variant_new_fixed_array(
1247 G_VARIANT_TYPE_INT64
, seq
.getConstArray(),
1248 seq
.getLength(), sizeof (sal_Int64
)));
1249 //TODO: endian-ness?
1252 case TYPE_DOUBLE_LIST
:
1254 css::uno::Sequence
<double> seq(
1255 value
.get
<css::uno::Sequence
<double>>());
1257 sizeof(sal_Int32
) <= sizeof(gsize
),
1258 "G_MAXSIZE too small");
1260 sizeof (double) == sizeof (gdouble
), "size mismatch");
1262 g_variant_new_fixed_array(
1263 G_VARIANT_TYPE_DOUBLE
, seq
.getConstArray(),
1264 seq
.getLength(), sizeof (double)));
1265 //TODO: endian-ness?
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) {
1278 "configmgr.dconf", "g_variant_new_string failed");
1281 vs
.push_back(children
.front().get());
1284 sizeof(sal_Int32
) <= sizeof(gsize
),
1285 "G_MAXSIZE too small");
1287 g_variant_new_array(
1288 G_VARIANT_TYPE_STRING
, vs
.data(), seq
.getLength()));
1291 case TYPE_HEXBINARY_LIST
:
1293 const css::uno::Sequence
<css::uno::Sequence
<sal_Int8
>> seqSeq(
1295 css::uno::Sequence
<css::uno::Sequence
<sal_Int8
>>>());
1296 std::vector
<GVariant
*> vs
;
1297 for (css::uno::Sequence
<sal_Int8
> const & seq
: seqSeq
) {
1299 sizeof(sal_Int32
) <= sizeof(gsize
),
1300 "G_MAXSIZE too small");
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) {
1310 "g_variant_new_fixed_array failed");
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");
1321 sizeof(sal_Int32
) <= sizeof(gsize
),
1322 "G_MAXSIZE too small");
1324 g_variant_new_array(ty
.get(), vs
.data(), seqSeq
.getLength()));
1328 assert(false); // this cannot happen
1331 if (v
.get() == nullptr) {
1332 SAL_WARN("configmgr.dconf", "GVariant creation failed");
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");
1346 dconf_changeset_set(
1347 changeset
.get(), pathRepresentation
.getStr(), v
.get());
1348 for (auto & i
: children
) {
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());
1365 changeset
, pathRepresentation
, prop
->getStaticType(),
1366 prop
->isNillable(), prop
->getValue(components
)))
1372 case Node::KIND_LOCALIZED_VALUE
:
1374 //TODO: name.isEmpty()?
1375 LocalizedPropertyNode
* locprop
1376 = static_cast<LocalizedPropertyNode
*>(parent
.get());
1378 changeset
, pathRepresentation
,
1379 locprop
->getStaticType(), locprop
->isNillable(),
1380 static_cast<LocalizedValueNode
*>(node
.get())->getValue()))
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());
1392 pathRepresentation
+ "/"
1393 + encodeSegment(i
.first
, !templ
.isEmpty()));
1394 if (!templ
.isEmpty()) {
1396 GVariantHolder
v(g_variant_new_string("replace"));
1397 if (v
.get() == nullptr) {
1398 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1401 dconf_changeset_set(
1402 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
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");
1409 dconf_changeset_set(
1410 changeset
.get(), OString(path
+ "template").getStr(),
1415 if (!addNode(components
, changeset
, parent
, path
, i
.second
)) {
1420 case Node::KIND_ROOT
:
1421 assert(false); // this cannot happen
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
1440 OUString
templ(node
->getTemplateName());
1442 parentPathRepresentation
+ "/"
1443 + encodeSegment(nodeName
, !templ
.isEmpty()));
1444 if (!templ
.isEmpty()) {
1446 GVariantHolder
v(g_variant_new_string("replace"));
1447 if (v
.get() == nullptr) {
1448 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1451 dconf_changeset_set(
1452 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
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");
1459 dconf_changeset_set(
1460 changeset
.get(), OString(path
+ "template").getStr(),
1465 if (!addNode(components
, changeset
, parent
, path
, node
)) {
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) {
1476 "configmgr.dconf", "g_variant_new_tuple failed");
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());
1488 case Node::KIND_SET
:
1491 parentPathRepresentation
+ "/"
1492 + encodeSegment(nodeName
, true) + "/");
1493 GVariantHolder
v(g_variant_new_string("remove"));
1494 if (v
.get() == nullptr) {
1496 "configmgr.dconf", "g_variant_new_string failed");
1499 dconf_changeset_set(
1500 changeset
.get(), OString(path
+ "op").getStr(),
1503 dconf_changeset_set(
1504 changeset
.get(), OString(path
+ "template").getStr(),
1506 dconf_changeset_set(
1507 changeset
.get(), OString(path
+ "content/").getStr(),
1512 assert(false); // this cannot happen
1518 OUString
templ(node
->getTemplateName());
1520 parentPathRepresentation
+ "/"
1521 + encodeSegment(nodeName
, !templ
.isEmpty()));
1522 if (!templ
.isEmpty()) {
1524 GVariantHolder
v(g_variant_new_string("fuse"));
1525 if (v
.get() == nullptr) {
1526 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1529 dconf_changeset_set(
1530 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
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");
1537 dconf_changeset_set(
1538 changeset
.get(), OString(path
+ "template").getStr(), v
.get());
1542 for (auto const & i
: modifications
.children
) {
1543 if (!addModifications(
1544 components
, changeset
, path
, node
, i
.first
,
1545 node
->getMember(i
.first
), i
.second
))
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");
1563 data
, layer
, rtl::Reference
<Node
>(), data
.getComponents(), client
,
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");
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
),
1586 if (!dconf_client_change_sync(
1587 client
.get(), cs
.get(), nullptr, nullptr, nullptr))
1590 SAL_WARN("configmgr.dconf", "dconf_client_change_sync failed");
1593 data
.modifications
.clear();
1598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */