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>
17 #include <type_traits>
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>
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
{
118 template<typename T
> class GObjectHolder
{
120 explicit GObjectHolder(T
* object
): object_(object
) {}
123 if (object_
!= nullptr) {
124 g_object_unref(object_
);
128 T
* get() const { return object_
; }
131 GObjectHolder(GObjectHolder
const &) = delete;
132 GObjectHolder
& operator =(GObjectHolder
const &) = delete;
137 class GVariantHolder
{
139 explicit GVariantHolder(GVariant
* variant
= nullptr): variant_(variant
) {}
141 ~GVariantHolder() { unref(); }
143 void reset(GVariant
* variant
) {
148 void release() { variant_
= nullptr; }
150 GVariant
* get() const { return variant_
; }
153 GVariantHolder(GVariantHolder
const &) = delete;
154 GVariantHolder
& operator =(GVariantHolder
const &) = delete;
157 if (variant_
!= nullptr) {
158 g_variant_unref(variant_
);
165 class GVariantTypeHolder
{
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_
; }
178 GVariantTypeHolder(GVariantTypeHolder
const &) = delete;
179 GVariantTypeHolder
& operator =(GVariantTypeHolder
const &) = delete;
181 GVariantType
* type_
;
184 class StringArrayHolder
{
186 explicit StringArrayHolder(gchar
** array
): array_(array
) {}
188 ~StringArrayHolder() { g_strfreev(array_
); }
190 gchar
** get() const { return array_
; }
193 StringArrayHolder(StringArrayHolder
const &) = delete;
194 StringArrayHolder
& operator =(StringArrayHolder
const &) = delete;
199 class ChangesetHolder
{
201 explicit ChangesetHolder(DConfChangeset
* changeset
):
202 changeset_(changeset
)
206 if (changeset_
!= nullptr) {
207 dconf_changeset_unref(changeset_
);
211 DConfChangeset
* get() const { return changeset_
; }
214 ChangesetHolder(ChangesetHolder
const &) = delete;
215 ChangesetHolder
& operator =(ChangesetHolder
const &) = delete;
217 DConfChangeset
* changeset_
;
221 return "/org/libreoffice/registry";
224 bool decode(OUString
* string
, bool slash
) {
225 for (sal_Int32 i
= 0;; ++i
) {
226 i
= string
->indexOf('\\', i
);
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, "");
237 SAL_WARN("configmgr.dconf", "bad escape in " << *string
);
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
)) {
250 "bad key " << key
<< " does not match boolean property");
253 *value
<<= bool(g_variant_get_boolean(variant
.get()));
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
)) {
264 "bad key " << key
<< " does not match short property");
267 *value
<<= sal_Int16(g_variant_get_int16(variant
.get()));
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
)) {
278 "bad key " << key
<< " does not match int property");
281 *value
<<= sal_Int32(g_variant_get_int32(variant
.get()));
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
)) {
292 "bad key " << key
<< " does not match long property");
295 *value
<<= sal_Int64(g_variant_get_int64(variant
.get()));
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
)) {
306 "bad key " << key
<< " does not match double property");
309 *value
<<= double(g_variant_get_double(variant
.get()));
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
)) {
320 "bad key " << key
<< " does not match string property");
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
);
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
);
340 return decode(value
, false);
344 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
346 assert(value
!= nullptr);
348 if (!getStringValue(key
, variant
, &v
)) {
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) {
363 "bad key " << key
<< " does not match hexbinary property");
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
);
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
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
)) {
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) {
401 "bad key " << key
<< " does not match boolean list property");
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
);
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
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) {
428 "bad key " << key
<< " does not match short list property");
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
);
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
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) {
455 "bad key " << key
<< " does not match int list property");
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
);
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
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) {
482 "bad key " << key
<< " does not match long list property");
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
);
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
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) {
509 "bad key " << key
<< " does not match double list property");
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
);
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
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) {
536 "bad key " << key
<< " does not match string list property");
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
);
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
)) {
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) {
564 "bad key " << key
<< " does not match hexbinary list property");
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
);
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
)) {
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
);
633 "configmgr.dconf", "bad key " << key
<< " does not match any property");
637 enum class ReadValue
{ Error
, Value
, Remove
};
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
;
657 if (g_variant_classify(v
.get()) != G_VARIANT_CLASS_MAYBE
) {
660 "bad key " << path
<< " does not match nillable property");
662 v
.reset(g_variant_get_maybe(v
.get()));
663 nil
= v
.get() == nullptr;
670 if (!getAny(path
, v
, value
)) {
671 return ReadValue::Error
;
675 if (!getBoolean(path
, v
, value
)) {
676 return ReadValue::Error
;
680 if (!getShort(path
, v
, value
)) {
681 return ReadValue::Error
;
685 if (!getInt(path
, v
, value
)) {
686 return ReadValue::Error
;
690 if (!getLong(path
, v
, value
)) {
691 return ReadValue::Error
;
695 if (!getDouble(path
, v
, value
)) {
696 return ReadValue::Error
;
700 if (!getString(path
, v
, value
)) {
701 return ReadValue::Error
;
705 if (!getHexbinary(path
, v
, value
)) {
706 return ReadValue::Error
;
709 case TYPE_BOOLEAN_LIST
:
710 if (!getBooleanList(path
, v
, value
)) {
711 return ReadValue::Error
;
714 case TYPE_SHORT_LIST
:
715 if (!getShortList(path
, v
, value
)) {
716 return ReadValue::Error
;
720 if (!getIntList(path
, v
, value
)) {
721 return ReadValue::Error
;
725 if (!getLongList(path
, v
, value
)) {
726 return ReadValue::Error
;
729 case TYPE_DOUBLE_LIST
:
730 if (!getDoubleList(path
, v
, value
)) {
731 return ReadValue::Error
;
734 case TYPE_STRING_LIST
:
735 if (!getStringList(path
, v
, value
)) {
736 return ReadValue::Error
;
739 case TYPE_HEXBINARY_LIST
:
740 if (!getHexbinaryList(path
, v
, value
)) {
741 return ReadValue::Error
;
745 assert(false); // cannot happen
748 return ReadValue::Value
;
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
);
761 Data
& data
, int layer
, rtl::Reference
<Node
> const & node
,
762 NodeMap
& members
, GObjectHolder
<DConfClient
> const & client
,
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
);
774 OString
s(*p
, static_cast<sal_Int32
>(n
));
775 OString
path(dir
+ s
);
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
);
786 bool isDir
= name
.endsWith("/", &name
);
790 if (node
.is() && node
->kind() == Node::KIND_SET
) {
794 "bad key " << path
<< " does not match set element");
797 if (!decode(&name
, true)) {
800 enum class Op
{ None
, Fuse
, Replace
, Remove
};
802 bool content
= 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");
810 dconf_client_read(client
.get(), path2
.getStr()));
811 if (v
.get() == nullptr) {
813 "configmgr.dconf", "cannot read key " << path2
);
818 if (!getStringValue(path2
, v
, &ops
)) {
824 } else if (ops
== "replace") {
826 } else if (ops
== "remove") {
831 "bad key " << path2
<< " value " << ops
);
835 } else if (std::strcmp(*p2
, "template") == 0) {
836 OString
path2(path
+ "template");
838 dconf_client_read(client
.get(), path2
.getStr()));
839 if (v
.get() == nullptr) {
841 "configmgr.dconf", "cannot read key " << path2
);
845 if (!getStringValue(path2
, v
, &templ
)) {
849 if (!static_cast<SetNode
*>(node
.get())->
850 isValidTemplate(templ
))
854 "bad key " << path2
<< " value " << templ
855 << " denotes unsupported set element template");
859 } else if (std::strcmp(*p2
, "content/") == 0) {
865 << " in set element indirection dir " << path
);
874 default: // case Op::None:
877 "bad set element indirection dir " << path
878 << " missing \"op\" key");
882 if (templ
.isEmpty() || !content
) {
885 "missing \"content\" and/or \"template\" dir/key in "
886 "\"op\" = \"fuse\"/\"remove\" set element"
887 " indirection dir " << path
);
892 replace
= op
== Op::Replace
;
895 if (!templ
.isEmpty() || content
) {
898 "bad \"content\" and/or \"template\" dir/key in \"op\" "
899 "= \"remove\" set element indirection dir "
911 rtl::Reference
<Node
> member(members
.findNode(layer
, name
));
912 bool insert
= !member
.is();
914 if (replace
|| insert
) {
916 SAL_WARN("configmgr.dconf", "bad unmatched " << path
);
919 switch (node
->kind()) {
920 case Node::KIND_LOCALIZED_PROPERTY
:
921 member
.set(new LocalizedValueNode(layer
));
923 case Node::KIND_GROUP
:
924 if (!static_cast<GroupNode
*>(node
.get())->isExtensible()) {
925 SAL_WARN("configmgr.dconf", "bad unmatched " << path
);
930 layer
, TYPE_ANY
, true, css::uno::Any(), true));
933 assert(!templ
.isEmpty());
934 member
= data
.getTemplate(layer
, templ
);
938 "bad " << path
<< " denoting undefined template "
942 member
= member
->clone(true);
945 assert(false); // cannot happen
947 } else if (!templ
.isEmpty() && templ
!= member
->getTemplateName()) {
951 << " denoting set element of non-matching template "
952 << member
->getTemplateName());
957 if (member
->getFinalized() < layer
) {
960 switch (member
->kind()) {
961 case Node::KIND_PROPERTY
:
966 "bad dir " << path
<< " does not match property");
969 rtl::Reference
<PropertyNode
> prop(
970 static_cast<PropertyNode
*>(member
.get()));
973 client
, path
, prop
->getStaticType(),
974 prop
->isNillable(), prop
->isExtension(),
977 case ReadValue::Error
:
979 case ReadValue::Value
:
980 prop
->setValue(layer
, value
);
981 finalize(client
, path
, member
, layer
);
983 case ReadValue::Remove
:
989 case Node::KIND_LOCALIZED_VALUE
:
995 << " does not match localized value");
1000 && node
->kind() == Node::KIND_LOCALIZED_PROPERTY
);
1001 rtl::Reference
<LocalizedPropertyNode
> locProp(
1002 static_cast<LocalizedPropertyNode
*>(node
.get()));
1003 css::uno::Any value
;
1005 client
, path
, locProp
->getStaticType(),
1006 locProp
->isNillable(), false, &value
)
1007 == ReadValue::Error
)
1011 static_cast<LocalizedValueNode
*>(member
.get())->setValue(
1013 finalize(client
, path
, member
, layer
);
1016 case Node::KIND_LOCALIZED_PROPERTY
:
1017 case Node::KIND_GROUP
:
1018 case Node::KIND_SET
:
1023 << " does not match localized property, group, or"
1024 " set, respectively");
1027 assert(path
.endsWith("/"));
1029 data
, layer
, member
, member
->getMembers(), client
, path
);
1032 assert(false); // cannot happen
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
) {
1050 return name
.toUtf8();
1053 for (sal_Int32 i
= 0; i
!= name
.getLength(); ++i
) {
1054 sal_Unicode c
= name
[i
];
1069 return buf
.makeStringAndClear().toUtf8();
1072 OString
encodeString(OUString
const & value
) {
1074 for (sal_Int32 i
= 0; i
!= value
.getLength(); ++i
) {
1075 sal_Unicode c
= value
[i
];
1087 return buf
.makeStringAndClear().toUtf8();
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
) {
1100 std::forward_list
<GVariantHolder
> children
;
1101 if (dynType
== TYPE_NIL
) {
1104 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN
, nullptr));
1107 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16
, nullptr));
1110 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32
, nullptr));
1113 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64
, nullptr));
1116 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE
, nullptr));
1119 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING
, nullptr));
1121 case TYPE_HEXBINARY
:
1122 case TYPE_BOOLEAN_LIST
:
1123 case TYPE_SHORT_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");
1139 v
.reset(g_variant_new_maybe(ty
.get(), nullptr));
1143 assert(false); // this cannot happen
1146 if (v
.get() == nullptr) {
1147 SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1153 v
.reset(g_variant_new_boolean(value
.get
<bool>()));
1156 v
.reset(g_variant_new_int16(value
.get
<sal_Int16
>()));
1159 v
.reset(g_variant_new_int32(value
.get
<sal_Int32
>()));
1162 v
.reset(g_variant_new_int64(value
.get
<sal_Int64
>()));
1165 v
.reset(g_variant_new_double(value
.get
<double>()));
1169 g_variant_new_string(
1170 encodeString(value
.get
<OUString
>()).getStr()));
1172 case TYPE_HEXBINARY
:
1174 css::uno::Sequence
<sal_Int8
> seq(
1175 value
.get
<css::uno::Sequence
<sal_Int8
>>());
1177 sizeof(sal_Int32
) <= sizeof(gsize
),
1178 "G_MAXSIZE too small");
1180 sizeof (sal_Int8
) == sizeof (guchar
), "size mismatch");
1182 g_variant_new_fixed_array(
1183 G_VARIANT_TYPE_BYTE
, seq
.getConstArray(),
1184 seq
.getLength(), sizeof (sal_Int8
)));
1187 case TYPE_BOOLEAN_LIST
:
1189 css::uno::Sequence
<sal_Bool
> seq(
1190 value
.get
<css::uno::Sequence
<sal_Bool
>>());
1192 sizeof(sal_Int32
) <= sizeof(gsize
),
1193 "G_MAXSIZE too small");
1194 static_assert(sizeof (sal_Bool
) == 1, "size mismatch");
1196 g_variant_new_fixed_array(
1197 G_VARIANT_TYPE_BOOLEAN
, seq
.getConstArray(),
1198 seq
.getLength(), sizeof (sal_Bool
)));
1201 case TYPE_SHORT_LIST
:
1203 css::uno::Sequence
<sal_Int16
> seq(
1204 value
.get
<css::uno::Sequence
<sal_Int16
>>());
1206 sizeof(sal_Int32
) <= sizeof(gsize
),
1207 "G_MAXSIZE too small");
1209 sizeof (sal_Int16
) == sizeof (gint16
), "size mismatch");
1211 g_variant_new_fixed_array(
1212 G_VARIANT_TYPE_INT16
, seq
.getConstArray(),
1213 seq
.getLength(), sizeof (sal_Int16
)));
1214 //TODO: endian-ness?
1219 css::uno::Sequence
<sal_Int32
> seq(
1220 value
.get
<css::uno::Sequence
<sal_Int32
>>());
1222 sizeof(sal_Int32
) <= sizeof(gsize
),
1223 "G_MAXSIZE too small");
1225 sizeof (sal_Int32
) == sizeof (gint32
), "size mismatch");
1227 g_variant_new_fixed_array(
1228 G_VARIANT_TYPE_INT32
, seq
.getConstArray(),
1229 seq
.getLength(), sizeof (sal_Int32
)));
1230 //TODO: endian-ness?
1233 case TYPE_LONG_LIST
:
1235 css::uno::Sequence
<sal_Int64
> seq(
1236 value
.get
<css::uno::Sequence
<sal_Int64
>>());
1238 sizeof(sal_Int32
) <= sizeof(gsize
),
1239 "G_MAXSIZE too small");
1241 sizeof (sal_Int64
) == sizeof (gint64
), "size mismatch");
1243 g_variant_new_fixed_array(
1244 G_VARIANT_TYPE_INT64
, seq
.getConstArray(),
1245 seq
.getLength(), sizeof (sal_Int64
)));
1246 //TODO: endian-ness?
1249 case TYPE_DOUBLE_LIST
:
1251 css::uno::Sequence
<double> seq(
1252 value
.get
<css::uno::Sequence
<double>>());
1254 sizeof(sal_Int32
) <= sizeof(gsize
),
1255 "G_MAXSIZE too small");
1257 sizeof (double) == sizeof (gdouble
), "size mismatch");
1259 g_variant_new_fixed_array(
1260 G_VARIANT_TYPE_DOUBLE
, seq
.getConstArray(),
1261 seq
.getLength(), sizeof (double)));
1262 //TODO: endian-ness?
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) {
1275 "configmgr.dconf", "g_variant_new_string failed");
1278 vs
.push_back(children
.front().get());
1281 sizeof(sal_Int32
) <= sizeof(gsize
),
1282 "G_MAXSIZE too small");
1284 g_variant_new_array(
1285 G_VARIANT_TYPE_STRING
, vs
.data(), seq
.getLength()));
1288 case TYPE_HEXBINARY_LIST
:
1290 css::uno::Sequence
<css::uno::Sequence
<sal_Int8
>> seq(
1292 css::uno::Sequence
<css::uno::Sequence
<sal_Int8
>>>());
1293 std::vector
<GVariant
*> vs
;
1294 for (sal_Int32 i
= 0; i
!= seq
.getLength(); ++i
) {
1296 sizeof(sal_Int32
) <= sizeof(gsize
),
1297 "G_MAXSIZE too small");
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) {
1307 "g_variant_new_fixed_array failed");
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");
1318 sizeof(sal_Int32
) <= sizeof(gsize
),
1319 "G_MAXSIZE too small");
1321 g_variant_new_array(ty
.get(), vs
.data(), seq
.getLength()));
1325 assert(false); // this cannot happen
1328 if (v
.get() == nullptr) {
1329 SAL_WARN("configmgr.dconf", "GVariant creation failed");
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");
1343 dconf_changeset_set(
1344 changeset
.get(), pathRepresentation
.getStr(), v
.get());
1345 for (auto & i
: children
) {
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());
1362 changeset
, pathRepresentation
, prop
->getStaticType(),
1363 prop
->isNillable(), prop
->getValue(components
)))
1369 case Node::KIND_LOCALIZED_VALUE
:
1371 //TODO: name.isEmpty()?
1372 LocalizedPropertyNode
* locprop
1373 = static_cast<LocalizedPropertyNode
*>(parent
.get());
1375 changeset
, pathRepresentation
,
1376 locprop
->getStaticType(), locprop
->isNillable(),
1377 static_cast<LocalizedValueNode
*>(node
.get())->getValue()))
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());
1389 pathRepresentation
+ "/"
1390 + encodeSegment(i
.first
, !templ
.isEmpty()));
1391 if (!templ
.isEmpty()) {
1393 GVariantHolder
v(g_variant_new_string("replace"));
1394 if (v
.get() == nullptr) {
1395 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1398 dconf_changeset_set(
1399 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
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");
1406 dconf_changeset_set(
1407 changeset
.get(), OString(path
+ "template").getStr(),
1412 if (!addNode(components
, changeset
, parent
, path
, i
.second
)) {
1417 case Node::KIND_ROOT
:
1418 assert(false); // this cannot happen
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
1437 OUString
templ(node
->getTemplateName());
1439 parentPathRepresentation
+ "/"
1440 + encodeSegment(nodeName
, !templ
.isEmpty()));
1441 if (!templ
.isEmpty()) {
1443 GVariantHolder
v(g_variant_new_string("replace"));
1444 if (v
.get() == nullptr) {
1445 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1448 dconf_changeset_set(
1449 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
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");
1456 dconf_changeset_set(
1457 changeset
.get(), OString(path
+ "template").getStr(),
1462 if (!addNode(components
, changeset
, parent
, path
, node
)) {
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) {
1473 "configmgr.dconf", "g_variant_new_tuple failed");
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());
1485 case Node::KIND_SET
:
1488 parentPathRepresentation
+ "/"
1489 + encodeSegment(nodeName
, true) + "/");
1490 GVariantHolder
v(g_variant_new_string("remove"));
1491 if (v
.get() == nullptr) {
1493 "configmgr.dconf", "g_variant_new_string failed");
1496 dconf_changeset_set(
1497 changeset
.get(), OString(path
+ "op").getStr(),
1500 dconf_changeset_set(
1501 changeset
.get(), OString(path
+ "template").getStr(),
1503 dconf_changeset_set(
1504 changeset
.get(), OString(path
+ "content/").getStr(),
1509 assert(false); // this cannot happen
1515 OUString
templ(node
->getTemplateName());
1517 parentPathRepresentation
+ "/"
1518 + encodeSegment(nodeName
, !templ
.isEmpty()));
1519 if (!templ
.isEmpty()) {
1521 GVariantHolder
v(g_variant_new_string("fuse"));
1522 if (v
.get() == nullptr) {
1523 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1526 dconf_changeset_set(
1527 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
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");
1534 dconf_changeset_set(
1535 changeset
.get(), OString(path
+ "template").getStr(), v
.get());
1539 for (auto const & i
: modifications
.children
) {
1540 if (!addModifications(
1541 components
, changeset
, path
, node
, i
.first
,
1542 node
->getMember(i
.first
), i
.second
))
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");
1560 data
, layer
, rtl::Reference
<Node
>(), data
.getComponents(), client
,
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");
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
),
1583 if (!dconf_client_change_sync(
1584 client
.get(), cs
.get(), nullptr, nullptr, nullptr))
1587 SAL_WARN("configmgr.dconf", "dconf_client_change_sync failed");
1590 data
.modifications
.clear();
1595 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */