diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /configmgr/source/components.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'configmgr/source/components.cxx')
-rw-r--r-- | configmgr/source/components.cxx | 895 |
1 files changed, 895 insertions, 0 deletions
diff --git a/configmgr/source/components.cxx b/configmgr/source/components.cxx new file mode 100644 index 000000000..0693ec26b --- /dev/null +++ b/configmgr/source/components.cxx @@ -0,0 +1,895 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <chrono> +#include <utility> +#include <vector> +#include <set> + +#include <com/sun/star/beans/Optional.hpp> +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/NoSuchElementException.hpp> +#include <com/sun/star/lang/WrappedTargetException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <config_dconf.h> +#include <config_folders.h> +#include <osl/conditn.hxx> +#include <osl/file.hxx> +#include <osl/mutex.hxx> +#include <rtl/bootstrap.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <sal/types.h> +#include <salhelper/thread.hxx> +#include <tools/diagnose_ex.h> +#include <comphelper/backupfilehelper.hxx> +#include <o3tl/string_view.hxx> + +#include "additions.hxx" +#include "components.hxx" +#include "data.hxx" +#include "lock.hxx" +#include "modifications.hxx" +#include "node.hxx" +#include "nodemap.hxx" +#include "parsemanager.hxx" +#include "partial.hxx" +#include "rootaccess.hxx" +#include "writemodfile.hxx" +#include "xcdparser.hxx" +#include "xcuparser.hxx" +#include "xcsparser.hxx" + +#if ENABLE_DCONF +#include "dconf.hxx" +#endif + +#if defined(_WIN32) +#include "winreg.hxx" +#endif + +namespace configmgr { + +namespace { + +struct UnresolvedVectorItem { + OUString name; + rtl::Reference< ParseManager > manager; + + UnresolvedVectorItem( + OUString theName, + rtl::Reference< ParseManager > theManager): + name(std::move(theName)), manager(std::move(theManager)) {} +}; + +typedef std::vector< UnresolvedVectorItem > UnresolvedVector; + +void parseXcsFile( + OUString const & url, int layer, Data & data, Partial const * partial, + Modifications * modifications, Additions * additions) +{ + assert(partial == nullptr && modifications == nullptr && additions == nullptr); + (void) partial; (void) modifications; (void) additions; + bool ok = rtl::Reference< ParseManager >( + new ParseManager(url, new XcsParser(layer, data)))->parse(nullptr); + assert(ok); + (void) ok; // avoid warnings +} + +void parseXcuFile( + OUString const & url, int layer, Data & data, Partial const * partial, + Modifications * modifications, Additions * additions) +{ + bool ok = rtl::Reference< ParseManager >( + new ParseManager( + url, + new XcuParser(layer, data, partial, modifications, additions)))-> + parse(nullptr); + assert(ok); + (void) ok; // avoid warnings +} + +OUString expand(OUString const & str) { + OUString s(str); + rtl::Bootstrap::expandMacros(s); //TODO: detect failure + return s; +} + +bool canRemoveFromLayer(int layer, rtl::Reference< Node > const & node) { + assert(node.is()); + if (node->getLayer() > layer && node->getLayer() < Data::NO_LAYER) { + return false; + } + switch (node->kind()) { + case Node::KIND_LOCALIZED_PROPERTY: + case Node::KIND_GROUP: + for (auto const& member : node->getMembers()) + { + if (!canRemoveFromLayer(layer, member.second)) { + return false; + } + } + return true; + case Node::KIND_SET: + return node->getMembers().empty(); + default: // Node::KIND_PROPERTY, Node::KIND_LOCALIZED_VALUE + return true; + } +} + +} + +class Components::WriteThread: public salhelper::Thread { +public: + WriteThread( + rtl::Reference< WriteThread > * reference, Components & components, + OUString url, Data const & data); + + void flush() { delay_.set(); } + +private: + virtual ~WriteThread() override {} + + virtual void execute() override; + + rtl::Reference< WriteThread > * reference_; + Components & components_; + OUString url_; + Data const & data_; + osl::Condition delay_; + std::shared_ptr<osl::Mutex> lock_; +}; + +Components::WriteThread::WriteThread( + rtl::Reference< WriteThread > * reference, Components & components, + OUString url, Data const & data): + Thread("configmgrWriter"), reference_(reference), components_(components), + url_(std::move(url)), data_(data), + lock_( lock() ) +{ + assert(reference != nullptr); +} + +void Components::WriteThread::execute() { + delay_.wait(std::chrono::seconds(1)); // must not throw; result_error is harmless and ignored + osl::MutexGuard g(*lock_); // must not throw + try { + try { + writeModFile(components_, url_, data_); + } catch (css::uno::RuntimeException &) { + // Ignore write errors, instead of aborting: + TOOLS_WARN_EXCEPTION("configmgr", "error writing modifications"); + } + } catch (...) { + reference_->clear(); + throw; + } + reference_->clear(); +} + +Components & Components::getSingleton( + css::uno::Reference< css::uno::XComponentContext > const & context) +{ + assert(context.is()); + static Components singleton(context); + return singleton; +} + +bool Components::allLocales(std::u16string_view locale) { + return locale == u"*"; +} + +rtl::Reference< Node > Components::resolvePathRepresentation( + OUString const & pathRepresentation, + OUString * canonicRepresentation, std::vector<OUString> * path, int * finalizedLayer) + const +{ + return data_.resolvePathRepresentation( + pathRepresentation, canonicRepresentation, path, finalizedLayer); +} + +rtl::Reference< Node > Components::getTemplate(OUString const & fullName) const +{ + return data_.getTemplate(Data::NO_LAYER, fullName); +} + +void Components::addRootAccess(rtl::Reference< RootAccess > const & access) { + roots_.insert(access.get()); +} + +void Components::removeRootAccess(RootAccess * access) { + roots_.erase(access); +} + +void Components::initGlobalBroadcaster( + Modifications const & modifications, + rtl::Reference< RootAccess > const & exclude, Broadcaster * broadcaster) +{ + //TODO: Iterate only over roots w/ listeners: + for (auto const& elemRoot : roots_) + { + rtl::Reference< RootAccess > root; + if (elemRoot->acquireCounting() > 1) { + root.set(elemRoot); // must not throw + } + elemRoot->releaseNondeleting(); + if (root.is()) { + if (root != exclude) { + std::vector<OUString> path(root->getAbsolutePath()); + Modifications::Node const * mods = &modifications.getRoot(); + for (auto const& pathElem : path) + { + Modifications::Node::Children::const_iterator k( + mods->children.find(pathElem)); + if (k == mods->children.end()) { + mods = nullptr; + break; + } + mods = &k->second; + } + //TODO: If the complete tree of which root is a part is deleted, + // or replaced, mods will be null, but some of the listeners + // from within root should probably fire nonetheless: + if (mods != nullptr) { + root->initBroadcaster(*mods, broadcaster); + } + } + } + } +} + +void Components::addModification(std::vector<OUString> const & path) { + data_.modifications.add(path); +} + +void Components::writeModifications() { + + if (data_.modifications.empty()) + return; + + switch (modificationTarget_) { + case ModificationTarget::None: + break; + case ModificationTarget::File: + if (!writeThread_.is()) { + writeThread_ = new WriteThread( + &writeThread_, *this, modificationFileUrl_, data_); + writeThread_->launch(); + } + break; + case ModificationTarget::Dconf: +#if ENABLE_DCONF + dconf::writeModifications(*this, data_); +#endif + break; + } +} + +void Components::flushModifications() { + rtl::Reference< WriteThread > thread; + { + osl::MutexGuard g(*lock_); + thread = writeThread_; + } + if (thread.is()) { + thread->flush(); + thread->join(); + } +} + +void Components::insertExtensionXcsFile( + bool shared, OUString const & fileUri) +{ + int layer = getExtensionLayer(shared); + try { + parseXcsFile(fileUri, layer, data_, nullptr, nullptr, nullptr); + } catch (css::container::NoSuchElementException & e) { + throw css::uno::RuntimeException( + "insertExtensionXcsFile does not exist: " + e.Message); + } +} + +void Components::insertExtensionXcuFile( + bool shared, OUString const & fileUri, Modifications * modifications) +{ + assert(modifications != nullptr); + int layer = getExtensionLayer(shared) + 1; + Additions * adds = data_.addExtensionXcuAdditions(fileUri, layer); + try { + parseXcuFile(fileUri, layer, data_, nullptr, modifications, adds); + } catch (css::container::NoSuchElementException & e) { + data_.removeExtensionXcuAdditions(fileUri); + throw css::uno::RuntimeException( + "insertExtensionXcuFile does not exist: " + e.Message); + } +} + +void Components::removeExtensionXcuFile( + OUString const & fileUri, Modifications * modifications) +{ + //TODO: Ideally, exactly the data coming from the specified xcu file would + // be removed. However, not enough information is recorded in the in-memory + // data structures to do so. So, as a workaround, all those set elements + // that were freshly added by the xcu and have afterwards been left + // unchanged or have only had their properties changed in the user layer are + // removed (and nothing else). The heuristic to determine + // whether a node has been left unchanged is to check the layer ID (as + // usual) and additionally to check that the node does not recursively + // contain any non-empty sets (multiple extension xcu files are merged into + // one layer, so checking layer ID alone is not enough). Since + // item->additions records all additions of set members in textual order, + // the latter check works well when iterating through item->additions in + // reverse order. + assert(modifications != nullptr); + rtl::Reference< Data::ExtensionXcu > item( + data_.removeExtensionXcuAdditions(fileUri)); + if (!item.is()) + return; + + for (Additions::reverse_iterator i(item->additions.rbegin()); + i != item->additions.rend(); ++i) + { + rtl::Reference< Node > parent; + NodeMap const * map = &data_.getComponents(); + rtl::Reference< Node > node; + for (auto const& j : *i) + { + parent = node; + node = map->findNode(Data::NO_LAYER, j); + if (!node.is()) { + break; + } + map = &node->getMembers(); + } + if (node.is()) { + assert(parent.is()); + if (parent->kind() == Node::KIND_SET) { + assert( + node->kind() == Node::KIND_GROUP || + node->kind() == Node::KIND_SET); + if (canRemoveFromLayer(item->layer, node)) { + parent->getMembers().erase(i->back()); + data_.modifications.remove(*i); + modifications->add(*i); + } + } + } + } + writeModifications(); +} + +void Components::insertModificationXcuFile( + OUString const & fileUri, + std::set< OUString > const & includedPaths, + std::set< OUString > const & excludedPaths, + Modifications * modifications) +{ + assert(modifications != nullptr); + Partial part(includedPaths, excludedPaths); + try { + parseFileLeniently( + &parseXcuFile, fileUri, Data::NO_LAYER, &part, modifications, nullptr); + } catch (const css::container::NoSuchElementException &) { + TOOLS_WARN_EXCEPTION( + "configmgr", + "error inserting non-existing \"" << fileUri << "\""); + } +} + +css::beans::Optional< css::uno::Any > Components::getExternalValue( + std::u16string_view descriptor) +{ + size_t i = descriptor.find(' '); + if (i == 0 || i == std::u16string_view::npos) { + throw css::uno::RuntimeException( + OUString::Concat("bad external value descriptor ") + descriptor); + } + //TODO: Do not make calls with mutex locked: + OUString name(descriptor.substr(0, i)); + ExternalServices::iterator j(externalServices_.find(name)); + if (j == externalServices_.end()) { + css::uno::Reference< css::uno::XInterface > service; + try { + service = context_->getServiceManager()->createInstanceWithContext( + name, context_); + } catch (const css::uno::RuntimeException &) { + // Assuming these exceptions are real errors: + throw; + } catch (const css::uno::Exception &) { + // Assuming these exceptions indicate that the service is not + // installed: + TOOLS_WARN_EXCEPTION( + "configmgr", + "createInstance(" << name << ") failed"); + } + css::uno::Reference< css::beans::XPropertySet > propset; + if (service.is()) { + propset.set( service, css::uno::UNO_QUERY_THROW); + } + j = externalServices_.emplace(name, propset).first; + } + css::beans::Optional< css::uno::Any > value; + if (j->second.is()) { + try { + if (!(j->second->getPropertyValue(OUString(descriptor.substr(i + 1))) >>= + value)) + { + throw css::uno::RuntimeException( + OUString::Concat("cannot obtain external value through ") + descriptor); + } + } catch (css::beans::UnknownPropertyException & e) { + throw css::uno::RuntimeException( + "unknown external value descriptor ID: " + e.Message); + } catch (css::lang::WrappedTargetException & e) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "cannot obtain external value: " + e.Message, + nullptr, anyEx ); + } + } + return value; +} + +Components::Components( + css::uno::Reference< css::uno::XComponentContext > const & context): + context_(context), sharedExtensionLayer_(-1), userExtensionLayer_(-1), + modificationTarget_(ModificationTarget::None) +{ + assert(context.is()); + lock_ = lock(); + OUString conf(expand("${CONFIGURATION_LAYERS}")); + int layer = 0; + for (sal_Int32 i = 0;;) { + while (i != conf.getLength() && conf[i] == ' ') { + ++i; + } + if (i == conf.getLength()) { + break; + } + if (modificationTarget_ != ModificationTarget::None) { + throw css::uno::RuntimeException( + "CONFIGURATION_LAYERS: modification target layer followed by" + " further layers"); + } + sal_Int32 c = i; + for (;; ++c) { + if (c == conf.getLength() || conf[c] == ' ') { + throw css::uno::RuntimeException( + "CONFIGURATION_LAYERS: missing ':' in \"" + conf + "\""); + } + if (conf[c] == ':') { + break; + } + } + sal_Int32 n = conf.indexOf(' ', c + 1); + if (n == -1) { + n = conf.getLength(); + } + OUString type(conf.copy(i, c - i)); + OUString url(conf.copy(c + 1, n - c - 1)); + if (type == "xcsxcu") { + sal_uInt32 nStartTime = osl_getGlobalTimer(); + parseXcsXcuLayer(layer, url); + SAL_INFO("configmgr", "parseXcsXcuLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms"); + layer += 2; //TODO: overflow + } else if (type == "bundledext") { + parseXcsXcuIniLayer(layer, url, false); + layer += 2; //TODO: overflow + } else if (type == "sharedext") { + if (sharedExtensionLayer_ != -1) { + throw css::uno::RuntimeException( + "CONFIGURATION_LAYERS: multiple \"sharedext\" layers"); + } + sharedExtensionLayer_ = layer; + parseXcsXcuIniLayer(layer, url, true); + layer += 2; //TODO: overflow + } else if (type == "userext") { + if (userExtensionLayer_ != -1) { + throw css::uno::RuntimeException( + "CONFIGURATION_LAYERS: multiple \"userext\" layers"); + } + userExtensionLayer_ = layer; + parseXcsXcuIniLayer(layer, url, true); + layer += 2; //TODO: overflow + } else if (type == "res") { + sal_uInt32 nStartTime = osl_getGlobalTimer(); + parseResLayer(layer, url); + SAL_INFO("configmgr", "parseResLayer() took " << (osl_getGlobalTimer() - nStartTime) << " ms"); + ++layer; //TODO: overflow +#if ENABLE_DCONF + } else if (type == "dconf") { + if (url == "!") { + modificationTarget_ = ModificationTarget::Dconf; + dconf::readLayer(data_, Data::NO_LAYER); + } else if (url == "*") { + dconf::readLayer(data_, layer); + } else { + throw css::uno::RuntimeException( + "CONFIGURATION_LAYERS: unknown \"dconf\" kind \"" + url + + "\""); + } + ++layer; //TODO: overflow +#endif +#if defined(_WIN32) + } else if (type == "winreg") { + WinRegType eType; + if (url == "LOCAL_MACHINE" || url.isEmpty()/*backwards comp.*/) { + eType = WinRegType::LOCAL_MACHINE; + } else if (url == "CURRENT_USER") { + eType = WinRegType::CURRENT_USER; + } else { + throw css::uno::RuntimeException( + "CONFIGURATION_LAYERS: unknown \"winreg\" kind \"" + url + + "\""); + } + OUString aTempFileURL; + if (dumpWindowsRegistry(&aTempFileURL, eType)) { + parseFileLeniently(&parseXcuFile, aTempFileURL, layer, nullptr, nullptr, nullptr); + if (!getenv("SAL_CONFIG_WINREG_RETAIN_TMP")) + osl::File::remove(aTempFileURL); + } + ++layer; //TODO: overflow +#endif + } else if (type == "user") { + bool write; + if (url.startsWith("!", &url)) { + write = true; + } else if (url.startsWith("*", &url)) { + write = false; + } else { + write = true; // for backwards compatibility + } + if (url.isEmpty()) { + throw css::uno::RuntimeException( + "CONFIGURATION_LAYERS: empty \"user\" URL"); + } + bool ignore = false; +#if ENABLE_DCONF + if (write) { + OUString token( + expand("${SYSUSERCONFIG}/libreoffice/dconfwrite")); + osl::DirectoryItem it; + osl::FileBase::RC e = osl::DirectoryItem::get(token, it); + ignore = e == osl::FileBase::E_None; + SAL_INFO( + "configmgr", + "dconf write (<" << token << "> " << +e << "): " + << int(ignore)); + if (ignore) { + modificationTarget_ = ModificationTarget::Dconf; + } + } +#endif + if (!ignore) { + if (write) { + modificationTarget_ = ModificationTarget::File; + modificationFileUrl_ = url; + } + parseModificationLayer(write ? Data::NO_LAYER : layer, url); + } + ++layer; //TODO: overflow + } else { + throw css::uno::RuntimeException( + "CONFIGURATION_LAYERS: unknown layer type \"" + type + "\""); + } + i = n; + } +} + +Components::~Components() +{ + // get flag if _exit was already called which is a sign to not secure user config. + // this is used for win only currently where calling _exit() unfortunately still + // calls destructors (what is not wanted). May be needed for other systems, too + // (unknown yet) but can do no harm + const bool bExitWasCalled(comphelper::BackupFileHelper::getExitWasCalled()); + +#ifndef _WIN32 + // we can add a SAL_WARN here for other systems where the destructor gets called after + // an _exit() call. Still safe - the getExitWasCalled() is used, but a hint that _exit + // behaves different on a system + SAL_WARN_IF(bExitWasCalled, "configmgr", "Components::~Components() called after _exit() call"); +#endif + + if (bExitWasCalled) + { + // do not write, re-join threads + osl::MutexGuard g(*lock_); + + if (writeThread_.is()) + { + writeThread_->join(); + } + } + else + { + // write changes + flushModifications(); + } + + for (auto const& rootElem : roots_) + { + rootElem->setAlive(false); + } +} + +void Components::parseFileLeniently( + FileParser * parseFile, OUString const & url, int layer, + Partial const * partial, Modifications * modifications, + Additions * additions) +{ + assert(parseFile != nullptr); + try { + (*parseFile)(url, layer, data_, partial, modifications, additions); + } catch (const css::container::NoSuchElementException &) { + throw; + } catch (const css::uno::Exception &) { //TODO: more specific exception catching + // Ignore invalid XML files, instead of completely preventing OOo from + // starting: + TOOLS_WARN_EXCEPTION( + "configmgr", + "error reading \"" << url << "\""); + } +} + +void Components::parseFiles( + int layer, OUString const & extension, FileParser * parseFile, + OUString const & url, bool recursive) +{ + osl::Directory dir(url); + switch (dir.open()) { + case osl::FileBase::E_None: + break; + case osl::FileBase::E_NOENT: + if (!recursive) { + return; + } + [[fallthrough]]; + default: + throw css::uno::RuntimeException( + "cannot open directory " + url); + } + for (;;) { + osl::DirectoryItem i; + osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32); + if (rc == osl::FileBase::E_NOENT) { + break; + } + if (rc != osl::FileBase::E_None) { + throw css::uno::RuntimeException( + "cannot iterate directory " + url); + } + osl::FileStatus stat( + osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName | + osl_FileStatus_Mask_FileURL); + if (i.getFileStatus(stat) != osl::FileBase::E_None) { + throw css::uno::RuntimeException( + "cannot stat in directory " + url); + } + if (stat.getFileType() == osl::FileStatus::Directory) { //TODO: symlinks + parseFiles(layer, extension, parseFile, stat.getFileURL(), true); + } else { + OUString file(stat.getFileName()); + if (file.endsWith(extension)) { + try { + parseFileLeniently( + parseFile, stat.getFileURL(), layer, nullptr, nullptr, nullptr); + } catch (css::container::NoSuchElementException & e) { + if (stat.getFileType() == osl::FileStatus::Link) { + SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">"); + continue; + } + throw css::uno::RuntimeException( + "stat'ed file does not exist: " + e.Message); + } + } + } + } +} + +void Components::parseFileList( + int layer, FileParser * parseFile, std::u16string_view urls, + bool recordAdditions) +{ + for (sal_Int32 i = 0;;) { + OUString url(o3tl::getToken(urls, 0, ' ', i)); + if (!url.isEmpty()) { + Additions * adds = nullptr; + if (recordAdditions) { + adds = data_.addExtensionXcuAdditions(url, layer); + } + try { + parseFileLeniently(parseFile, url, layer, nullptr, nullptr, adds); + } catch (const css::container::NoSuchElementException &) { + TOOLS_WARN_EXCEPTION("configmgr", "file does not exist"); + if (adds != nullptr) { + data_.removeExtensionXcuAdditions(url); + } + } + } + if (i == -1) { + break; + } + } +} + +void Components::parseXcdFiles(int layer, OUString const & url) { + osl::Directory dir(url); + switch (dir.open()) { + case osl::FileBase::E_None: + break; + case osl::FileBase::E_NOENT: + return; + default: + throw css::uno::RuntimeException( + "cannot open directory " + url); + } + UnresolvedVector unres; + std::set< OUString > existingDeps; + std::set< OUString > processedDeps; + for (;;) { + osl::DirectoryItem i; + osl::FileBase::RC rc = dir.getNextItem(i, SAL_MAX_UINT32); + if (rc == osl::FileBase::E_NOENT) { + break; + } + if (rc != osl::FileBase::E_None) { + throw css::uno::RuntimeException( + "cannot iterate directory " + url); + } + osl::FileStatus stat( + osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName | + osl_FileStatus_Mask_FileURL); + if (i.getFileStatus(stat) != osl::FileBase::E_None) { + throw css::uno::RuntimeException( + "cannot stat in directory " + url); + } + if (stat.getFileType() != osl::FileStatus::Directory) { //TODO: symlinks + OUString file(stat.getFileName()); + OUString name; + if (file.endsWith(".xcd", &name)) { + existingDeps.insert(name); + rtl::Reference< ParseManager > manager; + try { + manager = new ParseManager( + stat.getFileURL(), + new XcdParser(layer, processedDeps, data_)); + } catch (css::container::NoSuchElementException & e) { + if (stat.getFileType() == osl::FileStatus::Link) { + SAL_WARN("configmgr", "dangling link <" << stat.getFileURL() << ">"); + continue; + } + throw css::uno::RuntimeException( + "stat'ed file does not exist: " + e.Message); + } + if (manager->parse(nullptr)) { + processedDeps.insert(name); + } else { + unres.emplace_back(name, manager); + } + } + } + } + while (!unres.empty()) { + bool resolved = false; + for (UnresolvedVector::iterator i(unres.begin()); i != unres.end();) { + if (i->manager->parse(&existingDeps)) { + processedDeps.insert(i->name); + i = unres.erase(i); + resolved = true; + } else { + ++i; + } + } + if (!resolved) { + throw css::uno::RuntimeException( + "xcd: unresolved dependencies in " + url); + } + } +} + +void Components::parseXcsXcuLayer(int layer, OUString const & url) { + parseXcdFiles(layer, url); + parseFiles(layer, ".xcs", &parseXcsFile, url + "/schema", false); + parseFiles(layer + 1, ".xcu", &parseXcuFile, url + "/data", false); +} + +void Components::parseXcsXcuIniLayer( + int layer, OUString const & url, bool recordAdditions) +{ + // Check if ini file exists (otherwise .override would still read global + // SCHEMA/DATA variables, which could interfere with unrelated environment + // variables): + if (rtl::Bootstrap(url).getHandle() == nullptr) return; + + OUStringBuffer prefix("${.override:"); + for (sal_Int32 i = 0; i != url.getLength(); ++i) { + sal_Unicode c = url[i]; + switch (c) { + case '$': + case ':': + case '\\': + prefix.append('\\'); + [[fallthrough]]; + default: + prefix.append(c); + } + } + prefix.append(':'); + OUString urls(prefix + "SCHEMA}"); + rtl::Bootstrap::expandMacros(urls); + if (!urls.isEmpty()) { + parseFileList(layer, &parseXcsFile, urls, false); + } + urls = prefix.makeStringAndClear() + "DATA}"; + rtl::Bootstrap::expandMacros(urls); + if (!urls.isEmpty()) { + parseFileList(layer + 1, &parseXcuFile, urls, recordAdditions); + } +} + +void Components::parseResLayer(int layer, std::u16string_view url) { + OUString resUrl(OUString::Concat(url) + "/res"); + parseXcdFiles(layer, resUrl); + parseFiles(layer, ".xcu", &parseXcuFile, resUrl, false); +} + +void Components::parseModificationLayer(int layer, OUString const & url) { + try { + parseFileLeniently(&parseXcuFile, url, layer, nullptr, nullptr, nullptr); + } catch (css::container::NoSuchElementException &) { + SAL_INFO( + "configmgr", "user registrymodifications.xcu does not (yet) exist"); + // Migrate old user layer data (can be removed once migration is no + // longer relevant, probably OOo 4; also see hack for xsi namespace in + // xmlreader::XmlReader::registerNamespaceIri): + parseFiles( + layer, ".xcu", &parseXcuFile, + expand( + "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") + ":UserInstallation}/user/registry/data"), + false); + } +} + +int Components::getExtensionLayer(bool shared) const { + int layer = shared ? sharedExtensionLayer_ : userExtensionLayer_; + if (layer == -1) { + throw css::uno::RuntimeException( + "insert extension xcs/xcu file into undefined layer"); + } + return layer; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |