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