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>
19 #include <dconf/dconf.h>
21 #include <com/sun/star/uno/Sequence.hxx>
22 #include <o3tl/safeint.hxx>
23 #include <rtl/ustrbuf.hxx>
24 #include <sal/log.hxx>
28 #include "groupnode.hxx"
29 #include "localizedpropertynode.hxx"
30 #include "localizedvaluenode.hxx"
31 #include "nodemap.hxx"
32 #include "propertynode.hxx"
33 #include "setnode.hxx"
35 // component-data is encoded in dconf as follows:
37 // * The node hierarchy (starting at component nodes with names like
38 // "org.openoffice.Setup") maps to dconf paths underneath
39 // "/org/libreoffice/registry/".
41 // * Component, group, set, and localized property nodes map to dconf dirs,
42 // while property and localized value nodes map to dconf keys.
44 // * The names of nodes that are not set elements are used directly as dconf
45 // path segments. (The syntax for node names is any non-empty sequences of
46 // any Unicode scalar values except U+0000--0008, U+000B--000C, U+000E--001F,
47 // U+002F SOLIDUS, and U+FFFE--FFFF. TODO: "<aruiz> sberg, in general I think
48 // it'd be nice if you used path separators instead of dots though, they have
49 // meaning in dconf/gvdb world :-)"?)
51 // * The names of set element nodes are encoded as dconf path segments as
52 // follows: each occurrence of U+0000 NULL is replace by the three characters
53 // "\00", each occurrence of U+002F SOLIDUS is replaced by the three
54 // characters "\2F", and each occurrence of U+005C REVERSE SOLIDUS is replaced
55 // by the three characters "\5C".
57 // * Set elements (which must themselves be either sets or groups) map to
58 // "indirection" dconf dirs as follows:
60 // ** The dir must contain a key named "op" of string type, with a value of
61 // "fuse", "replace", or "remove".
63 // ** If "op" is "fuse" or "replace", the dir must contain exactly the following
64 // further keys and dirs:
66 // *** The dir must contain a key named "template" of string type, containing
67 // the full template name, encoded as follows: each occurrence of U+0000
68 // NULL is replace by the three characters "\00" and each occurrence of
69 // U+005C REVERSE SOLIDUS is replaced by the three characters "\5C".
71 // *** The dir must contain a dir named "content" that contains the set
72 // element's (i.e., set or group node's) real content.
74 // ** If "op" is "remove", the dir must contain no further keys or dirs.
76 // * Property and localized property value "fuse" operations map to GVariant
77 // instances as follows:
79 // ** Non-nillable boolean values map to GVariant boolean instances.
81 // ** Non-nillable short values map to GVariant int16 instances.
83 // ** Non-nillable int values map to GVariant int32 instances.
85 // ** Non-nillable long values map to GVariant int64 instances.
87 // ** Non-nillable double values map to GVariant double instances.
89 // ** Non-nillable string values map to GVariant string instances, with the
90 // following encoding: each occurrence of U+0000 NULL is replace by the three
91 // characters "\00" and each occurrence of U+005C REVERSE SOLIDUS is replaced
92 // by the three characters "\5C".
94 // ** Non-nillable hexbinary values map to GVariant byte array instances.
96 // ** Non-nillable list values recursively map to GVariant array instances.
98 // ** Nillable values recursively map to GVariant maybe instances.
100 // * Property "remove" operations map to GVariant instances of empty tuple type.
102 // Finalization: The component-update.dtd allows for finalization of
103 // oor:component-data, node, and prop elements, while dconf allows for locking
104 // of individual keys. That does not match, but just mark the individual Node
105 // instances that correspond to individual dconf keys as finalized for
106 // non-writable dconf keys.
108 // TODO: support "mandatory" and "external"?
110 namespace configmgr::dconf
{
114 template<typename T
> class GObjectHolder
{
116 explicit GObjectHolder(T
* object
): object_(object
) {}
119 if (object_
!= nullptr) {
120 g_object_unref(object_
);
124 T
* get() const { return object_
; }
127 GObjectHolder(GObjectHolder
const &) = delete;
128 GObjectHolder
& operator =(GObjectHolder
const &) = delete;
133 class GVariantHolder
{
135 explicit GVariantHolder(GVariant
* variant
= nullptr): variant_(variant
) {}
137 ~GVariantHolder() { unref(); }
139 void reset(GVariant
* variant
) {
144 void release() { variant_
= nullptr; }
146 GVariant
* get() const { return variant_
; }
149 GVariantHolder(GVariantHolder
const &) = delete;
150 GVariantHolder
& operator =(GVariantHolder
const &) = delete;
153 if (variant_
!= nullptr) {
154 g_variant_unref(variant_
);
161 class GVariantTypeHolder
{
163 explicit GVariantTypeHolder(GVariantType
* type
): type_(type
) {}
165 ~GVariantTypeHolder() {
166 if (type_
!= nullptr) {
167 g_variant_type_free(type_
);
171 GVariantType
* get() const { return type_
; }
174 GVariantTypeHolder(GVariantTypeHolder
const &) = delete;
175 GVariantTypeHolder
& operator =(GVariantTypeHolder
const &) = delete;
177 GVariantType
* type_
;
180 class StringArrayHolder
{
182 explicit StringArrayHolder(gchar
** array
): array_(array
) {}
184 ~StringArrayHolder() { g_strfreev(array_
); }
186 gchar
** get() const { return array_
; }
189 StringArrayHolder(StringArrayHolder
const &) = delete;
190 StringArrayHolder
& operator =(StringArrayHolder
const &) = delete;
195 class ChangesetHolder
{
197 explicit ChangesetHolder(DConfChangeset
* changeset
):
198 changeset_(changeset
)
202 if (changeset_
!= nullptr) {
203 dconf_changeset_unref(changeset_
);
207 DConfChangeset
* get() const { return changeset_
; }
210 ChangesetHolder(ChangesetHolder
const &) = delete;
211 ChangesetHolder
& operator =(ChangesetHolder
const &) = delete;
213 DConfChangeset
* changeset_
;
217 return "/org/libreoffice/registry"_ostr
;
220 bool decode(OUString
* string
, bool slash
) {
221 for (sal_Int32 i
= 0;; ++i
) {
222 i
= string
->indexOf('\\', i
);
226 if (string
->match("00", i
+ 1)) {
227 *string
= string
->replaceAt(i
, 3, OUStringChar(u
'\0'));
228 } else if (slash
&& string
->match("2F", i
+ 1)) {
229 *string
= string
->replaceAt(i
, 3, u
"/");
230 } else if (string
->match("5C", i
+ 1)) {
231 *string
= string
->replaceAt(i
+ 1, 2, u
"");
233 SAL_WARN("configmgr.dconf", "bad escape in " << *string
);
240 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
242 assert(value
!= nullptr);
243 if (!g_variant_is_of_type(variant
.get(), G_VARIANT_TYPE_BOOLEAN
)) {
246 "bad key " << key
<< " does not match boolean property");
249 *value
<<= bool(g_variant_get_boolean(variant
.get()));
254 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
256 assert(value
!= nullptr);
257 if (!g_variant_is_of_type(variant
.get(), G_VARIANT_TYPE_INT16
)) {
260 "bad key " << key
<< " does not match short property");
263 *value
<<= sal_Int16(g_variant_get_int16(variant
.get()));
268 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
270 assert(value
!= nullptr);
271 if (!g_variant_is_of_type(variant
.get(), G_VARIANT_TYPE_INT32
)) {
274 "bad key " << key
<< " does not match int property");
277 *value
<<= sal_Int32(g_variant_get_int32(variant
.get()));
282 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
284 assert(value
!= nullptr);
285 if (!g_variant_is_of_type(variant
.get(), G_VARIANT_TYPE_INT64
)) {
288 "bad key " << key
<< " does not match long property");
291 *value
<<= sal_Int64(g_variant_get_int64(variant
.get()));
296 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
298 assert(value
!= nullptr);
299 if (!g_variant_is_of_type(variant
.get(), G_VARIANT_TYPE_DOUBLE
)) {
302 "bad key " << key
<< " does not match double property");
305 *value
<<= double(g_variant_get_double(variant
.get()));
310 OString
const & key
, GVariantHolder
const & variant
, OUString
* value
)
312 assert(value
!= nullptr);
313 if (!g_variant_is_of_type(variant
.get(), G_VARIANT_TYPE_STRING
)) {
316 "bad key " << key
<< " does not match string property");
320 char const * p
= g_variant_get_string(variant
.get(), &n
);
321 if (n
> o3tl::make_unsigned(
322 std::numeric_limits
<sal_Int32
>::max()))
324 SAL_WARN("configmgr.dconf", "too long string value for key " << key
);
327 if (!rtl_convertStringToUString(
328 &value
->pData
, p
, static_cast<sal_Int32
>(n
), RTL_TEXTENCODING_UTF8
,
329 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
330 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
331 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR
)))
333 SAL_WARN("configmgr.dconf", "non--UTF-8 string value for key " << key
);
336 return decode(value
, false);
340 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
342 assert(value
!= nullptr);
344 if (!getStringValue(key
, variant
, &v
)) {
351 bool getHexbinaryValue(
352 OString
const & key
, GVariantHolder
const & variant
,
353 css::uno::Sequence
<sal_Int8
> * value
)
355 assert(value
!= nullptr);
356 if (std::strcmp(g_variant_get_type_string(variant
.get()), "ay") != 0) {
359 "bad key " << key
<< " does not match hexbinary property");
363 gconstpointer p
= g_variant_get_fixed_array(
364 variant
.get(), &n
, sizeof (guchar
));
365 if (n
> o3tl::make_unsigned(
366 std::numeric_limits
<sal_Int32
>::max()))
368 SAL_WARN("configmgr.dconf", "too long hexbinary value for key " << key
);
371 value
->realloc(static_cast<sal_Int32
>(n
));
372 static_assert(sizeof (sal_Int8
) == sizeof (guchar
), "size mismatch");
373 std::memcpy(value
->getArray(), p
, n
* sizeof (guchar
));
374 // assuming that n * sizeof (guchar) is small enough for std::size_t
379 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
381 assert(value
!= nullptr);
382 css::uno::Sequence
<sal_Int8
> v
;
383 if (!getHexbinaryValue(key
, variant
, &v
)) {
391 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
393 assert(value
!= nullptr);
394 if (std::strcmp(g_variant_get_type_string(variant
.get()), "ab") != 0) {
397 "bad key " << key
<< " does not match boolean list property");
401 gconstpointer p
= g_variant_get_fixed_array(
402 variant
.get(), &n
, sizeof (guchar
));
403 if (n
> o3tl::make_unsigned(
404 std::numeric_limits
<sal_Int32
>::max()))
406 SAL_WARN("configmgr.dconf", "too long boolean list for key " << key
);
409 css::uno::Sequence
<sal_Bool
> v(static_cast<sal_Int32
>(n
));
410 static_assert(sizeof (sal_Bool
) == sizeof (guchar
), "size mismatch");
411 std::memcpy(v
.getArray(), p
, n
* sizeof (guchar
));
412 // assuming that n * sizeof (guchar) is small enough for std::size_t
418 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
420 assert(value
!= nullptr);
421 if (std::strcmp(g_variant_get_type_string(variant
.get()), "an") != 0) {
424 "bad key " << key
<< " does not match short list property");
428 gconstpointer p
= g_variant_get_fixed_array(
429 variant
.get(), &n
, sizeof (gint16
));
430 if (n
> o3tl::make_unsigned(
431 std::numeric_limits
<sal_Int32
>::max()))
433 SAL_WARN("configmgr.dconf", "too long short list for key " << key
);
436 css::uno::Sequence
<sal_Int16
> v(static_cast<sal_Int32
>(n
));
437 static_assert(sizeof (sal_Int16
) == sizeof (gint16
), "size mismatch");
438 std::memcpy(v
.getArray(), p
, n
* sizeof (gint16
));
439 // assuming that n * sizeof (gint16) is small enough for std::size_t
445 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
447 assert(value
!= nullptr);
448 if (std::strcmp(g_variant_get_type_string(variant
.get()), "ai") != 0) {
451 "bad key " << key
<< " does not match int list property");
455 gconstpointer p
= g_variant_get_fixed_array(
456 variant
.get(), &n
, sizeof (gint32
));
457 if (n
> o3tl::make_unsigned(
458 std::numeric_limits
<sal_Int32
>::max()))
460 SAL_WARN("configmgr.dconf", "too long int list for key " << key
);
463 css::uno::Sequence
<sal_Int32
> v(static_cast<sal_Int32
>(n
));
464 static_assert(sizeof (sal_Int32
) == sizeof (gint32
), "size mismatch");
465 std::memcpy(v
.getArray(), p
, n
* sizeof (gint32
));
466 // assuming that n * sizeof (gint32) is small enough for std::size_t
472 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
474 assert(value
!= nullptr);
475 if (std::strcmp(g_variant_get_type_string(variant
.get()), "ax") != 0) {
478 "bad key " << key
<< " does not match long list property");
482 gconstpointer p
= g_variant_get_fixed_array(
483 variant
.get(), &n
, sizeof (gint64
));
484 if (n
> o3tl::make_unsigned(
485 std::numeric_limits
<sal_Int32
>::max()))
487 SAL_WARN("configmgr.dconf", "too long long list for key " << key
);
490 css::uno::Sequence
<sal_Int64
> v(static_cast<sal_Int32
>(n
));
491 static_assert(sizeof (sal_Int64
) == sizeof (gint64
), "size mismatch");
492 std::memcpy(v
.getArray(), p
, n
* sizeof (gint64
));
493 // assuming that n * sizeof (gint64) is small enough for std::size_t
499 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
501 assert(value
!= nullptr);
502 if (std::strcmp(g_variant_get_type_string(variant
.get()), "ad") != 0) {
505 "bad key " << key
<< " does not match double list property");
509 gconstpointer p
= g_variant_get_fixed_array(
510 variant
.get(), &n
, sizeof (gdouble
));
511 if (n
> o3tl::make_unsigned(
512 std::numeric_limits
<sal_Int32
>::max()))
514 SAL_WARN("configmgr.dconf", "too long double list for key " << key
);
517 css::uno::Sequence
<double> v(static_cast<sal_Int32
>(n
));
518 static_assert(std::is_same
<double, gdouble
>::value
, "type mismatch");
519 std::memcpy(v
.getArray(), p
, n
* sizeof (gdouble
));
520 // assuming that n * sizeof (gdouble) is small enough for std::size_t
526 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
528 assert(value
!= nullptr);
529 if (std::strcmp(g_variant_get_type_string(variant
.get()), "as") != 0) {
532 "bad key " << key
<< " does not match string list property");
535 gsize n
= g_variant_n_children(variant
.get());
536 if (n
> o3tl::make_unsigned(
537 std::numeric_limits
<sal_Int32
>::max()))
539 SAL_WARN("configmgr.dconf", "too long string list for key " << key
);
542 css::uno::Sequence
<OUString
> v(static_cast<sal_Int32
>(n
));
543 for (gsize i
= 0; i
!= n
; ++i
) {
544 GVariantHolder
c(g_variant_get_child_value(variant
.get(), i
));
545 if (!getStringValue(key
, c
, v
.getArray() + i
)) {
553 bool getHexbinaryList(
554 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
556 assert(value
!= nullptr);
557 if (std::strcmp(g_variant_get_type_string(variant
.get()), "aay") != 0) {
560 "bad key " << key
<< " does not match hexbinary list property");
563 gsize n
= g_variant_n_children(variant
.get());
564 if (n
> o3tl::make_unsigned(
565 std::numeric_limits
<sal_Int32
>::max()))
567 SAL_WARN("configmgr.dconf", "too long hexbinary list for key " << key
);
570 css::uno::Sequence
<css::uno::Sequence
<sal_Int8
>> v(
571 static_cast<sal_Int32
>(n
));
572 for (gsize i
= 0; i
!= n
; ++i
) {
573 GVariantHolder
c(g_variant_get_child_value(variant
.get(), i
));
574 if (!getHexbinaryValue(key
, c
, v
.getArray() + i
)) {
583 OString
const & key
, GVariantHolder
const & variant
, css::uno::Any
* value
)
585 char const * t
= g_variant_get_type_string(variant
.get());
586 if (std::strcmp(t
, "b") == 0) {
587 return getBoolean(key
, variant
, value
);
589 if (std::strcmp(t
, "n") == 0) {
590 return getShort(key
, variant
, value
);
592 if (std::strcmp(t
, "i") == 0) {
593 return getInt(key
, variant
, value
);
595 if (std::strcmp(t
, "x") == 0) {
596 return getLong(key
, variant
, value
);
598 if (std::strcmp(t
, "d") == 0) {
599 return getDouble(key
, variant
, value
);
601 if (std::strcmp(t
, "s") == 0) {
602 return getString(key
, variant
, value
);
604 if (std::strcmp(t
, "ay") == 0) {
605 return getHexbinary(key
, variant
, value
);
607 if (std::strcmp(t
, "ab") == 0) {
608 return getBooleanList(key
, variant
, value
);
610 if (std::strcmp(t
, "an") == 0) {
611 return getShortList(key
, variant
, value
);
613 if (std::strcmp(t
, "ai") == 0) {
614 return getIntList(key
, variant
, value
);
616 if (std::strcmp(t
, "ax") == 0) {
617 return getLongList(key
, variant
, value
);
619 if (std::strcmp(t
, "ad") == 0) {
620 return getDoubleList(key
, variant
, value
);
622 if (std::strcmp(t
, "as") == 0) {
623 return getStringList(key
, variant
, value
);
625 if (std::strcmp(t
, "aay") == 0) {
626 return getHexbinaryList(key
, variant
, value
);
629 "configmgr.dconf", "bad key " << key
<< " does not match any property");
633 enum class ReadValue
{ Error
, Value
, Remove
};
636 GObjectHolder
<DConfClient
> const & client
, OString
const & path
, Type type
,
637 bool nillable
, bool removable
, css::uno::Any
* value
)
639 assert(value
!= nullptr);
640 assert(!value
->hasValue());
641 assert(!path
.endsWith("/"));
642 GVariantHolder
v(dconf_client_read(client
.get(), path
.getStr()));
643 if (v
.get() == nullptr) {
644 SAL_WARN("configmgr.dconf", "cannot read key " << path
);
645 return ReadValue::Error
;
647 if (removable
&& std::strcmp(g_variant_get_type_string(v
.get()), "()") == 0)
649 return ReadValue::Remove
;
653 if (g_variant_classify(v
.get()) != G_VARIANT_CLASS_MAYBE
) {
656 "bad key " << path
<< " does not match nillable property");
658 v
.reset(g_variant_get_maybe(v
.get()));
659 nil
= v
.get() == nullptr;
666 if (!getAny(path
, v
, value
)) {
667 return ReadValue::Error
;
671 if (!getBoolean(path
, v
, value
)) {
672 return ReadValue::Error
;
676 if (!getShort(path
, v
, value
)) {
677 return ReadValue::Error
;
681 if (!getInt(path
, v
, value
)) {
682 return ReadValue::Error
;
686 if (!getLong(path
, v
, value
)) {
687 return ReadValue::Error
;
691 if (!getDouble(path
, v
, value
)) {
692 return ReadValue::Error
;
696 if (!getString(path
, v
, value
)) {
697 return ReadValue::Error
;
701 if (!getHexbinary(path
, v
, value
)) {
702 return ReadValue::Error
;
705 case TYPE_BOOLEAN_LIST
:
706 if (!getBooleanList(path
, v
, value
)) {
707 return ReadValue::Error
;
710 case TYPE_SHORT_LIST
:
711 if (!getShortList(path
, v
, value
)) {
712 return ReadValue::Error
;
716 if (!getIntList(path
, v
, value
)) {
717 return ReadValue::Error
;
721 if (!getLongList(path
, v
, value
)) {
722 return ReadValue::Error
;
725 case TYPE_DOUBLE_LIST
:
726 if (!getDoubleList(path
, v
, value
)) {
727 return ReadValue::Error
;
730 case TYPE_STRING_LIST
:
731 if (!getStringList(path
, v
, value
)) {
732 return ReadValue::Error
;
735 case TYPE_HEXBINARY_LIST
:
736 if (!getHexbinaryList(path
, v
, value
)) {
737 return ReadValue::Error
;
741 assert(false); // cannot happen
744 return ReadValue::Value
;
748 GObjectHolder
<DConfClient
> const & client
, OString
const & path
,
749 rtl::Reference
<Node
> const & node
, int layer
)
751 if (!dconf_client_is_writable(client
.get(), path
.getStr())) {
752 node
->setFinalized(layer
);
757 Data
& data
, int layer
, rtl::Reference
<Node
> const & node
,
758 NodeMap
& members
, GObjectHolder
<DConfClient
> const & client
,
761 StringArrayHolder
a(dconf_client_list(client
.get(), dir
.getStr(), nullptr));
762 for (char const * const * p
= a
.get(); *p
!= nullptr; ++p
) {
763 std::size_t n
= std::strlen(*p
);
764 if (n
> o3tl::make_unsigned(
765 std::numeric_limits
<sal_Int32
>::max()))
767 SAL_WARN("configmgr.dconf", "too long dir/key in dir " << dir
);
770 OString
s(*p
, static_cast<sal_Int32
>(n
));
771 OString
path(dir
+ s
);
773 if (!rtl_convertStringToUString(
774 &name
.pData
, s
.getStr(), s
.getLength(), RTL_TEXTENCODING_UTF8
,
775 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
776 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
777 | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR
)))
779 SAL_WARN("configmgr.dconf", "non--UTF-8 dir/key in dir " << dir
);
782 bool isDir
= name
.endsWith("/", &name
);
786 if (node
.is() && node
->kind() == Node::KIND_SET
) {
790 "bad key " << path
<< " does not match set element");
793 if (!decode(&name
, true)) {
796 enum class Op
{ None
, Fuse
, Replace
, Remove
};
798 bool content
= false;
800 StringArrayHolder
a2(
801 dconf_client_list(client
.get(), path
.getStr(), nullptr));
802 for (char const * const * p2
= a2
.get(); *p2
!= nullptr; ++p2
) {
803 if (std::strcmp(*p2
, "op") == 0) {
804 OString
path2(path
+ "op");
806 dconf_client_read(client
.get(), path2
.getStr()));
807 if (v
.get() == nullptr) {
809 "configmgr.dconf", "cannot read key " << path2
);
814 if (!getStringValue(path2
, v
, &ops
)) {
820 } else if (ops
== "replace") {
822 } else if (ops
== "remove") {
827 "bad key " << path2
<< " value " << ops
);
831 } else if (std::strcmp(*p2
, "template") == 0) {
832 OString
path2(path
+ "template");
834 dconf_client_read(client
.get(), path2
.getStr()));
835 if (v
.get() == nullptr) {
837 "configmgr.dconf", "cannot read key " << path2
);
841 if (!getStringValue(path2
, v
, &templ
)) {
845 if (!static_cast<SetNode
*>(node
.get())->
846 isValidTemplate(templ
))
850 "bad key " << path2
<< " value " << templ
851 << " denotes unsupported set element template");
855 } else if (std::strcmp(*p2
, "content/") == 0) {
861 << " in set element indirection dir " << path
);
870 default: // case Op::None:
873 "bad set element indirection dir " << path
874 << " missing \"op\" key");
878 if (templ
.isEmpty() || !content
) {
881 "missing \"content\" and/or \"template\" dir/key in "
882 "\"op\" = \"fuse\"/\"remove\" set element"
883 " indirection dir " << path
);
888 replace
= op
== Op::Replace
;
891 if (!templ
.isEmpty() || content
) {
894 "bad \"content\" and/or \"template\" dir/key in \"op\" "
895 "= \"remove\" set element indirection dir "
907 rtl::Reference
<Node
> member(members
.findNode(layer
, name
));
908 bool insert
= !member
.is();
910 if (replace
|| insert
) {
912 SAL_WARN("configmgr.dconf", "bad unmatched " << path
);
915 switch (node
->kind()) {
916 case Node::KIND_LOCALIZED_PROPERTY
:
917 member
.set(new LocalizedValueNode(layer
));
919 case Node::KIND_GROUP
:
920 if (!static_cast<GroupNode
*>(node
.get())->isExtensible()) {
921 SAL_WARN("configmgr.dconf", "bad unmatched " << path
);
926 layer
, TYPE_ANY
, true, css::uno::Any(), true));
929 assert(!templ
.isEmpty());
930 member
= data
.getTemplate(layer
, templ
);
934 "bad " << path
<< " denoting undefined template "
938 member
= member
->clone(true);
941 assert(false); // cannot happen
943 } else if (!templ
.isEmpty() && templ
!= member
->getTemplateName()) {
947 << " denoting set element of non-matching template "
948 << member
->getTemplateName());
953 if (member
->getFinalized() < layer
) {
956 switch (member
->kind()) {
957 case Node::KIND_PROPERTY
:
962 "bad dir " << path
<< " does not match property");
965 rtl::Reference
<PropertyNode
> prop(
966 static_cast<PropertyNode
*>(member
.get()));
969 client
, path
, prop
->getStaticType(),
970 prop
->isNillable(), prop
->isExtension(),
973 case ReadValue::Error
:
975 case ReadValue::Value
:
976 prop
->setValue(layer
, value
, false);
977 finalize(client
, path
, member
, layer
);
979 case ReadValue::Remove
:
985 case Node::KIND_LOCALIZED_VALUE
:
991 << " does not match localized value");
996 && node
->kind() == Node::KIND_LOCALIZED_PROPERTY
);
997 rtl::Reference
<LocalizedPropertyNode
> locProp(
998 static_cast<LocalizedPropertyNode
*>(node
.get()));
1001 client
, path
, locProp
->getStaticType(),
1002 locProp
->isNillable(), false, &value
)
1003 == ReadValue::Error
)
1007 static_cast<LocalizedValueNode
*>(member
.get())->setValue(
1008 layer
, value
, false);
1009 finalize(client
, path
, member
, layer
);
1012 case Node::KIND_LOCALIZED_PROPERTY
:
1013 case Node::KIND_GROUP
:
1014 case Node::KIND_SET
:
1019 << " does not match localized property, group, or"
1020 " set, respectively");
1023 assert(path
.endsWith("/"));
1025 data
, layer
, member
, member
->getMembers(), client
, path
);
1028 assert(false); // cannot happen
1032 if (!(member
.is() && member
->getMandatory())) {
1033 members
.erase(name
);
1035 } else if (replace
) {
1036 members
.erase(name
);
1037 members
.insert(NodeMap::value_type(name
, member
));
1038 } else if (insert
) {
1039 members
.insert(NodeMap::value_type(name
, member
));
1044 OString
encodeSegment(OUString
const & name
, bool setElement
) {
1046 return name
.toUtf8();
1049 for (sal_Int32 i
= 0; i
!= name
.getLength(); ++i
) {
1050 sal_Unicode c
= name
[i
];
1065 return buf
.makeStringAndClear().toUtf8();
1068 OString
encodeString(OUString
const & value
) {
1070 for (sal_Int32 i
= 0; i
!= value
.getLength(); ++i
) {
1071 sal_Unicode c
= value
[i
];
1083 return buf
.makeStringAndClear().toUtf8();
1087 ChangesetHolder
const & changeset
, OString
const & pathRepresentation
,
1088 Type type
, bool nillable
, css::uno::Any
const & value
)
1090 Type dynType
= getDynamicType(value
);
1091 assert(dynType
!= TYPE_ERROR
);
1092 if (type
== TYPE_ANY
) {
1096 std::forward_list
<GVariantHolder
> children
;
1097 if (dynType
== TYPE_NIL
) {
1100 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN
, nullptr));
1103 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16
, nullptr));
1106 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32
, nullptr));
1109 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64
, nullptr));
1112 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE
, nullptr));
1115 v
.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING
, nullptr));
1117 case TYPE_HEXBINARY
:
1118 case TYPE_BOOLEAN_LIST
:
1119 case TYPE_SHORT_LIST
:
1121 case TYPE_LONG_LIST
:
1122 case TYPE_DOUBLE_LIST
:
1123 case TYPE_STRING_LIST
:
1124 case TYPE_HEXBINARY_LIST
:
1126 static char const * const typeString
[
1127 TYPE_HEXBINARY_LIST
- TYPE_HEXBINARY
+ 1]
1128 = { "ay", "ab", "an", "ai", "ax", "ad", "as", "aay" };
1129 GVariantTypeHolder
ty(
1130 g_variant_type_new(typeString
[type
- TYPE_HEXBINARY
]));
1131 if (ty
.get() == nullptr) {
1132 SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
1135 v
.reset(g_variant_new_maybe(ty
.get(), nullptr));
1139 assert(false); // this cannot happen
1142 if (v
.get() == nullptr) {
1143 SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1149 v
.reset(g_variant_new_boolean(value
.get
<bool>()));
1152 v
.reset(g_variant_new_int16(value
.get
<sal_Int16
>()));
1155 v
.reset(g_variant_new_int32(value
.get
<sal_Int32
>()));
1158 v
.reset(g_variant_new_int64(value
.get
<sal_Int64
>()));
1161 v
.reset(g_variant_new_double(value
.get
<double>()));
1165 g_variant_new_string(
1166 encodeString(value
.get
<OUString
>()).getStr()));
1168 case TYPE_HEXBINARY
:
1170 css::uno::Sequence
<sal_Int8
> seq(
1171 value
.get
<css::uno::Sequence
<sal_Int8
>>());
1173 sizeof(sal_Int32
) <= sizeof(gsize
),
1174 "G_MAXSIZE too small");
1176 sizeof (sal_Int8
) == sizeof (guchar
), "size mismatch");
1178 g_variant_new_fixed_array(
1179 G_VARIANT_TYPE_BYTE
, seq
.getConstArray(),
1180 seq
.getLength(), sizeof (sal_Int8
)));
1183 case TYPE_BOOLEAN_LIST
:
1185 css::uno::Sequence
<sal_Bool
> seq(
1186 value
.get
<css::uno::Sequence
<sal_Bool
>>());
1188 sizeof(sal_Int32
) <= sizeof(gsize
),
1189 "G_MAXSIZE too small");
1190 static_assert(sizeof (sal_Bool
) == 1, "size mismatch");
1192 g_variant_new_fixed_array(
1193 G_VARIANT_TYPE_BOOLEAN
, seq
.getConstArray(),
1194 seq
.getLength(), sizeof (sal_Bool
)));
1197 case TYPE_SHORT_LIST
:
1199 css::uno::Sequence
<sal_Int16
> seq(
1200 value
.get
<css::uno::Sequence
<sal_Int16
>>());
1202 sizeof(sal_Int32
) <= sizeof(gsize
),
1203 "G_MAXSIZE too small");
1205 sizeof (sal_Int16
) == sizeof (gint16
), "size mismatch");
1207 g_variant_new_fixed_array(
1208 G_VARIANT_TYPE_INT16
, seq
.getConstArray(),
1209 seq
.getLength(), sizeof (sal_Int16
)));
1210 //TODO: endian-ness?
1215 css::uno::Sequence
<sal_Int32
> seq(
1216 value
.get
<css::uno::Sequence
<sal_Int32
>>());
1218 sizeof(sal_Int32
) <= sizeof(gsize
),
1219 "G_MAXSIZE too small");
1221 sizeof (sal_Int32
) == sizeof (gint32
), "size mismatch");
1223 g_variant_new_fixed_array(
1224 G_VARIANT_TYPE_INT32
, seq
.getConstArray(),
1225 seq
.getLength(), sizeof (sal_Int32
)));
1226 //TODO: endian-ness?
1229 case TYPE_LONG_LIST
:
1231 css::uno::Sequence
<sal_Int64
> seq(
1232 value
.get
<css::uno::Sequence
<sal_Int64
>>());
1234 sizeof(sal_Int32
) <= sizeof(gsize
),
1235 "G_MAXSIZE too small");
1237 sizeof (sal_Int64
) == sizeof (gint64
), "size mismatch");
1239 g_variant_new_fixed_array(
1240 G_VARIANT_TYPE_INT64
, seq
.getConstArray(),
1241 seq
.getLength(), sizeof (sal_Int64
)));
1242 //TODO: endian-ness?
1245 case TYPE_DOUBLE_LIST
:
1247 css::uno::Sequence
<double> seq(
1248 value
.get
<css::uno::Sequence
<double>>());
1250 sizeof(sal_Int32
) <= sizeof(gsize
),
1251 "G_MAXSIZE too small");
1253 sizeof (double) == sizeof (gdouble
), "size mismatch");
1255 g_variant_new_fixed_array(
1256 G_VARIANT_TYPE_DOUBLE
, seq
.getConstArray(),
1257 seq
.getLength(), sizeof (double)));
1258 //TODO: endian-ness?
1261 case TYPE_STRING_LIST
:
1263 const css::uno::Sequence
<OUString
> seq(
1264 value
.get
<css::uno::Sequence
<OUString
>>());
1265 std::vector
<GVariant
*> vs
;
1266 for (OUString
const & s
: seq
) {
1267 children
.emplace_front(
1268 g_variant_new_string(encodeString(s
).getStr()));
1269 if (children
.front().get() == nullptr) {
1271 "configmgr.dconf", "g_variant_new_string failed");
1274 vs
.push_back(children
.front().get());
1277 sizeof(sal_Int32
) <= sizeof(gsize
),
1278 "G_MAXSIZE too small");
1280 g_variant_new_array(
1281 G_VARIANT_TYPE_STRING
, vs
.data(), seq
.getLength()));
1284 case TYPE_HEXBINARY_LIST
:
1286 const css::uno::Sequence
<css::uno::Sequence
<sal_Int8
>> seqSeq(
1288 css::uno::Sequence
<css::uno::Sequence
<sal_Int8
>>>());
1289 std::vector
<GVariant
*> vs
;
1290 for (css::uno::Sequence
<sal_Int8
> const & seq
: seqSeq
) {
1292 sizeof(sal_Int32
) <= sizeof(gsize
),
1293 "G_MAXSIZE too small");
1295 sizeof (sal_Int8
) == sizeof (guchar
), "size mismatch");
1296 children
.emplace_front(
1297 g_variant_new_fixed_array(
1298 G_VARIANT_TYPE_BYTE
, seq
.getConstArray(),
1299 seq
.getLength(), sizeof (sal_Int8
)));
1300 if (children
.front().get() == nullptr) {
1303 "g_variant_new_fixed_array failed");
1306 vs
.push_back(children
.front().get());
1308 GVariantTypeHolder
ty(g_variant_type_new("aay"));
1309 if (ty
.get() == nullptr) {
1310 SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
1314 sizeof(sal_Int32
) <= sizeof(gsize
),
1315 "G_MAXSIZE too small");
1317 g_variant_new_array(ty
.get(), vs
.data(), seqSeq
.getLength()));
1321 assert(false); // this cannot happen
1324 if (v
.get() == nullptr) {
1325 SAL_WARN("configmgr.dconf", "GVariant creation failed");
1329 GVariantHolder
v1(g_variant_new_maybe(nullptr, v
.get()));
1330 if (v1
.get() == nullptr) {
1331 SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1339 dconf_changeset_set(
1340 changeset
.get(), pathRepresentation
.getStr(), v
.get());
1341 for (auto & i
: children
) {
1349 Components
& components
, ChangesetHolder
const & changeset
,
1350 rtl::Reference
<Node
> const & parent
, OString
const & pathRepresentation
,
1351 rtl::Reference
<Node
> const & node
)
1353 switch (node
->kind()) {
1354 case Node::KIND_PROPERTY
:
1356 PropertyNode
* prop
= static_cast<PropertyNode
*>(node
.get());
1358 changeset
, pathRepresentation
, prop
->getStaticType(),
1359 prop
->isNillable(), prop
->getValue(components
)))
1365 case Node::KIND_LOCALIZED_VALUE
:
1367 //TODO: name.isEmpty()?
1368 LocalizedPropertyNode
* locprop
1369 = static_cast<LocalizedPropertyNode
*>(parent
.get());
1371 changeset
, pathRepresentation
,
1372 locprop
->getStaticType(), locprop
->isNillable(),
1373 static_cast<LocalizedValueNode
*>(node
.get())->getValue()))
1379 case Node::KIND_LOCALIZED_PROPERTY
:
1380 case Node::KIND_GROUP
:
1381 case Node::KIND_SET
:
1382 for (auto const & i
: node
->getMembers()) {
1383 OUString
templ(i
.second
->getTemplateName());
1385 pathRepresentation
+ "/"
1386 + encodeSegment(i
.first
, !templ
.isEmpty()));
1387 if (!templ
.isEmpty()) {
1389 GVariantHolder
v(g_variant_new_string("replace"));
1390 if (v
.get() == nullptr) {
1391 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1394 dconf_changeset_set(
1395 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
1397 v
.reset(g_variant_new_string(encodeString(templ
).getStr()));
1398 if (v
.get() == nullptr) {
1399 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1402 dconf_changeset_set(
1403 changeset
.get(), OString(path
+ "template").getStr(),
1408 if (!addNode(components
, changeset
, parent
, path
, i
.second
)) {
1413 case Node::KIND_ROOT
:
1414 assert(false); // this cannot happen
1420 bool addModifications(
1421 Components
& components
, ChangesetHolder
const & changeset
,
1422 OString
const & parentPathRepresentation
,
1423 rtl::Reference
<Node
> const & parent
, OUString
const & nodeName
,
1424 rtl::Reference
<Node
> const & node
,
1425 Modifications::Node
const & modifications
)
1427 // It is never necessary to write oor:finalized or oor:mandatory attributes,
1428 // as they cannot be set via the UNO API.
1429 if (modifications
.children
.empty()) {
1430 assert(parent
.is());
1431 // components themselves have no parent but must have children
1433 OUString
templ(node
->getTemplateName());
1435 parentPathRepresentation
+ "/"
1436 + encodeSegment(nodeName
, !templ
.isEmpty()));
1437 if (!templ
.isEmpty()) {
1439 GVariantHolder
v(g_variant_new_string("replace"));
1440 if (v
.get() == nullptr) {
1441 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1444 dconf_changeset_set(
1445 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
1447 v
.reset(g_variant_new_string(encodeString(templ
).getStr()));
1448 if (v
.get() == nullptr) {
1449 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1452 dconf_changeset_set(
1453 changeset
.get(), OString(path
+ "template").getStr(),
1458 if (!addNode(components
, changeset
, parent
, path
, node
)) {
1462 switch (parent
->kind()) {
1463 case Node::KIND_LOCALIZED_PROPERTY
:
1464 case Node::KIND_GROUP
:
1466 GVariantHolder
v(g_variant_new_tuple(nullptr, 0));
1467 if (v
.get() == nullptr) {
1469 "configmgr.dconf", "g_variant_new_tuple failed");
1472 OString
path(parentPathRepresentation
);
1473 if (!nodeName
.isEmpty()) { // KIND_LOCALIZED_PROPERTY
1474 path
+= "/" + encodeSegment(nodeName
, false);
1476 dconf_changeset_set(
1477 changeset
.get(), path
.getStr(), v
.get());
1481 case Node::KIND_SET
:
1484 parentPathRepresentation
+ "/"
1485 + encodeSegment(nodeName
, true) + "/");
1486 GVariantHolder
v(g_variant_new_string("remove"));
1487 if (v
.get() == nullptr) {
1489 "configmgr.dconf", "g_variant_new_string failed");
1492 dconf_changeset_set(
1493 changeset
.get(), OString(path
+ "op").getStr(),
1496 dconf_changeset_set(
1497 changeset
.get(), OString(path
+ "template").getStr(),
1499 dconf_changeset_set(
1500 changeset
.get(), OString(path
+ "content/").getStr(),
1505 assert(false); // this cannot happen
1511 OUString
templ(node
->getTemplateName());
1513 parentPathRepresentation
+ "/"
1514 + encodeSegment(nodeName
, !templ
.isEmpty()));
1515 if (!templ
.isEmpty()) {
1517 GVariantHolder
v(g_variant_new_string("fuse"));
1518 if (v
.get() == nullptr) {
1519 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1522 dconf_changeset_set(
1523 changeset
.get(), OString(path
+ "op").getStr(), v
.get());
1525 v
.reset(g_variant_new_string(encodeString(templ
).getStr()));
1526 if (v
.get() == nullptr) {
1527 SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1530 dconf_changeset_set(
1531 changeset
.get(), OString(path
+ "template").getStr(), v
.get());
1535 for (auto const & i
: modifications
.children
) {
1536 if (!addModifications(
1537 components
, changeset
, path
, node
, i
.first
,
1538 node
->getMember(i
.first
), i
.second
))
1549 void readLayer(Data
& data
, int layer
) {
1550 GObjectHolder
<DConfClient
> client(dconf_client_new());
1551 if (client
.get() == nullptr) {
1552 SAL_WARN("configmgr.dconf", "dconf_client_new failed");
1556 data
, layer
, rtl::Reference
<Node
>(), data
.getComponents(), client
,
1560 void writeModifications(Components
& components
, Data
& data
) {
1561 GObjectHolder
<DConfClient
> client(dconf_client_new());
1562 if (client
.get() == nullptr) {
1563 SAL_WARN("configmgr.dconf", "dconf_client_new failed");
1565 ChangesetHolder
cs(dconf_changeset_new());
1566 if (cs
.get() == nullptr) {
1567 SAL_WARN("configmgr.dconf", "dconf_changeset_new failed");
1570 for (auto const & i
: data
.modifications
.getRoot().children
) {
1571 if (!addModifications(
1572 components
, cs
, getRoot(), rtl::Reference
<Node
>(), i
.first
,
1573 data
.getComponents().findNode(Data::NO_LAYER
, i
.first
),
1579 if (!dconf_client_change_sync(
1580 client
.get(), cs
.get(), nullptr, nullptr, nullptr))
1583 SAL_WARN("configmgr.dconf", "dconf_client_change_sync failed");
1586 data
.modifications
.clear();
1591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */