LibreOffice Module configmgr (master)  1
dconf.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <sal/config.h>
11 
12 #include <cassert>
13 #include <cstddef>
14 #include <cstring>
15 #include <forward_list>
16 #include <limits>
17 #include <vector>
18 
19 extern "C" {
20  // <https://bugzilla.gnome.org/show_bug.cgi?id=754245>
21  // "common/dconf-changeset.h etc. lack extern "C" wrapper for C++"
22 #include <dconf/dconf.h>
23 }
24 
25 #include <com/sun/star/uno/Sequence.hxx>
26 #include <o3tl/safeint.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <sal/log.hxx>
29 
30 #include "data.hxx"
31 #include "dconf.hxx"
32 #include "groupnode.hxx"
34 #include "localizedvaluenode.hxx"
35 #include "nodemap.hxx"
36 #include "propertynode.hxx"
37 #include "setnode.hxx"
38 
39 // component-data is encoded in dconf as follows:
40 //
41 // * The node hierarchy (starting at component nodes with names like
42 // "org.openoffice.Setup") maps to dconf paths underneath
43 // "/org/libreoffice/registry/".
44 //
45 // * Component, group, set, and localized property nodes map to dconf dirs,
46 // while property and localized value nodes map to dconf keys.
47 //
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 :-)"?)
54 //
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".
60 //
61 // * Set elements (which must themselves be either sets or groups) map to
62 // "indirection" dconf dirs as follows:
63 //
64 // ** The dir must contain a key named "op" of string type, with a value of
65 // "fuse", "replace", or "remove".
66 //
67 // ** If "op" is "fuse" or "replace", the dir must contain exactly the following
68 // further keys and dirs:
69 //
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".
74 //
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.
77 //
78 // ** If "op" is "remove", the dir must contain no further keys or dirs.
79 //
80 // * Property and localized property value "fuse" operations map to GVariant
81 // instances as follows:
82 //
83 // ** Non-nillable boolean values map to GVariant boolean instances.
84 //
85 // ** Non-nillable short values map to GVariant int16 instances.
86 //
87 // ** Non-nillable int values map to GVariant int32 instances.
88 //
89 // ** Non-nillable long values map to GVariant int64 instances.
90 //
91 // ** Non-nillable double values map to GVariant double instances.
92 //
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".
97 //
98 // ** Non-nillable hexbinary values map to GVariant byte array instances.
99 //
100 // ** Non-nillable list values recursively map to GVariant array instances.
101 //
102 // ** Nillable values recursively map to GVariant maybe instances.
103 //
104 // * Property "remove" operations map to GVariant instances of empty tuple type.
105 //
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.
111 //
112 // TODO: support "mandatory" and "external"?
113 
114 namespace configmgr::dconf {
115 
116 namespace {
117 
118 template<typename T> class GObjectHolder {
119 public:
120  explicit GObjectHolder(T * object): object_(object) {}
121 
122  ~GObjectHolder() {
123  if (object_ != nullptr) {
124  g_object_unref(object_);
125  }
126  }
127 
128  T * get() const { return object_; }
129 
130 private:
131  GObjectHolder(GObjectHolder const &) = delete;
132  GObjectHolder& operator =(GObjectHolder const &) = delete;
133 
134  T * object_;
135 };
136 
137 class GVariantHolder {
138 public:
139  explicit GVariantHolder(GVariant * variant = nullptr): variant_(variant) {}
140 
141  ~GVariantHolder() { unref(); }
142 
143  void reset(GVariant * variant) {
144  unref();
145  variant_ = variant;
146  }
147 
148  void release() { variant_ = nullptr; }
149 
150  GVariant * get() const { return variant_; }
151 
152 private:
153  GVariantHolder(GVariantHolder const &) = delete;
154  GVariantHolder& operator =(GVariantHolder const &) = delete;
155 
156  void unref() {
157  if (variant_ != nullptr) {
158  g_variant_unref(variant_);
159  }
160  }
161 
162  GVariant * variant_;
163 };
164 
165 class GVariantTypeHolder {
166 public:
167  explicit GVariantTypeHolder(GVariantType * type): type_(type) {}
168 
169  ~GVariantTypeHolder() {
170  if (type_ != nullptr) {
171  g_variant_type_free(type_);
172  }
173  }
174 
175  GVariantType * get() const { return type_; }
176 
177 private:
178  GVariantTypeHolder(GVariantTypeHolder const &) = delete;
179  GVariantTypeHolder& operator =(GVariantTypeHolder const &) = delete;
180 
181  GVariantType * type_;
182 };
183 
184 class StringArrayHolder {
185 public:
186  explicit StringArrayHolder(gchar ** array): array_(array) {}
187 
188  ~StringArrayHolder() { g_strfreev(array_); }
189 
190  gchar ** get() const { return array_; }
191 
192 private:
193  StringArrayHolder(StringArrayHolder const &) = delete;
194  StringArrayHolder& operator =(StringArrayHolder const &) = delete;
195 
196  gchar ** array_;
197 };
198 
199 class ChangesetHolder {
200 public:
201  explicit ChangesetHolder(DConfChangeset * changeset):
202  changeset_(changeset)
203  {}
204 
205  ~ChangesetHolder() {
206  if (changeset_ != nullptr) {
207  dconf_changeset_unref(changeset_);
208  }
209  }
210 
211  DConfChangeset * get() const { return changeset_; }
212 
213 private:
214  ChangesetHolder(ChangesetHolder const &) = delete;
215  ChangesetHolder& operator =(ChangesetHolder const &) = delete;
216 
217  DConfChangeset * changeset_;
218 };
219 
220 OString getRoot() {
221  return "/org/libreoffice/registry";
222 }
223 
224 bool decode(OUString * string, bool slash) {
225  for (sal_Int32 i = 0;; ++i) {
226  i = string->indexOf('\\', i);
227  if (i == -1) {
228  return true;
229  }
230  if (string->match("00", i + 1)) {
231  *string = string->replaceAt(i, 3, OUString(u'\0'));
232  } else if (slash && string->match("2F", i + 1)) {
233  *string = string->replaceAt(i, 3, "/");
234  } else if (string->match("5C", i + 1)) {
235  *string = string->replaceAt(i + 1, 2, "");
236  } else {
237  SAL_WARN("configmgr.dconf", "bad escape in " << *string);
238  return false;
239  }
240  }
241 }
242 
243 bool getBoolean(
244  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
245 {
246  assert(value != nullptr);
247  if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_BOOLEAN)) {
248  SAL_WARN(
249  "configmgr.dconf",
250  "bad key " << key << " does not match boolean property");
251  return false;
252  }
253  *value <<= bool(g_variant_get_boolean(variant.get()));
254  return true;
255 }
256 
257 bool getShort(
258  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
259 {
260  assert(value != nullptr);
261  if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT16)) {
262  SAL_WARN(
263  "configmgr.dconf",
264  "bad key " << key << " does not match short property");
265  return false;
266  }
267  *value <<= sal_Int16(g_variant_get_int16(variant.get()));
268  return true;
269 }
270 
271 bool getInt(
272  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
273 {
274  assert(value != nullptr);
275  if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT32)) {
276  SAL_WARN(
277  "configmgr.dconf",
278  "bad key " << key << " does not match int property");
279  return false;
280  }
281  *value <<= sal_Int32(g_variant_get_int32(variant.get()));
282  return true;
283 }
284 
285 bool getLong(
286  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
287 {
288  assert(value != nullptr);
289  if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_INT64)) {
290  SAL_WARN(
291  "configmgr.dconf",
292  "bad key " << key << " does not match long property");
293  return false;
294  }
295  *value <<= sal_Int64(g_variant_get_int64(variant.get()));
296  return true;
297 }
298 
299 bool getDouble(
300  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
301 {
302  assert(value != nullptr);
303  if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_DOUBLE)) {
304  SAL_WARN(
305  "configmgr.dconf",
306  "bad key " << key << " does not match double property");
307  return false;
308  }
309  *value <<= double(g_variant_get_double(variant.get()));
310  return true;
311 }
312 
313 bool getStringValue(
314  OString const & key, GVariantHolder const & variant, OUString * value)
315 {
316  assert(value != nullptr);
317  if (!g_variant_is_of_type(variant.get(), G_VARIANT_TYPE_STRING)) {
318  SAL_WARN(
319  "configmgr.dconf",
320  "bad key " << key << " does not match string property");
321  return false;
322  }
323  gsize n;
324  char const * p = g_variant_get_string(variant.get(), &n);
325  if (n > o3tl::make_unsigned(
326  std::numeric_limits<sal_Int32>::max()))
327  {
328  SAL_WARN("configmgr.dconf", "too long string value for key " << key);
329  return false;
330  }
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)))
336  {
337  SAL_WARN("configmgr.dconf", "non--UTF-8 string value for key " << key);
338  return false;
339  }
340  return decode(value, false);
341 }
342 
343 bool getString(
344  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
345 {
346  assert(value != nullptr);
347  OUString v;
348  if (!getStringValue(key, variant, &v)) {
349  return false;
350  }
351  *value <<= v;
352  return true;
353 }
354 
355 bool getHexbinaryValue(
356  OString const & key, GVariantHolder const & variant,
357  css::uno::Sequence<sal_Int8> * value)
358 {
359  assert(value != nullptr);
360  if (std::strcmp(g_variant_get_type_string(variant.get()), "ay") != 0) {
361  SAL_WARN(
362  "configmgr.dconf",
363  "bad key " << key << " does not match hexbinary property");
364  return false;
365  }
366  gsize n;
367  gconstpointer p = g_variant_get_fixed_array(
368  variant.get(), &n, sizeof (guchar));
369  if (n > o3tl::make_unsigned(
370  std::numeric_limits<sal_Int32>::max()))
371  {
372  SAL_WARN("configmgr.dconf", "too long hexbinary value for key " << key);
373  return false;
374  }
375  value->realloc(static_cast<sal_Int32>(n));
376  static_assert(sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
377  std::memcpy(value->getArray(), p, n * sizeof (guchar));
378  // assuming that n * sizeof (guchar) is small enough for std::size_t
379  return true;
380 }
381 
382 bool getHexbinary(
383  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
384 {
385  assert(value != nullptr);
386  css::uno::Sequence<sal_Int8> v;
387  if (!getHexbinaryValue(key, variant, &v)) {
388  return false;
389  }
390  *value <<= v;
391  return true;
392 }
393 
394 bool getBooleanList(
395  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
396 {
397  assert(value != nullptr);
398  if (std::strcmp(g_variant_get_type_string(variant.get()), "ab") != 0) {
399  SAL_WARN(
400  "configmgr.dconf",
401  "bad key " << key << " does not match boolean list property");
402  return false;
403  }
404  gsize n;
405  gconstpointer p = g_variant_get_fixed_array(
406  variant.get(), &n, sizeof (guchar));
407  if (n > o3tl::make_unsigned(
408  std::numeric_limits<sal_Int32>::max()))
409  {
410  SAL_WARN("configmgr.dconf", "too long boolean list for key " << key);
411  return false;
412  }
413  css::uno::Sequence<sal_Bool> v(static_cast<sal_Int32>(n));
414  static_assert(sizeof (sal_Bool) == sizeof (guchar), "size mismatch");
415  std::memcpy(v.getArray(), p, n * sizeof (guchar));
416  // assuming that n * sizeof (guchar) is small enough for std::size_t
417  *value <<= v;
418  return true;
419 }
420 
421 bool getShortList(
422  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
423 {
424  assert(value != nullptr);
425  if (std::strcmp(g_variant_get_type_string(variant.get()), "an") != 0) {
426  SAL_WARN(
427  "configmgr.dconf",
428  "bad key " << key << " does not match short list property");
429  return false;
430  }
431  gsize n;
432  gconstpointer p = g_variant_get_fixed_array(
433  variant.get(), &n, sizeof (gint16));
434  if (n > o3tl::make_unsigned(
435  std::numeric_limits<sal_Int32>::max()))
436  {
437  SAL_WARN("configmgr.dconf", "too long short list for key " << key);
438  return false;
439  }
440  css::uno::Sequence<sal_Int16> v(static_cast<sal_Int32>(n));
441  static_assert(sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
442  std::memcpy(v.getArray(), p, n * sizeof (gint16));
443  // assuming that n * sizeof (gint16) is small enough for std::size_t
444  *value <<= v;
445  return true;
446 }
447 
448 bool getIntList(
449  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
450 {
451  assert(value != nullptr);
452  if (std::strcmp(g_variant_get_type_string(variant.get()), "ai") != 0) {
453  SAL_WARN(
454  "configmgr.dconf",
455  "bad key " << key << " does not match int list property");
456  return false;
457  }
458  gsize n;
459  gconstpointer p = g_variant_get_fixed_array(
460  variant.get(), &n, sizeof (gint32));
461  if (n > o3tl::make_unsigned(
462  std::numeric_limits<sal_Int32>::max()))
463  {
464  SAL_WARN("configmgr.dconf", "too long int list for key " << key);
465  return false;
466  }
467  css::uno::Sequence<sal_Int32> v(static_cast<sal_Int32>(n));
468  static_assert(sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
469  std::memcpy(v.getArray(), p, n * sizeof (gint32));
470  // assuming that n * sizeof (gint32) is small enough for std::size_t
471  *value <<= v;
472  return true;
473 }
474 
475 bool getLongList(
476  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
477 {
478  assert(value != nullptr);
479  if (std::strcmp(g_variant_get_type_string(variant.get()), "ax") != 0) {
480  SAL_WARN(
481  "configmgr.dconf",
482  "bad key " << key << " does not match long list property");
483  return false;
484  }
485  gsize n;
486  gconstpointer p = g_variant_get_fixed_array(
487  variant.get(), &n, sizeof (gint64));
488  if (n > o3tl::make_unsigned(
489  std::numeric_limits<sal_Int32>::max()))
490  {
491  SAL_WARN("configmgr.dconf", "too long long list for key " << key);
492  return false;
493  }
494  css::uno::Sequence<sal_Int64> v(static_cast<sal_Int32>(n));
495  static_assert(sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
496  std::memcpy(v.getArray(), p, n * sizeof (gint64));
497  // assuming that n * sizeof (gint64) is small enough for std::size_t
498  *value <<= v;
499  return true;
500 }
501 
502 bool getDoubleList(
503  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
504 {
505  assert(value != nullptr);
506  if (std::strcmp(g_variant_get_type_string(variant.get()), "ad") != 0) {
507  SAL_WARN(
508  "configmgr.dconf",
509  "bad key " << key << " does not match double list property");
510  return false;
511  }
512  gsize n;
513  gconstpointer p = g_variant_get_fixed_array(
514  variant.get(), &n, sizeof (gdouble));
515  if (n > o3tl::make_unsigned(
516  std::numeric_limits<sal_Int32>::max()))
517  {
518  SAL_WARN("configmgr.dconf", "too long double list for key " << key);
519  return false;
520  }
521  css::uno::Sequence<double> v(static_cast<sal_Int32>(n));
522  static_assert(std::is_same<double, gdouble>::value, "type mismatch");
523  std::memcpy(v.getArray(), p, n * sizeof (gdouble));
524  // assuming that n * sizeof (gdouble) is small enough for std::size_t
525  *value <<= v;
526  return true;
527 }
528 
529 bool getStringList(
530  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
531 {
532  assert(value != nullptr);
533  if (std::strcmp(g_variant_get_type_string(variant.get()), "as") != 0) {
534  SAL_WARN(
535  "configmgr.dconf",
536  "bad key " << key << " does not match string list property");
537  return false;
538  }
539  gsize n = g_variant_n_children(variant.get());
540  if (n > o3tl::make_unsigned(
541  std::numeric_limits<sal_Int32>::max()))
542  {
543  SAL_WARN("configmgr.dconf", "too long string list for key " << key);
544  return false;
545  }
546  css::uno::Sequence<OUString> v(static_cast<sal_Int32>(n));
547  for (gsize i = 0; i != n; ++i) {
548  GVariantHolder c(g_variant_get_child_value(variant.get(), i));
549  if (!getStringValue(key, c, v.getArray() + i)) {
550  return false;
551  }
552  }
553  *value <<= v;
554  return true;
555 }
556 
557 bool getHexbinaryList(
558  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
559 {
560  assert(value != nullptr);
561  if (std::strcmp(g_variant_get_type_string(variant.get()), "aay") != 0) {
562  SAL_WARN(
563  "configmgr.dconf",
564  "bad key " << key << " does not match hexbinary list property");
565  return false;
566  }
567  gsize n = g_variant_n_children(variant.get());
568  if (n > o3tl::make_unsigned(
569  std::numeric_limits<sal_Int32>::max()))
570  {
571  SAL_WARN("configmgr.dconf", "too long hexbinary list for key " << key);
572  return false;
573  }
574  css::uno::Sequence<css::uno::Sequence<sal_Int8>> v(
575  static_cast<sal_Int32>(n));
576  for (gsize i = 0; i != n; ++i) {
577  GVariantHolder c(g_variant_get_child_value(variant.get(), i));
578  if (!getHexbinaryValue(key, c, v.getArray() + i)) {
579  return false;
580  }
581  }
582  *value <<= v;
583  return true;
584 }
585 
586 bool getAny(
587  OString const & key, GVariantHolder const & variant, css::uno::Any * value)
588 {
589  char const * t = g_variant_get_type_string(variant.get());
590  if (std::strcmp(t, "b") == 0) {
591  return getBoolean(key, variant, value);
592  }
593  if (std::strcmp(t, "n") == 0) {
594  return getShort(key, variant, value);
595  }
596  if (std::strcmp(t, "i") == 0) {
597  return getInt(key, variant, value);
598  }
599  if (std::strcmp(t, "x") == 0) {
600  return getLong(key, variant, value);
601  }
602  if (std::strcmp(t, "d") == 0) {
603  return getDouble(key, variant, value);
604  }
605  if (std::strcmp(t, "s") == 0) {
606  return getString(key, variant, value);
607  }
608  if (std::strcmp(t, "ay") == 0) {
609  return getHexbinary(key, variant, value);
610  }
611  if (std::strcmp(t, "ab") == 0) {
612  return getBooleanList(key, variant, value);
613  }
614  if (std::strcmp(t, "an") == 0) {
615  return getShortList(key, variant, value);
616  }
617  if (std::strcmp(t, "ai") == 0) {
618  return getIntList(key, variant, value);
619  }
620  if (std::strcmp(t, "ax") == 0) {
621  return getLongList(key, variant, value);
622  }
623  if (std::strcmp(t, "ad") == 0) {
624  return getDoubleList(key, variant, value);
625  }
626  if (std::strcmp(t, "as") == 0) {
627  return getStringList(key, variant, value);
628  }
629  if (std::strcmp(t, "aay") == 0) {
630  return getHexbinaryList(key, variant, value);
631  }
632  SAL_WARN(
633  "configmgr.dconf", "bad key " << key << " does not match any property");
634  return false;
635 }
636 
637 enum class ReadValue { Error, Value, Remove };
638 
639 ReadValue readValue(
640  GObjectHolder<DConfClient> const & client, OString const & path, Type type,
641  bool nillable, bool removable, css::uno::Any * value)
642 {
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;
650  }
651  if (removable && std::strcmp(g_variant_get_type_string(v.get()), "()") == 0)
652  {
653  return ReadValue::Remove;
654  }
655  bool nil;
656  if (nillable) {
657  if (g_variant_classify(v.get()) != G_VARIANT_CLASS_MAYBE) {
658  SAL_WARN(
659  "configmgr.dconf",
660  "bad key " << path << " does not match nillable property");
661  }
662  v.reset(g_variant_get_maybe(v.get()));
663  nil = v.get() == nullptr;
664  } else {
665  nil = false;
666  }
667  if (!nil) {
668  switch (type) {
669  case TYPE_ANY:
670  if (!getAny(path, v, value)) {
671  return ReadValue::Error;
672  }
673  break;
674  case TYPE_BOOLEAN:
675  if (!getBoolean(path, v, value)) {
676  return ReadValue::Error;
677  }
678  break;
679  case TYPE_SHORT:
680  if (!getShort(path, v, value)) {
681  return ReadValue::Error;
682  }
683  break;
684  case TYPE_INT:
685  if (!getInt(path, v, value)) {
686  return ReadValue::Error;
687  }
688  break;
689  case TYPE_LONG:
690  if (!getLong(path, v, value)) {
691  return ReadValue::Error;
692  }
693  break;
694  case TYPE_DOUBLE:
695  if (!getDouble(path, v, value)) {
696  return ReadValue::Error;
697  }
698  break;
699  case TYPE_STRING:
700  if (!getString(path, v, value)) {
701  return ReadValue::Error;
702  }
703  break;
704  case TYPE_HEXBINARY:
705  if (!getHexbinary(path, v, value)) {
706  return ReadValue::Error;
707  }
708  break;
709  case TYPE_BOOLEAN_LIST:
710  if (!getBooleanList(path, v, value)) {
711  return ReadValue::Error;
712  }
713  break;
714  case TYPE_SHORT_LIST:
715  if (!getShortList(path, v, value)) {
716  return ReadValue::Error;
717  }
718  break;
719  case TYPE_INT_LIST:
720  if (!getIntList(path, v, value)) {
721  return ReadValue::Error;
722  }
723  break;
724  case TYPE_LONG_LIST:
725  if (!getLongList(path, v, value)) {
726  return ReadValue::Error;
727  }
728  break;
729  case TYPE_DOUBLE_LIST:
730  if (!getDoubleList(path, v, value)) {
731  return ReadValue::Error;
732  }
733  break;
734  case TYPE_STRING_LIST:
735  if (!getStringList(path, v, value)) {
736  return ReadValue::Error;
737  }
738  break;
739  case TYPE_HEXBINARY_LIST:
740  if (!getHexbinaryList(path, v, value)) {
741  return ReadValue::Error;
742  }
743  break;
744  default:
745  assert(false); // cannot happen
746  }
747  }
748  return ReadValue::Value;
749 }
750 
751 void finalize(
752  GObjectHolder<DConfClient> const & client, OString const & path,
753  rtl::Reference<Node> const & node, int layer)
754 {
755  if (!dconf_client_is_writable(client.get(), path.getStr())) {
756  node->setFinalized(layer);
757  }
758 }
759 
760 void readDir(
761  Data & data, int layer, rtl::Reference<Node> const & node,
762  NodeMap & members, GObjectHolder<DConfClient> const & client,
763  OString const & dir)
764 {
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 > o3tl::make_unsigned(
769  std::numeric_limits<sal_Int32>::max()))
770  {
771  SAL_WARN("configmgr.dconf", "too long dir/key in dir " << dir);
772  continue;
773  }
774  OString s(*p, static_cast<sal_Int32>(n));
775  OString path(dir + s);
776  OUString name;
777  if (!rtl_convertStringToUString(
778  &name.pData, s.getStr(), s.getLength(), RTL_TEXTENCODING_UTF8,
779  (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
780  | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
781  | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
782  {
783  SAL_WARN("configmgr.dconf", "non--UTF-8 dir/key in dir " << dir);
784  continue;
785  }
786  bool isDir = name.endsWith("/", &name);
787  OUString templ;
788  bool remove;
789  bool replace;
790  if (node.is() && node->kind() == Node::KIND_SET) {
791  if (!isDir) {
792  SAL_WARN(
793  "configmgr.dconf",
794  "bad key " << path << " does not match set element");
795  continue;
796  }
797  if (!decode(&name, true)) {
798  continue;
799  }
800  enum class Op { None, Fuse, Replace, Remove };
801  Op op = Op::None;
802  bool content = false;
803  bool bad = false;
804  StringArrayHolder a2(
805  dconf_client_list(client.get(), path.getStr(), nullptr));
806  for (char const * const * p2 = a2.get(); *p2 != nullptr; ++p2) {
807  if (std::strcmp(*p2, "op") == 0) {
808  OString path2(path + "op");
809  GVariantHolder v(
810  dconf_client_read(client.get(), path2.getStr()));
811  if (v.get() == nullptr) {
812  SAL_WARN(
813  "configmgr.dconf", "cannot read key " << path2);
814  bad = true;
815  break;
816  }
817  OUString ops;
818  if (!getStringValue(path2, v, &ops)) {
819  bad = true;
820  break;
821  }
822  if (ops == "fuse") {
823  op = Op::Fuse;
824  } else if (ops == "replace") {
825  op = Op::Replace;
826  } else if (ops == "remove") {
827  op = Op::Remove;
828  } else {
829  SAL_WARN(
830  "configmgr.dconf",
831  "bad key " << path2 << " value " << ops);
832  bad = true;
833  break;
834  }
835  } else if (std::strcmp(*p2, "template") == 0) {
836  OString path2(path + "template");
837  GVariantHolder v(
838  dconf_client_read(client.get(), path2.getStr()));
839  if (v.get() == nullptr) {
840  SAL_WARN(
841  "configmgr.dconf", "cannot read key " << path2);
842  bad = true;
843  break;
844  }
845  if (!getStringValue(path2, v, &templ)) {
846  bad = true;
847  break;
848  }
849  if (!static_cast<SetNode *>(node.get())->
850  isValidTemplate(templ))
851  {
852  SAL_WARN(
853  "configmgr.dconf",
854  "bad key " << path2 << " value " << templ
855  << " denotes unsupported set element template");
856  bad = true;
857  break;
858  }
859  } else if (std::strcmp(*p2, "content/") == 0) {
860  content = true;
861  } else {
862  SAL_WARN(
863  "configmgr.dconf",
864  "bad dir/key " << p2
865  << " in set element indirection dir " << path);
866  bad = true;
867  break;
868  }
869  }
870  if (bad) {
871  continue;
872  }
873  switch (op) {
874  default: // case Op::None:
875  SAL_WARN(
876  "configmgr.dconf",
877  "bad set element indirection dir " << path
878  << " missing \"op\" key");
879  continue;
880  case Op::Fuse:
881  case Op::Replace:
882  if (templ.isEmpty() || !content) {
883  SAL_WARN(
884  "configmgr.dconf",
885  "missing \"content\" and/or \"template\" dir/key in "
886  "\"op\" = \"fuse\"/\"remove\" set element"
887  " indirection dir " << path);
888  continue;
889  }
890  path += "content/";
891  remove = false;
892  replace = op == Op::Replace;
893  break;
894  case Op::Remove:
895  if (!templ.isEmpty() || content) {
896  SAL_WARN(
897  "configmgr.dconf",
898  "bad \"content\" and/or \"template\" dir/key in \"op\" "
899  "= \"remove\" set element indirection dir "
900  << path);
901  continue;
902  }
903  remove = true;
904  replace = false;
905  break;
906  }
907  } else {
908  remove = false;
909  replace = false;
910  }
911  rtl::Reference<Node> member(members.findNode(layer, name));
912  bool insert = !member.is();
913  if (!remove) {
914  if (replace || insert) {
915  if (!node.is()) {
916  SAL_WARN("configmgr.dconf", "bad unmatched " << path);
917  continue;
918  }
919  switch (node->kind()) {
921  member.set(new LocalizedValueNode(layer));
922  break;
923  case Node::KIND_GROUP:
924  if (!static_cast<GroupNode *>(node.get())->isExtensible()) {
925  SAL_WARN("configmgr.dconf", "bad unmatched " << path);
926  continue;
927  }
928  member.set(
929  new PropertyNode(
930  layer, TYPE_ANY, true, css::uno::Any(), true));
931  break;
932  case Node::KIND_SET:
933  assert(!templ.isEmpty());
934  member = data.getTemplate(layer, templ);
935  if (!member.is()) {
936  SAL_WARN(
937  "configmgr.dconf",
938  "bad " << path << " denoting undefined template "
939  << templ);
940  continue;
941  }
942  member = member->clone(true);
943  break;
944  default:
945  assert(false); // cannot happen
946  }
947  } else if (!templ.isEmpty() && templ != member->getTemplateName()) {
948  SAL_WARN(
949  "configmgr.dconf",
950  "bad " << path
951  << " denoting set element of non-matching template "
952  << member->getTemplateName());
953  continue;
954  }
955  }
956  if (member.is()) {
957  if (member->getFinalized() < layer) {
958  continue;
959  }
960  switch (member->kind()) {
961  case Node::KIND_PROPERTY:
962  {
963  if (isDir) {
964  SAL_WARN(
965  "configmgr.dconf",
966  "bad dir " << path << " does not match property");
967  continue;
968  }
970  static_cast<PropertyNode *>(member.get()));
971  css::uno::Any value;
972  switch (readValue(
973  client, path, prop->getStaticType(),
974  prop->isNillable(), prop->isExtension(),
975  &value))
976  {
977  case ReadValue::Error:
978  continue;
979  case ReadValue::Value:
980  prop->setValue(layer, value);
981  finalize(client, path, member, layer);
982  break;
983  case ReadValue::Remove:
984  remove = true;
985  break;
986  }
987  break;
988  }
990  {
991  if (isDir) {
992  SAL_WARN(
993  "configmgr.dconf",
994  "bad dir " << path
995  << " does not match localized value");
996  continue;
997  }
998  assert(
999  node.is()
1000  && node->kind() == Node::KIND_LOCALIZED_PROPERTY);
1002  static_cast<LocalizedPropertyNode *>(node.get()));
1003  css::uno::Any value;
1004  if (readValue(
1005  client, path, locProp->getStaticType(),
1006  locProp->isNillable(), false, &value)
1007  == ReadValue::Error)
1008  {
1009  continue;
1010  }
1011  static_cast<LocalizedValueNode *>(member.get())->setValue(
1012  layer, value);
1013  finalize(client, path, member, layer);
1014  break;
1015  }
1017  case Node::KIND_GROUP:
1018  case Node::KIND_SET:
1019  if (!isDir) {
1020  SAL_WARN(
1021  "configmgr.dconf",
1022  "bad key " << path
1023  << " does not match localized property, group, or"
1024  " set, respectively");
1025  continue;
1026  }
1027  assert(path.endsWith("/"));
1028  readDir(
1029  data, layer, member, member->getMembers(), client, path);
1030  break;
1031  default:
1032  assert(false); // cannot happen
1033  }
1034  }
1035  if (remove) {
1036  if (!(member.is() && member->getMandatory())) {
1037  members.erase(name);
1038  }
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));
1044  }
1045  }
1046 }
1047 
1048 OString encodeSegment(OUString const & name, bool setElement) {
1049  if (!setElement) {
1050  return name.toUtf8();
1051  }
1052  OUStringBuffer buf;
1053  for (sal_Int32 i = 0; i != name.getLength(); ++i) {
1054  sal_Unicode c = name[i];
1055  switch (c) {
1056  case '\0':
1057  buf.append("\\00");
1058  break;
1059  case '/':
1060  buf.append("\\2F");
1061  break;
1062  case '\\':
1063  buf.append("\\5C");
1064  break;
1065  default:
1066  buf.append(c);
1067  }
1068  }
1069  return buf.makeStringAndClear().toUtf8();
1070 }
1071 
1072 OString encodeString(OUString const & value) {
1073  OUStringBuffer buf;
1074  for (sal_Int32 i = 0; i != value.getLength(); ++i) {
1075  sal_Unicode c = value[i];
1076  switch (c) {
1077  case '\0':
1078  buf.append("\\00");
1079  break;
1080  case '\\':
1081  buf.append("\\5C");
1082  break;
1083  default:
1084  buf.append(c);
1085  }
1086  }
1087  return buf.makeStringAndClear().toUtf8();
1088 }
1089 
1090 bool addProperty(
1091  ChangesetHolder const & changeset, OString const & pathRepresentation,
1092  Type type, bool nillable, css::uno::Any const & value)
1093 {
1094  Type dynType = getDynamicType(value);
1095  assert(dynType != TYPE_ERROR);
1096  if (type == TYPE_ANY) {
1097  type = dynType;
1098  }
1099  GVariantHolder v;
1100  std::forward_list<GVariantHolder> children;
1101  if (dynType == TYPE_NIL) {
1102  switch (type) {
1103  case TYPE_BOOLEAN:
1104  v.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN, nullptr));
1105  break;
1106  case TYPE_SHORT:
1107  v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16, nullptr));
1108  break;
1109  case TYPE_INT:
1110  v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32, nullptr));
1111  break;
1112  case TYPE_LONG:
1113  v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64, nullptr));
1114  break;
1115  case TYPE_DOUBLE:
1116  v.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE, nullptr));
1117  break;
1118  case TYPE_STRING:
1119  v.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING, nullptr));
1120  break;
1121  case TYPE_HEXBINARY:
1122  case TYPE_BOOLEAN_LIST:
1123  case TYPE_SHORT_LIST:
1124  case TYPE_INT_LIST:
1125  case TYPE_LONG_LIST:
1126  case TYPE_DOUBLE_LIST:
1127  case TYPE_STRING_LIST:
1128  case TYPE_HEXBINARY_LIST:
1129  {
1130  static char const * const typeString[
1132  = { "ay", "ab", "an", "ai", "ax", "ad", "as", "aay" };
1133  GVariantTypeHolder ty(
1134  g_variant_type_new(typeString[type - TYPE_HEXBINARY]));
1135  if (ty.get() == nullptr) {
1136  SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
1137  return false;
1138  }
1139  v.reset(g_variant_new_maybe(ty.get(), nullptr));
1140  break;
1141  }
1142  default:
1143  assert(false); // this cannot happen
1144  break;
1145  }
1146  if (v.get() == nullptr) {
1147  SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1148  return false;
1149  }
1150  } else {
1151  switch (type) {
1152  case TYPE_BOOLEAN:
1153  v.reset(g_variant_new_boolean(value.get<bool>()));
1154  break;
1155  case TYPE_SHORT:
1156  v.reset(g_variant_new_int16(value.get<sal_Int16>()));
1157  break;
1158  case TYPE_INT:
1159  v.reset(g_variant_new_int32(value.get<sal_Int32>()));
1160  break;
1161  case TYPE_LONG:
1162  v.reset(g_variant_new_int64(value.get<sal_Int64>()));
1163  break;
1164  case TYPE_DOUBLE:
1165  v.reset(g_variant_new_double(value.get<double>()));
1166  break;
1167  case TYPE_STRING:
1168  v.reset(
1169  g_variant_new_string(
1170  encodeString(value.get<OUString>()).getStr()));
1171  break;
1172  case TYPE_HEXBINARY:
1173  {
1174  css::uno::Sequence<sal_Int8> seq(
1175  value.get<css::uno::Sequence<sal_Int8>>());
1176  static_assert(
1177  sizeof(sal_Int32) <= sizeof(gsize),
1178  "G_MAXSIZE too small");
1179  static_assert(
1180  sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
1181  v.reset(
1182  g_variant_new_fixed_array(
1183  G_VARIANT_TYPE_BYTE, seq.getConstArray(),
1184  seq.getLength(), sizeof (sal_Int8)));
1185  break;
1186  }
1187  case TYPE_BOOLEAN_LIST:
1188  {
1189  css::uno::Sequence<sal_Bool> seq(
1190  value.get<css::uno::Sequence<sal_Bool>>());
1191  static_assert(
1192  sizeof(sal_Int32) <= sizeof(gsize),
1193  "G_MAXSIZE too small");
1194  static_assert(sizeof (sal_Bool) == 1, "size mismatch");
1195  v.reset(
1196  g_variant_new_fixed_array(
1197  G_VARIANT_TYPE_BOOLEAN, seq.getConstArray(),
1198  seq.getLength(), sizeof (sal_Bool)));
1199  break;
1200  }
1201  case TYPE_SHORT_LIST:
1202  {
1203  css::uno::Sequence<sal_Int16> seq(
1204  value.get<css::uno::Sequence<sal_Int16>>());
1205  static_assert(
1206  sizeof(sal_Int32) <= sizeof(gsize),
1207  "G_MAXSIZE too small");
1208  static_assert(
1209  sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
1210  v.reset(
1211  g_variant_new_fixed_array(
1212  G_VARIANT_TYPE_INT16, seq.getConstArray(),
1213  seq.getLength(), sizeof (sal_Int16)));
1214  //TODO: endian-ness?
1215  break;
1216  }
1217  case TYPE_INT_LIST:
1218  {
1219  css::uno::Sequence<sal_Int32> seq(
1220  value.get<css::uno::Sequence<sal_Int32>>());
1221  static_assert(
1222  sizeof(sal_Int32) <= sizeof(gsize),
1223  "G_MAXSIZE too small");
1224  static_assert(
1225  sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
1226  v.reset(
1227  g_variant_new_fixed_array(
1228  G_VARIANT_TYPE_INT32, seq.getConstArray(),
1229  seq.getLength(), sizeof (sal_Int32)));
1230  //TODO: endian-ness?
1231  break;
1232  }
1233  case TYPE_LONG_LIST:
1234  {
1235  css::uno::Sequence<sal_Int64> seq(
1236  value.get<css::uno::Sequence<sal_Int64>>());
1237  static_assert(
1238  sizeof(sal_Int32) <= sizeof(gsize),
1239  "G_MAXSIZE too small");
1240  static_assert(
1241  sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
1242  v.reset(
1243  g_variant_new_fixed_array(
1244  G_VARIANT_TYPE_INT64, seq.getConstArray(),
1245  seq.getLength(), sizeof (sal_Int64)));
1246  //TODO: endian-ness?
1247  break;
1248  }
1249  case TYPE_DOUBLE_LIST:
1250  {
1251  css::uno::Sequence<double> seq(
1252  value.get<css::uno::Sequence<double>>());
1253  static_assert(
1254  sizeof(sal_Int32) <= sizeof(gsize),
1255  "G_MAXSIZE too small");
1256  static_assert(
1257  sizeof (double) == sizeof (gdouble), "size mismatch");
1258  v.reset(
1259  g_variant_new_fixed_array(
1260  G_VARIANT_TYPE_DOUBLE, seq.getConstArray(),
1261  seq.getLength(), sizeof (double)));
1262  //TODO: endian-ness?
1263  break;
1264  }
1265  case TYPE_STRING_LIST:
1266  {
1267  const css::uno::Sequence<OUString> seq(
1268  value.get<css::uno::Sequence<OUString>>());
1269  std::vector<GVariant *> vs;
1270  for (OUString const & s : seq) {
1271  children.emplace_front(
1272  g_variant_new_string(encodeString(s).getStr()));
1273  if (children.front().get() == nullptr) {
1274  SAL_WARN(
1275  "configmgr.dconf", "g_variant_new_string failed");
1276  return false;
1277  }
1278  vs.push_back(children.front().get());
1279  }
1280  static_assert(
1281  sizeof(sal_Int32) <= sizeof(gsize),
1282  "G_MAXSIZE too small");
1283  v.reset(
1284  g_variant_new_array(
1285  G_VARIANT_TYPE_STRING, vs.data(), seq.getLength()));
1286  break;
1287  }
1288  case TYPE_HEXBINARY_LIST:
1289  {
1290  const css::uno::Sequence<css::uno::Sequence<sal_Int8>> seqSeq(
1291  value.get<
1292  css::uno::Sequence<css::uno::Sequence<sal_Int8>>>());
1293  std::vector<GVariant *> vs;
1294  for (css::uno::Sequence<sal_Int8> const & seq : seqSeq) {
1295  static_assert(
1296  sizeof(sal_Int32) <= sizeof(gsize),
1297  "G_MAXSIZE too small");
1298  static_assert(
1299  sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
1300  children.emplace_front(
1301  g_variant_new_fixed_array(
1302  G_VARIANT_TYPE_BYTE, seq.getConstArray(),
1303  seq.getLength(), sizeof (sal_Int8)));
1304  if (children.front().get() == nullptr) {
1305  SAL_WARN(
1306  "configmgr.dconf",
1307  "g_variant_new_fixed_array failed");
1308  return false;
1309  }
1310  vs.push_back(children.front().get());
1311  }
1312  GVariantTypeHolder ty(g_variant_type_new("aay"));
1313  if (ty.get() == nullptr) {
1314  SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
1315  return false;
1316  }
1317  static_assert(
1318  sizeof(sal_Int32) <= sizeof(gsize),
1319  "G_MAXSIZE too small");
1320  v.reset(
1321  g_variant_new_array(ty.get(), vs.data(), seqSeq.getLength()));
1322  break;
1323  }
1324  default:
1325  assert(false); // this cannot happen
1326  break;
1327  }
1328  if (v.get() == nullptr) {
1329  SAL_WARN("configmgr.dconf", "GVariant creation failed");
1330  return false;
1331  }
1332  if (nillable) {
1333  GVariantHolder v1(g_variant_new_maybe(nullptr, v.get()));
1334  if (v1.get() == nullptr) {
1335  SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
1336  return false;
1337  }
1338  v.release();
1339  v.reset(v1.get());
1340  v1.release();
1341  }
1342  }
1343  dconf_changeset_set(
1344  changeset.get(), pathRepresentation.getStr(), v.get());
1345  for (auto & i: children) {
1346  i.release();
1347  }
1348  v.release();
1349  return true;
1350 }
1351 
1352 bool addNode(
1353  Components & components, ChangesetHolder const & changeset,
1354  rtl::Reference<Node> const & parent, OString const & pathRepresentation,
1355  rtl::Reference<Node> const & node)
1356 {
1357  switch (node->kind()) {
1358  case Node::KIND_PROPERTY:
1359  {
1360  PropertyNode * prop = static_cast<PropertyNode *>(node.get());
1361  if (!addProperty(
1362  changeset, pathRepresentation, prop->getStaticType(),
1363  prop->isNillable(), prop->getValue(components)))
1364  {
1365  return false;
1366  }
1367  break;
1368  }
1370  {
1371  //TODO: name.isEmpty()?
1372  LocalizedPropertyNode * locprop
1373  = static_cast<LocalizedPropertyNode *>(parent.get());
1374  if (!addProperty(
1375  changeset, pathRepresentation,
1376  locprop->getStaticType(), locprop->isNillable(),
1377  static_cast<LocalizedValueNode *>(node.get())->getValue()))
1378  {
1379  return false;
1380  }
1381  break;
1382  }
1384  case Node::KIND_GROUP:
1385  case Node::KIND_SET:
1386  for (auto const & i: node->getMembers()) {
1387  OUString templ(i.second->getTemplateName());
1388  OString path(
1389  pathRepresentation + "/"
1390  + encodeSegment(i.first, !templ.isEmpty()));
1391  if (!templ.isEmpty()) {
1392  path += "/";
1393  GVariantHolder v(g_variant_new_string("replace"));
1394  if (v.get() == nullptr) {
1395  SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1396  return false;
1397  }
1398  dconf_changeset_set(
1399  changeset.get(), OString(path + "op").getStr(), v.get());
1400  v.release();
1401  v.reset(g_variant_new_string(encodeString(templ).getStr()));
1402  if (v.get() == nullptr) {
1403  SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1404  return false;
1405  }
1406  dconf_changeset_set(
1407  changeset.get(), OString(path + "template").getStr(),
1408  v.get());
1409  v.release();
1410  path += "content";
1411  }
1412  if (!addNode(components, changeset, parent, path, i.second)) {
1413  return false;
1414  }
1415  }
1416  break;
1417  case Node::KIND_ROOT:
1418  assert(false); // this cannot happen
1419  break;
1420  }
1421  return true;
1422 }
1423 
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)
1430 {
1431  // It is never necessary to write oor:finalized or oor:mandatory attributes,
1432  // as they cannot be set via the UNO API.
1433  if (modifications.children.empty()) {
1434  assert(parent.is());
1435  // components themselves have no parent but must have children
1436  if (node.is()) {
1437  OUString templ(node->getTemplateName());
1438  OString path(
1439  parentPathRepresentation + "/"
1440  + encodeSegment(nodeName, !templ.isEmpty()));
1441  if (!templ.isEmpty()) {
1442  path += "/";
1443  GVariantHolder v(g_variant_new_string("replace"));
1444  if (v.get() == nullptr) {
1445  SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1446  return false;
1447  }
1448  dconf_changeset_set(
1449  changeset.get(), OString(path + "op").getStr(), v.get());
1450  v.release();
1451  v.reset(g_variant_new_string(encodeString(templ).getStr()));
1452  if (v.get() == nullptr) {
1453  SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1454  return false;
1455  }
1456  dconf_changeset_set(
1457  changeset.get(), OString(path + "template").getStr(),
1458  v.get());
1459  v.release();
1460  path += "content";
1461  }
1462  if (!addNode(components, changeset, parent, path, node)) {
1463  return false;
1464  }
1465  } else {
1466  switch (parent->kind()) {
1468  case Node::KIND_GROUP:
1469  {
1470  GVariantHolder v(g_variant_new_tuple(nullptr, 0));
1471  if (v.get() == nullptr) {
1472  SAL_WARN(
1473  "configmgr.dconf", "g_variant_new_tuple failed");
1474  return false;
1475  }
1476  OString path(parentPathRepresentation);
1477  if (!nodeName.isEmpty()) { // KIND_LOCALIZED_PROPERTY
1478  path += "/" + encodeSegment(nodeName, false);
1479  }
1480  dconf_changeset_set(
1481  changeset.get(), path.getStr(), v.get());
1482  v.release();
1483  break;
1484  }
1485  case Node::KIND_SET:
1486  {
1487  OString path(
1488  parentPathRepresentation + "/"
1489  + encodeSegment(nodeName, true) + "/");
1490  GVariantHolder v(g_variant_new_string("remove"));
1491  if (v.get() == nullptr) {
1492  SAL_WARN(
1493  "configmgr.dconf", "g_variant_new_string failed");
1494  return false;
1495  }
1496  dconf_changeset_set(
1497  changeset.get(), OString(path + "op").getStr(),
1498  v.get());
1499  v.release();
1500  dconf_changeset_set(
1501  changeset.get(), OString(path + "template").getStr(),
1502  nullptr);
1503  dconf_changeset_set(
1504  changeset.get(), OString(path + "content/").getStr(),
1505  nullptr);
1506  break;
1507  }
1508  default:
1509  assert(false); // this cannot happen
1510  break;
1511  }
1512  }
1513  } else {
1514  assert(node.is());
1515  OUString templ(node->getTemplateName());
1516  OString path(
1517  parentPathRepresentation + "/"
1518  + encodeSegment(nodeName, !templ.isEmpty()));
1519  if (!templ.isEmpty()) {
1520  path += "/";
1521  GVariantHolder v(g_variant_new_string("fuse"));
1522  if (v.get() == nullptr) {
1523  SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1524  return false;
1525  }
1526  dconf_changeset_set(
1527  changeset.get(), OString(path + "op").getStr(), v.get());
1528  v.release();
1529  v.reset(g_variant_new_string(encodeString(templ).getStr()));
1530  if (v.get() == nullptr) {
1531  SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
1532  return false;
1533  }
1534  dconf_changeset_set(
1535  changeset.get(), OString(path + "template").getStr(), v.get());
1536  v.release();
1537  path += "content";
1538  }
1539  for (auto const & i: modifications.children) {
1540  if (!addModifications(
1541  components, changeset, path, node, i.first,
1542  node->getMember(i.first), i.second))
1543  {
1544  return false;
1545  }
1546  }
1547  }
1548  return true;
1549 }
1550 
1551 }
1552 
1553 void readLayer(Data & data, int layer) {
1554  GObjectHolder<DConfClient> client(dconf_client_new());
1555  if (client.get() == nullptr) {
1556  SAL_WARN("configmgr.dconf", "dconf_client_new failed");
1557  return;
1558  }
1559  readDir(
1560  data, layer, rtl::Reference<Node>(), data.getComponents(), client,
1561  getRoot() + "/");
1562 }
1563 
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");
1568  }
1569  ChangesetHolder cs(dconf_changeset_new());
1570  if (cs.get() == nullptr) {
1571  SAL_WARN("configmgr.dconf", "dconf_changeset_new failed");
1572  return;
1573  }
1574  for (auto const & i: data.modifications.getRoot().children) {
1575  if (!addModifications(
1576  components, cs, getRoot(), rtl::Reference<Node>(), i.first,
1577  data.getComponents().findNode(Data::NO_LAYER, i.first),
1578  i.second))
1579  {
1580  return;
1581  }
1582  }
1583  if (!dconf_client_change_sync(
1584  client.get(), cs.get(), nullptr, nullptr, nullptr))
1585  {
1586  //TODO: GError
1587  SAL_WARN("configmgr.dconf", "dconf_client_change_sync failed");
1588  return;
1589  }
1590  data.modifications.clear();
1591 }
1592 
1593 }
1594 
1595 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
RegError REGISTRY_CALLTYPE setValue(RegKeyHandle hKey, rtl_uString *keyName, RegValueType valueType, RegValue pData, sal_uInt32 valueSize)
Node const & getRoot() const
GVariantType * type_
Definition: dconf.cxx:181
void writeModifications(Components &components, Data &data)
Definition: dconf.cxx:1564
gchar ** array_
Definition: dconf.cxx:196
signed char sal_Int8
sal_Int64 n
Status finalize()
None
OUString encodeSegment(const OUString &rSegment)
Value
sal_uInt16 sal_Unicode
Op_< std::function< void(double &, double)>> Op
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
Modifications modifications
Definition: data.hxx:51
T * object_
Definition: dconf.cxx:134
void readLayer(Data &data, int layer)
Definition: dconf.cxx:1553
int i
uno_Any a
NodeMapImpl::value_type value_type
Definition: nodemap.hxx:39
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
DConfChangeset * changeset_
Definition: dconf.cxx:217
unsigned char sal_Bool
Error
css::beans::Optional< css::uno::Any > getValue(std::u16string_view id)
XPropertyListType t
std::map< const SwTextNode *, const sal_uInt32 > NodeMap
NodeMap & getComponents() const
Definition: data.cxx:291
float v
std::vector< uno::Reference< sheet::XSpreadsheetDocument > > Components
Type getDynamicType(css::uno::Any const &value)
Definition: type.cxx:101
void * p
double getDouble(const Any &_rAny)
ResultType type
#define SAL_WARN(area, stream)
GVariant * variant_
Definition: dconf.cxx:162
OUString name
Definition: components.cxx:83
OUString getString(const Any &_rAny)
rtl::Reference< Node > findNode(int layer, OUString const &name) const
Definition: nodemap.cxx:43