diff options
Diffstat (limited to 'desktop/source/deployment/registry/dp_backend.cxx')
-rw-r--r-- | desktop/source/deployment/registry/dp_backend.cxx | 771 |
1 files changed, 771 insertions, 0 deletions
diff --git a/desktop/source/deployment/registry/dp_backend.cxx b/desktop/source/deployment/registry/dp_backend.cxx new file mode 100644 index 000000000..2a681fe87 --- /dev/null +++ b/desktop/source/deployment/registry/dp_backend.cxx @@ -0,0 +1,771 @@ +/* -*- 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 <dp_backend.h> +#include <dp_misc.h> +#include <dp_ucb.h> +#include <rtl/ustring.hxx> +#include <rtl/bootstrap.hxx> +#include <sal/log.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/unwrapargs.hxx> +#include <ucbhelper/content.hxx> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/deployment/DeploymentException.hpp> +#include <com/sun/star/deployment/ExtensionRemovedException.hpp> +#include <com/sun/star/deployment/InvalidRemovedParameterException.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/ucb/CommandFailedException.hpp> +#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp> +#include <com/sun/star/ucb/IOErrorCode.hpp> +#include <com/sun/star/beans/StringPair.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <tools/diagnose_ex.h> +#include <unotools/tempfile.hxx> +#include <optional> +#include <utility> + +using namespace ::dp_misc; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; + +namespace dp_registry::backend { + + +PackageRegistryBackend::~PackageRegistryBackend() +{ +} + + +void PackageRegistryBackend::disposing( lang::EventObject const & event ) +{ + Reference<deployment::XPackage> xPackage( + event.Source, UNO_QUERY_THROW ); + OUString url( xPackage->getURL() ); + ::osl::MutexGuard guard( m_aMutex ); + if ( m_bound.erase( url ) != 1 ) + { + SAL_WARN("desktop.deployment", "erase(" << url << ") != 1"); + } +} + + +PackageRegistryBackend::PackageRegistryBackend( + Sequence<Any> const & args, + Reference<XComponentContext> const & xContext ) + : t_BackendBase( m_aMutex ), + m_xComponentContext( xContext ), + m_eContext( Context::Unknown ) +{ + assert(xContext.is()); + std::optional<OUString> cachePath; + std::optional<bool> readOnly; + comphelper::unwrapArgs( args, m_context, cachePath, readOnly ); + if (cachePath) + m_cachePath = *cachePath; + + if ( m_context == "user" ) + m_eContext = Context::User; + else if ( m_context == "shared" ) + m_eContext = Context::Shared; + else if ( m_context == "bundled" ) + m_eContext = Context::Bundled; + else if ( m_context == "tmp" ) + m_eContext = Context::Tmp; + else if (m_context.matchIgnoreAsciiCase("vnd.sun.star.tdoc:/")) + m_eContext = Context::Document; + else + m_eContext = Context::Unknown; +} + + +void PackageRegistryBackend::check() +{ + ::osl::MutexGuard guard( m_aMutex ); + if (rBHelper.bInDispose || rBHelper.bDisposed) { + throw lang::DisposedException( + "PackageRegistryBackend instance has already been disposed!", + static_cast<OWeakObject *>(this) ); + } +} + + +void PackageRegistryBackend::disposing() +{ + try { + for (auto const& elem : m_bound) + elem.second->removeEventListener(this); + m_bound.clear(); + m_xComponentContext.clear(); + WeakComponentImplHelperBase::disposing(); + } + catch (const RuntimeException &) { + throw; + } + catch (const Exception &) { + Any exc( ::cppu::getCaughtException() ); + throw lang::WrappedTargetRuntimeException( + "caught unexpected exception while disposing!", + static_cast<OWeakObject *>(this), exc ); + } +} + +// XPackageRegistry + +Reference<deployment::XPackage> PackageRegistryBackend::bindPackage( + OUString const & url, OUString const & mediaType, sal_Bool bRemoved, + OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) +{ + ::osl::ResettableMutexGuard guard( m_aMutex ); + check(); + + t_string2ref::const_iterator const iFind( m_bound.find( url ) ); + if (iFind != m_bound.end()) + { + Reference<deployment::XPackage> xPackage( iFind->second ); + if (xPackage.is()) + { + if (!mediaType.isEmpty() && + mediaType != xPackage->getPackageType()->getMediaType()) + throw lang::IllegalArgumentException + ("XPackageRegistry::bindPackage: media type does not match", + static_cast<OWeakObject*>(this), 1); + if (xPackage->isRemoved() != bRemoved) + throw deployment::InvalidRemovedParameterException( + "XPackageRegistry::bindPackage: bRemoved parameter does not match", + static_cast<OWeakObject*>(this), xPackage->isRemoved(), xPackage); + return xPackage; + } + } + + guard.clear(); + + Reference<deployment::XPackage> xNewPackage; + try { + xNewPackage = bindPackage_( url, mediaType, bRemoved, + identifier, xCmdEnv ); + } + catch (const RuntimeException &) { + throw; + } + catch (const CommandFailedException &) { + throw; + } + catch (const deployment::DeploymentException &) { + throw; + } + catch (const Exception &) { + Any exc( ::cppu::getCaughtException() ); + throw deployment::DeploymentException( + "Error binding package: " + url, + static_cast<OWeakObject *>(this), exc ); + } + + guard.reset(); + + std::pair< t_string2ref::iterator, bool > insertion( + m_bound.emplace( url, xNewPackage ) ); + if (insertion.second) + { // first insertion + SAL_WARN_IF( + Reference<XInterface>(insertion.first->second) != xNewPackage, + "desktop.deployment", "mismatch"); + } + else + { // found existing entry + Reference<deployment::XPackage> xPackage( insertion.first->second ); + if (xPackage.is()) + return xPackage; + insertion.first->second = xNewPackage; + } + + guard.clear(); + xNewPackage->addEventListener( this ); // listen for disposing events + return xNewPackage; +} + +OUString PackageRegistryBackend::createFolder( + Reference<ucb::XCommandEnvironment> const & xCmdEnv) +{ + const OUString sDataFolder = makeURL(getCachePath(), OUString()); + //make sure the folder exist + ucbhelper::Content dataContent; + ::dp_misc::create_folder(&dataContent, sDataFolder, xCmdEnv); + + const OUString baseDir(sDataFolder); + ::utl::TempFile aTemp(&baseDir, true); + const OUString& url = aTemp.GetURL(); + return sDataFolder + url.subView(url.lastIndexOf('/')); +} + +//folderURL can have the extension .tmp or .tmp_ +//Before OOo 3.4 the created a tmp file with osl_createTempFile and +//then created a Folder with a same name and a trailing '_' +//If the folderURL has no '_' then there is no corresponding tmp file. +void PackageRegistryBackend::deleteTempFolder( + OUString const & folderUrl) +{ + if (!folderUrl.isEmpty()) + { + erase_path( folderUrl, Reference<XCommandEnvironment>(), + false /* no throw: ignore errors */ ); + + if (folderUrl.endsWith("_")) + { + const OUString tempFile = folderUrl.copy(0, folderUrl.getLength() - 1); + erase_path( tempFile, Reference<XCommandEnvironment>(), + false /* no throw: ignore errors */ ); + } + } +} + +//usedFolders can contain folder names which have the extension .tmp or .tmp_ +//Before OOo 3.4 we created a tmp file with osl_createTempFile and +//then created a Folder with a same name and a trailing '_' +//If the folderURL has no '_' then there is no corresponding tmp file. +void PackageRegistryBackend::deleteUnusedFolders( + std::vector< OUString> const & usedFolders) +{ + try + { + const OUString sDataFolder = makeURL(getCachePath(), OUString()); + ::ucbhelper::Content tempFolder( + sDataFolder, Reference<ucb::XCommandEnvironment>(), m_xComponentContext); + + Reference<sdbc::XResultSet> xResultSet( + StrTitle::createCursor( tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) ); + + // get all temp directories: + std::vector<OUString> tempEntries; + + while (xResultSet->next()) + { + OUString title( + Reference<sdbc::XRow>( + xResultSet, UNO_QUERY_THROW )->getString( + 1 /* Title */ ) ); + + if (title.endsWith(".tmp")) + tempEntries.push_back( + makeURLAppendSysPathSegment(sDataFolder, title)); + } + + for (const OUString & tempEntrie : tempEntries) + { + if (std::find( usedFolders.begin(), usedFolders.end(), tempEntrie ) == + usedFolders.end()) + { + deleteTempFolder(tempEntrie); + } + } + } + catch (const ucb::InteractiveAugmentedIOException& e) + { + //In case the folder containing all the data folder does not + //exist yet, we ignore the exception + if (e.Code != ucb::IOErrorCode_NOT_EXISTING) + throw; + } + +} + + +Package::~Package() +{ +} + + +Package::Package( ::rtl::Reference<PackageRegistryBackend> myBackend, + OUString url, + OUString aName, + OUString displayName, + Reference<deployment::XPackageTypeInfo> const & xPackageType, + bool bRemoved, + OUString identifier) + : t_PackageBase( m_aMutex ), + m_myBackend(std::move( myBackend )), + m_url(std::move( url )), + m_name(std::move( aName )), + m_displayName(std::move( displayName )), + m_xPackageType( xPackageType ), + m_bRemoved(bRemoved), + m_identifier(std::move(identifier)) +{ + if (m_bRemoved) + { + //We use the last segment of the URL + SAL_WARN_IF( + !m_name.isEmpty(), "desktop.deployment", "non-empty m_name"); + OUString name = m_url; + rtl::Bootstrap::expandMacros(name); + sal_Int32 index = name.lastIndexOf('/'); + if (index != -1 && index < name.getLength()) + m_name = name.copy(index + 1); + } +} + + +void Package::disposing() +{ + m_myBackend.clear(); + WeakComponentImplHelperBase::disposing(); +} + + +void Package::check() const +{ + ::osl::MutexGuard guard( m_aMutex ); + if (rBHelper.bInDispose || rBHelper.bDisposed) { + throw lang::DisposedException( + "Package instance has already been disposed!", + static_cast<OWeakObject *>(const_cast<Package *>(this))); + } +} + +// XComponent + +void Package::dispose() +{ + //Do not call check here. We must not throw an exception here if the object + //is being disposed or is already disposed. See com.sun.star.lang.XComponent + WeakComponentImplHelperBase::dispose(); +} + + +void Package::addEventListener( + Reference<lang::XEventListener> const & xListener ) +{ + //Do not call check here. We must not throw an exception here if the object + //is being disposed or is already disposed. See com.sun.star.lang.XComponent + WeakComponentImplHelperBase::addEventListener( xListener ); +} + + +void Package::removeEventListener( + Reference<lang::XEventListener> const & xListener ) +{ + //Do not call check here. We must not throw an exception here if the object + //is being disposed or is already disposed. See com.sun.star.lang.XComponent + WeakComponentImplHelperBase::removeEventListener( xListener ); +} + +// XModifyBroadcaster + +void Package::addModifyListener( + Reference<util::XModifyListener> const & xListener ) +{ + check(); + rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener ); +} + + +void Package::removeModifyListener( + Reference<util::XModifyListener> const & xListener ) +{ + check(); + rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener ); +} + + +void Package::checkAborted( + ::rtl::Reference<AbortChannel> const & abortChannel ) +{ + if (abortChannel.is() && abortChannel->isAborted()) { + throw CommandAbortedException( + "abort!", static_cast<OWeakObject *>(this) ); + } +} + +// XPackage + +Reference<task::XAbortChannel> Package::createAbortChannel() +{ + check(); + return new AbortChannel; +} + + +sal_Bool Package::isBundle() +{ + return false; // default +} + + +::sal_Int32 Package::checkPrerequisites( + const css::uno::Reference< css::task::XAbortChannel >&, + const css::uno::Reference< css::ucb::XCommandEnvironment >&, + sal_Bool) +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + return 0; +} + + +sal_Bool Package::checkDependencies( + const css::uno::Reference< css::ucb::XCommandEnvironment >& ) +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + return true; +} + + +Sequence< Reference<deployment::XPackage> > Package::getBundle( + Reference<task::XAbortChannel> const &, + Reference<XCommandEnvironment> const & ) +{ + return Sequence< Reference<deployment::XPackage> >(); +} + + +OUString Package::getName() +{ + return m_name; +} + +beans::Optional<OUString> Package::getIdentifier() +{ + if (m_bRemoved) + return beans::Optional<OUString>(true, m_identifier); + + return beans::Optional<OUString>(); +} + + +OUString Package::getVersion() +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + return OUString(); +} + + +OUString Package::getURL() +{ + return m_url; +} + + +OUString Package::getDisplayName() +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + return m_displayName; +} + + +OUString Package::getDescription() +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + return OUString(); +} + + +OUString Package::getLicenseText() +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + return OUString(); +} + + +Sequence<OUString> Package::getUpdateInformationURLs() +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + return Sequence<OUString>(); +} + + +css::beans::StringPair Package::getPublisherInfo() +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + css::beans::StringPair aEmptyPair; + return aEmptyPair; +} + + +uno::Reference< css::graphic::XGraphic > Package::getIcon( sal_Bool /*bHighContrast*/ ) +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + + uno::Reference< css::graphic::XGraphic > aEmpty; + return aEmpty; +} + + +Reference<deployment::XPackageTypeInfo> Package::getPackageType() +{ + return m_xPackageType; +} + +void Package::exportTo( + OUString const & destFolderURL, OUString const & newTitle, + sal_Int32 nameClashAction, Reference<XCommandEnvironment> const & xCmdEnv ) +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + + ::ucbhelper::Content destFolder( destFolderURL, xCmdEnv, getMyBackend()->getComponentContext() ); + ::ucbhelper::Content sourceContent( getURL(), xCmdEnv, getMyBackend()->getComponentContext() ); + bool bOk=true; + try + { + destFolder.transferContent( + sourceContent, ::ucbhelper::InsertOperation::Copy, + newTitle, nameClashAction); + } + catch (const css::ucb::ContentCreationException&) + { + bOk = false; + } + + if (!bOk) + throw RuntimeException( "UCB transferContent() failed!", nullptr ); +} + +void Package::fireModified() +{ + ::cppu::OInterfaceContainerHelper * container = rBHelper.getContainer( + cppu::UnoType<util::XModifyListener>::get() ); + if (container == nullptr) + return; + + const Sequence< Reference<XInterface> > elements( + container->getElements() ); + lang::EventObject evt( static_cast<OWeakObject *>(this) ); + for ( const Reference<XInterface>& x : elements ) + { + Reference<util::XModifyListener> xListener( x, UNO_QUERY ); + if (xListener.is()) + xListener->modified( evt ); + } +} + +// XPackage + +beans::Optional< beans::Ambiguous<sal_Bool> > Package::isRegistered( + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<XCommandEnvironment> const & xCmdEnv ) +{ + try { + ::osl::ResettableMutexGuard guard( m_aMutex ); + return isRegistered_( guard, + AbortChannel::get(xAbortChannel), + xCmdEnv ); + } + catch (const RuntimeException &) { + throw; + } + catch (const CommandFailedException &) { + throw; + } + catch (const CommandAbortedException &) { + throw; + } + catch (const deployment::DeploymentException &) { + throw; + } + catch (const Exception & e) { + Any exc( ::cppu::getCaughtException() ); + throw deployment::DeploymentException( + "unexpected " + exc.getValueTypeName() + ": " + e.Message, + static_cast<OWeakObject *>(this), exc ); + } +} + + +void Package::processPackage_impl( + bool doRegisterPackage, + bool startup, + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<XCommandEnvironment> const & xCmdEnv ) +{ + check(); + bool action = false; + + try { + try { + ::osl::ResettableMutexGuard guard( m_aMutex ); + beans::Optional< beans::Ambiguous<sal_Bool> > option( + isRegistered_( guard, AbortChannel::get(xAbortChannel), + xCmdEnv ) ); + action = (option.IsPresent && + (option.Value.IsAmbiguous || + (doRegisterPackage ? !option.Value.Value + : option.Value.Value))); + if (action) { + + OUString displayName = isRemoved() ? getName() : getDisplayName(); + ProgressLevel progress( + xCmdEnv, + (doRegisterPackage + ? PackageRegistryBackend::StrRegisteringPackage() + : PackageRegistryBackend::StrRevokingPackage()) + + displayName ); + processPackage_( guard, + doRegisterPackage, + startup, + AbortChannel::get(xAbortChannel), + xCmdEnv ); + } + } + catch (lang::IllegalArgumentException &) { + Any e(cppu::getCaughtException()); + throw deployment::DeploymentException( + ((doRegisterPackage + ? DpResId(RID_STR_ERROR_WHILE_REGISTERING) + : DpResId(RID_STR_ERROR_WHILE_REVOKING)) + + getDisplayName()), + static_cast< OWeakObject * >(this), e); + } + catch (const RuntimeException &) { + TOOLS_WARN_EXCEPTION("desktop.deployment", "unexpected"); + throw; + } + catch (const CommandFailedException &) { + throw; + } + catch (const CommandAbortedException &) { + throw; + } + catch (const deployment::DeploymentException &) { + throw; + } + catch (const Exception & e) { + Any exc( ::cppu::getCaughtException() ); + throw deployment::DeploymentException( + (doRegisterPackage + ? DpResId(RID_STR_ERROR_WHILE_REGISTERING) + : DpResId(RID_STR_ERROR_WHILE_REVOKING)) + + getDisplayName() + ": " + exc.getValueType().getTypeName() + " \"" + e.Message + + "\"", + static_cast<OWeakObject *>(this), exc ); + } + } + catch (...) { + if (action) + fireModified(); + throw; + } + if (action) + fireModified(); +} + + +void Package::registerPackage( + sal_Bool startup, + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<XCommandEnvironment> const & xCmdEnv ) +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + processPackage_impl( true /* register */, startup, xAbortChannel, xCmdEnv ); +} + + +void Package::revokePackage( + sal_Bool startup, + Reference<task::XAbortChannel> const & xAbortChannel, + Reference<XCommandEnvironment> const & xCmdEnv ) +{ + processPackage_impl( false /* revoke */, startup, xAbortChannel, xCmdEnv ); + +} + +PackageRegistryBackend * Package::getMyBackend() const +{ + PackageRegistryBackend * pBackend = m_myBackend.get(); + if (nullptr == pBackend) + { + //May throw a DisposedException + check(); + //We should never get here... + throw RuntimeException( + "Failed to get the BackendImpl", + static_cast<OWeakObject*>(const_cast<Package *>(this))); + } + return pBackend; +} + +OUString Package::getRepositoryName() +{ + PackageRegistryBackend * backEnd = getMyBackend(); + return backEnd->getContext(); +} + +beans::Optional< OUString > Package::getRegistrationDataURL() +{ + if (m_bRemoved) + throw deployment::ExtensionRemovedException(); + return beans::Optional<OUString>(); +} + +sal_Bool Package::isRemoved() +{ + return m_bRemoved; +} + +Package::TypeInfo::~TypeInfo() +{ +} + +// XPackageTypeInfo + +OUString Package::TypeInfo::getMediaType() +{ + return m_mediaType; +} + + +OUString Package::TypeInfo::getDescription() +{ + return getShortDescription(); +} + + +OUString Package::TypeInfo::getShortDescription() +{ + return m_shortDescr; +} + +OUString Package::TypeInfo::getFileFilter() +{ + return m_fileFilter; +} + +Any Package::TypeInfo::getIcon( sal_Bool /*highContrast*/, sal_Bool /*smallIcon*/ ) +{ + return Any(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |