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