LibreOffice Module configmgr (master)  1
components.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  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <cassert>
23 #include <chrono>
24 #include <vector>
25 #include <set>
26 
27 #include <com/sun/star/beans/Optional.hpp>
28 #include <com/sun/star/beans/UnknownPropertyException.hpp>
29 #include <com/sun/star/beans/XPropertySet.hpp>
30 #include <com/sun/star/container/NoSuchElementException.hpp>
31 #include <com/sun/star/lang/WrappedTargetException.hpp>
32 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
33 #include <com/sun/star/uno/Any.hxx>
34 #include <com/sun/star/uno/Exception.hpp>
35 #include <com/sun/star/uno/Reference.hxx>
36 #include <com/sun/star/uno/RuntimeException.hpp>
37 #include <com/sun/star/uno/XComponentContext.hpp>
38 #include <com/sun/star/uno/XInterface.hpp>
39 #include <cppuhelper/exc_hlp.hxx>
40 #include <config_dconf.h>
41 #include <config_folders.h>
42 #include <osl/conditn.hxx>
43 #include <osl/file.hxx>
44 #include <osl/mutex.hxx>
45 #include <rtl/bootstrap.hxx>
46 #include <rtl/ref.hxx>
47 #include <rtl/ustrbuf.hxx>
48 #include <rtl/ustring.hxx>
49 #include <sal/log.hxx>
50 #include <sal/types.h>
51 #include <salhelper/thread.hxx>
52 #include <tools/diagnose_ex.h>
54 #include <o3tl/string_view.hxx>
55 
56 #include "additions.hxx"
57 #include "components.hxx"
58 #include "data.hxx"
59 #include "lock.hxx"
60 #include "modifications.hxx"
61 #include "node.hxx"
62 #include "nodemap.hxx"
63 #include "parsemanager.hxx"
64 #include "partial.hxx"
65 #include "rootaccess.hxx"
66 #include "writemodfile.hxx"
67 #include "xcdparser.hxx"
68 #include "xcuparser.hxx"
69 #include "xcsparser.hxx"
70 
71 #if ENABLE_DCONF
72 #include "dconf.hxx"
73 #endif
74 
75 #if defined(_WIN32)
76 #include "winreg.hxx"
77 #endif
78 
79 namespace configmgr {
80 
81 namespace {
82 
83 struct UnresolvedVectorItem {
84  OUString name;
86 
87  UnresolvedVectorItem(
88  OUString const & theName,
89  rtl::Reference< ParseManager > const & theManager):
90  name(theName), manager(theManager) {}
91 };
92 
93 typedef std::vector< UnresolvedVectorItem > UnresolvedVector;
94 
95 void parseXcsFile(
96  OUString const & url, int layer, Data & data, Partial const * partial,
97  Modifications * modifications, Additions * additions)
98 {
99  assert(partial == nullptr && modifications == nullptr && additions == nullptr);
100  (void) partial; (void) modifications; (void) additions;
102  new ParseManager(url, new XcsParser(layer, data)))->parse(nullptr);
103  assert(ok);
104  (void) ok; // avoid warnings
105 }
106 
107 void parseXcuFile(
108  OUString const & url, int layer, Data & data, Partial const * partial,
109  Modifications * modifications, Additions * additions)
110 {
112  new ParseManager(
113  url,
114  new XcuParser(layer, data, partial, modifications, additions)))->
115  parse(nullptr);
116  assert(ok);
117  (void) ok; // avoid warnings
118 }
119 
120 OUString expand(OUString const & str) {
121  OUString s(str);
122  rtl::Bootstrap::expandMacros(s); //TODO: detect failure
123  return s;
124 }
125 
126 bool canRemoveFromLayer(int layer, rtl::Reference< Node > const & node) {
127  assert(node.is());
128  if (node->getLayer() > layer && node->getLayer() < Data::NO_LAYER) {
129  return false;
130  }
131  switch (node->kind()) {
133  case Node::KIND_GROUP:
134  for (auto const& member : node->getMembers())
135  {
136  if (!canRemoveFromLayer(layer, member.second)) {
137  return false;
138  }
139  }
140  return true;
141  case Node::KIND_SET:
142  return node->getMembers().empty();
143  default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE
144  return true;
145  }
146 }
147 
148 }
149 
150 class Components::WriteThread: public salhelper::Thread {
151 public:
152  WriteThread(
153  rtl::Reference< WriteThread > * reference, Components & components,
154  OUString const & url, Data const & data);
155 
156  void flush() { delay_.set(); }
157 
158 private:
159  virtual ~WriteThread() override {}
160 
161  virtual void execute() override;
162 
165  OUString url_;
166  Data const & data_;
167  osl::Condition delay_;
168  std::shared_ptr<osl::Mutex> lock_;
169 };
170 
172  rtl::Reference< WriteThread > * reference, Components & components,
173  OUString const & url, Data const & data):
174  Thread("configmgrWriter"), reference_(reference), components_(components),
175  url_(url), data_(data),
176  lock_( lock() )
177 {
178  assert(reference != nullptr);
179 }
180 
182  delay_.wait(std::chrono::seconds(1)); // must not throw; result_error is harmless and ignored
183  osl::MutexGuard g(*lock_); // must not throw
184  try {
185  try {
186  writeModFile(components_, url_, data_);
187  } catch (css::uno::RuntimeException &) {
188  // Ignore write errors, instead of aborting:
189  TOOLS_WARN_EXCEPTION("configmgr", "error writing modifications");
190  }
191  } catch (...) {
192  reference_->clear();
193  throw;
194  }
195  reference_->clear();
196 }
197 
199  css::uno::Reference< css::uno::XComponentContext > const & context)
200 {
201  assert(context.is());
202  static Components singleton(context);
203  return singleton;
204 }
205 
206 bool Components::allLocales(std::u16string_view locale) {
207  return locale == u"*";
208 }
209 
211  OUString const & pathRepresentation,
212  OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer)
213  const
214 {
216  pathRepresentation, canonicRepresentation, path, finalizedLayer);
217 }
218 
219 rtl::Reference< Node > Components::getTemplate(OUString const & fullName) const
220 {
221  return data_.getTemplate(Data::NO_LAYER, fullName);
222 }
223 
225  roots_.insert(access.get());
226 }
227 
229  roots_.erase(access);
230 }
231 
233  Modifications const & modifications,
234  rtl::Reference< RootAccess > const & exclude, Broadcaster * broadcaster)
235 {
236  //TODO: Iterate only over roots w/ listeners:
237  for (auto const& elemRoot : roots_)
238  {
240  if (elemRoot->acquireCounting() > 1) {
241  root.set(elemRoot); // must not throw
242  }
243  elemRoot->releaseNondeleting();
244  if (root.is()) {
245  if (root != exclude) {
246  std::vector<OUString> path(root->getAbsolutePath());
247  Modifications::Node const * mods = &modifications.getRoot();
248  for (auto const& pathElem : path)
249  {
250  Modifications::Node::Children::const_iterator k(
251  mods->children.find(pathElem));
252  if (k == mods->children.end()) {
253  mods = nullptr;
254  break;
255  }
256  mods = &k->second;
257  }
258  //TODO: If the complete tree of which root is a part is deleted,
259  // or replaced, mods will be null, but some of the listeners
260  // from within root should probably fire nonetheless:
261  if (mods != nullptr) {
262  root->initBroadcaster(*mods, broadcaster);
263  }
264  }
265  }
266  }
267 }
268 
269 void Components::addModification(std::vector<OUString> const & path) {
270  data_.modifications.add(path);
271 }
272 
274 
275  if (data_.modifications.empty())
276  return;
277 
278  switch (modificationTarget_) {
279  case ModificationTarget::None:
280  break;
281  case ModificationTarget::File:
282  if (!writeThread_.is()) {
285  writeThread_->launch();
286  }
287  break;
288  case ModificationTarget::Dconf:
289 #if ENABLE_DCONF
291 #endif
292  break;
293  }
294 }
295 
298  {
299  osl::MutexGuard g(*lock_);
300  thread = writeThread_;
301  }
302  if (thread.is()) {
303  thread->flush();
304  thread->join();
305  }
306 }
307 
309  bool shared, OUString const & fileUri)
310 {
311  int layer = getExtensionLayer(shared);
312  try {
313  parseXcsFile(fileUri, layer, data_, nullptr, nullptr, nullptr);
314  } catch (css::container::NoSuchElementException & e) {
315  throw css::uno::RuntimeException(
316  "insertExtensionXcsFile does not exist: " + e.Message);
317  }
318 }
319 
321  bool shared, OUString const & fileUri, Modifications * modifications)
322 {
323  assert(modifications != nullptr);
324  int layer = getExtensionLayer(shared) + 1;
325  Additions * adds = data_.addExtensionXcuAdditions(fileUri, layer);
326  try {
327  parseXcuFile(fileUri, layer, data_, nullptr, modifications, adds);
328  } catch (css::container::NoSuchElementException & e) {
330  throw css::uno::RuntimeException(
331  "insertExtensionXcuFile does not exist: " + e.Message);
332  }
333 }
334 
336  OUString const & fileUri, Modifications * modifications)
337 {
338  //TODO: Ideally, exactly the data coming from the specified xcu file would
339  // be removed. However, not enough information is recorded in the in-memory
340  // data structures to do so. So, as a workaround, all those set elements
341  // that were freshly added by the xcu and have afterwards been left
342  // unchanged or have only had their properties changed in the user layer are
343  // removed (and nothing else). The heuristic to determine
344  // whether a node has been left unchanged is to check the layer ID (as
345  // usual) and additionally to check that the node does not recursively
346  // contain any non-empty sets (multiple extension xcu files are merged into
347  // one layer, so checking layer ID alone is not enough). Since
348  // item->additions records all additions of set members in textual order,
349  // the latter check works well when iterating through item->additions in
350  // reverse order.
351  assert(modifications != nullptr);
354  if (!item.is())
355  return;
356 
357  for (Additions::reverse_iterator i(item->additions.rbegin());
358  i != item->additions.rend(); ++i)
359  {
360  rtl::Reference< Node > parent;
361  NodeMap const * map = &data_.getComponents();
363  for (auto const& j : *i)
364  {
365  parent = node;
366  node = map->findNode(Data::NO_LAYER, j);
367  if (!node.is()) {
368  break;
369  }
370  map = &node->getMembers();
371  }
372  if (node.is()) {
373  assert(parent.is());
374  if (parent->kind() == Node::KIND_SET) {
375  assert(
376  node->kind() == Node::KIND_GROUP ||
377  node->kind() == Node::KIND_SET);
378  if (canRemoveFromLayer(item->layer, node)) {
379  parent->getMembers().erase(i->back());
381  modifications->add(*i);
382  }
383  }
384  }
385  }
387 }
388 
390  OUString const & fileUri,
391  std::set< OUString > const & includedPaths,
392  std::set< OUString > const & excludedPaths,
393  Modifications * modifications)
394 {
395  assert(modifications != nullptr);
396  Partial part(includedPaths, excludedPaths);
397  try {
399  &parseXcuFile, fileUri, Data::NO_LAYER, &part, modifications, nullptr);
400  } catch (const css::container::NoSuchElementException &) {
402  "configmgr",
403  "error inserting non-existing \"" << fileUri << "\"");
404  }
405 }
406 
407 css::beans::Optional< css::uno::Any > Components::getExternalValue(
408  std::u16string_view descriptor)
409 {
410  size_t i = descriptor.find(' ');
411  if (i == 0 || i == std::u16string_view::npos) {
412  throw css::uno::RuntimeException(
413  OUString::Concat("bad external value descriptor ") + descriptor);
414  }
415  //TODO: Do not make calls with mutex locked:
416  OUString name(descriptor.substr(0, i));
417  ExternalServices::iterator j(externalServices_.find(name));
418  if (j == externalServices_.end()) {
419  css::uno::Reference< css::uno::XInterface > service;
420  try {
421  service = context_->getServiceManager()->createInstanceWithContext(
422  name, context_);
423  } catch (const css::uno::RuntimeException &) {
424  // Assuming these exceptions are real errors:
425  throw;
426  } catch (const css::uno::Exception &) {
427  // Assuming these exceptions indicate that the service is not
428  // installed:
430  "configmgr",
431  "createInstance(" << name << ") failed");
432  }
433  css::uno::Reference< css::beans::XPropertySet > propset;
434  if (service.is()) {
435  propset.set( service, css::uno::UNO_QUERY_THROW);
436  }
437  j = externalServices_.emplace(name, propset).first;
438  }
439  css::beans::Optional< css::uno::Any > value;
440  if (j->second.is()) {
441  try {
442  if (!(j->second->getPropertyValue(OUString(descriptor.substr(i + 1))) >>=
443  value))
444  {
445  throw css::uno::RuntimeException(
446  OUString::Concat("cannot obtain external value through ") + descriptor);
447  }
448  } catch (css::beans::UnknownPropertyException & e) {
449  throw css::uno::RuntimeException(
450  "unknown external value descriptor ID: " + e.Message);
451  } catch (css::lang::WrappedTargetException & e) {
452  css::uno::Any anyEx = cppu::getCaughtException();
453  throw css::lang::WrappedTargetRuntimeException(
454  "cannot obtain external value: " + e.Message,
455  nullptr, anyEx );
456  }
457  }
458  return value;
459 }
460 
462  css::uno::Reference< css::uno::XComponentContext > const & context):
465 {
466  assert(context.is());
467  lock_ = lock();
468  OUString conf(expand("${CONFIGURATION_LAYERS}"));
469  int layer = 0;
470  for (sal_Int32 i = 0;;) {
471  while (i != conf.getLength() && conf[i] == ' ') {
472  ++i;
473  }
474  if (i == conf.getLength()) {
475  break;
476  }
477  if (modificationTarget_ != ModificationTarget::None) {
478  throw css::uno::RuntimeException(
479  "CONFIGURATION_LAYERS: modification target layer followed by"
480  " further layers");
481  }
482  sal_Int32 c = i;
483  for (;; ++c) {
484  if (c == conf.getLength() || conf[c] == ' ') {
485  throw css::uno::RuntimeException(
486  "CONFIGURATION_LAYERS: missing ':' in \"" + conf + "\"");
487  }
488  if (conf[c] == ':') {
489  break;
490  }
491  }
492  sal_Int32 n = conf.indexOf(' ', c + 1);
493  if (n == -1) {
494  n = conf.getLength();
495  }
496  OUString type(conf.copy(i, c - i));
497  OUString url(conf.copy(c + 1, n - c - 1));
498  if (type == "xcsxcu") {
499  sal_uInt32 nStartTime = osl_getGlobalTimer();
500  parseXcsXcuLayer(layer, url);
501  SAL_INFO("configmgr", "parseXcsXcuLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
502  layer += 2; //TODO: overflow
503  } else if (type == "bundledext") {
504  parseXcsXcuIniLayer(layer, url, false);
505  layer += 2; //TODO: overflow
506  } else if (type == "sharedext") {
507  if (sharedExtensionLayer_ != -1) {
508  throw css::uno::RuntimeException(
509  "CONFIGURATION_LAYERS: multiple \"sharedext\" layers");
510  }
511  sharedExtensionLayer_ = layer;
512  parseXcsXcuIniLayer(layer, url, true);
513  layer += 2; //TODO: overflow
514  } else if (type == "userext") {
515  if (userExtensionLayer_ != -1) {
516  throw css::uno::RuntimeException(
517  "CONFIGURATION_LAYERS: multiple \"userext\" layers");
518  }
519  userExtensionLayer_ = layer;
520  parseXcsXcuIniLayer(layer, url, true);
521  layer += 2; //TODO: overflow
522  } else if (type == "res") {
523  sal_uInt32 nStartTime = osl_getGlobalTimer();
524  parseResLayer(layer, url);
525  SAL_INFO("configmgr", "parseResLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms");
526  ++layer; //TODO: overflow
527 #if ENABLE_DCONF
528  } else if (type == "dconf") {
529  if (url == "!") {
530  modificationTarget_ = ModificationTarget::Dconf;
532  } else if (url == "*") {
533  dconf::readLayer(data_, layer);
534  } else {
535  throw css::uno::RuntimeException(
536  "CONFIGURATION_LAYERS: unknown \"dconf\" kind \"" + url
537  + "\"");
538  }
539  ++layer; //TODO: overflow
540 #endif
541 #if defined(_WIN32)
542  } else if (type == "winreg") {
544  if (url == "LOCAL_MACHINE" || url.isEmpty()/*backwards comp.*/) {
546  } else if (url == "CURRENT_USER") {
547  eType = WinRegType::CURRENT_USER;
548  } else {
549  throw css::uno::RuntimeException(
550  "CONFIGURATION_LAYERS: unknown \"winreg\" kind \"" + url
551  + "\"");
552  }
553  OUString aTempFileURL;
554  if (dumpWindowsRegistry(&aTempFileURL, eType)) {
555  parseFileLeniently(&parseXcuFile, aTempFileURL, layer, nullptr, nullptr, nullptr);
556  if (!getenv("SAL_CONFIG_WINREG_RETAIN_TMP"))
557  osl::File::remove(aTempFileURL);
558  }
559  ++layer; //TODO: overflow
560 #endif
561  } else if (type == "user") {
562  bool write;
563  if (url.startsWith("!", &url)) {
564  write = true;
565  } else if (url.startsWith("*", &url)) {
566  write = false;
567  } else {
568  write = true; // for backwards compatibility
569  }
570  if (url.isEmpty()) {
571  throw css::uno::RuntimeException(
572  "CONFIGURATION_LAYERS: empty \"user\" URL");
573  }
574  bool ignore = false;
575 #if ENABLE_DCONF
576  if (write) {
577  OUString token(
578  expand("${SYSUSERCONFIG}/libreoffice/dconfwrite"));
579  osl::DirectoryItem it;
580  osl::FileBase::RC e = osl::DirectoryItem::get(token, it);
581  ignore = e == osl::FileBase::E_None;
582  SAL_INFO(
583  "configmgr",
584  "dconf write (<" << token << "> " << +e << "): "
585  << int(ignore));
586  if (ignore) {
587  modificationTarget_ = ModificationTarget::Dconf;
588  }
589  }
590 #endif
591  if (!ignore) {
592  if (write) {
593  modificationTarget_ = ModificationTarget::File;
594  modificationFileUrl_ = url;
595  }
596  parseModificationLayer(write ? Data::NO_LAYER : layer, url);
597  }
598  ++layer; //TODO: overflow
599  } else {
600  throw css::uno::RuntimeException(
601  "CONFIGURATION_LAYERS: unknown layer type \"" + type + "\"");
602  }
603  i = n;
604  }
605 }
606 
608 {
609  // get flag if _exit was already called which is a sign to not secure user config.
610  // this is used for win only currently where calling _exit() unfortunately still
611  // calls destructors (what is not wanted). May be needed for other systems, too
612  // (unknown yet) but can do no harm
613  const bool bExitWasCalled(comphelper::BackupFileHelper::getExitWasCalled());
614 
615 #ifndef _WIN32
616  // we can add a SAL_WARN here for other systems where the destructor gets called after
617  // an _exit() call. Still safe - the getExitWasCalled() is used, but a hint that _exit
618  // behaves different on a system
619  SAL_WARN_IF(bExitWasCalled, "configmgr", "Components::~Components() called after _exit() call");
620 #endif
621 
622  if (bExitWasCalled)
623  {
624  // do not write, re-join threads
625  osl::MutexGuard g(*lock_);
626 
627  if (writeThread_.is())
628  {
629  writeThread_->join();
630  }
631  }
632  else
633  {
634  // write changes
636  }
637 
638  for (auto const& rootElem : roots_)
639  {
640  rootElem->setAlive(false);
641  }
642 }
643 
645  FileParser * parseFile, OUString const & url, int layer,
646  Partial const * partial, Modifications * modifications,
647  Additions * additions)
648 {
649  assert(parseFile != nullptr);
650  try {
651  (*parseFile)(url, layer, data_, partial, modifications, additions);
652  } catch (const css::container::NoSuchElementException &) {
653  throw;
654  } catch (const css::uno::Exception &) { //TODO: more specific exception catching
655  // Ignore invalid XML files, instead of completely preventing OOo from
656  // starting:
658  "configmgr",
659  "error reading \"" << url << "\"");
660  }
661 }
662 
664  int layer, OUString const & extension, FileParser * parseFile,
665  OUString const & url, bool recursive)
666 {
667  osl::Directory dir(url);
668  switch (dir.open()) {
669  case osl::FileBase::E_None:
670  break;
671  case osl::FileBase::E_NOENT:
672  if (!recursive) {
673  return;
674  }
675  [[fallthrough]];
676  default:
677  throw css::uno::RuntimeException(
678  "cannot open directory " + url);
679  }
680  for (;;) {
681  osl::DirectoryItem i;
682  osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
683  if (rc == osl::FileBase::E_NOENT) {
684  break;
685  }
686  if (rc != osl::FileBase::E_None) {
687  throw css::uno::RuntimeException(
688  "cannot iterate directory " + url);
689  }
690  osl::FileStatus stat(
691  osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
692  osl_FileStatus_Mask_FileURL);
693  if (i.getFileStatus(stat) != osl::FileBase::E_None) {
694  throw css::uno::RuntimeException(
695  "cannot stat in directory " + url);
696  }
697  if (stat.getFileType() == osl::FileStatus::Directory) { //TODO: symlinks
698  parseFiles(layer, extension, parseFile, stat.getFileURL(), true);
699  } else {
700  OUString file(stat.getFileName());
701  if (file.endsWith(extension)) {
702  try {
704  parseFile, stat.getFileURL(), layer, nullptr, nullptr, nullptr);
705  } catch (css::container::NoSuchElementException & e) {
706  if (stat.getFileType() == osl::FileStatus::Link) {
707  SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
708  continue;
709  }
710  throw css::uno::RuntimeException(
711  "stat'ed file does not exist: " + e.Message);
712  }
713  }
714  }
715  }
716 }
717 
719  int layer, FileParser * parseFile, std::u16string_view urls,
720  bool recordAdditions)
721 {
722  for (sal_Int32 i = 0;;) {
723  OUString url(o3tl::getToken(urls, 0, ' ', i));
724  if (!url.isEmpty()) {
725  Additions * adds = nullptr;
726  if (recordAdditions) {
727  adds = data_.addExtensionXcuAdditions(url, layer);
728  }
729  try {
730  parseFileLeniently(parseFile, url, layer, nullptr, nullptr, adds);
731  } catch (const css::container::NoSuchElementException &) {
732  TOOLS_WARN_EXCEPTION("configmgr", "file does not exist");
733  if (adds != nullptr) {
735  }
736  }
737  }
738  if (i == -1) {
739  break;
740  }
741  }
742 }
743 
744 void Components::parseXcdFiles(int layer, OUString const & url) {
745  osl::Directory dir(url);
746  switch (dir.open()) {
747  case osl::FileBase::E_None:
748  break;
749  case osl::FileBase::E_NOENT:
750  return;
751  default:
752  throw css::uno::RuntimeException(
753  "cannot open directory " + url);
754  }
755  UnresolvedVector unres;
756  std::set< OUString > existingDeps;
757  std::set< OUString > processedDeps;
758  for (;;) {
759  osl::DirectoryItem i;
760  osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32);
761  if (rc == osl::FileBase::E_NOENT) {
762  break;
763  }
764  if (rc != osl::FileBase::E_None) {
765  throw css::uno::RuntimeException(
766  "cannot iterate directory " + url);
767  }
768  osl::FileStatus stat(
769  osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
770  osl_FileStatus_Mask_FileURL);
771  if (i.getFileStatus(stat) != osl::FileBase::E_None) {
772  throw css::uno::RuntimeException(
773  "cannot stat in directory " + url);
774  }
775  if (stat.getFileType() != osl::FileStatus::Directory) { //TODO: symlinks
776  OUString file(stat.getFileName());
777  OUString name;
778  if (file.endsWith(".xcd", &name)) {
779  existingDeps.insert(name);
781  try {
782  manager = new ParseManager(
783  stat.getFileURL(),
784  new XcdParser(layer, processedDeps, data_));
785  } catch (css::container::NoSuchElementException & e) {
786  if (stat.getFileType() == osl::FileStatus::Link) {
787  SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">");
788  continue;
789  }
790  throw css::uno::RuntimeException(
791  "stat'ed file does not exist: " + e.Message);
792  }
793  if (manager->parse(nullptr)) {
794  processedDeps.insert(name);
795  } else {
796  unres.emplace_back(name, manager);
797  }
798  }
799  }
800  }
801  while (!unres.empty()) {
802  bool resolved = false;
803  for (UnresolvedVector::iterator i(unres.begin()); i != unres.end();) {
804  if (i->manager->parse(&existingDeps)) {
805  processedDeps.insert(i->name);
806  i = unres.erase(i);
807  resolved = true;
808  } else {
809  ++i;
810  }
811  }
812  if (!resolved) {
813  throw css::uno::RuntimeException(
814  "xcd: unresolved dependencies in " + url);
815  }
816  }
817 }
818 
819 void Components::parseXcsXcuLayer(int layer, OUString const & url) {
820  parseXcdFiles(layer, url);
821  parseFiles(layer, ".xcs", &parseXcsFile, url + "/schema", false);
822  parseFiles(layer + 1, ".xcu", &parseXcuFile, url + "/data", false);
823 }
824 
826  int layer, OUString const & url, bool recordAdditions)
827 {
828  // Check if ini file exists (otherwise .override would still read global
829  // SCHEMA/DATA variables, which could interfere with unrelated environment
830  // variables):
831  if (rtl::Bootstrap(url).getHandle() == nullptr) return;
832 
833  OUStringBuffer prefix("${.override:");
834  for (sal_Int32 i = 0; i != url.getLength(); ++i) {
835  sal_Unicode c = url[i];
836  switch (c) {
837  case '$':
838  case ':':
839  case '\\':
840  prefix.append('\\');
841  [[fallthrough]];
842  default:
843  prefix.append(c);
844  }
845  }
846  prefix.append(':');
847  OUString urls(prefix + "SCHEMA}");
848  rtl::Bootstrap::expandMacros(urls);
849  if (!urls.isEmpty()) {
850  parseFileList(layer, &parseXcsFile, urls, false);
851  }
852  urls = prefix.makeStringAndClear() + "DATA}";
853  rtl::Bootstrap::expandMacros(urls);
854  if (!urls.isEmpty()) {
855  parseFileList(layer + 1, &parseXcuFile, urls, recordAdditions);
856  }
857 }
858 
859 void Components::parseResLayer(int layer, std::u16string_view url) {
860  OUString resUrl(OUString::Concat(url) + "/res");
861  parseXcdFiles(layer, resUrl);
862  parseFiles(layer, ".xcu", &parseXcuFile, resUrl, false);
863 }
864 
865 void Components::parseModificationLayer(int layer, OUString const & url) {
866  try {
867  parseFileLeniently(&parseXcuFile, url, layer, nullptr, nullptr, nullptr);
868  } catch (css::container::NoSuchElementException &) {
869  SAL_INFO(
870  "configmgr", "user registrymodifications.xcu does not (yet) exist");
871  // Migrate old user layer data (can be removed once migration is no
872  // longer relevant, probably OOo 4; also see hack for xsi namespace in
873  // xmlreader::XmlReader::registerNamespaceIri):
874  parseFiles(
875  layer, ".xcu", &parseXcuFile,
876  expand(
877  "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
878  ":UserInstallation}/user/registry/data"),
879  false);
880  }
881 }
882 
883 int Components::getExtensionLayer(bool shared) const {
884  int layer = shared ? sharedExtensionLayer_ : userExtensionLayer_;
885  if (layer == -1) {
886  throw css::uno::RuntimeException(
887  "insert extension xcs/xcu file into undefined layer");
888  }
889  return layer;
890 }
891 
892 }
893 
894 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
rtl::Reference< WriteThread > * reference_
Definition: components.cxx:163
void insertExtensionXcsFile(bool shared, OUString const &fileUri)
Definition: components.cxx:308
void remove(std::vector< OUString > const &path)
Node const & getRoot() const
static bool allLocales(std::u16string_view locale)
Definition: components.cxx:206
void writeModifications(Components &components, Data &data)
Definition: dconf.cxx:1567
void writeModFile(Components &components, OUString const &url, Data const &data)
rtl::Reference< Node > resolvePathRepresentation(OUString const &pathRepresentation, OUString *canonicRepresentation, std::vector< OUString > *path, int *finalizedLayer) const
Definition: components.cxx:210
sal_Int64 n
std::shared_ptr< osl::Mutex > lock_
Definition: components.hxx:161
void insertExtensionXcuFile(bool shared, OUString const &fileUri, Modifications *modifications)
Definition: components.cxx:320
void parseXcsXcuIniLayer(int layer, OUString const &url, bool recordAdditions)
Definition: components.cxx:825
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
ignore
rtl::Reference< WriteThread > writeThread_
Definition: components.hxx:156
None
virtual void execute() override
Definition: components.cxx:181
std::shared_ptr< osl::Mutex > lock_
Definition: components.cxx:168
#define SAL_MAX_UINT32
void parseXcdFiles(int layer, OUString const &url)
Definition: components.cxx:744
sal_uInt16 sal_Unicode
rtl::Reference< ExtensionXcu > removeExtensionXcuAdditions(OUString const &url)
Definition: data.cxx:313
Any SAL_CALL getCaughtException()
Additions * addExtensionXcuAdditions(OUString const &url, int layer)
Definition: data.cxx:297
css::uno::Reference< css::uno::XComponentContext > context_
Definition: components.hxx:152
Modifications modifications
Definition: data.hxx:51
Components(const Components &)=delete
void insertModificationXcuFile(OUString const &fileUri, std::set< OUString > const &includedPaths, std::set< OUString > const &excludedPaths, Modifications *modifications)
Definition: components.cxx:389
void removeRootAccess(RootAccess *access)
Definition: components.cxx:228
rtl::Reference< Node > getTemplate(OUString const &fullName) const
Definition: components.cxx:219
DocumentType eType
std::vector< std::vector< OUString > > Additions
Definition: additions.hxx:31
#define SAL_CONFIGFILE(name)
void add(std::vector< OUString > const &path)
#define TOOLS_WARN_EXCEPTION(area, stream)
void readLayer(Data &data, int layer)
Definition: dconf.cxx:1556
int i
rtl::Reference< Node > resolvePathRepresentation(OUString const &pathRepresentation, OUString *canonicRepresentation, std::vector< OUString > *path, int *finalizedLayer) const
Definition: data.cxx:181
OUString url_
int getExtensionLayer(bool shared) const
Definition: components.cxx:883
OUString modificationFileUrl_
Definition: components.hxx:160
rtl::Reference< ParseManager > manager
Definition: components.cxx:85
float u
void addRootAccess(rtl::Reference< RootAccess > const &access)
Definition: components.cxx:224
css::beans::Optional< css::uno::Any > getExternalValue(std::u16string_view descriptor)
Definition: components.cxx:407
void addModification(std::vector< OUString > const &path)
Definition: components.cxx:269
ModificationTarget modificationTarget_
Definition: components.hxx:159
void parseFiles(int layer, OUString const &extension, FileParser *parseFile, OUString const &url, bool recursive)
Definition: components.cxx:663
void parseFileLeniently(FileParser *parseFile, OUString const &url, int layer, Partial const *partial, Modifications *modifications, Additions *additions)
Definition: components.cxx:644
NodeMap & getComponents() const
Definition: data.cxx:293
ExternalServices externalServices_
Definition: components.hxx:155
void parseModificationLayer(int layer, OUString const &url)
Definition: components.cxx:865
void removeExtensionXcuFile(OUString const &fileUri, Modifications *modifications)
Definition: components.cxx:335
void parseXcsXcuLayer(int layer, OUString const &url)
Definition: components.cxx:819
#define SAL_WARN_IF(condition, area, stream)
#define SAL_INFO(area, stream)
std::map< OUString, rtl::Reference< Entity > > map
void parseFileList(int layer, FileParser *parseFile, std::u16string_view urls, bool recordAdditions)
Definition: components.cxx:718
o3tl::sorted_vector< RootAccess * > roots_
Definition: components.hxx:154
Any value
void parseResLayer(int layer, std::u16string_view url)
Definition: components.cxx:859
rtl::Reference< Node > getTemplate(int layer, OUString const &fullName) const
Definition: data.cxx:287
ResultType type
#define SAL_WARN(area, stream)
bool dumpWindowsRegistry(OUString *pFileURL, WinRegType eType)
Definition: winreg.cxx:289
void initGlobalBroadcaster(Modifications const &modifications, rtl::Reference< RootAccess > const &exclude, Broadcaster *broadcaster)
Definition: components.cxx:232
OUString name
Definition: components.cxx:84
std::shared_ptr< osl::Mutex > lock_
bool parse(OUString const &uri, SourceProviderScannerData *data)
rtl::Reference< Node > findNode(int layer, OUString const &name) const
Definition: nodemap.cxx:43
WriteThread(rtl::Reference< WriteThread > *reference, Components &components, OUString const &url, Data const &data)
Definition: components.cxx:171
std::shared_ptr< osl::Mutex > const & lock()
Definition: lock.cxx:28
static Components & getSingleton(css::uno::Reference< css::uno::XComponentContext > const &context)
Definition: components.cxx:198
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo