summaryrefslogtreecommitdiffstats
path: root/desktop/source/deployment
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /desktop/source/deployment
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.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 'desktop/source/deployment')
-rw-r--r--desktop/source/deployment/deployment.component65
-rw-r--r--desktop/source/deployment/dp_log.cxx143
-rw-r--r--desktop/source/deployment/dp_persmap.cxx312
-rw-r--r--desktop/source/deployment/dp_xml.cxx51
-rw-r--r--desktop/source/deployment/gui/deploymentgui.component34
-rw-r--r--desktop/source/deployment/gui/dp_gui.h28
-rw-r--r--desktop/source/deployment/gui/dp_gui_dependencydialog.cxx42
-rw-r--r--desktop/source/deployment/gui/dp_gui_dependencydialog.hxx49
-rw-r--r--desktop/source/deployment/gui/dp_gui_dialog2.cxx1403
-rw-r--r--desktop/source/deployment/gui/dp_gui_dialog2.hxx267
-rw-r--r--desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx1115
-rw-r--r--desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx94
-rw-r--r--desktop/source/deployment/gui/dp_gui_extlistbox.cxx1144
-rw-r--r--desktop/source/deployment/gui/dp_gui_extlistbox.hxx214
-rw-r--r--desktop/source/deployment/gui/dp_gui_service.cxx316
-rw-r--r--desktop/source/deployment/gui/dp_gui_theextmgr.cxx537
-rw-r--r--desktop/source/deployment/gui/dp_gui_theextmgr.hxx127
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedata.hxx74
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedialog.cxx994
-rw-r--r--desktop/source/deployment/gui/dp_gui_updatedialog.hxx168
-rw-r--r--desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx665
-rw-r--r--desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx112
-rw-r--r--desktop/source/deployment/gui/license_dialog.cxx244
-rw-r--r--desktop/source/deployment/gui/license_dialog.hxx53
-rw-r--r--desktop/source/deployment/inc/dp_dependencies.hxx72
-rw-r--r--desktop/source/deployment/inc/dp_descriptioninfoset.hxx287
-rw-r--r--desktop/source/deployment/inc/dp_identifier.hxx84
-rw-r--r--desktop/source/deployment/inc/dp_interact.h140
-rw-r--r--desktop/source/deployment/inc/dp_misc_api.hxx31
-rw-r--r--desktop/source/deployment/inc/dp_package.hxx42
-rw-r--r--desktop/source/deployment/inc/dp_persmap.h60
-rw-r--r--desktop/source/deployment/inc/dp_platform.hxx41
-rw-r--r--desktop/source/deployment/inc/dp_registry.hxx40
-rw-r--r--desktop/source/deployment/inc/dp_resource.h30
-rw-r--r--desktop/source/deployment/inc/dp_ucb.h94
-rw-r--r--desktop/source/deployment/inc/dp_update.hxx136
-rw-r--r--desktop/source/deployment/inc/dp_version.hxx36
-rw-r--r--desktop/source/deployment/inc/dp_xml.h42
-rw-r--r--desktop/source/deployment/inc/lockfile.hxx89
-rw-r--r--desktop/source/deployment/manager/dp_activepackages.cxx217
-rw-r--r--desktop/source/deployment/manager/dp_activepackages.hxx95
-rw-r--r--desktop/source/deployment/manager/dp_commandenvironments.cxx246
-rw-r--r--desktop/source/deployment/manager/dp_commandenvironments.hxx139
-rw-r--r--desktop/source/deployment/manager/dp_extensionmanager.cxx1432
-rw-r--r--desktop/source/deployment/manager/dp_extensionmanager.hxx227
-rw-r--r--desktop/source/deployment/manager/dp_informationprovider.cxx338
-rw-r--r--desktop/source/deployment/manager/dp_manager.cxx1600
-rw-r--r--desktop/source/deployment/manager/dp_manager.h232
-rw-r--r--desktop/source/deployment/manager/dp_managerfac.cxx186
-rw-r--r--desktop/source/deployment/manager/dp_properties.cxx145
-rw-r--r--desktop/source/deployment/manager/dp_properties.hxx58
-rw-r--r--desktop/source/deployment/misc/dp_dependencies.cxx196
-rw-r--r--desktop/source/deployment/misc/dp_descriptioninfoset.cxx809
-rw-r--r--desktop/source/deployment/misc/dp_identifier.cxx56
-rw-r--r--desktop/source/deployment/misc/dp_interact.cxx137
-rw-r--r--desktop/source/deployment/misc/dp_misc.cxx561
-rw-r--r--desktop/source/deployment/misc/dp_platform.cxx207
-rw-r--r--desktop/source/deployment/misc/dp_resource.cxx42
-rw-r--r--desktop/source/deployment/misc/dp_ucb.cxx304
-rw-r--r--desktop/source/deployment/misc/dp_update.cxx408
-rw-r--r--desktop/source/deployment/misc/dp_version.cxx63
-rw-r--r--desktop/source/deployment/misc/lockfile.cxx211
-rw-r--r--desktop/source/deployment/registry/component/dp_compbackenddb.cxx131
-rw-r--r--desktop/source/deployment/registry/component/dp_compbackenddb.hxx95
-rw-r--r--desktop/source/deployment/registry/component/dp_component.cxx1720
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configuration.cxx818
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx161
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx74
-rw-r--r--desktop/source/deployment/registry/dp_backend.cxx771
-rw-r--r--desktop/source/deployment/registry/dp_backenddb.cxx655
-rw-r--r--desktop/source/deployment/registry/dp_registry.cxx528
-rw-r--r--desktop/source/deployment/registry/executable/dp_executable.cxx336
-rw-r--r--desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx64
-rw-r--r--desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx55
-rw-r--r--desktop/source/deployment/registry/help/README.md1
-rw-r--r--desktop/source/deployment/registry/help/dp_help.cxx621
-rw-r--r--desktop/source/deployment/registry/help/dp_helpbackenddb.cxx126
-rw-r--r--desktop/source/deployment/registry/help/dp_helpbackenddb.hxx70
-rw-r--r--desktop/source/deployment/registry/inc/dp_backend.h287
-rw-r--r--desktop/source/deployment/registry/inc/dp_backenddb.hxx165
-rw-r--r--desktop/source/deployment/registry/package/dp_extbackenddb.cxx111
-rw-r--r--desktop/source/deployment/registry/package/dp_extbackenddb.hxx69
-rw-r--r--desktop/source/deployment/registry/package/dp_package.cxx1594
-rw-r--r--desktop/source/deployment/registry/script/dp_lib_container.cxx64
-rw-r--r--desktop/source/deployment/registry/script/dp_lib_container.h48
-rw-r--r--desktop/source/deployment/registry/script/dp_script.cxx480
-rw-r--r--desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx64
-rw-r--r--desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx55
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx103
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx64
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_sfwk.cxx378
91 files changed, 27092 insertions, 0 deletions
diff --git a/desktop/source/deployment/deployment.component b/desktop/source/deployment/deployment.component
new file mode 100644
index 000000000..f7a481bf4
--- /dev/null
+++ b/desktop/source/deployment/deployment.component
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.deployment.ExtensionManager"
+ constructor="com_sun_star_comp_deployment_ExtensionManager_get_implementation">
+ <service name="com.sun.star.comp.deployment.ExtensionManager"/>
+ <singleton name="com.sun.star.deployment.ExtensionManager"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.PackageInformationProvider"
+ constructor="com_sun_star_comp_deployment_PackageInformationProvider_get_implementation">
+ <service name="com.sun.star.comp.deployment.PackageInformationProvider"/>
+ <singleton name="com.sun.star.deployment.PackageInformationProvider"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.PackageManagerFactory"
+ constructor="com_sun_star_comp_deployment_PackageManagerFactory_get_implementation">
+ <service name="com.sun.star.comp.deployment.PackageManagerFactory"/>
+ <singleton name="com.sun.star.deployment.thePackageManagerFactory"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.ProgressLog"
+ constructor="com_sun_star_comp_deployment_ProgressLog_get_implementation">
+ <service name="com.sun.star.comp.deployment.ProgressLog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.component.PackageRegistryBackend"
+ constructor="com_sun_star_comp_deployment_component_PackageRegistryBackend_get_implementation">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.configuration.PackageRegistryBackend"
+ constructor="com_sun_star_comp_deployment_configuration_PackageRegistryBackend_get_implementation">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.executable.PackageRegistryBackend"
+ constructor="com_sun_star_comp_deployment_executable_PackageRegistryBackend_get_implementation">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.help.PackageRegistryBackend"
+ constructor="com_sun_star_comp_deployment_help_PackageRegistryBackend_get_implementation">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.script.PackageRegistryBackend"
+ constructor="com_sun_star_comp_deployment_script_PackageRegistryBackend_get_implementation">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.sfwk.PackageRegistryBackend"
+ constructor="com_sun_star_comp_deployment_sfwk_PackageRegistryBackend_get_implementation">
+ <service name="com.sun.star.deployment.PackageRegistryBackend"/>
+ </implementation>
+</component>
diff --git a/desktop/source/deployment/dp_log.cxx b/desktop/source/deployment/dp_log.cxx
new file mode 100644
index 000000000..9cdf8d071
--- /dev/null
+++ b/desktop/source/deployment/dp_log.cxx
@@ -0,0 +1,143 @@
+/* -*- 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 <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/anytostring.hxx>
+#include <comphelper/logging.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::logging;
+
+namespace dp_log {
+
+typedef ::cppu::WeakComponentImplHelper<ucb::XProgressHandler, lang::XServiceInfo> t_log_helper;
+
+namespace {
+
+class ProgressLogImpl : public cppu::BaseMutex, public t_log_helper
+{
+ std::unique_ptr<comphelper::EventLogger> m_logger;
+
+protected:
+ virtual void SAL_CALL disposing() override;
+ virtual ~ProgressLogImpl() override;
+
+public:
+ ProgressLogImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( Any const & Status ) override;
+ virtual void SAL_CALL update( Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+}
+
+ProgressLogImpl::~ProgressLogImpl()
+{
+}
+
+
+void ProgressLogImpl::disposing()
+{
+}
+
+
+ProgressLogImpl::ProgressLogImpl(
+ Sequence<Any> const & /* args */,
+ Reference<XComponentContext> const & xContext )
+ : t_log_helper( m_aMutex )
+{
+ // Use the logger created by unopkg app
+ m_logger.reset(new comphelper::EventLogger(xContext, "unopkg"));
+}
+
+// XServiceInfo
+OUString ProgressLogImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.ProgressLog";
+}
+
+sal_Bool ProgressLogImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > ProgressLogImpl::getSupportedServiceNames()
+{
+ // a private one
+ return { "com.sun.star.comp.deployment.ProgressLog" };
+}
+
+// XProgressHandler
+
+void ProgressLogImpl::push( Any const & Status )
+{
+ update( Status );
+}
+
+void ProgressLogImpl::update( Any const & Status )
+{
+ if (! Status.hasValue())
+ return;
+
+ OUStringBuffer buf;
+
+ OUString msg;
+ sal_Int32 logLevel = LogLevel::INFO;
+ if (Status >>= msg) {
+ buf.append( msg );
+ }
+ else {
+ logLevel = LogLevel::SEVERE;
+ buf.append( ::comphelper::anyToString(Status) );
+ }
+ m_logger->log(logLevel, buf.makeStringAndClear());
+}
+
+
+void ProgressLogImpl::pop()
+{
+}
+
+} // namespace dp_log
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_ProgressLog_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_log::ProgressLogImpl(args, context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/dp_persmap.cxx b/desktop/source/deployment/dp_persmap.cxx
new file mode 100644
index 000000000..8b032fffb
--- /dev/null
+++ b/desktop/source/deployment/dp_persmap.cxx
@@ -0,0 +1,312 @@
+/* -*- 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 <cstddef>
+
+#include <dp_misc.h>
+#include <dp_persmap.h>
+#include <o3tl/safeint.hxx>
+#include <rtl/byteseq.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+
+using namespace ::rtl;
+
+// the persistent map is used to manage a handful of key-value string pairs
+// this implementation replaces a rather heavy-weight berkeleydb integration
+
+// the file backing up a persistent map consists of line pairs with
+// - a key string (encoded with chars 0x00..0x0F being escaped)
+// - a value string (encoded with chars 0x00..0x0F being escaped)
+
+namespace dp_misc
+{
+
+const char PmapMagic[4] = {'P','m','p','1'};
+
+PersistentMap::PersistentMap( OUString const & url_ )
+: m_MapFile( expandUnoRcUrl(url_) )
+, m_bIsOpen( false )
+, m_bToBeCreated( true )
+, m_bIsDirty( false )
+{
+ open();
+}
+
+PersistentMap::PersistentMap()
+: m_MapFile( OUString() )
+, m_bIsOpen( false )
+, m_bToBeCreated( false )
+, m_bIsDirty( false )
+{}
+
+PersistentMap::~PersistentMap()
+{
+ if( m_bIsDirty )
+ flush();
+ if( m_bIsOpen )
+ m_MapFile.close();
+}
+
+
+// replace 0x00..0x0F with "%0".."%F"
+// replace "%" with "%%"
+static OString encodeString( const OString& rStr)
+{
+ const char* pChar = rStr.getStr();
+ const sal_Int32 nLen = rStr.getLength();
+ sal_Int32 i = nLen;
+ // short circuit for the simple non-encoded case
+ while( --i >= 0)
+ {
+ const unsigned char c = static_cast<unsigned char>(*(pChar++));
+ if( c <= 0x0F )
+ break;
+ if( c == '%')
+ break;
+ }
+ if( i < 0)
+ return rStr;
+
+ // escape chars 0x00..0x0F with "%0".."%F"
+ OStringBuffer aEncStr( nLen + 32);
+ aEncStr.append( pChar - (nLen-i), nLen - i);
+ while( --i >= 0)
+ {
+ unsigned char c = static_cast<unsigned char>(*(pChar++));
+ if( c <= 0x0F )
+ {
+ aEncStr.append( '%');
+ c += (c <= 0x09) ? '0' : 'A'-10;
+ } else if( c == '%')
+ aEncStr.append( '%');
+ aEncStr.append( char(c) );
+ }
+
+ return aEncStr.makeStringAndClear();
+}
+
+// replace "%0".."%F" with 0x00..0x0F
+// replace "%%" with "%"
+static OString decodeString( const char* pEncChars, int nLen)
+{
+ const char* pChar = pEncChars;
+ sal_Int32 i = nLen;
+ // short circuit for the simple non-encoded case
+ while( --i >= 0)
+ if( *(pChar++) == '%')
+ break;
+ if( i < 0)
+ return OString( pEncChars, nLen);
+
+ // replace escaped chars with their decoded counterparts
+ OStringBuffer aDecStr( nLen);
+ pChar = pEncChars;
+ for( i = nLen; --i >= 0;)
+ {
+ char c = *(pChar++);
+ // handle escaped character
+ if( c == '%')
+ {
+ --i;
+ OSL_ASSERT( i >= 0);
+ c = *(pChar++);
+ if( ('0' <= c) && (c <= '9'))
+ c -= '0';
+ else
+ {
+ OSL_ASSERT( ('A' <= c) && (c <= 'F'));
+ c -= ('A'-10);
+ }
+ }
+ aDecStr.append( c);
+ }
+
+ return aDecStr.makeStringAndClear();
+}
+
+void PersistentMap::open()
+{
+ // open the existing file
+ sal_uInt32 const nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
+
+ const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
+ m_bIsOpen = (rcOpen == osl::File::E_None);
+
+ // or create later if needed
+ m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen;
+
+ if( !m_bIsOpen)
+ return;
+
+ readAll();
+}
+
+
+void PersistentMap::readAll()
+{
+ // prepare for re-reading the map-file
+ m_entries.clear();
+ const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
+ if (nRes != osl::FileBase::E_None)
+ {
+ SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
+ return;
+ }
+
+ // read header and check magic
+ char aHeaderBytes[ sizeof(PmapMagic)];
+ sal_uInt64 nBytesRead = 0;
+ m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead);
+ OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes));
+ if( nBytesRead != sizeof(aHeaderBytes))
+ return;
+ // check header magic
+ for( std::size_t i = 0; i < sizeof(PmapMagic); ++i)
+ if( aHeaderBytes[i] != PmapMagic[i])
+ return;
+
+ // read key value pairs and add them to the map
+ ByteSequence aKeyLine;
+ ByteSequence aValLine;
+ for(;;)
+ {
+ // read key-value line pair
+ // an empty key name indicates the end of the line pairs
+ if( m_MapFile.readLine( aKeyLine) != osl::File::E_None)
+ return;
+ if( !aKeyLine.getLength())
+ break;
+ if( m_MapFile.readLine( aValLine) != osl::File::E_None)
+ return;
+ // decode key and value strings
+ const OString aKeyName = decodeString( reinterpret_cast<char const *>(aKeyLine.getConstArray()), aKeyLine.getLength());
+ const OString aValName = decodeString( reinterpret_cast<char const *>(aValLine.getConstArray()), aValLine.getLength());
+ // insert key-value pair into map
+ add( aKeyName, aValName );
+ // check end-of-file status
+ sal_Bool bIsEOF = true;
+ if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None )
+ return;
+ if( bIsEOF )
+ break;
+ }
+
+ m_bIsDirty = false;
+}
+
+void PersistentMap::flush()
+{
+ if( !m_bIsDirty)
+ return;
+ if( m_bToBeCreated && !m_entries.empty())
+ {
+ const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
+ const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
+ m_bIsOpen = (rcOpen == osl::File::E_None);
+ m_bToBeCreated = !m_bIsOpen;
+ }
+ if( !m_bIsOpen)
+ return;
+
+ // write header magic
+ const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
+ if (nRes != osl::FileBase::E_None)
+ {
+ SAL_WARN("desktop.deployment", "setPos failed with " << +nRes);
+ return;
+ }
+ sal_uInt64 nBytesWritten = 0;
+ m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten);
+
+ // write key value pairs
+ for (auto const& entry : m_entries)
+ {
+ // write line for key
+ const OString aKeyString = encodeString( entry.first);
+ const sal_Int32 nKeyLen = aKeyString.getLength();
+ m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten);
+ OSL_ASSERT( o3tl::make_unsigned(nKeyLen) == nBytesWritten);
+ m_MapFile.write( "\n", 1, nBytesWritten);
+ // write line for value
+ const OString& rValString = encodeString( entry.second);
+ const sal_Int32 nValLen = rValString.getLength();
+ m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten);
+ OSL_ASSERT( o3tl::make_unsigned(nValLen) == nBytesWritten);
+ m_MapFile.write( "\n", 1, nBytesWritten);
+ }
+
+ // write a file delimiter (an empty key-string)
+ m_MapFile.write( "\n", 1, nBytesWritten);
+ // truncate file here
+ sal_uInt64 nNewFileSize;
+ if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None)
+ m_MapFile.setSize( nNewFileSize);
+ // flush to disk
+ m_MapFile.sync();
+ // the in-memory map now matches to the file on disk
+ m_bIsDirty = false;
+}
+
+bool PersistentMap::has( OString const & key ) const
+{
+ return get( nullptr, key );
+}
+
+bool PersistentMap::get( OString * value, OString const & key ) const
+{
+ t_string2string_map::const_iterator it = m_entries.find( key);
+ if( it == m_entries.end())
+ return false;
+ if( value)
+ *value = it->second;
+ return true;
+}
+
+void PersistentMap::add( OString const & key, OString const & value )
+{
+ auto r = m_entries.emplace(key,value);
+ m_bIsDirty = r.second;
+}
+
+
+void PersistentMap::put( OString const & key, OString const & value )
+{
+ add( key, value);
+ // HACK: flush now as the extension manager does not seem
+ // to properly destruct this object in some situations
+ if(m_bIsDirty)
+ flush();
+}
+
+bool PersistentMap::erase( OString const & key )
+{
+ size_t nCount = m_entries.erase( key);
+ if( !nCount)
+ return false;
+ m_bIsDirty = true;
+ flush();
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/dp_xml.cxx b/desktop/source/deployment/dp_xml.cxx
new file mode 100644
index 000000000..d61267e2f
--- /dev/null
+++ b/desktop/source/deployment/dp_xml.cxx
@@ -0,0 +1,51 @@
+/* -*- 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 <dp_xml.h>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/xml/sax/Parser.hpp>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_misc
+{
+
+
+void xml_parse(
+ Reference<xml::sax::XDocumentHandler> const & xDocHandler,
+ ::ucbhelper::Content & ucb_content,
+ Reference<XComponentContext> const & xContext )
+{
+ // raise sax parser:
+ Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xContext);
+
+ // error handler, entity resolver omitted
+ xParser->setDocumentHandler( xDocHandler );
+ xml::sax::InputSource source;
+ source.aInputStream = ucb_content.openStream();
+ source.sSystemId = ucb_content.getURL();
+ xParser->parseStream( source );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/deploymentgui.component b/desktop/source/deployment/gui/deploymentgui.component
new file mode 100644
index 000000000..3b56863c1
--- /dev/null
+++ b/desktop/source/deployment/gui/deploymentgui.component
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.deployment.ui.LicenseDialog"
+ constructor="desktop_LicenseDialog_get_implementation">
+ <service name="com.sun.star.deployment.ui.LicenseDialog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.ui.PackageManagerDialog"
+ constructor="desktop_ServiceImpl_get_implementation">
+ <service name="com.sun.star.deployment.ui.PackageManagerDialog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.deployment.ui.UpdateRequiredDialog"
+ constructor="desktop_UpdateRequiredDialogService_get_implementation">
+ <service name="com.sun.star.deployment.ui.UpdateRequiredDialog"/>
+ </implementation>
+</component>
diff --git a/desktop/source/deployment/gui/dp_gui.h b/desktop/source/deployment/gui/dp_gui.h
new file mode 100644
index 000000000..cb2932ca4
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui.h
@@ -0,0 +1,28 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+namespace dp_gui {
+
+enum PackageState { REGISTERED, NOT_REGISTERED, AMBIGUOUS, NOT_AVAILABLE };
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx b/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx
new file mode 100644
index 000000000..f94b29891
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dependencydialog.cxx
@@ -0,0 +1,42 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_dependencydialog.hxx"
+
+using dp_gui::DependencyDialog;
+
+DependencyDialog::DependencyDialog(weld::Window* parent, std::vector<OUString> const& dependencies)
+ : GenericDialogController(parent, "desktop/ui/dependenciesdialog.ui", "Dependencies")
+ , m_xList(m_xBuilder->weld_tree_view("depListTreeview"))
+{
+ m_xList->set_size_request(-1, m_xList->get_height_rows(10));
+ for (auto const& dependency : dependencies)
+ {
+ m_xList->append_text(dependency);
+ }
+}
+
+DependencyDialog::~DependencyDialog() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx b/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx
new file mode 100644
index 000000000..bdbab41d1
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dependencydialog.hxx
@@ -0,0 +1,49 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vcl/weld.hxx>
+
+#include <vector>
+
+namespace vcl
+{
+class Window;
+}
+
+namespace dp_gui
+{
+class DependencyDialog : public weld::GenericDialogController
+{
+public:
+ DependencyDialog(weld::Window* parent, std::vector<OUString> const& dependencies);
+ virtual ~DependencyDialog() override;
+
+private:
+ DependencyDialog(DependencyDialog const&) = delete;
+ DependencyDialog& operator=(DependencyDialog const&) = delete;
+
+ std::unique_ptr<weld::TreeView> m_xList;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dialog2.cxx b/desktop/source/deployment/gui/dp_gui_dialog2.cxx
new file mode 100644
index 000000000..7fd0bdac3
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dialog2.cxx
@@ -0,0 +1,1403 @@
+/* -*- 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 <config_extensions.h>
+
+#include <strings.hrc>
+#include <helpids.h>
+
+#include "dp_gui.h"
+#include "dp_gui_dialog2.hxx"
+#include "dp_gui_extlistbox.hxx"
+#include <dp_shared.hxx>
+#include "dp_gui_theextmgr.hxx"
+#include "dp_gui_extensioncmdqueue.hxx"
+#include <dp_misc.h>
+#include <dp_update.hxx>
+#include <dp_identifier.hxx>
+
+#include <fpicker/strings.hrc>
+
+#include <utility>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <svtools/restartdialog.hxx>
+
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/sfxdlg.hxx>
+
+#include <comphelper/anytostring.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/configmgr.hxx>
+
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+
+#include <officecfg/Office/ExtensionManager.hxx>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::system;
+
+
+namespace dp_gui {
+
+constexpr OUStringLiteral USER_PACKAGE_MANAGER = u"user";
+constexpr OUStringLiteral SHARED_PACKAGE_MANAGER = u"shared";
+constexpr OUStringLiteral BUNDLED_PACKAGE_MANAGER = u"bundled";
+
+// ExtBoxWithBtns_Impl
+class ExtBoxWithBtns_Impl : public ExtensionBox_Impl
+{
+ bool m_bInterfaceLocked;
+
+ ExtMgrDialog* m_pParent;
+
+ void SetButtonStatus( const TEntry_Impl& rEntry );
+ OString ShowPopupMenu( const Point &rPos, const tools::Long nPos );
+
+public:
+ explicit ExtBoxWithBtns_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll);
+
+ void InitFromDialog(ExtMgrDialog *pParentDialog);
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool Command( const CommandEvent& rCEvt ) override;
+
+ virtual void RecalcAll() override;
+ virtual void selectEntry( const tools::Long nPos ) override;
+
+ void enableButtons( bool bEnable );
+};
+
+ExtBoxWithBtns_Impl::ExtBoxWithBtns_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll)
+ : ExtensionBox_Impl(std::move(xScroll))
+ , m_bInterfaceLocked(false)
+ , m_pParent(nullptr)
+{
+}
+
+void ExtBoxWithBtns_Impl::InitFromDialog(ExtMgrDialog *pParentDialog)
+{
+ setExtensionManager(pParentDialog->getExtensionManager());
+
+ m_pParent = pParentDialog;
+}
+
+void ExtBoxWithBtns_Impl::RecalcAll()
+{
+ const sal_Int32 nActive = getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ SetButtonStatus( GetEntryData( nActive) );
+ }
+ else
+ {
+ m_pParent->enableOptionsButton( false );
+ m_pParent->enableRemoveButton( false );
+ m_pParent->enableEnableButton( false );
+ }
+
+ ExtensionBox_Impl::RecalcAll();
+}
+
+
+//This function may be called with nPos < 0
+void ExtBoxWithBtns_Impl::selectEntry( const tools::Long nPos )
+{
+ if ( HasActive() && ( nPos == getSelIndex() ) )
+ return;
+
+ ExtensionBox_Impl::selectEntry( nPos );
+}
+
+void ExtBoxWithBtns_Impl::SetButtonStatus(const TEntry_Impl& rEntry)
+{
+ bool bShowOptionBtn = true;
+
+ rEntry->m_bHasButtons = false;
+ if ( ( rEntry->m_eState == REGISTERED ) || ( rEntry->m_eState == NOT_AVAILABLE ) )
+ {
+ m_pParent->enableButtontoEnable( false );
+ }
+ else
+ {
+ m_pParent->enableButtontoEnable( true );
+ bShowOptionBtn = false;
+ }
+
+ if ( ( !rEntry->m_bUser || ( rEntry->m_eState == NOT_AVAILABLE ) || rEntry->m_bMissingDeps )
+ && !rEntry->m_bMissingLic )
+ {
+ m_pParent->enableEnableButton( false );
+ }
+ else
+ {
+ m_pParent->enableEnableButton( !rEntry->m_bLocked );
+ rEntry->m_bHasButtons = true;
+ }
+
+ if ( rEntry->m_bHasOptions && bShowOptionBtn )
+ {
+ m_pParent->enableOptionsButton( true );
+ rEntry->m_bHasButtons = true;
+ }
+ else
+ {
+ m_pParent->enableOptionsButton( false );
+ }
+
+ if ( rEntry->m_bUser || rEntry->m_bShared )
+ {
+ m_pParent->enableRemoveButton( !rEntry->m_bLocked );
+ rEntry->m_bHasButtons = true;
+ }
+ else
+ {
+ m_pParent->enableRemoveButton( false );
+ }
+}
+
+bool ExtBoxWithBtns_Impl::Command(const CommandEvent& rCEvt)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return ExtensionBox_Impl::Command(rCEvt);
+
+ const Point aMousePos(rCEvt.GetMousePosPixel());
+ const auto nPos = PointToPos(aMousePos);
+ OString sCommand = ShowPopupMenu(aMousePos, nPos);
+
+ if (sCommand == "CMD_ENABLE")
+ m_pParent->enablePackage( GetEntryData( nPos )->m_xPackage, true );
+ else if (sCommand == "CMD_DISABLE")
+ m_pParent->enablePackage( GetEntryData( nPos )->m_xPackage, false );
+ else if (sCommand == "CMD_UPDATE")
+ m_pParent->updatePackage( GetEntryData( nPos )->m_xPackage );
+ else if (sCommand == "CMD_REMOVE")
+ m_pParent->removePackage( GetEntryData( nPos )->m_xPackage );
+ else if (sCommand == "CMD_SHOW_LICENSE")
+ {
+ m_pParent->incBusy();
+ ShowLicenseDialog aLicenseDlg(m_pParent->getDialog(), GetEntryData(nPos)->m_xPackage);
+ aLicenseDlg.run();
+ m_pParent->decBusy();
+ }
+
+ return true;
+}
+
+OString ExtBoxWithBtns_Impl::ShowPopupMenu( const Point & rPos, const tools::Long nPos )
+{
+ if ( nPos >= static_cast<tools::Long>(getItemCount()) )
+ return "CMD_NONE";
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "desktop/ui/extensionmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu"));
+
+#if ENABLE_EXTENSION_UPDATE
+ xPopup->append("CMD_UPDATE", DpResId( RID_CTX_ITEM_CHECK_UPDATE ) );
+#endif
+
+ if ( ! GetEntryData( nPos )->m_bLocked )
+ {
+ if ( GetEntryData( nPos )->m_bUser )
+ {
+ if ( GetEntryData( nPos )->m_eState == REGISTERED )
+ xPopup->append("CMD_DISABLE", DpResId(RID_CTX_ITEM_DISABLE));
+ else if ( GetEntryData( nPos )->m_eState != NOT_AVAILABLE )
+ xPopup->append("CMD_ENABLE", DpResId(RID_CTX_ITEM_ENABLE));
+ }
+ if (!officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
+ {
+ xPopup->append("CMD_REMOVE", DpResId(RID_CTX_ITEM_REMOVE));
+ }
+ }
+
+ if ( !GetEntryData( nPos )->m_sLicenseText.isEmpty() )
+ xPopup->append("CMD_SHOW_LICENSE", DpResId(RID_STR_SHOW_LICENSE_CMD));
+
+ return xPopup->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPos, Size(1, 1)));
+}
+
+bool ExtBoxWithBtns_Impl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (m_bInterfaceLocked)
+ return false;
+ return ExtensionBox_Impl::MouseButtonDown(rMEvt);
+}
+
+void ExtBoxWithBtns_Impl::enableButtons( bool bEnable )
+{
+ m_bInterfaceLocked = ! bEnable;
+
+ if ( bEnable )
+ {
+ sal_Int32 nIndex = getSelIndex();
+ if ( nIndex != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ SetButtonStatus( GetEntryData( nIndex ) );
+ }
+ else
+ {
+ m_pParent->enableEnableButton( false );
+ m_pParent->enableOptionsButton( false );
+ m_pParent->enableRemoveButton( false );
+ }
+}
+
+// DialogHelper
+
+DialogHelper::DialogHelper(const uno::Reference< uno::XComponentContext > &xContext,
+ weld::Window* pWindow)
+ : m_pWindow(pWindow)
+ , m_nEventID(nullptr)
+{
+ m_xContext = xContext;
+}
+
+DialogHelper::~DialogHelper()
+{
+ if ( m_nEventID )
+ Application::RemoveUserEvent( m_nEventID );
+}
+
+
+bool DialogHelper::IsSharedPkgMgr( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ return xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER;
+}
+
+bool DialogHelper::continueOnSharedExtension( const uno::Reference< deployment::XPackage > &xPackage,
+ weld::Widget* pParent,
+ TranslateId pResID,
+ bool &bHadWarning )
+{
+ if ( !bHadWarning && IsSharedPkgMgr( xPackage ) )
+ {
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(pResID)));
+ bHadWarning = true;
+
+ bool bRet = RET_OK == xBox->run();
+ xBox.reset();
+ decBusy();
+ return bRet;
+ }
+ else
+ return true;
+}
+
+void DialogHelper::openWebBrowser(const OUString& sURL, const OUString& sTitle)
+{
+ if ( sURL.isEmpty() ) // Nothing to do, when the URL is empty
+ return;
+
+ try
+ {
+ uno::Reference< XSystemShellExecute > xSystemShellExecute(
+ SystemShellExecute::create(m_xContext));
+ //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException
+ xSystemShellExecute->execute( sURL, OUString(), SystemShellExecuteFlags::URIS_ONLY );
+ }
+ catch ( const uno::Exception& )
+ {
+ uno::Any exc( ::cppu::getCaughtException() );
+ OUString msg( ::comphelper::anyToString( exc ) );
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(getFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok, msg));
+ xErrorBox->set_title(sTitle);
+ xErrorBox->run();
+ xErrorBox.reset();
+ decBusy();
+ }
+}
+
+bool DialogHelper::installExtensionWarn(std::u16string_view rExtensionName)
+{
+ const SolarMutexGuard guard;
+
+ // Check if extension installation is disabled in the expert configurations
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
+ {
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xWarnBox(Application::CreateMessageDialog(getFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED)));
+ xWarnBox->run();
+ xWarnBox.reset();
+ decBusy();
+
+ return false;
+ }
+
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(getFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::OkCancel,
+ DpResId(RID_STR_WARNING_INSTALL_EXTENSION)));
+ OUString sText(xInfoBox->get_primary_text());
+ sText = sText.replaceAll("%NAME", rExtensionName);
+ xInfoBox->set_primary_text(sText);
+
+ bool bRet = RET_OK == xInfoBox->run();
+ xInfoBox.reset();
+ decBusy();
+ return bRet;
+}
+
+bool DialogHelper::installForAllUsers(bool &bInstallForAll)
+{
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(getFrameWeld(), "desktop/ui/installforalldialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("InstallForAllDialog"));
+ short nRet = xQuery->run();
+ xQuery.reset();
+ decBusy();
+ if (nRet == RET_CANCEL)
+ return false;
+
+ bInstallForAll = ( nRet == RET_NO );
+ return true;
+}
+
+void DialogHelper::PostUserEvent( const Link<void*,void>& rLink, void* pCaller )
+{
+ if ( m_nEventID )
+ Application::RemoveUserEvent( m_nEventID );
+
+ m_nEventID = Application::PostUserEvent(rLink, pCaller);
+}
+
+// ExtMgrDialog
+ExtMgrDialog::ExtMgrDialog(weld::Window *pParent, TheExtensionManager *pManager)
+ : GenericDialogController(pParent, "desktop/ui/extensionmanager.ui", "ExtensionManagerDialog")
+ , DialogHelper(pManager->getContext(), m_xDialog.get())
+ , m_sAddPackages(DpResId(RID_STR_ADD_PACKAGES))
+ , m_bHasProgress(false)
+ , m_bProgressChanged(false)
+ , m_bStartProgress(false)
+ , m_bStopProgress(false)
+ , m_bEnableWarning(false)
+ , m_bDisableWarning(false)
+ , m_bDeleteWarning(false)
+ , m_bClosed(false)
+ , m_nProgress(0)
+ , m_aIdle( "ExtMgrDialog m_aIdle TimeOutHdl" )
+ , m_pManager(pManager)
+ , m_xExtensionBox(new ExtBoxWithBtns_Impl(m_xBuilder->weld_scrolled_window("scroll", true)))
+ , m_xExtensionBoxWnd(new weld::CustomWeld(*m_xBuilder, "extensions", *m_xExtensionBox))
+ , m_xOptionsBtn(m_xBuilder->weld_button("optionsbtn"))
+ , m_xAddBtn(m_xBuilder->weld_button("addbtn"))
+ , m_xRemoveBtn(m_xBuilder->weld_button("removebtn"))
+ , m_xEnableBtn(m_xBuilder->weld_button("enablebtn"))
+ , m_xUpdateBtn(m_xBuilder->weld_button("updatebtn"))
+ , m_xCloseBtn(m_xBuilder->weld_button("close"))
+ , m_xBundledCbx(m_xBuilder->weld_check_button("bundled"))
+ , m_xSharedCbx(m_xBuilder->weld_check_button("shared"))
+ , m_xUserCbx(m_xBuilder->weld_check_button("user"))
+ , m_xGetExtensions(m_xBuilder->weld_link_button("getextensions"))
+ , m_xProgressText(m_xBuilder->weld_label("progressft"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progressbar"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+ , m_xSearchEntry(m_xBuilder->weld_entry("search"))
+{
+ m_xExtensionBox->InitFromDialog(this);
+
+ m_xEnableBtn->set_help_id(HID_EXTENSION_MANAGER_LISTBOX_ENABLE);
+
+ m_xOptionsBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleOptionsBtn ) );
+ m_xAddBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleAddBtn ) );
+ m_xRemoveBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleRemoveBtn ) );
+ m_xEnableBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleEnableBtn ) );
+ m_xCloseBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleCloseBtn ) );
+
+ m_xCancelBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleCancelBtn ) );
+
+ m_xBundledCbx->connect_toggled( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );
+ m_xSharedCbx->connect_toggled( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );
+ m_xUserCbx->connect_toggled( LINK( this, ExtMgrDialog, HandleExtTypeCbx ) );
+
+ m_xSearchEntry->connect_changed( LINK( this, ExtMgrDialog, HandleSearch ) );
+
+ m_xBundledCbx->set_active(true);
+ m_xSharedCbx->set_active(true);
+ m_xUserCbx->set_active(true);
+
+ m_xProgressBar->hide();
+
+#if ENABLE_EXTENSION_UPDATE
+ m_xUpdateBtn->connect_clicked( LINK( this, ExtMgrDialog, HandleUpdateBtn ) );
+ m_xUpdateBtn->set_sensitive(false);
+#else
+ m_xUpdateBtn->hide();
+#endif
+
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
+ {
+ m_xAddBtn->set_sensitive(false);
+ m_xAddBtn->set_tooltip_text(DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED));
+ }
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
+ {
+ m_xRemoveBtn->set_sensitive(false);
+ m_xRemoveBtn->set_tooltip_text(DpResId(RID_STR_WARNING_REMOVE_EXTENSION_DISABLED));
+ }
+
+ m_aIdle.SetPriority(TaskPriority::LOWEST);
+ m_aIdle.SetInvokeHandler( LINK( this, ExtMgrDialog, TimeOutHdl ) );
+}
+
+ExtMgrDialog::~ExtMgrDialog()
+{
+ m_aIdle.Stop();
+}
+
+void ExtMgrDialog::setGetExtensionsURL( const OUString &rURL )
+{
+ m_xGetExtensions->set_uri( rURL );
+}
+
+void ExtMgrDialog::addPackageToList( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bLicenseMissing )
+{
+ const SolarMutexGuard aGuard;
+ m_xUpdateBtn->set_sensitive(true);
+
+ bool bSearchMatch = m_xSearchEntry->get_text().isEmpty();
+ if (!m_xSearchEntry->get_text().isEmpty()
+ && xPackage->getDisplayName().toAsciiLowerCase().indexOf(
+ m_xSearchEntry->get_text().toAsciiLowerCase())
+ >= 0)
+ {
+ bSearchMatch = true;
+ }
+
+ if (!bSearchMatch)
+ return;
+
+ if (m_xBundledCbx->get_active() && (xPackage->getRepositoryName() == BUNDLED_PACKAGE_MANAGER) )
+ {
+ m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
+ }
+ else if (m_xSharedCbx->get_active() && (xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER) )
+ {
+ m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
+ }
+ else if (m_xUserCbx->get_active() && (xPackage->getRepositoryName() == USER_PACKAGE_MANAGER ))
+ {
+ m_xExtensionBox->addEntry( xPackage, bLicenseMissing );
+ }
+}
+
+void ExtMgrDialog::updateList()
+{
+ // re-creates the list of packages with addEntry selecting the packages
+ prepareChecking();
+ m_pManager->createPackageList();
+ checkEntries();
+}
+
+void ExtMgrDialog::prepareChecking()
+{
+ m_xExtensionBox->prepareChecking();
+}
+
+void ExtMgrDialog::checkEntries()
+{
+ const SolarMutexGuard guard;
+ m_xExtensionBox->checkEntries();
+}
+
+bool ExtMgrDialog::removeExtensionWarn(std::u16string_view rExtensionName)
+{
+ const SolarMutexGuard guard;
+ incBusy();
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::OkCancel,
+ DpResId(RID_STR_WARNING_REMOVE_EXTENSION)));
+
+ OUString sText(xInfoBox->get_primary_text());
+ sText = sText.replaceAll("%NAME", rExtensionName);
+ xInfoBox->set_primary_text(sText);
+
+ bool bRet = RET_OK == xInfoBox->run();
+ xInfoBox.reset();
+ decBusy();
+
+ return bRet;
+}
+
+void ExtMgrDialog::enablePackage( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bEnable )
+{
+ if ( !xPackage.is() )
+ return;
+
+ if ( bEnable )
+ {
+ if (!continueOnSharedExtension(xPackage, m_xDialog.get(), RID_STR_WARNING_ENABLE_SHARED_EXTENSION, m_bEnableWarning))
+ return;
+ }
+ else
+ {
+ if (!continueOnSharedExtension(xPackage, m_xDialog.get(), RID_STR_WARNING_DISABLE_SHARED_EXTENSION, m_bDisableWarning))
+ return;
+ }
+
+ m_pManager->getCmdQueue()->enableExtension( xPackage, bEnable );
+}
+
+
+void ExtMgrDialog::removePackage( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ if ( !IsSharedPkgMgr( xPackage ) || m_bDeleteWarning )
+ {
+ if ( ! removeExtensionWarn( xPackage->getDisplayName() ) )
+ return;
+ }
+
+ if (!continueOnSharedExtension(xPackage, m_xDialog.get(), RID_STR_WARNING_REMOVE_SHARED_EXTENSION, m_bDeleteWarning))
+ return;
+
+ m_pManager->getCmdQueue()->removeExtension( xPackage );
+}
+
+
+void ExtMgrDialog::updatePackage( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ // get the extension with highest version
+ uno::Sequence<uno::Reference<deployment::XPackage> > seqExtensions =
+ m_pManager->getExtensionManager()->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(xPackage), xPackage->getName(), uno::Reference<ucb::XCommandEnvironment>());
+ uno::Reference<deployment::XPackage> extension =
+ dp_misc::getExtensionWithHighestVersion(seqExtensions);
+ OSL_ASSERT(extension.is());
+ std::vector< css::uno::Reference< css::deployment::XPackage > > vEntries { extension };
+
+ m_pManager->getCmdQueue()->checkForUpdates( std::move(vEntries) );
+}
+
+
+bool ExtMgrDialog::acceptLicense( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return false;
+
+ m_pManager->getCmdQueue()->acceptLicense( xPackage );
+
+ return true;
+}
+
+
+uno::Sequence< OUString > ExtMgrDialog::raiseAddPicker()
+{
+ sfx2::FileDialogHelper aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, m_xDialog.get());
+ aDlgHelper.SetContext(sfx2::FileDialogHelper::ExtensionManager);
+ const uno::Reference<ui::dialogs::XFilePicker3>& xFilePicker = aDlgHelper.GetFilePicker();
+ xFilePicker->setTitle( m_sAddPackages );
+
+ // collect and set filter list:
+ typedef std::map< OUString, OUString > t_string2string;
+ t_string2string title2filter;
+ OUStringBuffer supportedFilters;
+
+ const uno::Sequence< uno::Reference< deployment::XPackageTypeInfo > > packageTypes(
+ m_pManager->getExtensionManager()->getSupportedPackageTypes() );
+
+ for ( uno::Reference< deployment::XPackageTypeInfo > const & xPackageType : packageTypes )
+ {
+ const OUString filter( xPackageType->getFileFilter() );
+ if (!filter.isEmpty())
+ {
+ const OUString title( xPackageType->getShortDescription() );
+ const std::pair< t_string2string::iterator, bool > insertion(
+ title2filter.emplace( title, filter ) );
+ if (!supportedFilters.isEmpty())
+ supportedFilters.append(';');
+ supportedFilters.append(filter);
+ if ( ! insertion.second )
+ { // already existing, append extensions:
+ insertion.first->second = insertion.first->second +
+ ";" + filter;
+ }
+ }
+ }
+
+ static const OUString StrAllFiles = []()
+ {
+ const SolarMutexGuard guard;
+ std::locale loc = Translate::Create("fps");
+ return Translate::get(STR_FILTERNAME_ALL, loc);
+ }();
+
+ // All files at top:
+ xFilePicker->appendFilter( StrAllFiles, "*.*" );
+ xFilePicker->appendFilter( DpResId(RID_STR_ALL_SUPPORTED), supportedFilters.makeStringAndClear() );
+ // then supported ones:
+ for (auto const& elem : title2filter)
+ {
+ try
+ {
+ xFilePicker->appendFilter( elem.first, elem.second );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+ xFilePicker->setCurrentFilter( DpResId(RID_STR_ALL_SUPPORTED) );
+
+ if ( xFilePicker->execute() != ui::dialogs::ExecutableDialogResults::OK )
+ return uno::Sequence<OUString>(); // cancelled
+
+ uno::Sequence< OUString > files( xFilePicker->getSelectedFiles() );
+ OSL_ASSERT( files.hasElements() );
+ return files;
+}
+
+void ExtMgrDialog::enableOptionsButton( bool bEnable )
+{
+ m_xOptionsBtn->set_sensitive( bEnable );
+}
+
+void ExtMgrDialog::enableRemoveButton( bool bEnable )
+{
+ m_xRemoveBtn->set_sensitive( bEnable && !officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get());
+
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionRemoval::get())
+ {
+ m_xRemoveBtn->set_tooltip_text(DpResId(RID_STR_WARNING_REMOVE_EXTENSION_DISABLED));
+ }
+ else
+ {
+ m_xRemoveBtn->set_tooltip_text("");
+ }
+}
+
+void ExtMgrDialog::enableEnableButton( bool bEnable )
+{
+ m_xEnableBtn->set_sensitive( bEnable );
+}
+
+void ExtMgrDialog::enableButtontoEnable( bool bEnable )
+{
+ if (bEnable)
+ {
+ m_xEnableBtn->set_label( DpResId( RID_CTX_ITEM_ENABLE ) );
+ m_xEnableBtn->set_help_id( HID_EXTENSION_MANAGER_LISTBOX_ENABLE );
+ }
+ else
+ {
+ m_xEnableBtn->set_label( DpResId( RID_CTX_ITEM_DISABLE ) );
+ m_xEnableBtn->set_help_id( HID_EXTENSION_MANAGER_LISTBOX_DISABLE );
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleCancelBtn, weld::Button&, void)
+{
+ if ( m_xAbortChannel.is() )
+ {
+ try
+ {
+ m_xAbortChannel->sendAbort();
+ }
+ catch ( const uno::RuntimeException & )
+ {
+ TOOLS_WARN_EXCEPTION( "dbaccess", "" );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleCloseBtn, weld::Button&, void)
+{
+ bool bCallClose = true;
+
+ //only suggest restart if modified and this is the first close attempt
+ if (!m_bClosed && m_pManager->isModified())
+ {
+ m_pManager->clearModified();
+
+ //only suggest restart if we're actually running, e.g. not from standalone unopkg gui
+ if (dp_misc::office_is_running())
+ {
+ SolarMutexGuard aGuard;
+ bCallClose = !::svtools::executeRestartDialog(comphelper::getProcessComponentContext(),
+ m_xDialog.get(),
+ svtools::RESTART_REASON_EXTENSION_INSTALL);
+ }
+ }
+
+ if (bCallClose)
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK( ExtMgrDialog, startProgress, void*, _bLockInterface, void )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ bool bLockInterface = static_cast<bool>(_bLockInterface);
+
+ if ( m_bStartProgress && !m_bHasProgress )
+ m_aIdle.Start();
+
+ if ( m_bStopProgress )
+ {
+ if ( m_xProgressBar->get_visible() )
+ m_xProgressBar->set_percentage( 100 );
+ m_xAbortChannel.clear();
+
+ SAL_INFO( "desktop.deployment", " startProgress handler: stop" );
+ }
+ else
+ {
+ SAL_INFO( "desktop.deployment", " startProgress handler: start" );
+ }
+
+ m_xCancelBtn->set_sensitive( bLockInterface );
+ m_xAddBtn->set_sensitive( !bLockInterface && !officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get());
+ if (officecfg::Office::ExtensionManager::ExtensionSecurity::DisableExtensionInstallation::get())
+ {
+ m_xAddBtn->set_tooltip_text(DpResId(RID_STR_WARNING_INSTALL_EXTENSION_DISABLED));
+ }
+ else
+ {
+ m_xAddBtn->set_tooltip_text("");
+ }
+
+ m_xUpdateBtn->set_sensitive( !bLockInterface && m_xExtensionBox->getItemCount() );
+ m_xExtensionBox->enableButtons( !bLockInterface );
+
+ clearEventID();
+}
+
+
+void ExtMgrDialog::showProgress( bool _bStart )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ bool bStart = _bStart;
+
+ if ( bStart )
+ {
+ m_nProgress = 0;
+ m_bStartProgress = true;
+ SAL_INFO( "desktop.deployment", "showProgress start" );
+ }
+ else
+ {
+ m_nProgress = 100;
+ m_bStopProgress = true;
+ SAL_INFO( "desktop.deployment", "showProgress stop!" );
+ }
+
+ DialogHelper::PostUserEvent( LINK( this, ExtMgrDialog, startProgress ), reinterpret_cast<void*>(bStart) );
+ m_aIdle.Start();
+}
+
+
+void ExtMgrDialog::updateProgress( const tools::Long nProgress )
+{
+ if ( m_nProgress != nProgress )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nProgress = nProgress;
+ m_aIdle.Start();
+ }
+}
+
+
+void ExtMgrDialog::updateProgress( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_xAbortChannel = xAbortChannel;
+ m_sProgressText = rText;
+ m_bProgressChanged = true;
+ m_aIdle.Start();
+}
+
+
+void ExtMgrDialog::updatePackageInfo( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ const SolarMutexGuard aGuard;
+ m_xExtensionBox->updateEntry( xPackage );
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleOptionsBtn, weld::Button&, void)
+{
+ const sal_Int32 nActive = m_xExtensionBox->getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+
+ OUString sExtensionId = m_xExtensionBox->GetEntryData( nActive )->m_xPackage->getIdentifier().Value;
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateOptionsDialog(m_xDialog.get(), sExtensionId));
+
+ pDlg->Execute();
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleAddBtn, weld::Button&, void)
+{
+ incBusy();
+
+ uno::Sequence< OUString > aFileList = raiseAddPicker();
+
+ if ( aFileList.hasElements() )
+ {
+ m_pManager->installPackage( aFileList[0] );
+ }
+
+ decBusy();
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleRemoveBtn, weld::Button&, void)
+{
+ const sal_Int32 nActive = m_xExtensionBox->getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nActive );
+ removePackage( pEntry->m_xPackage );
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleEnableBtn, weld::Button&, void)
+{
+ const sal_Int32 nActive = m_xExtensionBox->getSelIndex();
+
+ if ( nActive != ExtensionBox_Impl::ENTRY_NOTFOUND )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nActive );
+
+ if ( pEntry->m_bMissingLic )
+ acceptLicense( pEntry->m_xPackage );
+ else
+ {
+ const bool bEnable( pEntry->m_eState != REGISTERED );
+ enablePackage( pEntry->m_xPackage, bEnable );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleExtTypeCbx, weld::Toggleable&, void)
+{
+ updateList();
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleSearch, weld::Entry&, void)
+{
+ updateList();
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, HandleUpdateBtn, weld::Button&, void)
+{
+#if ENABLE_EXTENSION_UPDATE
+ m_pManager->checkUpdates();
+#else
+ (void) this;
+#endif
+}
+
+IMPL_LINK_NOARG(ExtMgrDialog, TimeOutHdl, Timer *, void)
+{
+ if ( m_bStopProgress )
+ {
+ m_bHasProgress = false;
+ m_bStopProgress = false;
+ m_xProgressText->hide();
+ m_xProgressBar->hide();
+ m_xCancelBtn->hide();
+ }
+ else
+ {
+ if ( m_bProgressChanged )
+ {
+ m_bProgressChanged = false;
+ m_xProgressText->set_label(m_sProgressText);
+ }
+
+ if ( m_bStartProgress )
+ {
+ m_bStartProgress = false;
+ m_bHasProgress = true;
+ m_xProgressBar->show();
+ m_xProgressText->show();
+ m_xCancelBtn->set_sensitive(true);
+ m_xCancelBtn->show();
+ }
+
+ if ( m_xProgressBar->get_visible() )
+ m_xProgressBar->set_percentage( static_cast<sal_uInt16>(m_nProgress) );
+ }
+}
+
+void ExtMgrDialog::Close()
+{
+ m_pManager->terminateDialog();
+ m_bClosed = true;
+}
+
+//UpdateRequiredDialog
+UpdateRequiredDialog::UpdateRequiredDialog(weld::Window *pParent, TheExtensionManager *pManager)
+ : GenericDialogController(pParent, "desktop/ui/updaterequireddialog.ui", "UpdateRequiredDialog")
+ , DialogHelper(pManager->getContext(), m_xDialog.get())
+ , m_sCloseText(DpResId(RID_STR_CLOSE_BTN))
+ , m_bHasProgress(false)
+ , m_bProgressChanged(false)
+ , m_bStartProgress(false)
+ , m_bStopProgress(false)
+ , m_bHasLockedEntries(false)
+ , m_nProgress(0)
+ , m_aIdle( "UpdateRequiredDialog m_aIdle TimeOutHdl" )
+ , m_pManager(pManager)
+ , m_xExtensionBox(new ExtensionBox_Impl(m_xBuilder->weld_scrolled_window("scroll", true)))
+ , m_xExtensionBoxWnd(new weld::CustomWeld(*m_xBuilder, "extensions", *m_xExtensionBox))
+ , m_xUpdateNeeded(m_xBuilder->weld_label("updatelabel"))
+ , m_xUpdateBtn(m_xBuilder->weld_button("ok"))
+ , m_xCloseBtn(m_xBuilder->weld_button("disable"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+ , m_xProgressText(m_xBuilder->weld_label("progresslabel"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
+{
+ m_xExtensionBox->setExtensionManager(pManager);
+
+ m_xUpdateBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleUpdateBtn ) );
+ m_xCloseBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleCloseBtn ) );
+ m_xCancelBtn->connect_clicked( LINK( this, UpdateRequiredDialog, HandleCancelBtn ) );
+
+ OUString aText = m_xUpdateNeeded->get_label();
+ aText = aText.replaceAll(
+ "%PRODUCTNAME", utl::ConfigManager::getProductName());
+ m_xUpdateNeeded->set_label(aText);
+
+ m_xProgressBar->hide();
+ m_xUpdateBtn->set_sensitive( false );
+ m_xCloseBtn->grab_focus();
+
+ m_aIdle.SetPriority( TaskPriority::LOWEST );
+ m_aIdle.SetInvokeHandler( LINK( this, UpdateRequiredDialog, TimeOutHdl ) );
+}
+
+UpdateRequiredDialog::~UpdateRequiredDialog()
+{
+ m_aIdle.Stop();
+}
+
+void UpdateRequiredDialog::addPackageToList( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bLicenseMissing )
+{
+ // We will only add entries to the list with unsatisfied dependencies
+ if ( !bLicenseMissing && !checkDependencies( xPackage ) )
+ {
+ m_bHasLockedEntries |= m_pManager->isReadOnly( xPackage );
+ const SolarMutexGuard aGuard;
+ m_xUpdateBtn->set_sensitive(true);
+ m_xExtensionBox->addEntry( xPackage );
+ }
+}
+
+
+void UpdateRequiredDialog::prepareChecking()
+{
+ m_xExtensionBox->prepareChecking();
+}
+
+
+void UpdateRequiredDialog::checkEntries()
+{
+ const SolarMutexGuard guard;
+ m_xExtensionBox->checkEntries();
+
+ if ( ! hasActiveEntries() )
+ {
+ m_xCloseBtn->set_label( m_sCloseText );
+ m_xCloseBtn->grab_focus();
+ }
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, HandleCancelBtn, weld::Button&, void)
+{
+ if ( m_xAbortChannel.is() )
+ {
+ try
+ {
+ m_xAbortChannel->sendAbort();
+ }
+ catch ( const uno::RuntimeException & )
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+}
+
+
+IMPL_LINK( UpdateRequiredDialog, startProgress, void*, _bLockInterface, void )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ bool bLockInterface = static_cast<bool>(_bLockInterface);
+
+ if ( m_bStartProgress && !m_bHasProgress )
+ m_aIdle.Start();
+
+ if ( m_bStopProgress )
+ {
+ if ( m_xProgressBar->get_visible() )
+ m_xProgressBar->set_percentage( 100 );
+ m_xAbortChannel.clear();
+ SAL_INFO( "desktop.deployment", " startProgress handler: stop" );
+ }
+ else
+ {
+ SAL_INFO( "desktop.deployment", " startProgress handler: start" );
+ }
+
+ m_xCancelBtn->set_sensitive( bLockInterface );
+ m_xUpdateBtn->set_sensitive( false );
+ clearEventID();
+}
+
+
+void UpdateRequiredDialog::showProgress( bool _bStart )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ bool bStart = _bStart;
+
+ if ( bStart )
+ {
+ m_nProgress = 0;
+ m_bStartProgress = true;
+ SAL_INFO( "desktop.deployment", "showProgress start" );
+ }
+ else
+ {
+ m_nProgress = 100;
+ m_bStopProgress = true;
+ SAL_INFO( "desktop.deployment", "showProgress stop!" );
+ }
+
+ DialogHelper::PostUserEvent( LINK( this, UpdateRequiredDialog, startProgress ), reinterpret_cast<void*>(bStart) );
+ m_aIdle.Start();
+}
+
+
+void UpdateRequiredDialog::updateProgress( const tools::Long nProgress )
+{
+ if ( m_nProgress != nProgress )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nProgress = nProgress;
+ m_aIdle.Start();
+ }
+}
+
+
+void UpdateRequiredDialog::updateProgress( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_xAbortChannel = xAbortChannel;
+ m_sProgressText = rText;
+ m_bProgressChanged = true;
+ m_aIdle.Start();
+}
+
+
+void UpdateRequiredDialog::updatePackageInfo( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ // We will remove all updated packages with satisfied dependencies, but
+ // we will show all disabled entries so the user sees the result
+ // of the 'disable all' button
+ const SolarMutexGuard aGuard;
+ if ( isEnabled( xPackage ) && checkDependencies( xPackage ) )
+ m_xExtensionBox->removeEntry( xPackage );
+ else
+ m_xExtensionBox->updateEntry( xPackage );
+
+ if ( ! hasActiveEntries() )
+ {
+ m_xCloseBtn->set_label( m_sCloseText );
+ m_xCloseBtn->grab_focus();
+ }
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, HandleUpdateBtn, weld::Button&, void)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ std::vector< uno::Reference< deployment::XPackage > > vUpdateEntries;
+ sal_Int32 nCount = m_xExtensionBox->GetEntryCount();
+
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( i );
+ vUpdateEntries.push_back( pEntry->m_xPackage );
+ }
+
+ aGuard.clear();
+
+ m_pManager->getCmdQueue()->checkForUpdates( std::move(vUpdateEntries) );
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, HandleCloseBtn, weld::Button&, void)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !isBusy() )
+ {
+ if ( m_bHasLockedEntries )
+ m_xDialog->response(-1);
+ else if ( hasActiveEntries() )
+ disableAllEntries();
+ else
+ m_xDialog->response(RET_CANCEL);
+ }
+}
+
+
+IMPL_LINK_NOARG(UpdateRequiredDialog, TimeOutHdl, Timer *, void)
+{
+ if ( m_bStopProgress )
+ {
+ m_bHasProgress = false;
+ m_bStopProgress = false;
+ m_xProgressText->hide();
+ m_xProgressBar->hide();
+ m_xCancelBtn->hide();
+ }
+ else
+ {
+ if ( m_bProgressChanged )
+ {
+ m_bProgressChanged = false;
+ m_xProgressText->set_label( m_sProgressText );
+ }
+
+ if ( m_bStartProgress )
+ {
+ m_bStartProgress = false;
+ m_bHasProgress = true;
+ m_xProgressBar->show();
+ m_xProgressText->show();
+ m_xCancelBtn->set_sensitive(true);
+ m_xCancelBtn->show();
+ }
+
+ if (m_xProgressBar->get_visible())
+ m_xProgressBar->set_percentage(m_nProgress);
+ }
+}
+
+// VCL::Dialog
+short UpdateRequiredDialog::run()
+{
+ //ToDo
+ //I believe m_bHasLockedEntries was used to prevent showing extensions which cannot
+ //be disabled because they are in a read only repository. However, disabling extensions
+ //is now always possible because the registration data of all repositories
+ //are in the user installation.
+ //Therefore all extensions could be displayed and all the handling around m_bHasLockedEntries
+ //could be removed.
+ if ( m_bHasLockedEntries )
+ {
+ // Set other text, disable update btn, remove not shared entries from list;
+ m_xUpdateNeeded->set_label( DpResId( RID_STR_NO_ADMIN_PRIVILEGE ) );
+ m_xCloseBtn->set_label( DpResId( RID_STR_EXIT_BTN ) );
+ m_xUpdateBtn->set_sensitive( false );
+ m_xExtensionBox->RemoveUnlocked();
+ }
+
+ return GenericDialogController::run();
+}
+
+// Check dependencies of all packages
+
+bool UpdateRequiredDialog::isEnabled( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ bool bRegistered = false;
+ try {
+ beans::Optional< beans::Ambiguous< sal_Bool > > option( xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() ) );
+ if ( option.IsPresent )
+ {
+ ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
+ if ( reg.IsAmbiguous )
+ bRegistered = false;
+ else
+ bRegistered = reg.Value;
+ }
+ else
+ bRegistered = false;
+ }
+ catch ( const uno::RuntimeException & ) { throw; }
+ catch (const uno::Exception & ) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ bRegistered = false;
+ }
+
+ return bRegistered;
+}
+
+// Checks the dependencies no matter if the extension is enabled or disabled!
+bool UpdateRequiredDialog::checkDependencies( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ bool bDependenciesValid = false;
+ try {
+ bDependenciesValid = xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( const deployment::DeploymentException & ) {}
+ return bDependenciesValid;
+}
+
+
+bool UpdateRequiredDialog::hasActiveEntries()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ bool bRet = false;
+ tools::Long nCount = m_xExtensionBox->GetEntryCount();
+ for ( tools::Long nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nIndex );
+
+ if ( isEnabled(pEntry->m_xPackage) && !checkDependencies( pEntry->m_xPackage ) )
+ {
+ bRet = true;
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+
+void UpdateRequiredDialog::disableAllEntries()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ incBusy();
+
+ tools::Long nCount = m_xExtensionBox->GetEntryCount();
+ for ( tools::Long nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ TEntry_Impl pEntry = m_xExtensionBox->GetEntryData( nIndex );
+ m_pManager->getCmdQueue()->enableExtension( pEntry->m_xPackage, false );
+ }
+
+ decBusy();
+
+ if ( ! hasActiveEntries() )
+ m_xCloseBtn->set_label( m_sCloseText );
+}
+
+// ShowLicenseDialog
+ShowLicenseDialog::ShowLicenseDialog(weld::Window* pParent,
+ const uno::Reference< deployment::XPackage> &xPackage)
+ : GenericDialogController(pParent, "desktop/ui/showlicensedialog.ui", "ShowLicenseDialog")
+ , m_xLicenseText(m_xBuilder->weld_text_view("textview"))
+{
+ m_xLicenseText->set_size_request(m_xLicenseText->get_approximate_digit_width() * 72,
+ m_xLicenseText->get_height_rows(21));
+ m_xLicenseText->set_text(xPackage->getLicenseText());
+}
+
+ShowLicenseDialog::~ShowLicenseDialog()
+{
+}
+
+// UpdateRequiredDialogService
+
+UpdateRequiredDialogService::UpdateRequiredDialogService( SAL_UNUSED_PARAMETER uno::Sequence< uno::Any > const&,
+ uno::Reference< uno::XComponentContext > xComponentContext )
+ : m_xComponentContext(std::move( xComponentContext ))
+{
+}
+
+// XServiceInfo
+OUString UpdateRequiredDialogService::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.ui.UpdateRequiredDialog";
+}
+
+sal_Bool UpdateRequiredDialogService::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > UpdateRequiredDialogService::getSupportedServiceNames()
+{
+ return { "com.sun.star.deployment.ui.UpdateRequiredDialog" };
+}
+
+
+// XExecutableDialog
+
+void UpdateRequiredDialogService::setTitle( OUString const & )
+{
+}
+
+
+sal_Int16 UpdateRequiredDialogService::execute()
+{
+ ::rtl::Reference< ::dp_gui::TheExtensionManager > xManager( TheExtensionManager::get(
+ m_xComponentContext) );
+ xManager->createDialog( true );
+ sal_Int16 nRet = xManager->execute();
+
+ return nRet;
+}
+
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_dialog2.hxx b/desktop/source/deployment/gui/dp_gui_dialog2.hxx
new file mode 100644
index 000000000..a11ff494e
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_dialog2.hxx
@@ -0,0 +1,267 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/locktoplevels.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/weld.hxx>
+
+#include <osl/mutex.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <unotools/resmgr.hxx>
+
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+struct ImplSVEvent;
+
+namespace dp_gui {
+
+class ExtBoxWithBtns_Impl;
+class ExtensionBox_Impl;
+class TheExtensionManager;
+
+class DialogHelper
+{
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ weld::Window* m_pWindow;
+ ImplSVEvent * m_nEventID;
+ TopLevelWindowLocker m_aBusy;
+
+public:
+ DialogHelper(const css::uno::Reference< css::uno::XComponentContext > &,
+ weld::Window* pWindow);
+ virtual ~DialogHelper();
+
+ void openWebBrowser(const OUString& rURL, const OUString& rTitle);
+ weld::Window* getFrameWeld() const { return m_pWindow; }
+ void PostUserEvent( const Link<void*,void>& rLink, void* pCaller );
+ void clearEventID() { m_nEventID = nullptr; }
+
+ virtual void showProgress( bool bStart ) = 0;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) = 0;
+ virtual void updateProgress( const tools::Long nProgress ) = 0;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) = 0;
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &xPackage,
+ bool bLicenseMissing = false ) = 0;
+
+ virtual void prepareChecking() = 0;
+ virtual void checkEntries() = 0;
+
+ static bool IsSharedPkgMgr( const css::uno::Reference< css::deployment::XPackage > &);
+ bool continueOnSharedExtension( const css::uno::Reference< css::deployment::XPackage > &,
+ weld::Widget* pParent,
+ TranslateId pResID,
+ bool &bHadWarning );
+
+ void incBusy() { m_aBusy.incBusy(m_pWindow); }
+ void decBusy() { m_aBusy.decBusy(); }
+ bool isBusy() const { return m_aBusy.isBusy(); }
+ bool installExtensionWarn(std::u16string_view rExtensionURL);
+ bool installForAllUsers(bool &bInstallForAll);
+};
+
+class ExtMgrDialog : public weld::GenericDialogController
+ , public DialogHelper
+{
+ const OUString m_sAddPackages;
+ OUString m_sProgressText;
+ ::osl::Mutex m_aMutex;
+ bool m_bHasProgress;
+ bool m_bProgressChanged;
+ bool m_bStartProgress;
+ bool m_bStopProgress;
+ bool m_bEnableWarning;
+ bool m_bDisableWarning;
+ bool m_bDeleteWarning;
+ bool m_bClosed;
+ tools::Long m_nProgress;
+ Idle m_aIdle;
+ TheExtensionManager *m_pManager;
+
+ css::uno::Reference< css::task::XAbortChannel > m_xAbortChannel;
+
+ std::unique_ptr<ExtBoxWithBtns_Impl> m_xExtensionBox;
+ std::unique_ptr<weld::CustomWeld> m_xExtensionBoxWnd;
+ std::unique_ptr<weld::Button> m_xOptionsBtn;
+ std::unique_ptr<weld::Button> m_xAddBtn;
+ std::unique_ptr<weld::Button> m_xRemoveBtn;
+ std::unique_ptr<weld::Button> m_xEnableBtn;
+ std::unique_ptr<weld::Button> m_xUpdateBtn;
+ std::unique_ptr<weld::Button> m_xCloseBtn;
+ std::unique_ptr<weld::CheckButton> m_xBundledCbx;
+ std::unique_ptr<weld::CheckButton> m_xSharedCbx;
+ std::unique_ptr<weld::CheckButton> m_xUserCbx;
+ std::unique_ptr<weld::LinkButton> m_xGetExtensions;
+ std::unique_ptr<weld::Label> m_xProgressText;
+ std::unique_ptr<weld::ProgressBar> m_xProgressBar;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+ std::unique_ptr<weld::Entry> m_xSearchEntry;
+
+ bool removeExtensionWarn(std::u16string_view rExtensionTitle);
+
+ DECL_LINK( HandleOptionsBtn, weld::Button&, void );
+ DECL_LINK( HandleAddBtn, weld::Button&, void );
+ DECL_LINK( HandleRemoveBtn, weld::Button&, void );
+ DECL_LINK( HandleEnableBtn, weld::Button&, void );
+ DECL_LINK( HandleUpdateBtn, weld::Button&, void );
+ DECL_LINK( HandleCancelBtn, weld::Button&, void );
+ DECL_LINK( HandleCloseBtn, weld::Button&, void );
+ DECL_LINK( HandleExtTypeCbx, weld::Toggleable&, void );
+ DECL_LINK( HandleSearch, weld::Entry&, void );
+ DECL_LINK( TimeOutHdl, Timer *, void );
+ DECL_LINK( startProgress, void *, void );
+
+public:
+ ExtMgrDialog(weld::Window * pParent, TheExtensionManager *pManager);
+ virtual ~ExtMgrDialog() override;
+
+ virtual void showProgress( bool bStart ) override;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) override;
+ virtual void updateProgress( const tools::Long nProgress ) override;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) override;
+
+ void setGetExtensionsURL( const OUString &rURL );
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &,
+ bool bLicenseMissing = false ) override;
+ void enablePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage,
+ bool bEnable );
+ void removePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ void updatePackage(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ bool acceptLicense(const css::uno::Reference< css::deployment::XPackage > &xPackage );
+
+ void Close();
+
+ TheExtensionManager* getExtensionManager() const { return m_pManager; }
+
+ virtual void updateList();
+ virtual void prepareChecking() override;
+ virtual void checkEntries() override;
+
+ css::uno::Sequence< OUString > raiseAddPicker();
+
+ void enableOptionsButton( bool bEnable );
+ void enableRemoveButton( bool bEnable );
+ void enableEnableButton( bool bEnable );
+ /*
+ * Transform the button to "Enable", or to "Disable"
+ * based on the value of bEnable.
+ */
+ void enableButtontoEnable( bool bEnable );
+};
+
+
+class UpdateRequiredDialog : public weld::GenericDialogController
+ , public DialogHelper
+{
+ const OUString m_sCloseText;
+ OUString m_sProgressText;
+ ::osl::Mutex m_aMutex;
+ bool m_bHasProgress;
+ bool m_bProgressChanged;
+ bool m_bStartProgress;
+ bool m_bStopProgress;
+ bool m_bHasLockedEntries;
+ tools::Long m_nProgress;
+ Idle m_aIdle;
+ TheExtensionManager *m_pManager;
+
+ css::uno::Reference< css::task::XAbortChannel > m_xAbortChannel;
+
+ std::unique_ptr<ExtensionBox_Impl> m_xExtensionBox;
+ std::unique_ptr<weld::CustomWeld> m_xExtensionBoxWnd;
+ std::unique_ptr<weld::Label> m_xUpdateNeeded;
+ std::unique_ptr<weld::Button> m_xUpdateBtn;
+ std::unique_ptr<weld::Button> m_xCloseBtn;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+ std::unique_ptr<weld::Label> m_xProgressText;
+ std::unique_ptr<weld::ProgressBar> m_xProgressBar;
+
+ DECL_LINK( HandleUpdateBtn, weld::Button&, void );
+ DECL_LINK( HandleCloseBtn, weld::Button&, void );
+ DECL_LINK( HandleCancelBtn, weld::Button&, void );
+ DECL_LINK( TimeOutHdl, Timer *, void );
+ DECL_LINK( startProgress, void *, void );
+
+ static bool isEnabled( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ static bool checkDependencies( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ bool hasActiveEntries();
+ void disableAllEntries();
+
+public:
+ UpdateRequiredDialog(weld::Window * pParent, TheExtensionManager *pManager);
+ virtual ~UpdateRequiredDialog() override;
+
+ virtual short run() override;
+
+ virtual void showProgress( bool bStart ) override;
+ virtual void updateProgress( const OUString &rText,
+ const css::uno::Reference< css::task::XAbortChannel > &xAbortChannel) override;
+ virtual void updateProgress( const tools::Long nProgress ) override;
+
+ virtual void updatePackageInfo( const css::uno::Reference< css::deployment::XPackage > &xPackage ) override;
+
+ virtual void addPackageToList( const css::uno::Reference< css::deployment::XPackage > &,
+ bool bLicenseMissing = false ) override;
+
+ virtual void prepareChecking() override;
+ virtual void checkEntries() override;
+};
+
+
+class ShowLicenseDialog : public weld::GenericDialogController
+{
+ std::unique_ptr<weld::TextView> m_xLicenseText;
+public:
+ ShowLicenseDialog(weld::Window * pParent, const css::uno::Reference< css::deployment::XPackage > &xPackage);
+ virtual ~ShowLicenseDialog() override;
+};
+
+class UpdateRequiredDialogService : public ::cppu::WeakImplHelper< css::ui::dialogs::XExecutableDialog, css::lang::XServiceInfo >
+{
+ css::uno::Reference< css::uno::XComponentContext > const m_xComponentContext;
+public:
+ UpdateRequiredDialogService( css::uno::Sequence< css::uno::Any > const & args,
+ css::uno::Reference< css::uno::XComponentContext> xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XExecutableDialog
+ virtual void SAL_CALL setTitle( OUString const & title ) override;
+ virtual sal_Int16 SAL_CALL execute() override;
+};
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx
new file mode 100644
index 000000000..fd70b7982
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx
@@ -0,0 +1,1115 @@
+/* -*- 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 <com/sun/star/beans/NamedValue.hpp>
+
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+
+#include <com/sun/star/deployment/ui/LicenseDialog.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/TypeClass.hpp>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <salhelper/thread.hxx>
+#include <ucbhelper/content.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/anytostring.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_extensioncmdqueue.hxx"
+#include "dp_gui_dependencydialog.hxx"
+#include "dp_gui_dialog2.hxx"
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include "dp_gui_theextmgr.hxx"
+#include "dp_gui_updatedialog.hxx"
+#include "dp_gui_updateinstalldialog.hxx"
+#include <dp_dependencies.hxx>
+#include <dp_misc.h>
+#include <dp_identifier.hxx>
+#include <dp_version.hxx>
+
+#include <condition_variable>
+#include <queue>
+#include <memory>
+#include <mutex>
+
+#ifdef _WIN32
+#include <o3tl/safeCoInitUninit.hxx>
+#endif
+
+
+using namespace ::com::sun::star;
+
+namespace {
+
+OUString getVersion( OUString const & sVersion )
+{
+ return ( sVersion.isEmpty() ) ? OUString( "0" ) : sVersion;
+}
+
+OUString getVersion( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ return getVersion( rPackage->getVersion());
+}
+}
+
+
+namespace dp_gui {
+
+namespace {
+
+class ProgressCmdEnv
+ : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment,
+ task::XInteractionHandler,
+ ucb::XProgressHandler >
+{
+ uno::Reference< task::XInteractionHandler2> m_xHandler;
+ uno::Reference< uno::XComponentContext > m_xContext;
+
+ DialogHelper* m_pDialogHelper;
+ OUString m_sTitle;
+ bool m_bWarnUser;
+ sal_Int32 m_nCurrentProgress;
+
+ void updateProgress();
+
+ /// @throws uno::RuntimeException
+ void update_( uno::Any const & Status );
+
+public:
+ /** When param bAskWhenInstalling = true, then the user is asked if he
+ agrees to install this extension. In case this extension is already installed
+ then the user is also notified and asked if he wants to replace that existing
+ extension. In first case an interaction request with an InstallException
+ will be handled and in the second case a VersionException will be handled.
+ */
+
+ ProgressCmdEnv( uno::Reference< uno::XComponentContext > xContext,
+ DialogHelper* pDialogHelper,
+ OUString aTitle )
+ : m_xContext(std::move( xContext ))
+ , m_pDialogHelper( pDialogHelper )
+ , m_sTitle(std::move( aTitle ))
+ , m_bWarnUser( false )
+ , m_nCurrentProgress(0)
+ {}
+
+ weld::Window* activeDialog() { return m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr; }
+
+ void startProgress();
+ void stopProgress();
+ void progressSection( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel );
+ void setWarnUser( bool bNewVal ) { m_bWarnUser = bNewVal; }
+
+ // XCommandEnvironment
+ virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override;
+ virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle( uno::Reference< task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( uno::Any const & Status ) override;
+ virtual void SAL_CALL update( uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+struct ExtensionCmd
+{
+ enum E_CMD_TYPE { ADD, ENABLE, DISABLE, REMOVE, CHECK_FOR_UPDATES, ACCEPT_LICENSE };
+
+ E_CMD_TYPE m_eCmdType;
+ bool m_bWarnUser;
+ OUString m_sExtensionURL;
+ OUString m_sRepository;
+ uno::Reference< deployment::XPackage > m_xPackage;
+ std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
+
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ OUString aExtensionURL,
+ OUString aRepository,
+ const bool bWarnUser )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( bWarnUser ),
+ m_sExtensionURL(std::move( aExtensionURL )),
+ m_sRepository(std::move( aRepository )) {};
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ uno::Reference< deployment::XPackage > xPackage )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( false ),
+ m_xPackage(std::move( xPackage )) {};
+ ExtensionCmd( const E_CMD_TYPE eCommand,
+ std::vector<uno::Reference<deployment::XPackage > >&&vExtensionList )
+ : m_eCmdType( eCommand ),
+ m_bWarnUser( false ),
+ m_vExtensionList( std::move(vExtensionList) ) {};
+};
+
+}
+
+typedef std::shared_ptr< ExtensionCmd > TExtensionCmd;
+
+
+class ExtensionCmdQueue::Thread: public salhelper::Thread
+{
+public:
+ Thread( DialogHelper *pDialogHelper,
+ TheExtensionManager *pManager,
+ uno::Reference< uno::XComponentContext > xContext );
+
+ void addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void removeExtension( const uno::Reference< deployment::XPackage > &rPackage );
+ void enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable );
+ void checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > && vExtensionList );
+ void acceptLicense( const uno::Reference< deployment::XPackage > &rPackage );
+ void stop();
+ bool isBusy();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+
+ void _insert(const TExtensionCmd& rExtCmd);
+
+ void _addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const OUString &rPackageURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void _removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+ void _checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > &&vExtensionList );
+ void _acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage );
+
+ enum Input { NONE, START, STOP };
+
+ uno::Reference< uno::XComponentContext > m_xContext;
+ std::queue< TExtensionCmd > m_queue;
+
+ DialogHelper *m_pDialogHelper;
+ TheExtensionManager *m_pManager;
+
+ const OUString m_sEnablingPackages;
+ const OUString m_sDisablingPackages;
+ const OUString m_sAddingPackages;
+ const OUString m_sRemovingPackages;
+ const OUString m_sDefaultCmd;
+ const OUString m_sAcceptLicense;
+ std::condition_variable m_wakeup;
+ std::mutex m_mutex;
+ Input m_eInput;
+ bool m_bStopped;
+ bool m_bWorking;
+};
+
+
+void ProgressCmdEnv::startProgress()
+{
+ m_nCurrentProgress = 0;
+
+ if ( m_pDialogHelper )
+ m_pDialogHelper->showProgress( true );
+}
+
+
+void ProgressCmdEnv::stopProgress()
+{
+ if ( m_pDialogHelper )
+ m_pDialogHelper->showProgress( false );
+}
+
+
+void ProgressCmdEnv::progressSection( const OUString &rText,
+ const uno::Reference< task::XAbortChannel > &xAbortChannel )
+{
+ m_nCurrentProgress = 0;
+ if ( m_pDialogHelper )
+ {
+ m_pDialogHelper->updateProgress( rText, xAbortChannel );
+ m_pDialogHelper->updateProgress( 5 );
+ }
+}
+
+
+void ProgressCmdEnv::updateProgress()
+{
+ tools::Long nProgress = ((m_nCurrentProgress*5) % 100) + 5;
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updateProgress( nProgress );
+}
+
+// XCommandEnvironment
+
+uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler()
+{
+ return this;
+}
+
+
+uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler()
+{
+ return this;
+}
+
+
+// XInteractionHandler
+
+void ProgressCmdEnv::handle( uno::Reference< task::XInteractionRequest > const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+ dp_misc::TRACE( "[dp_gui_cmdenv.cxx] incoming request:\n"
+ + ::comphelper::anyToString(request) + "\n");
+
+ lang::WrappedTargetException wtExc;
+ deployment::DependencyException depExc;
+ deployment::LicenseException licExc;
+ deployment::VersionException verExc;
+ deployment::InstallException instExc;
+ deployment::PlatformException platExc;
+
+ // selections:
+ bool approve = false;
+ bool abort = false;
+
+ if (request >>= wtExc) {
+ // handable deployment error signalled, e.g.
+ // bundle item registration failed, notify cause only:
+ uno::Any cause;
+ deployment::DeploymentException dpExc;
+ if (wtExc.TargetException >>= dpExc)
+ cause = dpExc.Cause;
+ else {
+ ucb::CommandFailedException cfExc;
+ if (wtExc.TargetException >>= cfExc)
+ cause = cfExc.Reason;
+ else
+ cause = wtExc.TargetException;
+ }
+ update_( cause );
+
+ // ignore intermediate errors of legacy packages, i.e.
+ // former pkgchk behaviour:
+ const uno::Reference< deployment::XPackage > xPackage( wtExc.Context, uno::UNO_QUERY );
+ OSL_ASSERT( xPackage.is() );
+ if ( xPackage.is() )
+ {
+ const uno::Reference< deployment::XPackageTypeInfo > xPackageType( xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ {
+ approve = ( xPackage->isBundle() &&
+ xPackageType->getMediaType().match(
+ "application/vnd.sun.star.legacy-package-bundle" ));
+ }
+ }
+ abort = !approve;
+ }
+ else if (request >>= depExc)
+ {
+ std::vector< OUString > deps;
+ deps.reserve(depExc.UnsatisfiedDependencies.getLength());
+ for (auto const & i : std::as_const(depExc.UnsatisfiedDependencies))
+ {
+ deps.push_back( dp_misc::Dependencies::getErrorText(i) );
+ }
+ {
+ SolarMutexGuard guard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ DependencyDialog aDlg(activeDialog(), deps);
+ short n = aDlg.run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ // Distinguish between closing the dialog and programmatically
+ // canceling the dialog (headless VCL):
+ approve = n == RET_OK
+ || (n == RET_CANCEL && !Application::IsDialogCancelEnabled());
+ }
+ }
+ else if (request >>= licExc)
+ {
+ SolarMutexGuard guard;
+
+ weld::Window *pTopLevel = activeDialog();
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
+ deployment::ui::LicenseDialog::create(
+ m_xContext, pTopLevel ? pTopLevel->GetXWindow() : nullptr,
+ licExc.ExtensionName, licExc.Text ) );
+ sal_Int16 res = xDialog->execute();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
+ abort = true;
+ else if ( res == ui::dialogs::ExecutableDialogResults::OK )
+ approve = true;
+ else
+ {
+ OSL_ASSERT(false);
+ }
+ }
+ else if (request >>= verExc)
+ {
+ TranslateId id;
+ switch (dp_misc::compareVersions(
+ verExc.NewVersion, verExc.Deployed->getVersion() ))
+ {
+ case dp_misc::LESS:
+ id = RID_STR_WARNING_VERSION_LESS;
+ break;
+ case dp_misc::EQUAL:
+ id = RID_STR_WARNING_VERSION_EQUAL;
+ break;
+ default: // dp_misc::GREATER
+ id = RID_STR_WARNING_VERSION_GREATER;
+ break;
+ }
+ OSL_ASSERT( verExc.Deployed.is() );
+ bool bEqualNames = verExc.NewDisplayName ==
+ verExc.Deployed->getDisplayName();
+ {
+ SolarMutexGuard guard;
+
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::OkCancel, DpResId(id)));
+ OUString s;
+ if (bEqualNames)
+ {
+ s = xBox->get_primary_text();
+ }
+ else if (id != RID_STR_WARNING_VERSION_EQUAL)
+ {
+ //hypothetical: requires two instances of an extension with the same
+ //version to have different display names. Probably the developer forgot
+ //to change the version.
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_EQUAL_DIFFERENT_NAMES);
+ }
+ else if (id != RID_STR_WARNING_VERSION_LESS)
+ {
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_LESS_DIFFERENT_NAMES);
+ }
+ else if (id != RID_STR_WARNING_VERSION_GREATER)
+ {
+ s = DpResId(RID_STR_WARNINGBOX_VERSION_GREATER_DIFFERENT_NAMES);
+ }
+ s = s.replaceAll("$NAME", verExc.NewDisplayName);
+ s = s.replaceAll("$OLDNAME", verExc.Deployed->getDisplayName());
+ s = s.replaceAll("$NEW", getVersion(verExc.NewVersion));
+ s = s.replaceAll("$DEPLOYED", getVersion(verExc.Deployed));
+ xBox->set_primary_text(s);
+ approve = xBox->run() == RET_OK;
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ abort = !approve;
+ }
+ }
+ else if (request >>= instExc)
+ {
+ if ( ! m_bWarnUser )
+ {
+ approve = true;
+ }
+ else
+ {
+ if ( m_pDialogHelper )
+ {
+ SolarMutexGuard guard;
+
+ approve = m_pDialogHelper->installExtensionWarn( instExc.displayName );
+ }
+ else
+ approve = false;
+ abort = !approve;
+ }
+ }
+ else if (request >>= platExc)
+ {
+ SolarMutexGuard guard;
+ OUString sMsg(DpResId(RID_STR_UNSUPPORTED_PLATFORM));
+ sMsg = sMsg.replaceAll("%Name", platExc.package->getDisplayName());
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, sMsg));
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ approve = true;
+ }
+
+ if (!approve && !abort)
+ {
+ // forward to UUI handler:
+ if (! m_xHandler.is()) {
+ // late init:
+ m_xHandler = task::InteractionHandler::createWithParentAndContext(m_xContext, nullptr, m_sTitle);
+ }
+ m_xHandler->handle( xRequest );
+ }
+ else
+ {
+ // select:
+ uno::Sequence< uno::Reference< task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ uno::Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ else if (abort) {
+ uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionAbort.is()) {
+ xInteractionAbort->select();
+ // don't query again for ongoing continuations:
+ abort = false;
+ }
+ }
+ }
+ }
+}
+
+
+// XProgressHandler
+
+void ProgressCmdEnv::push( uno::Any const & rStatus )
+{
+ update_( rStatus );
+}
+
+
+void ProgressCmdEnv::update_( uno::Any const & rStatus )
+{
+ OUString text;
+ if ( rStatus.hasValue() && !( rStatus >>= text) )
+ {
+ if ( auto e = o3tl::tryAccess<uno::Exception>(rStatus) )
+ text = e->Message;
+ if ( text.isEmpty() )
+ text = ::comphelper::anyToString( rStatus ); // fallback
+
+ const SolarMutexGuard aGuard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, text));
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ }
+ ++m_nCurrentProgress;
+ updateProgress();
+}
+
+
+void ProgressCmdEnv::update( uno::Any const & rStatus )
+{
+ update_( rStatus );
+}
+
+
+void ProgressCmdEnv::pop()
+{
+ update_( uno::Any() ); // no message
+}
+
+
+ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper,
+ TheExtensionManager *pManager,
+ uno::Reference< uno::XComponentContext > xContext ) :
+ salhelper::Thread( "dp_gui_extensioncmdqueue" ),
+ m_xContext(std::move( xContext )),
+ m_pDialogHelper( pDialogHelper ),
+ m_pManager( pManager ),
+ m_sEnablingPackages( DpResId( RID_STR_ENABLING_PACKAGES ) ),
+ m_sDisablingPackages( DpResId( RID_STR_DISABLING_PACKAGES ) ),
+ m_sAddingPackages( DpResId( RID_STR_ADDING_PACKAGES ) ),
+ m_sRemovingPackages( DpResId( RID_STR_REMOVING_PACKAGES ) ),
+ m_sDefaultCmd( DpResId( RID_STR_ADD_PACKAGES ) ),
+ m_sAcceptLicense( DpResId( RID_STR_ACCEPT_LICENSE ) ),
+ m_eInput( NONE ),
+ m_bStopped( false ),
+ m_bWorking( false )
+{
+ OSL_ASSERT( pDialogHelper );
+}
+
+
+void ExtensionCmdQueue::Thread::addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser )
+{
+ if ( !rExtensionURL.isEmpty() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::REMOVE, rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::ACCEPT_LICENSE, rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable )
+{
+ if ( rPackage.is() )
+ {
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( bEnable ? ExtensionCmd::ENABLE :
+ ExtensionCmd::DISABLE,
+ rPackage );
+ _insert( pEntry );
+ }
+}
+
+
+void ExtensionCmdQueue::Thread::checkForUpdates(
+ std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
+{
+ TExtensionCmd pEntry = std::make_shared<ExtensionCmd>( ExtensionCmd::CHECK_FOR_UPDATES, std::move(vExtensionList) );
+ _insert( pEntry );
+}
+
+
+//Stopping this thread will not abort the installation of extensions.
+void ExtensionCmdQueue::Thread::stop()
+{
+ std::scoped_lock aGuard( m_mutex );
+ m_bStopped = true;
+ m_eInput = STOP;
+ m_wakeup.notify_all();
+}
+
+
+bool ExtensionCmdQueue::Thread::isBusy()
+{
+ std::scoped_lock aGuard( m_mutex );
+ return m_bWorking;
+}
+
+
+ExtensionCmdQueue::Thread::~Thread() {}
+
+
+void ExtensionCmdQueue::Thread::execute()
+{
+#ifdef _WIN32
+ //Needed for use of the service "com.sun.star.system.SystemShellExecute" in
+ //DialogHelper::openWebBrowser
+ int nNbCallCoInitializeExForReinit = 0;
+ o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, nNbCallCoInitializeExForReinit);
+#endif
+ for (;;)
+ {
+ int nSize;
+ Input eInput;
+ {
+ std::unique_lock aGuard( m_mutex );
+ while (m_eInput == NONE) {
+ m_wakeup.wait(aGuard);
+ }
+ eInput = m_eInput;
+ m_eInput = NONE;
+ nSize = m_queue.size();
+ // coverity[missing_lock: FALSE] - maybe due to (by-design) unique_lock vs. scoped_lock?
+ m_bWorking = false;
+ }
+
+ if ( eInput == STOP )
+ break;
+
+ // We only install the extension which are currently in the queue.
+ // The progressbar will be set to show the progress of the current number
+ // of extensions. If we allowed to add extensions now then the progressbar may
+ // have reached the end while we still install newly added extensions.
+ if ( nSize == 0 )
+ continue;
+
+ ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) );
+
+ // Do not lock the following part with addExtension. addExtension may be called in the main thread.
+ // If the message box "Do you want to install the extension (or similar)" is shown and then
+ // addExtension is called, which then blocks the main thread, then we deadlock.
+ bool bStartProgress = true;
+
+ while ( --nSize >= 0 )
+ {
+ {
+ std::scoped_lock aGuard( m_mutex );
+ m_bWorking = true;
+ }
+
+ try
+ {
+ TExtensionCmd pEntry;
+ {
+ std::scoped_lock queueGuard( m_mutex );
+ pEntry = m_queue.front();
+ m_queue.pop();
+ }
+
+ if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) )
+ {
+ currentCmdEnv->startProgress();
+ bStartProgress = false;
+ }
+
+ switch ( pEntry->m_eCmdType ) {
+ case ExtensionCmd::ADD :
+ _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser );
+ break;
+ case ExtensionCmd::REMOVE :
+ _removeExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::ENABLE :
+ _enableExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::DISABLE :
+ _disableExtension( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ case ExtensionCmd::CHECK_FOR_UPDATES :
+ _checkForUpdates( std::vector(pEntry->m_vExtensionList) );
+ break;
+ case ExtensionCmd::ACCEPT_LICENSE :
+ _acceptLicense( currentCmdEnv, pEntry->m_xPackage );
+ break;
+ }
+ }
+ catch ( const ucb::CommandAbortedException & )
+ {
+ //This exception is thrown when the user clicks cancel on the progressbar.
+ //Then we cancel the installation of all extensions and remove them from
+ //the queue.
+ {
+ std::scoped_lock queueGuard2(m_mutex);
+ while ( --nSize >= 0 )
+ m_queue.pop();
+ }
+ break;
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ //This exception is thrown when a user clicked cancel in the messagebox which was
+ //started by the interaction handler. For example the user will be asked if he/she
+ //really wants to install the extension.
+ //These interactions run for exactly one extension at a time. Therefore we continue
+ //with installing the remaining extensions.
+ continue;
+ }
+ catch ( const uno::Exception & )
+ {
+ //Todo display the user an error
+ //see also DialogImpl::SyncPushButton::Click()
+ uno::Any exc( ::cppu::getCaughtException() );
+ OUString msg;
+ deployment::DeploymentException dpExc;
+ if (exc >>= dpExc)
+ {
+ if (auto e = o3tl::tryAccess<uno::Exception>(dpExc.Cause))
+ {
+ // notify error cause only:
+ msg = e->Message;
+ }
+ }
+ if (msg.isEmpty()) // fallback for debugging purposes
+ msg = ::comphelper::anyToString(exc);
+
+ const SolarMutexGuard guard;
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(currentCmdEnv->activeDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, msg));
+ if (m_pDialogHelper)
+ xBox->set_title(m_pDialogHelper->getFrameWeld()->get_title());
+ xBox->run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ //Continue with installation of the remaining extensions
+ }
+ {
+ std::scoped_lock aGuard( m_mutex );
+ m_bWorking = false;
+ }
+ }
+
+ {
+ // when leaving the while loop with break, we should set working to false, too
+ std::scoped_lock aGuard( m_mutex );
+ m_bWorking = false;
+ }
+
+ if ( !bStartProgress )
+ currentCmdEnv->stopProgress();
+ }
+ //end for
+#ifdef _WIN32
+ o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, nNbCallCoInitializeExForReinit);
+#endif
+}
+
+
+void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const OUString &rPackageURL,
+ const OUString &rRepository,
+ const bool bWarnUser )
+{
+ //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void
+ //and anyTitle.get<OUString> throws as RuntimeException.
+ uno::Any anyTitle;
+ try
+ {
+ anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv, m_xContext ).getPropertyValue( "Title" );
+ }
+ catch ( const uno::Exception & )
+ {
+ return;
+ }
+
+ OUString sName;
+ if ( ! (anyTitle >>= sName) )
+ {
+ OSL_FAIL("Could not get file name for extension.");
+ return;
+ }
+
+ rCmdEnv->setWarnUser( bWarnUser );
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sAddingPackages.replaceAll("%EXTENSION_NAME", sName));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(),
+ rRepository, xAbortChannel, rCmdEnv );
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press
+ // cancel this exception is thrown.
+ }
+ catch ( const ucb::CommandAbortedException & )
+ {
+ // User clicked the cancel button
+ // TODO: handle cancel
+ }
+ rCmdEnv->setWarnUser( false );
+}
+
+
+void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sRemovingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ OUString id( dp_misc::getIdentifier( xPackage ) );
+ try
+ {
+ xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv );
+ }
+ catch ( const deployment::DeploymentException & )
+ {}
+ catch ( const ucb::CommandFailedException & )
+ {}
+ catch ( const ucb::CommandAbortedException & )
+ {}
+
+ // Check, if there are still updates to be notified via menu bar icon
+ uno::Sequence< uno::Sequence< OUString > > aItemList;
+ UpdateDialog::createNotifyJob( false, aItemList );
+}
+
+
+void ExtensionCmdQueue::Thread::_checkForUpdates(
+ std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
+{
+ const SolarMutexGuard guard;
+
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+
+ std::vector< UpdateData > vData;
+ UpdateDialog aUpdateDialog(m_xContext, m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, std::move(vExtensionList), &vData);
+
+ aUpdateDialog.notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon
+
+ bool bOk = aUpdateDialog.run() == RET_OK;
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+
+ if (bOk && !vData.empty())
+ {
+ // If there is at least one directly downloadable extension then we
+ // open the install dialog.
+ std::vector< UpdateData > dataDownload;
+
+ for (auto const& data : vData)
+ {
+ if ( data.sWebsiteURL.isEmpty() )
+ dataDownload.push_back(data);
+ }
+
+ short nDialogResult = RET_OK;
+ if ( !dataDownload.empty() )
+ {
+ if (m_pDialogHelper)
+ m_pDialogHelper->incBusy();
+ UpdateInstallDialog aDlg(m_pDialogHelper ? m_pDialogHelper->getFrameWeld() : nullptr, dataDownload, m_xContext);
+ nDialogResult = aDlg.run();
+ if (m_pDialogHelper)
+ m_pDialogHelper->decBusy();
+ aUpdateDialog.notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon
+ }
+ else
+ aUpdateDialog.notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon
+
+ //Now start the webbrowser and navigate to the websites where we get the updates
+ if ( RET_OK == nDialogResult )
+ {
+ for (auto const& data : vData)
+ {
+ if ( m_pDialogHelper && ( !data.sWebsiteURL.isEmpty() ) )
+ m_pDialogHelper->openWebBrowser( data.sWebsiteURL, m_pDialogHelper->getFrameWeld()->get_title() );
+ }
+ }
+ }
+ else
+ aUpdateDialog.notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon
+}
+
+
+void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sEnablingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+
+void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sDisablingPackages.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+
+void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > const &rCmdEnv,
+ const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( !xPackage.is() )
+ return;
+
+ uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager();
+ uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() );
+ OUString sTitle(
+ m_sAcceptLicense.replaceAll("%EXTENSION_NAME",
+ xPackage->getDisplayName()));
+ rCmdEnv->progressSection( sTitle, xAbortChannel );
+
+ try
+ {
+ xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv );
+ if ( m_pDialogHelper )
+ m_pDialogHelper->updatePackageInfo( xPackage );
+ }
+ catch ( const ::ucb::CommandAbortedException & )
+ {}
+}
+
+void ExtensionCmdQueue::Thread::_insert(const TExtensionCmd& rExtCmd)
+{
+ std::scoped_lock aGuard( m_mutex );
+
+ // If someone called stop then we do not process the command -> game over!
+ if ( m_bStopped )
+ return;
+
+ m_queue.push( rExtCmd );
+ m_eInput = START;
+ m_wakeup.notify_all();
+}
+
+
+ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper,
+ TheExtensionManager *pManager,
+ const uno::Reference< uno::XComponentContext > &rContext )
+ : m_thread( new Thread( pDialogHelper, pManager, rContext ) )
+{
+ m_thread->launch();
+}
+
+ExtensionCmdQueue::~ExtensionCmdQueue() {
+ m_thread->stop();
+ m_thread->join();
+}
+
+void ExtensionCmdQueue::addExtension( const OUString & extensionURL,
+ const OUString & repository,
+ const bool bWarnUser )
+{
+ m_thread->addExtension( extensionURL, repository, bWarnUser );
+}
+
+void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ m_thread->removeExtension( rPackage );
+}
+
+void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage,
+ const bool bEnable )
+{
+ m_thread->enableExtension( rPackage, bEnable );
+}
+
+void ExtensionCmdQueue::checkForUpdates( std::vector<uno::Reference<deployment::XPackage > > && vExtensionList )
+{
+ m_thread->checkForUpdates( std::move(vExtensionList) );
+}
+
+void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage )
+{
+ m_thread->acceptLicense( rPackage );
+}
+
+void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext )
+{
+ dp_misc::syncRepositories( false, new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
+}
+
+bool ExtensionCmdQueue::isBusy()
+{
+ return m_thread->isBusy();
+}
+
+void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext,
+ const uno::Reference< task::XInteractionRequest > & xRequest )
+{
+ ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, nullptr, "Extension Manager" ) );
+ xCmdEnv->handle( xRequest );
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx
new file mode 100644
index 000000000..3703d1e8c
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.hxx
@@ -0,0 +1,94 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ref.hxx>
+
+#include <vector>
+
+#include "dp_gui_updatedata.hxx"
+
+/// @HTML
+
+namespace com::sun::star {
+ namespace task { class XInteractionRequest; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_gui {
+
+class DialogHelper;
+class TheExtensionManager;
+
+/**
+ Manages installing of extensions in the GUI mode. Requests for installing
+ Extensions can be asynchronous. For example, the Extension Manager is running
+ in an office process and someone uses the system integration to install an Extension.
+ That is, the user double clicks an extension symbol in a file browser, which then
+ causes an invocation of "unopkg gui ext". When at that time the Extension Manager
+ already performs a task, triggered by the user (for example, add, update, disable,
+ enable) then adding of the extension will be postponed until the user has finished
+ the task.
+
+ This class also ensures that the extensions are not installed in the main thread.
+ Doing so would cause a deadlock because of the progress bar which needs to be constantly
+ updated.
+*/
+class ExtensionCmdQueue {
+
+public:
+ /**
+ Create an instance.
+ */
+ ExtensionCmdQueue( DialogHelper * pDialogHelper,
+ TheExtensionManager *pManager,
+ const css::uno::Reference< css::uno::XComponentContext > & rContext);
+
+ ~ExtensionCmdQueue();
+
+ void addExtension( const OUString &rExtensionURL,
+ const OUString &rRepository,
+ const bool bWarnUser );
+ void removeExtension( const css::uno::Reference< css::deployment::XPackage > &rPackage );
+ void enableExtension( const css::uno::Reference< css::deployment::XPackage > &rPackage,
+ const bool bEnable );
+ void checkForUpdates( std::vector< css::uno::Reference< css::deployment::XPackage > > && vList );
+ void acceptLicense( const css::uno::Reference< css::deployment::XPackage > &rPackage );
+ static void syncRepositories( const css::uno::Reference< css::uno::XComponentContext > & xContext );
+
+ bool isBusy();
+private:
+ ExtensionCmdQueue(ExtensionCmdQueue const &) = delete;
+ ExtensionCmdQueue& operator =(ExtensionCmdQueue const &) = delete;
+
+ class Thread;
+
+ rtl::Reference< Thread > m_thread;
+};
+
+void handleInteractionRequest( const css::uno::Reference< css::uno::XComponentContext > & xContext,
+ const css::uno::Reference< css::task::XInteractionRequest > & xRequest );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extlistbox.cxx b/desktop/source/deployment/gui/dp_gui_extlistbox.cxx
new file mode 100644
index 000000000..d30d24f79
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extlistbox.cxx
@@ -0,0 +1,1144 @@
+/* -*- 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 <dp_shared.hxx>
+#include <strings.hrc>
+#include "dp_gui.h"
+#include "dp_gui_extlistbox.hxx"
+#include "dp_gui_theextmgr.hxx"
+#include <dp_dependencies.hxx>
+#include <bitmaps.hlst>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/system/XSystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <cppuhelper/weakref.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <utility>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/weldutils.hxx>
+#include <algorithm>
+
+constexpr OUStringLiteral USER_PACKAGE_MANAGER = u"user";
+constexpr OUStringLiteral SHARED_PACKAGE_MANAGER = u"shared";
+
+using namespace ::com::sun::star;
+
+namespace dp_gui {
+
+namespace {
+
+struct FindWeakRef
+{
+ const uno::Reference<deployment::XPackage> m_extension;
+
+ explicit FindWeakRef( uno::Reference<deployment::XPackage> ext): m_extension(std::move(ext)) {}
+ bool operator () (uno::WeakReference< deployment::XPackage > const & ref);
+};
+
+bool FindWeakRef::operator () (uno::WeakReference< deployment::XPackage > const & ref)
+{
+ const uno::Reference<deployment::XPackage> ext(ref);
+ return ext == m_extension;
+}
+
+} // end namespace
+
+// struct Entry_Impl
+
+Entry_Impl::Entry_Impl( const uno::Reference< deployment::XPackage > &xPackage,
+ const PackageState eState, const bool bReadOnly ) :
+ m_bActive( false ),
+ m_bLocked( bReadOnly ),
+ m_bHasOptions( false ),
+ m_bUser( false ),
+ m_bShared( false ),
+ m_bNew( false ),
+ m_bChecked( false ),
+ m_bMissingDeps( false ),
+ m_bHasButtons( false ),
+ m_bMissingLic( false ),
+ m_eState( eState ),
+ m_xPackage( xPackage )
+{
+ try
+ {
+ m_sTitle = xPackage->getDisplayName();
+ m_sVersion = xPackage->getVersion();
+ m_sDescription = xPackage->getDescription();
+ m_sLicenseText = xPackage->getLicenseText();
+
+ beans::StringPair aInfo( m_xPackage->getPublisherInfo() );
+ m_sPublisher = aInfo.First;
+ m_sPublisherURL = aInfo.Second;
+
+ // get the icons for the package if there are any
+ uno::Reference< graphic::XGraphic > xGraphic = xPackage->getIcon( false );
+ if ( xGraphic.is() )
+ m_aIcon = Image( xGraphic );
+
+ if ( eState == AMBIGUOUS )
+ m_sErrorText = DpResId( RID_STR_ERROR_UNKNOWN_STATUS );
+ else if ( eState == NOT_REGISTERED )
+ checkDependencies();
+ }
+ catch (const deployment::ExtensionRemovedException &) {}
+ catch (const uno::RuntimeException &) {}
+}
+
+
+Entry_Impl::~Entry_Impl()
+{}
+
+
+sal_Int32 Entry_Impl::CompareTo( const CollatorWrapper *pCollator, const TEntry_Impl& rEntry ) const
+{
+ sal_Int32 eCompare = pCollator->compareString( m_sTitle, rEntry->m_sTitle );
+ if ( eCompare == 0 )
+ {
+ eCompare = m_sVersion.compareTo( rEntry->m_sVersion );
+ if ( eCompare == 0 )
+ {
+ sal_Int32 nCompare = m_xPackage->getRepositoryName().compareTo( rEntry->m_xPackage->getRepositoryName() );
+ if ( nCompare < 0 )
+ eCompare = -1;
+ else if ( nCompare > 0 )
+ eCompare = 1;
+ }
+ }
+ return eCompare;
+}
+
+
+void Entry_Impl::checkDependencies()
+{
+ try {
+ m_xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
+ }
+ catch ( const deployment::DeploymentException &e )
+ {
+ deployment::DependencyException depExc;
+ if ( e.Cause >>= depExc )
+ {
+ OUStringBuffer aMissingDep( DpResId( RID_STR_ERROR_MISSING_DEPENDENCIES ) );
+ for ( const auto& i : std::as_const(depExc.UnsatisfiedDependencies) )
+ {
+ aMissingDep.append("\n");
+ aMissingDep.append(dp_misc::Dependencies::getErrorText(i));
+ }
+ aMissingDep.append("\n");
+ m_sErrorText = aMissingDep.makeStringAndClear();
+ m_bMissingDeps = true;
+ }
+ }
+}
+
+// ExtensionRemovedListener
+
+void ExtensionRemovedListener::disposing( lang::EventObject const & rEvt )
+{
+ uno::Reference< deployment::XPackage > xPackage( rEvt.Source, uno::UNO_QUERY );
+
+ if ( xPackage.is() )
+ {
+ m_pParent->removeEntry( xPackage );
+ }
+}
+
+
+ExtensionRemovedListener::~ExtensionRemovedListener()
+{
+}
+
+
+// ExtensionBox_Impl
+ExtensionBox_Impl::ExtensionBox_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll)
+ : m_bHasScrollBar( false )
+ , m_bHasActive( false )
+ , m_bNeedsRecalc( true )
+ , m_bInCheckMode( false )
+ , m_bAdjustActive( false )
+ , m_bInDelete( false )
+ , m_nActive( 0 )
+ , m_nTopIndex( 0 )
+ , m_nStdHeight( 0 )
+ , m_nActiveHeight( 0 )
+ , m_aSharedImage(StockImage::Yes, RID_BMP_SHARED)
+ , m_aLockedImage(StockImage::Yes, RID_BMP_LOCKED)
+ , m_aWarningImage(StockImage::Yes, RID_BMP_WARNING)
+ , m_aDefaultImage(StockImage::Yes, RID_BMP_EXTENSION)
+ , m_pManager( nullptr )
+ , m_xScrollBar(std::move(xScroll))
+{
+}
+
+void ExtensionBox_Impl::Init()
+{
+ m_xScrollBar->connect_vadjustment_changed( LINK( this, ExtensionBox_Impl, ScrollHdl ) );
+
+ auto nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
+ auto nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ m_nStdHeight = nTitleHeight;
+ else
+ m_nStdHeight = nIconHeight;
+ m_nStdHeight += GetTextHeight() + TOP_OFFSET;
+
+ nIconHeight = ICON_HEIGHT + 2*TOP_OFFSET + 1;
+ if ( m_nStdHeight < nIconHeight )
+ m_nStdHeight = nIconHeight;
+
+ m_nActiveHeight = m_nStdHeight;
+
+ m_xRemoveListener = new ExtensionRemovedListener( this );
+
+ m_pLocale.reset( new lang::Locale( Application::GetSettings().GetLanguageTag().getLocale() ) );
+ m_pCollator.reset( new CollatorWrapper( ::comphelper::getProcessComponentContext() ) );
+ m_pCollator->loadDefaultCollator( *m_pLocale, i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
+}
+
+ExtensionBox_Impl::~ExtensionBox_Impl()
+{
+ if ( ! m_bInDelete )
+ DeleteRemoved();
+
+ m_bInDelete = true;
+
+ for (auto const& entry : m_vEntries)
+ {
+ entry->m_xPackage->removeEventListener( m_xRemoveListener );
+ }
+
+ m_vEntries.clear();
+
+ m_xRemoveListener.clear();
+
+ m_pLocale.reset();
+ m_pCollator.reset();
+}
+
+sal_Int32 ExtensionBox_Impl::getItemCount() const
+{
+ return static_cast< sal_Int32 >( m_vEntries.size() );
+}
+
+
+sal_Int32 ExtensionBox_Impl::getSelIndex() const
+{
+ if ( m_bHasActive )
+ {
+ OSL_ASSERT( m_nActive >= -1);
+ return static_cast< sal_Int32 >( m_nActive );
+ }
+ else
+ return ENTRY_NOTFOUND;
+}
+
+
+// Title + description
+void ExtensionBox_Impl::CalcActiveHeight( const tools::Long nPos )
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ // get title height
+ tools::Long aTextHeight;
+ tools::Long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
+ tools::Long nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ aTextHeight = nTitleHeight;
+ else
+ aTextHeight = nIconHeight;
+
+ // calc description height
+ Size aSize = GetOutputSizePixel();
+
+ aSize.AdjustWidth( -(ICON_OFFSET) );
+ aSize.setHeight( 10000 );
+
+ OUString aText( m_vEntries[ nPos ]->m_sErrorText );
+ if ( !aText.isEmpty() )
+ aText += "\n";
+ aText += m_vEntries[ nPos ]->m_sDescription;
+
+ tools::Rectangle aRect = GetDrawingArea()->get_ref_device().GetTextRect(tools::Rectangle( Point(), aSize ), aText,
+ DrawTextFlags::MultiLine | DrawTextFlags::WordBreak);
+ aTextHeight += aRect.GetHeight();
+
+ if ( aTextHeight < m_nStdHeight )
+ aTextHeight = m_nStdHeight;
+
+ m_nActiveHeight = aTextHeight;
+
+ if ( m_vEntries[ nPos ]->m_bHasButtons )
+ m_nActiveHeight += 2;
+}
+
+tools::Rectangle ExtensionBox_Impl::GetEntryRect( const tools::Long nPos ) const
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ Size aSize( GetOutputSizePixel() );
+
+ if ( m_vEntries[ nPos ]->m_bActive )
+ aSize.setHeight( m_nActiveHeight );
+ else
+ aSize.setHeight( m_nStdHeight );
+
+ Point aPos( 0, -m_nTopIndex + nPos * m_nStdHeight );
+ if ( m_bHasActive && ( nPos < m_nActive ) )
+ aPos.AdjustY(m_nActiveHeight - m_nStdHeight );
+
+ return tools::Rectangle( aPos, aSize );
+}
+
+
+void ExtensionBox_Impl::DeleteRemoved()
+{
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ m_bInDelete = true;
+
+ m_vRemovedEntries.clear();
+
+ m_bInDelete = false;
+}
+
+
+//This function may be called with nPos < 0
+void ExtensionBox_Impl::selectEntry( const tools::Long nPos )
+{
+ bool invalidate = false;
+ {
+ //ToDo we should not use the guard at such a big scope here.
+ //Currently it is used to guard m_vEntries and m_nActive. m_nActive will be
+ //modified in this function.
+ //It would be probably best to always use a copy of m_vEntries
+ //and some other state variables from ExtensionBox_Impl for
+ //the whole painting operation. See issue i86993
+ ::osl::MutexGuard guard(m_entriesMutex);
+
+ if ( m_bInCheckMode )
+ return;
+
+ if ( m_bHasActive )
+ {
+ if ( nPos == m_nActive )
+ return;
+
+ m_bHasActive = false;
+ m_vEntries[ m_nActive ]->m_bActive = false;
+ }
+
+ if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
+ {
+ m_bHasActive = true;
+ m_nActive = nPos;
+ m_vEntries[ nPos ]->m_bActive = true;
+
+ if ( IsReallyVisible() )
+ {
+ m_bAdjustActive = true;
+ }
+ }
+
+ if ( IsReallyVisible() )
+ {
+ m_bNeedsRecalc = true;
+ invalidate = true;
+ }
+ }
+
+ if (invalidate)
+ {
+ SolarMutexGuard g;
+ Invalidate();
+ }
+}
+
+
+void ExtensionBox_Impl::DrawRow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const TEntry_Impl& rEntry)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ if (rEntry->m_bActive)
+ rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
+ else if ((rEntry->m_eState != REGISTERED) && (rEntry->m_eState != NOT_AVAILABLE))
+ rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
+ else
+ rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
+
+ if (rEntry->m_bActive)
+ {
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
+ rRenderContext.DrawRect(rRect);
+ }
+ else
+ {
+ rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
+ rRenderContext.SetTextFillColor();
+ rRenderContext.Erase(rRect);
+ }
+
+ // Draw extension icon
+ Point aPos( rRect.TopLeft() );
+ aPos += Point(TOP_OFFSET, TOP_OFFSET);
+ Image aImage;
+ if (!rEntry->m_aIcon)
+ aImage = m_aDefaultImage;
+ else
+ aImage = rEntry->m_aIcon;
+ Size aImageSize = aImage.GetSizePixel();
+ if ((aImageSize.Width() <= ICON_WIDTH ) && ( aImageSize.Height() <= ICON_HEIGHT ) )
+ rRenderContext.DrawImage(Point(aPos.X() + ((ICON_WIDTH - aImageSize.Width()) / 2),
+ aPos.Y() + ((ICON_HEIGHT - aImageSize.Height()) / 2)),
+ aImage);
+ else
+ rRenderContext.DrawImage(aPos, Size(ICON_WIDTH, ICON_HEIGHT), aImage);
+
+ // Setup fonts
+ // expand the point size of the desired font to the equivalent pixel size
+ weld::SetPointFont(rRenderContext, GetDrawingArea()->get_font());
+ vcl::Font aStdFont(rRenderContext.GetFont());
+ vcl::Font aBoldFont(aStdFont);
+ aBoldFont.SetWeight(WEIGHT_BOLD);
+ rRenderContext.SetFont(aBoldFont);
+ auto aTextHeight = rRenderContext.GetTextHeight();
+
+ // Get max title width
+ auto nMaxTitleWidth = rRect.GetWidth() - ICON_OFFSET;
+ nMaxTitleWidth -= (2 * SMALL_ICON_SIZE) + (4 * SPACE_BETWEEN);
+ rRenderContext.SetFont(aStdFont);
+ tools::Long nLinkWidth = 0;
+ if (!rEntry->m_sPublisher.isEmpty())
+ {
+ nLinkWidth = rRenderContext.GetTextWidth(rEntry->m_sPublisher);
+ nMaxTitleWidth -= nLinkWidth + (2 * SPACE_BETWEEN);
+ }
+ tools::Long aVersionWidth = rRenderContext.GetTextWidth(rEntry->m_sVersion);
+
+ aPos = rRect.TopLeft() + Point(ICON_OFFSET, TOP_OFFSET);
+
+ rRenderContext.SetFont(aBoldFont);
+ tools::Long aTitleWidth = rRenderContext.GetTextWidth(rEntry->m_sTitle) + (aTextHeight / 3);
+ if (aTitleWidth > nMaxTitleWidth - aVersionWidth)
+ {
+ aTitleWidth = nMaxTitleWidth - aVersionWidth - (aTextHeight / 3);
+ OUString aShortTitle = rRenderContext.GetEllipsisString(rEntry->m_sTitle, aTitleWidth);
+ rRenderContext.DrawText(aPos, aShortTitle);
+ aTitleWidth += (aTextHeight / 3);
+ }
+ else
+ rRenderContext.DrawText(aPos, rEntry->m_sTitle);
+
+ rRenderContext.SetFont(aStdFont);
+ rRenderContext.DrawText(Point(aPos.X() + aTitleWidth, aPos.Y()), rEntry->m_sVersion);
+
+ tools::Long nIconHeight = TOP_OFFSET + SMALL_ICON_SIZE;
+ tools::Long nTitleHeight = TOP_OFFSET + GetTextHeight();
+ if ( nIconHeight < nTitleHeight )
+ aTextHeight = nTitleHeight;
+ else
+ aTextHeight = nIconHeight;
+
+ // draw description
+ OUString sDescription;
+ if (!rEntry->m_sErrorText.isEmpty())
+ {
+ if (rEntry->m_bActive)
+ sDescription = rEntry->m_sErrorText + "\n" + rEntry->m_sDescription;
+ else
+ sDescription = rEntry->m_sErrorText;
+ }
+ else
+ sDescription = rEntry->m_sDescription;
+
+ aPos.AdjustY(aTextHeight );
+ if (rEntry->m_bActive)
+ {
+ tools::Long nExtraHeight = 0;
+
+ if (rEntry->m_bHasButtons)
+ nExtraHeight = 2;
+
+ rRenderContext.DrawText(tools::Rectangle(aPos.X(), aPos.Y(), rRect.Right(), rRect.Bottom() - nExtraHeight),
+ sDescription, DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
+ }
+ else
+ {
+ //replace LF to space, so words do not stick together in one line view
+ sDescription = sDescription.replace(0x000A, ' ');
+ const tools::Long nWidth = rRenderContext.GetTextWidth( sDescription );
+ if (nWidth > rRect.GetWidth() - aPos.X())
+ sDescription = rRenderContext.GetEllipsisString(sDescription, rRect.GetWidth() - aPos.X());
+ rRenderContext.DrawText(aPos, sDescription);
+ }
+
+ // Draw publisher link
+ if (!rEntry->m_sPublisher.isEmpty())
+ {
+ aPos = rRect.TopLeft() + Point( ICON_OFFSET + nMaxTitleWidth + (2*SPACE_BETWEEN), TOP_OFFSET );
+
+ rRenderContext.Push(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::TEXTFILLCOLOR);
+ rRenderContext.SetTextColor(rStyleSettings.GetLinkColor());
+ rRenderContext.SetTextFillColor(rStyleSettings.GetFieldColor());
+ vcl::Font aFont = rRenderContext.GetFont();
+ // to underline
+ aFont.SetUnderline(LINESTYLE_SINGLE);
+ rRenderContext.SetFont(aFont);
+ rRenderContext.DrawText(aPos, rEntry->m_sPublisher);
+ rEntry->m_aLinkRect = tools::Rectangle(aPos, Size(nLinkWidth, aTextHeight));
+ rRenderContext.Pop();
+ }
+
+ // Draw status icons
+ if (!rEntry->m_bUser)
+ {
+ aPos = rRect.TopRight() + Point( -(RIGHT_ICON_OFFSET + SMALL_ICON_SIZE), TOP_OFFSET );
+ if (rEntry->m_bLocked)
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aLockedImage);
+ else
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aSharedImage);
+ }
+ if ((rEntry->m_eState == AMBIGUOUS ) || rEntry->m_bMissingDeps || rEntry->m_bMissingLic)
+ {
+ aPos = rRect.TopRight() + Point(-(RIGHT_ICON_OFFSET + SPACE_BETWEEN + 2 * SMALL_ICON_SIZE), TOP_OFFSET);
+ rRenderContext.DrawImage(aPos, Size(SMALL_ICON_SIZE, SMALL_ICON_SIZE), m_aWarningImage);
+ }
+
+ rRenderContext.SetLineColor(COL_LIGHTGRAY);
+ rRenderContext.DrawLine(rRect.BottomLeft(), rRect.BottomRight());
+}
+
+
+void ExtensionBox_Impl::RecalcAll()
+{
+ if ( m_bHasActive )
+ CalcActiveHeight( m_nActive );
+
+ SetupScrollBar();
+
+ if ( m_bHasActive )
+ {
+ tools::Rectangle aEntryRect = GetEntryRect( m_nActive );
+
+ if ( m_bAdjustActive )
+ {
+ m_bAdjustActive = false;
+
+ // If the top of the selected entry isn't visible, make it visible
+ if ( aEntryRect.Top() < 0 )
+ {
+ m_nTopIndex += aEntryRect.Top();
+ aEntryRect.Move( 0, -aEntryRect.Top() );
+ }
+
+ // If the bottom of the selected entry isn't visible, make it visible even if now the top
+ // isn't visible any longer ( the buttons are more important )
+ Size aOutputSize = GetOutputSizePixel();
+ if ( aEntryRect.Bottom() > aOutputSize.Height() )
+ {
+ m_nTopIndex += ( aEntryRect.Bottom() - aOutputSize.Height() );
+ aEntryRect.Move( 0, -( aEntryRect.Bottom() - aOutputSize.Height() ) );
+ }
+
+ // If there is unused space below the last entry but all entries don't fit into the box,
+ // move the content down to use the whole space
+ const tools::Long nTotalHeight = GetTotalHeight();
+ if ( m_bHasScrollBar && ( aOutputSize.Height() + m_nTopIndex > nTotalHeight ) )
+ {
+ tools::Long nOffset = m_nTopIndex;
+ m_nTopIndex = nTotalHeight - aOutputSize.Height();
+ nOffset -= m_nTopIndex;
+ aEntryRect.Move( 0, nOffset );
+ }
+
+ if ( m_bHasScrollBar )
+ m_xScrollBar->vadjustment_set_value( m_nTopIndex );
+ }
+ }
+
+ m_bNeedsRecalc = false;
+}
+
+
+bool ExtensionBox_Impl::HandleCursorKey( sal_uInt16 nKeyCode )
+{
+ if ( m_vEntries.empty() )
+ return true;
+
+ tools::Long nSelect = 0;
+
+ if ( m_bHasActive )
+ {
+ tools::Long nPageSize = GetOutputSizePixel().Height() / m_nStdHeight;
+ if ( nPageSize < 2 )
+ nPageSize = 2;
+
+ if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_RIGHT ) )
+ nSelect = m_nActive + 1;
+ else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_LEFT ) )
+ nSelect = m_nActive - 1;
+ else if ( nKeyCode == KEY_HOME )
+ nSelect = 0;
+ else if ( nKeyCode == KEY_END )
+ nSelect = m_vEntries.size() - 1;
+ else if ( nKeyCode == KEY_PAGEUP )
+ nSelect = m_nActive - nPageSize + 1;
+ else if ( nKeyCode == KEY_PAGEDOWN )
+ nSelect = m_nActive + nPageSize - 1;
+ }
+ else // when there is no selected entry, we will select the first or the last.
+ {
+ if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_PAGEDOWN ) || ( nKeyCode == KEY_HOME ) )
+ nSelect = 0;
+ else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_PAGEUP ) || ( nKeyCode == KEY_END ) )
+ nSelect = m_vEntries.size() - 1;
+ }
+
+ if ( nSelect < 0 )
+ nSelect = 0;
+ if ( o3tl::make_unsigned(nSelect) >= m_vEntries.size() )
+ nSelect = m_vEntries.size() - 1;
+
+ selectEntry( nSelect );
+
+ return true;
+}
+
+
+void ExtensionBox_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rPaintRect*/)
+{
+ if ( !m_bInDelete )
+ DeleteRemoved();
+
+ if ( m_bNeedsRecalc )
+ RecalcAll();
+
+ Point aStart( 0, -m_nTopIndex );
+ Size aSize(GetOutputSizePixel());
+
+ const ::osl::MutexGuard aGuard( m_entriesMutex );
+
+ for (auto const& entry : m_vEntries)
+ {
+ aSize.setHeight( entry->m_bActive ? m_nActiveHeight : m_nStdHeight );
+ tools::Rectangle aEntryRect( aStart, aSize );
+ DrawRow(rRenderContext, aEntryRect, entry);
+ aStart.AdjustY(aSize.Height() );
+ }
+}
+
+
+tools::Long ExtensionBox_Impl::GetTotalHeight() const
+{
+ tools::Long nHeight = m_vEntries.size() * m_nStdHeight;
+
+ if ( m_bHasActive )
+ {
+ nHeight += m_nActiveHeight - m_nStdHeight;
+ }
+
+ return nHeight;
+}
+
+
+void ExtensionBox_Impl::SetupScrollBar()
+{
+ const Size aSize = GetOutputSizePixel();
+ const auto nTotalHeight = GetTotalHeight();
+ const bool bNeedsScrollBar = ( nTotalHeight > aSize.Height() );
+
+ if ( bNeedsScrollBar )
+ {
+ if ( m_nTopIndex + aSize.Height() > nTotalHeight )
+ m_nTopIndex = nTotalHeight - aSize.Height();
+
+ m_xScrollBar->vadjustment_configure(m_nTopIndex, 0, nTotalHeight,
+ m_nStdHeight, ( aSize.Height() * 4 ) / 5,
+ aSize.Height());
+
+ if (!m_bHasScrollBar)
+ m_xScrollBar->set_vpolicy(VclPolicyType::ALWAYS);
+ }
+ else if ( m_bHasScrollBar )
+ {
+ m_xScrollBar->set_vpolicy(VclPolicyType::NEVER);
+ m_nTopIndex = 0;
+ }
+
+ m_bHasScrollBar = bNeedsScrollBar;
+}
+
+
+void ExtensionBox_Impl::Resize()
+{
+ RecalcAll();
+ Invalidate();
+}
+
+void ExtensionBox_Impl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(250, 150), MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+
+ Init();
+}
+
+tools::Long ExtensionBox_Impl::PointToPos( const Point& rPos )
+{
+ tools::Long nPos = ( rPos.Y() + m_nTopIndex ) / m_nStdHeight;
+
+ if ( m_bHasActive && ( nPos > m_nActive ) )
+ {
+ if ( rPos.Y() + m_nTopIndex <= m_nActive*m_nStdHeight + m_nActiveHeight )
+ nPos = m_nActive;
+ else
+ nPos = ( rPos.Y() + m_nTopIndex - (m_nActiveHeight - m_nStdHeight) ) / m_nStdHeight;
+ }
+
+ return nPos;
+}
+
+bool ExtensionBox_Impl::MouseMove( const MouseEvent& rMEvt )
+{
+ bool bOverHyperlink = false;
+
+ auto nPos = PointToPos( rMEvt.GetPosPixel() );
+ if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ bOverHyperlink = !rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rMEvt.GetPosPixel());
+ }
+
+ if (bOverHyperlink)
+ SetPointer(PointerStyle::RefHand);
+ else
+ SetPointer(PointerStyle::Arrow);
+
+ return false;
+}
+
+OUString ExtensionBox_Impl::RequestHelp(tools::Rectangle& rRect)
+{
+ auto nPos = PointToPos( rRect.TopLeft() );
+ if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ bool bOverHyperlink = !rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rRect);
+ if (bOverHyperlink)
+ {
+ rRect = rEntry->m_aLinkRect;
+ return rEntry->m_sPublisherURL;
+ }
+ }
+
+ return OUString();
+}
+
+bool ExtensionBox_Impl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( !rMEvt.IsLeft() )
+ return false;
+
+ if (rMEvt.IsMod1() && m_bHasActive)
+ selectEntry(ExtensionBox_Impl::ENTRY_NOTFOUND); // Selecting a not existing entry will deselect the current one
+ else
+ {
+ auto nPos = PointToPos( rMEvt.GetPosPixel() );
+
+ if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
+ {
+ const auto& rEntry = m_vEntries[nPos];
+ if (!rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rMEvt.GetPosPixel()))
+ {
+ try
+ {
+ css::uno::Reference<css::system::XSystemShellExecute> xSystemShellExecute(
+ css::system::SystemShellExecute::create(comphelper::getProcessComponentContext()));
+ //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException
+ xSystemShellExecute->execute(rEntry->m_sPublisherURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+ catch (...)
+ {
+ }
+ return true;
+ }
+ }
+
+ selectEntry( nPos );
+ }
+ return true;
+}
+
+bool ExtensionBox_Impl::KeyInput(const KeyEvent& rKEvt)
+{
+ if ( !m_bInDelete )
+ DeleteRemoved();
+
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+
+ bool bHandled = false;
+ if (nKeyCode != KEY_TAB && aKeyCode.GetGroup() == KEYGROUP_CURSOR)
+ bHandled = HandleCursorKey(nKeyCode);
+
+ return bHandled;
+}
+
+bool ExtensionBox_Impl::FindEntryPos( const TEntry_Impl& rEntry, const tools::Long nStart,
+ const tools::Long nEnd, tools::Long &nPos )
+{
+ nPos = nStart;
+ if ( nStart > nEnd )
+ return false;
+
+ sal_Int32 eCompare;
+
+ if ( nStart == nEnd )
+ {
+ eCompare = rEntry->CompareTo( m_pCollator.get(), m_vEntries[ nStart ] );
+ if ( eCompare < 0 )
+ return false;
+ else if ( eCompare == 0 )
+ {
+ //Workaround. See i86963.
+ if (rEntry->m_xPackage != m_vEntries[nStart]->m_xPackage)
+ return false;
+
+ if ( m_bInCheckMode )
+ m_vEntries[ nStart ]->m_bChecked = true;
+ return true;
+ }
+ else
+ {
+ nPos = nStart + 1;
+ return false;
+ }
+ }
+
+ const tools::Long nMid = nStart + ( ( nEnd - nStart ) / 2 );
+ eCompare = rEntry->CompareTo( m_pCollator.get(), m_vEntries[ nMid ] );
+
+ if ( eCompare < 0 )
+ return FindEntryPos( rEntry, nStart, nMid-1, nPos );
+ else if ( eCompare > 0 )
+ return FindEntryPos( rEntry, nMid+1, nEnd, nPos );
+ else
+ {
+ //Workaround.See i86963.
+ if (rEntry->m_xPackage != m_vEntries[nMid]->m_xPackage)
+ return false;
+
+ if ( m_bInCheckMode )
+ m_vEntries[ nMid ]->m_bChecked = true;
+ nPos = nMid;
+ return true;
+ }
+}
+
+void ExtensionBox_Impl::cleanVecListenerAdded()
+{
+ m_vListenerAdded.erase(std::remove_if(m_vListenerAdded.begin(), m_vListenerAdded.end(),
+ [](const uno::WeakReference<deployment::XPackage>& rxListener) {
+ const uno::Reference<deployment::XPackage> hardRef(rxListener);
+ return !hardRef.is();
+ }),
+ m_vListenerAdded.end());
+}
+
+void ExtensionBox_Impl::addEventListenerOnce(
+ uno::Reference<deployment::XPackage > const & extension)
+{
+ //make sure to only add the listener once
+ cleanVecListenerAdded();
+ if ( std::none_of(m_vListenerAdded.begin(), m_vListenerAdded.end(),
+ FindWeakRef(extension)) )
+ {
+ extension->addEventListener( m_xRemoveListener );
+ m_vListenerAdded.emplace_back(extension);
+ }
+}
+
+
+void ExtensionBox_Impl::addEntry( const uno::Reference< deployment::XPackage > &xPackage,
+ bool bLicenseMissing )
+{
+ PackageState eState = TheExtensionManager::getPackageState( xPackage );
+ bool bLocked = m_pManager->isReadOnly( xPackage );
+
+ TEntry_Impl pEntry = std::make_shared<Entry_Impl>( xPackage, eState, bLocked );
+
+ // Don't add empty entries
+ if ( pEntry->m_sTitle.isEmpty() )
+ return;
+
+ {
+ osl::MutexGuard guard(m_entriesMutex);
+ tools::Long nPos = 0;
+ if (m_vEntries.empty())
+ {
+ addEventListenerOnce(xPackage);
+ m_vEntries.push_back(pEntry);
+ }
+ else
+ {
+ if (!FindEntryPos(pEntry, 0, m_vEntries.size() - 1, nPos))
+ {
+ addEventListenerOnce(xPackage);
+ m_vEntries.insert(m_vEntries.begin() + nPos, pEntry);
+ }
+ else if (!m_bInCheckMode)
+ {
+ OSL_FAIL("ExtensionBox_Impl::addEntry(): Will not add duplicate entries");
+ }
+ }
+
+ pEntry->m_bHasOptions = m_pManager->supportsOptions(xPackage);
+ pEntry->m_bUser = (xPackage->getRepositoryName() == USER_PACKAGE_MANAGER);
+ pEntry->m_bShared = (xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER);
+ pEntry->m_bNew = m_bInCheckMode;
+ pEntry->m_bMissingLic = bLicenseMissing;
+
+ if (bLicenseMissing)
+ pEntry->m_sErrorText = DpResId(RID_STR_ERROR_MISSING_LICENSE);
+
+ //access to m_nActive must be guarded
+ if (!m_bInCheckMode && m_bHasActive && (m_nActive >= nPos))
+ m_nActive += 1;
+ }
+
+ if ( IsReallyVisible() )
+ Invalidate();
+
+ m_bNeedsRecalc = true;
+}
+
+void ExtensionBox_Impl::updateEntry( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ for (auto const& entry : m_vEntries)
+ {
+ if ( entry->m_xPackage == xPackage )
+ {
+ PackageState eState = TheExtensionManager::getPackageState( xPackage );
+ entry->m_bHasOptions = m_pManager->supportsOptions( xPackage );
+ entry->m_eState = eState;
+ entry->m_sTitle = xPackage->getDisplayName();
+ entry->m_sVersion = xPackage->getVersion();
+ entry->m_sDescription = xPackage->getDescription();
+
+ if ( eState == REGISTERED )
+ entry->m_bMissingLic = false;
+
+ if ( eState == AMBIGUOUS )
+ entry->m_sErrorText = DpResId( RID_STR_ERROR_UNKNOWN_STATUS );
+ else if ( ! entry->m_bMissingLic )
+ entry->m_sErrorText.clear();
+
+ if ( IsReallyVisible() )
+ Invalidate();
+ break;
+ }
+ }
+}
+
+//This function is also called as a result of removing an extension.
+//see PackageManagerImpl::removePackage
+//The gui is a registered as listener on the package. Removing it will cause the
+//listeners to be notified and then this function is called. At this moment xPackage
+//is in the disposing state and all calls on it may result in a DisposedException.
+void ExtensionBox_Impl::removeEntry( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ if ( m_bInDelete )
+ return;
+
+ bool invalidate = false;
+ {
+ ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
+
+ auto iIndex = std::find_if(m_vEntries.begin(), m_vEntries.end(),
+ [&xPackage](const TEntry_Impl& rxEntry) { return rxEntry->m_xPackage == xPackage; });
+ if (iIndex != m_vEntries.end())
+ {
+ tools::Long nPos = iIndex - m_vEntries.begin();
+
+ // Entries mustn't be removed here, because they contain a hyperlink control
+ // which can only be deleted when the thread has the solar mutex. Therefore
+ // the entry will be moved into the m_vRemovedEntries list which will be
+ // cleared on the next paint event
+ m_vRemovedEntries.push_back( *iIndex );
+ (*iIndex)->m_xPackage->removeEventListener(m_xRemoveListener);
+ m_vEntries.erase( iIndex );
+
+ m_bNeedsRecalc = true;
+
+ if ( IsReallyVisible() )
+ invalidate = true;
+
+ if ( m_bHasActive )
+ {
+ if ( nPos < m_nActive )
+ m_nActive -= 1;
+ else if ( ( nPos == m_nActive ) &&
+ ( nPos == static_cast<tools::Long>(m_vEntries.size()) ) )
+ m_nActive -= 1;
+
+ m_bHasActive = false;
+ //clear before calling out of this method
+ aGuard.clear();
+ selectEntry( m_nActive );
+ }
+ }
+ }
+
+ if (invalidate)
+ {
+ SolarMutexGuard g;
+ Invalidate();
+ }
+}
+
+
+void ExtensionBox_Impl::RemoveUnlocked()
+{
+ bool bAllRemoved = false;
+
+ while ( ! bAllRemoved )
+ {
+ bAllRemoved = true;
+
+ ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
+
+ for (auto const& entry : m_vEntries)
+ {
+ if ( !entry->m_bLocked )
+ {
+ bAllRemoved = false;
+ uno::Reference< deployment::XPackage> xPackage = entry->m_xPackage;
+ aGuard.clear();
+ removeEntry( xPackage );
+ break;
+ }
+ }
+ }
+}
+
+
+void ExtensionBox_Impl::prepareChecking()
+{
+ m_bInCheckMode = true;
+ for (auto const& entry : m_vEntries)
+ {
+ entry->m_bChecked = false;
+ entry->m_bNew = false;
+ }
+}
+
+
+void ExtensionBox_Impl::checkEntries()
+{
+ tools::Long nNewPos = -1;
+ tools::Long nChangedActivePos = -1;
+ tools::Long nPos = 0;
+ bool bNeedsUpdate = false;
+
+ {
+ osl::MutexGuard guard(m_entriesMutex);
+ auto iIndex = m_vEntries.begin();
+ while (iIndex != m_vEntries.end())
+ {
+ if (!(*iIndex)->m_bChecked)
+ {
+ (*iIndex)->m_bChecked = true;
+ bNeedsUpdate = true;
+ nPos = iIndex - m_vEntries.begin();
+ if ((*iIndex)->m_bNew)
+ { // add entry to list and correct active pos
+ if (nNewPos == -1)
+ nNewPos = nPos;
+ if (nPos <= m_nActive)
+ m_nActive += 1;
+ ++iIndex;
+ }
+ else
+ { // remove entry from list
+ if (nPos < nNewPos)
+ {
+ --nNewPos;
+ }
+ if (nPos < nChangedActivePos)
+ {
+ --nChangedActivePos;
+ }
+ if (nPos < m_nActive)
+ m_nActive -= 1;
+ else if (nPos == m_nActive)
+ {
+ nChangedActivePos = nPos;
+ m_nActive = -1;
+ m_bHasActive = false;
+ }
+ m_vRemovedEntries.push_back(*iIndex);
+ (*iIndex)->m_xPackage->removeEventListener(m_xRemoveListener);
+ iIndex = m_vEntries.erase(iIndex);
+ }
+ }
+ else
+ ++iIndex;
+ }
+ }
+
+ m_bInCheckMode = false;
+
+ if ( nNewPos != - 1)
+ selectEntry( nNewPos );
+ else if (nChangedActivePos != -1) {
+ selectEntry(nChangedActivePos);
+ }
+
+ if ( bNeedsUpdate )
+ {
+ m_bNeedsRecalc = true;
+ if ( IsReallyVisible() )
+ Invalidate();
+ }
+}
+
+IMPL_LINK(ExtensionBox_Impl, ScrollHdl, weld::ScrolledWindow&, rScrBar, void)
+{
+ m_nTopIndex = rScrBar.vadjustment_get_value();
+ Invalidate();
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_extlistbox.hxx b/desktop/source/deployment/gui/dp_gui_extlistbox.hxx
new file mode 100644
index 000000000..d706975dc
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_extlistbox.hxx
@@ -0,0 +1,214 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/image.hxx>
+#include <vcl/weld.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <unotools/collatorwrapper.hxx>
+
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+
+#include <memory>
+
+#include "dp_gui.h"
+
+namespace dp_gui {
+
+#define SMALL_ICON_SIZE 16
+#define TOP_OFFSET 5
+#define ICON_HEIGHT 42
+#define ICON_WIDTH 47
+#define ICON_OFFSET 72
+#define RIGHT_ICON_OFFSET 5
+#define SPACE_BETWEEN 3
+
+class TheExtensionManager;
+
+
+struct Entry_Impl;
+
+typedef std::shared_ptr< Entry_Impl > TEntry_Impl;
+
+struct Entry_Impl
+{
+ bool m_bActive :1;
+ bool m_bLocked :1;
+ bool m_bHasOptions :1;
+ bool m_bUser :1;
+ bool m_bShared :1;
+ bool m_bNew :1;
+ bool m_bChecked :1;
+ bool m_bMissingDeps :1;
+ bool m_bHasButtons :1;
+ bool m_bMissingLic :1;
+ PackageState m_eState;
+ OUString m_sTitle;
+ OUString m_sVersion;
+ OUString m_sDescription;
+ OUString m_sPublisher;
+ OUString m_sPublisherURL;
+ OUString m_sErrorText;
+ OUString m_sLicenseText;
+ Image m_aIcon;
+ tools::Rectangle m_aLinkRect;
+
+ css::uno::Reference<css::deployment::XPackage> m_xPackage;
+
+ Entry_Impl(const css::uno::Reference<css::deployment::XPackage> &xPackage,
+ const PackageState eState, const bool bReadOnly);
+ ~Entry_Impl();
+
+ sal_Int32 CompareTo(const CollatorWrapper *pCollator, const TEntry_Impl& rEntry) const;
+ void checkDependencies();
+};
+
+class ExtensionBox_Impl;
+
+
+class ExtensionRemovedListener : public ::cppu::WeakImplHelper<css::lang::XEventListener>
+{
+ ExtensionBox_Impl* m_pParent;
+
+public:
+
+ explicit ExtensionRemovedListener( ExtensionBox_Impl *pParent ) { m_pParent = pParent; }
+ virtual ~ExtensionRemovedListener() override;
+
+
+ // XEventListener
+ virtual void SAL_CALL disposing(css::lang::EventObject const& evt) override;
+};
+
+class ExtensionBox_Impl : public weld::CustomWidgetController
+{
+ bool m_bHasScrollBar : 1;
+ bool m_bHasActive : 1;
+ bool m_bNeedsRecalc : 1;
+ bool m_bInCheckMode : 1;
+ bool m_bAdjustActive : 1;
+ bool m_bInDelete : 1;
+ //Must be guarded together with m_vEntries to ensure a valid index at all times.
+ //Use m_entriesMutex as guard.
+ tools::Long m_nActive;
+ tools::Long m_nTopIndex;
+ tools::Long m_nStdHeight;
+ tools::Long m_nActiveHeight;
+ Image m_aSharedImage;
+ Image m_aLockedImage;
+ Image m_aWarningImage;
+ Image m_aDefaultImage;
+
+ rtl::Reference<ExtensionRemovedListener> m_xRemoveListener;
+
+ TheExtensionManager *m_pManager;
+ //This mutex is used for synchronizing access to m_vEntries.
+ //Currently it is used to synchronize adding, removing entries and
+ //functions like getItemName, getItemDescription, etc. to prevent
+ //that m_vEntries is accessed at an invalid index.
+ //ToDo: There are many more places where m_vEntries is read and which may
+ //fail. For example the Paint method is probable called from the main thread
+ //while new entries are added / removed in a separate thread.
+ mutable ::osl::Mutex m_entriesMutex;
+ std::vector< TEntry_Impl > m_vEntries;
+ std::vector< TEntry_Impl > m_vRemovedEntries;
+
+ std::unique_ptr<css::lang::Locale> m_pLocale;
+ std::unique_ptr<CollatorWrapper> m_pCollator;
+
+ //Holds weak references to extensions to which is we have added an XEventListener
+ std::vector< css::uno::WeakReference<
+ css::deployment::XPackage> > m_vListenerAdded;
+
+ std::unique_ptr<weld::ScrolledWindow> m_xScrollBar;
+
+ //Removes the dead weak references from m_vListenerAdded
+ void cleanVecListenerAdded();
+ void addEventListenerOnce(css::uno::Reference<css::deployment::XPackage> const & extension);
+
+ void CalcActiveHeight( const tools::Long nPos );
+ tools::Long GetTotalHeight() const;
+ void SetupScrollBar();
+ void DrawRow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const TEntry_Impl& rEntry);
+ bool HandleCursorKey( sal_uInt16 nKeyCode );
+ bool FindEntryPos( const TEntry_Impl& rEntry, tools::Long nStart, tools::Long nEnd, tools::Long &nFound );
+ void DeleteRemoved();
+
+ DECL_LINK( ScrollHdl, weld::ScrolledWindow&, void );
+
+ void Init();
+public:
+ explicit ExtensionBox_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll);
+ virtual ~ExtensionBox_Impl() override;
+
+ virtual bool MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual bool MouseMove( const MouseEvent& rMEvt ) override;
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle &rPaintRect ) override;
+ virtual void Resize() override;
+ virtual OUString RequestHelp(tools::Rectangle& rRect) override;
+
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+ TEntry_Impl const & GetEntryData( tools::Long nPos ) { return m_vEntries[ nPos ]; }
+ tools::Long GetEntryCount() const { return static_cast<tools::Long>(m_vEntries.size()); }
+ tools::Rectangle GetEntryRect( const tools::Long nPos ) const;
+ bool HasActive() const { return m_bHasActive; }
+ tools::Long PointToPos( const Point& rPos );
+ virtual void RecalcAll();
+ void RemoveUnlocked();
+
+
+ virtual void selectEntry( const tools::Long nPos );
+ void addEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage,
+ bool bLicenseMissing = false );
+ void updateEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage );
+ void removeEntry(const css::uno::Reference<css::deployment::XPackage> &xPackage );
+
+ void prepareChecking();
+ void checkEntries();
+
+ void setExtensionManager(TheExtensionManager* pManager) { m_pManager = pManager; }
+
+ //These functions are used for automatic testing
+public:
+ enum { ENTRY_NOTFOUND = -1 };
+
+ /** @return The count of the entries in the list box. */
+ sal_Int32 getItemCount() const;
+
+ /** @return The index of the first selected entry in the list box.
+ When nothing is selected, which is the case when getItemCount returns '0',
+ then this function returns ENTRY_NOTFOUND */
+ /** @return The index of the first selected entry in the list box.
+ When nothing is selected, which is the case when getItemCount returns '0',
+ then this function returns ENTRY_NOTFOUND */
+ sal_Int32 getSelIndex() const;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_service.cxx b/desktop/source/deployment/gui/dp_gui_service.cxx
new file mode 100644
index 000000000..c359cb750
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_service.cxx
@@ -0,0 +1,316 @@
+/* -*- 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 <memory>
+#include "dp_gui_theextmgr.hxx"
+#include <osl/diagnose.h>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/unwrapargs.hxx>
+#include <unotools/resmgr.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
+
+#include <optional>
+#include "license_dialog.hxx"
+#include "dp_gui_dialog2.hxx"
+#include "dp_gui_extensioncmdqueue.hxx"
+#include <dp_misc.h>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_gui {
+
+namespace {
+
+class MyApp : public Application
+{
+public:
+ MyApp();
+
+ MyApp(const MyApp&) = delete;
+ const MyApp& operator=(const MyApp&) = delete;
+
+ // Application
+ virtual int Main() override;
+ virtual void DeInit() override;
+};
+
+}
+
+MyApp::MyApp()
+{
+}
+
+
+int MyApp::Main()
+{
+ return EXIT_SUCCESS;
+}
+
+void MyApp::DeInit()
+{
+ css::uno::Reference< css::uno::XComponentContext > context(
+ comphelper::getProcessComponentContext());
+ dp_misc::disposeBridges(context);
+ css::uno::Reference< css::lang::XComponent >(
+ context, css::uno::UNO_QUERY_THROW)->dispose();
+ comphelper::setProcessServiceFactory(nullptr);
+}
+
+static OUString ReplaceProductNameHookProc( const OUString& rStr )
+{
+ if (rStr.indexOf( "%PRODUCT" ) == -1)
+ return rStr;
+
+ static const OUString sProductName = utl::ConfigManager::getProductName();
+ static const OUString sVersion = utl::ConfigManager::getProductVersion();
+ static const OUString sAboutBoxVersion = utl::ConfigManager::getAboutBoxProductVersion();
+ static const OUString sAboutBoxVersionSuffix = utl::ConfigManager::getAboutBoxProductVersionSuffix();
+ static const OUString sExtension = utl::ConfigManager::getProductExtension();
+ static const OUString sOOOVendor = utl::ConfigManager::getVendor();
+
+ OUString sRet = rStr.replaceAll( "%PRODUCTNAME", sProductName );
+ sRet = sRet.replaceAll( "%PRODUCTVERSION", sVersion );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSIONSUFFIX", sAboutBoxVersionSuffix );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSION", sAboutBoxVersion );
+ sRet = sRet.replaceAll( "%OOOVENDOR", sOOOVendor );
+ sRet = sRet.replaceAll( "%PRODUCTEXTENSION", sExtension );
+ return sRet;
+}
+
+namespace {
+
+class ServiceImpl
+ : public ::cppu::WeakImplHelper<ui::dialogs::XAsynchronousExecutableDialog,
+ task::XJobExecutor, css::lang::XServiceInfo>
+{
+ Reference<XComponentContext> const m_xComponentContext;
+ std::optional< Reference<awt::XWindow> > /* const */ m_parent;
+ std::optional<OUString> m_extensionURL;
+ OUString m_initialTitle;
+ bool m_bShowUpdateOnly;
+
+public:
+ ServiceImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XAsynchronousExecutableDialog
+ virtual void SAL_CALL setDialogTitle( OUString const & aTitle ) override;
+ virtual void SAL_CALL startExecuteModal(
+ Reference< ui::dialogs::XDialogClosedListener > const & xListener ) override;
+
+ // XJobExecutor
+ virtual void SAL_CALL trigger( OUString const & event ) override;
+};
+
+}
+
+ServiceImpl::ServiceImpl( Sequence<Any> const& args,
+ Reference<XComponentContext> const& xComponentContext)
+ : m_xComponentContext(xComponentContext),
+ m_bShowUpdateOnly( false )
+{
+ /* if true then this service is running in a unopkg process and not in an office process */
+ std::optional<OUString> view;
+ try {
+ std::optional<sal_Bool> unopkg;
+ comphelper::unwrapArgs( args, m_parent, view, unopkg );
+ return;
+ } catch ( const css::lang::IllegalArgumentException & ) {
+ }
+ try {
+ comphelper::unwrapArgs( args, m_extensionURL);
+ } catch ( const css::lang::IllegalArgumentException & ) {
+ }
+
+ ResHookProc pProc = Translate::GetReadStringHook();
+ if ( !pProc )
+ Translate::SetReadStringHook(ReplaceProductNameHookProc);
+}
+
+// XServiceInfo
+OUString ServiceImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.ui.PackageManagerDialog";
+}
+
+sal_Bool ServiceImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > ServiceImpl::getSupportedServiceNames()
+{
+ return { "com.sun.star.deployment.ui.PackageManagerDialog" };
+}
+
+// XAsynchronousExecutableDialog
+
+void ServiceImpl::setDialogTitle( OUString const & title )
+{
+ if ( dp_gui::TheExtensionManager::s_ExtMgr.is() )
+ {
+ const SolarMutexGuard guard;
+ ::rtl::Reference< ::dp_gui::TheExtensionManager > dialog(
+ ::dp_gui::TheExtensionManager::get( m_xComponentContext,
+ m_parent ? *m_parent : Reference<awt::XWindow>(),
+ m_extensionURL ? *m_extensionURL : OUString() ) );
+ dialog->SetText( title );
+ }
+ else
+ m_initialTitle = title;
+}
+
+
+void ServiceImpl::startExecuteModal(
+ Reference< ui::dialogs::XDialogClosedListener > const & xListener )
+{
+ bool bCloseDialog = true; // only used if m_bShowUpdateOnly is true
+ std::unique_ptr<Application> app;
+ //ToDo: synchronize access to s_dialog !!!
+ if (! dp_gui::TheExtensionManager::s_ExtMgr.is())
+ {
+ const bool bAppUp = (GetpApp() != nullptr);
+ bool bOfficePipePresent;
+ try {
+ bOfficePipePresent = dp_misc::office_is_running();
+ }
+ catch (const Exception & exc) {
+ if (bAppUp) {
+ const SolarMutexGuard guard;
+ vcl::Window* pWin = Application::GetActiveTopWindow();
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok, exc.Message));
+ xBox->run();
+ }
+ throw;
+ }
+
+ if (! bOfficePipePresent) {
+ OSL_ASSERT( ! bAppUp );
+ app.reset( new MyApp );
+ if (! InitVCL() )
+ throw RuntimeException( "Cannot initialize VCL!",
+ static_cast<OWeakObject *>(this) );
+ Application::SetDisplayName(
+ utl::ConfigManager::getProductName() +
+ " " +
+ utl::ConfigManager::getProductVersion());
+ ExtensionCmdQueue::syncRepositories( m_xComponentContext );
+ }
+ }
+ else
+ {
+ // When m_bShowUpdateOnly is set, we are inside the office and the user clicked
+ // the update notification icon in the menu bar. We must not close the extensions
+ // dialog after displaying the update dialog when it has been visible before
+ if ( m_bShowUpdateOnly )
+ bCloseDialog = ! dp_gui::TheExtensionManager::s_ExtMgr->isVisible();
+ }
+
+ {
+ const SolarMutexGuard guard;
+ ::rtl::Reference< ::dp_gui::TheExtensionManager > myExtMgr(
+ ::dp_gui::TheExtensionManager::get(
+ m_xComponentContext,
+ m_parent ? *m_parent : Reference<awt::XWindow>(),
+ m_extensionURL ? *m_extensionURL : OUString() ) );
+ myExtMgr->createDialog( false );
+ if (!m_initialTitle.isEmpty()) {
+ myExtMgr->SetText( m_initialTitle );
+ m_initialTitle.clear();
+ }
+ if ( m_bShowUpdateOnly )
+ {
+ myExtMgr->checkUpdates();
+ if ( bCloseDialog )
+ myExtMgr->Close();
+ else
+ myExtMgr->ToTop();
+ }
+ else
+ {
+ myExtMgr->Show();
+ myExtMgr->ToTop();
+ }
+ }
+
+ if (app != nullptr)
+ {
+ Application::Execute();
+ DeInitVCL();
+ }
+
+ if (xListener.is())
+ xListener->dialogClosed(
+ ui::dialogs::DialogClosedEvent(
+ static_cast< ::cppu::OWeakObject * >(this),
+ sal_Int16(0)) );
+}
+
+// XJobExecutor
+
+void ServiceImpl::trigger( OUString const &rEvent )
+{
+ if ( rEvent == "SHOW_UPDATE_DIALOG" )
+ m_bShowUpdateOnly = true;
+ else
+ m_bShowUpdateOnly = false;
+
+ startExecuteModal( Reference< ui::dialogs::XDialogClosedListener >() );
+}
+
+} // namespace dp_gui
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+desktop_LicenseDialog_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_gui::LicenseDialog(args, context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+desktop_ServiceImpl_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_gui::ServiceImpl(args, context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+desktop_UpdateRequiredDialogService_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_gui::UpdateRequiredDialogService(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_theextmgr.cxx b/desktop/source/deployment/gui/dp_gui_theextmgr.cxx
new file mode 100644
index 000000000..5ee3f53c6
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_theextmgr.cxx
@@ -0,0 +1,537 @@
+/* -*- 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 <utility>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+#include "dp_gui_dialog2.hxx"
+#include "dp_gui_extensioncmdqueue.hxx"
+#include "dp_gui_theextmgr.hxx"
+#include <dp_misc.h>
+#include <dp_update.hxx>
+
+constexpr OUStringLiteral USER_PACKAGE_MANAGER = u"user";
+constexpr OUStringLiteral SHARED_PACKAGE_MANAGER = u"shared";
+
+using namespace ::com::sun::star;
+
+namespace dp_gui {
+
+
+::rtl::Reference< TheExtensionManager > TheExtensionManager::s_ExtMgr;
+
+
+// TheExtensionManager
+
+
+TheExtensionManager::TheExtensionManager( uno::Reference< awt::XWindow > xParent,
+ const uno::Reference< uno::XComponentContext > &xContext ) :
+ m_xContext( xContext ),
+ m_xParent(std::move( xParent )),
+ m_bModified(false),
+ m_bExtMgrDialogExecuting(false)
+{
+ m_xExtensionManager = deployment::ExtensionManager::get( xContext );
+ m_xExtensionManager->addModifyListener( this );
+
+ uno::Reference< lang::XMultiServiceFactory > xConfig(
+ configuration::theDefaultProvider::get(xContext));
+ uno::Sequence<uno::Any> args(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.OptionsDialog/Nodes"))}
+ }));
+ m_xNameAccessNodes.set(
+ xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args),
+ uno::UNO_QUERY_THROW);
+
+ // get the 'get more extensions here' url
+ uno::Sequence<uno::Any> args2(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", uno::Any(OUString("/org.openoffice.Office.ExtensionManager/ExtensionRepositories"))}
+ }));
+ uno::Reference< container::XNameAccess > xNameAccessRepositories;
+ xNameAccessRepositories.set(
+ xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args2),
+ uno::UNO_QUERY_THROW);
+ try
+ { //throws css::container::NoSuchElementException, css::lang::WrappedTargetException
+ uno::Any value = xNameAccessRepositories->getByName("WebsiteLink");
+ m_sGetExtensionsURL = value.get< OUString > ();
+ }
+ catch ( const uno::Exception& )
+ {}
+
+ if ( dp_misc::office_is_running() )
+ {
+ // the registration should be done after the construction has been ended
+ // otherwise an exception prevents object creation, but it is registered as a listener
+ m_xDesktop.set( frame::Desktop::create(xContext), uno::UNO_SET_THROW );
+ m_xDesktop->addTerminateListener( this );
+ }
+}
+
+TheExtensionManager::~TheExtensionManager()
+{
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+}
+
+void TheExtensionManager::createDialog( const bool bCreateUpdDlg )
+{
+ const SolarMutexGuard guard;
+
+ if ( bCreateUpdDlg )
+ {
+ if ( !m_xUpdReqDialog )
+ {
+ m_xUpdReqDialog.reset(new UpdateRequiredDialog(Application::GetFrameWeld(m_xParent), this));
+ m_xExecuteCmdQueue.reset( new ExtensionCmdQueue( m_xUpdReqDialog.get(), this, m_xContext ) );
+ createPackageList();
+ }
+ }
+ else if ( !m_xExtMgrDialog )
+ {
+ m_xExtMgrDialog = std::make_shared<ExtMgrDialog>(Application::GetFrameWeld(m_xParent), this);
+ m_xExecuteCmdQueue.reset( new ExtensionCmdQueue( m_xExtMgrDialog.get(), this, m_xContext ) );
+ m_xExtMgrDialog->setGetExtensionsURL( m_sGetExtensionsURL );
+ createPackageList();
+ }
+}
+
+void TheExtensionManager::Show()
+{
+ const SolarMutexGuard guard;
+
+ m_bExtMgrDialogExecuting = true;
+
+ weld::DialogController::runAsync(m_xExtMgrDialog, [this](sal_Int32 /*nResult*/) {
+ m_bExtMgrDialogExecuting = false;
+ auto xExtMgrDialog = m_xExtMgrDialog;
+ m_xExtMgrDialog.reset();
+ xExtMgrDialog->Close();
+ });
+}
+
+void TheExtensionManager::SetText( const OUString &rTitle )
+{
+ const SolarMutexGuard guard;
+
+ getDialog()->set_title( rTitle );
+}
+
+
+void TheExtensionManager::ToTop()
+{
+ const SolarMutexGuard guard;
+
+ getDialog()->present();
+}
+
+void TheExtensionManager::Close()
+{
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ m_xExtMgrDialog->Close();
+ }
+ else if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+}
+
+sal_Int16 TheExtensionManager::execute()
+{
+ sal_Int16 nRet = 0;
+
+ if ( m_xUpdReqDialog )
+ {
+ nRet = m_xUpdReqDialog->run();
+ m_xUpdReqDialog.reset();
+ }
+
+ return nRet;
+}
+
+bool TheExtensionManager::isVisible()
+{
+ weld::Window* pDialog = getDialog();
+ return pDialog && pDialog->get_visible();
+}
+
+void TheExtensionManager::checkUpdates()
+{
+ std::vector< uno::Reference< deployment::XPackage > > vEntries;
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+
+ try {
+ xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() );
+ } catch ( const deployment::DeploymentException & ) {
+ return;
+ } catch ( const ucb::CommandFailedException & ) {
+ return;
+ } catch ( const ucb::CommandAbortedException & ) {
+ return;
+ } catch ( const lang::IllegalArgumentException & e ) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+ for ( auto const & i : std::as_const(xAllPackages) )
+ {
+ uno::Reference< deployment::XPackage > xPackage = dp_misc::getExtensionWithHighestVersion(i);
+ OSL_ASSERT(xPackage.is());
+ if ( xPackage.is() )
+ {
+ vEntries.push_back( xPackage );
+ }
+ }
+
+ m_xExecuteCmdQueue->checkForUpdates( std::move(vEntries) );
+}
+
+
+bool TheExtensionManager::installPackage( const OUString &rPackageURL, bool bWarnUser )
+{
+ if ( rPackageURL.isEmpty() )
+ return false;
+
+ createDialog( false );
+
+ bool bInstall = true;
+ bool bInstallForAll = false;
+
+ // DV! missing function is read only repository from extension manager
+ if ( !bWarnUser && ! m_xExtensionManager->isReadOnlyRepository( SHARED_PACKAGE_MANAGER ) )
+ bInstall = getDialogHelper()->installForAllUsers( bInstallForAll );
+
+ if ( !bInstall )
+ return false;
+
+ if ( bInstallForAll )
+ m_xExecuteCmdQueue->addExtension( rPackageURL, SHARED_PACKAGE_MANAGER, false );
+ else
+ m_xExecuteCmdQueue->addExtension( rPackageURL, USER_PACKAGE_MANAGER, bWarnUser );
+
+ return true;
+}
+
+
+void TheExtensionManager::terminateDialog()
+{
+ if ( dp_misc::office_is_running() )
+ return;
+
+ const SolarMutexGuard guard;
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ Application::Quit();
+}
+
+
+void TheExtensionManager::createPackageList()
+{
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+
+ try {
+ xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() );
+ } catch ( const deployment::DeploymentException & ) {
+ return;
+ } catch ( const ucb::CommandFailedException & ) {
+ return;
+ } catch ( const ucb::CommandAbortedException & ) {
+ return;
+ } catch ( const lang::IllegalArgumentException & e ) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( e.Message,
+ e.Context, anyEx );
+ }
+
+ for ( uno::Sequence< uno::Reference< deployment::XPackage > > const & xPackageList : std::as_const(xAllPackages) )
+ {
+ for ( uno::Reference< deployment::XPackage > const & xPackage : xPackageList )
+ {
+ if ( xPackage.is() )
+ {
+ PackageState eState = getPackageState( xPackage );
+ getDialogHelper()->addPackageToList( xPackage );
+ // When the package is enabled, we can stop here, otherwise we have to look for
+ // another version of this package
+ if ( ( eState == REGISTERED ) || ( eState == NOT_AVAILABLE ) )
+ break;
+ }
+ }
+ }
+
+ const uno::Sequence< uno::Reference< deployment::XPackage > > xNoLicPackages = m_xExtensionManager->getExtensionsWithUnacceptedLicenses( SHARED_PACKAGE_MANAGER,
+ uno::Reference< ucb::XCommandEnvironment >() );
+ for ( uno::Reference< deployment::XPackage > const & xPackage : xNoLicPackages )
+ {
+ if ( xPackage.is() )
+ {
+ getDialogHelper()->addPackageToList( xPackage, true );
+ }
+ }
+}
+
+
+PackageState TheExtensionManager::getPackageState( const uno::Reference< deployment::XPackage > &xPackage )
+{
+ try {
+ beans::Optional< beans::Ambiguous< sal_Bool > > option(
+ xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >() ) );
+ if ( option.IsPresent )
+ {
+ ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
+ if ( reg.IsAmbiguous )
+ return AMBIGUOUS;
+ else
+ return reg.Value ? REGISTERED : NOT_REGISTERED;
+ }
+ else
+ return NOT_AVAILABLE;
+ }
+ catch ( const uno::RuntimeException & ) {
+ throw;
+ }
+ catch (const uno::Exception &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ return NOT_AVAILABLE;
+ }
+}
+
+
+bool TheExtensionManager::isReadOnly( const uno::Reference< deployment::XPackage > &xPackage ) const
+{
+ if ( m_xExtensionManager.is() && xPackage.is() )
+ {
+ return m_xExtensionManager->isReadOnlyRepository( xPackage->getRepositoryName() );
+ }
+ else
+ return true;
+}
+
+
+// The function investigates if the extension supports options.
+bool TheExtensionManager::supportsOptions( const uno::Reference< deployment::XPackage > &xPackage ) const
+{
+ bool bOptions = false;
+
+ if ( ! xPackage->isBundle() )
+ return false;
+
+ beans::Optional< OUString > aId = xPackage->getIdentifier();
+
+ //a bundle must always have an id
+ OSL_ASSERT( aId.IsPresent );
+
+ //iterate over all available nodes
+ const uno::Sequence< OUString > seqNames = m_xNameAccessNodes->getElementNames();
+
+ for ( OUString const & nodeName : seqNames )
+ {
+ uno::Any anyNode = m_xNameAccessNodes->getByName( nodeName );
+ //If we have a node then it must contain the set of leaves. This is part of OptionsDialog.xcs
+ uno::Reference< XInterface> xIntNode = anyNode.get< uno::Reference< XInterface > >();
+ uno::Reference< container::XNameAccess > xNode( xIntNode, uno::UNO_QUERY_THROW );
+
+ uno::Any anyLeaves = xNode->getByName("Leaves");
+ uno::Reference< XInterface > xIntLeaves = anyLeaves.get< uno::Reference< XInterface > >();
+ uno::Reference< container::XNameAccess > xLeaves( xIntLeaves, uno::UNO_QUERY_THROW );
+
+ //iterate over all available leaves
+ const uno::Sequence< OUString > seqLeafNames = xLeaves->getElementNames();
+ for ( OUString const & leafName : seqLeafNames )
+ {
+ uno::Any anyLeaf = xLeaves->getByName( leafName );
+ uno::Reference< XInterface > xIntLeaf = anyLeaf.get< uno::Reference< XInterface > >();
+ uno::Reference< beans::XPropertySet > xLeaf( xIntLeaf, uno::UNO_QUERY_THROW );
+ //investigate the Id property if it matches the extension identifier which
+ //has been passed in.
+ uno::Any anyValue = xLeaf->getPropertyValue("Id");
+
+ OUString sId = anyValue.get< OUString >();
+ if ( sId == aId.Value )
+ {
+ bOptions = true;
+ break;
+ }
+ }
+ if ( bOptions )
+ break;
+ }
+ return bOptions;
+}
+
+
+// XEventListener
+void TheExtensionManager::disposing( lang::EventObject const & rEvt )
+{
+ bool shutDown = (rEvt.Source == m_xDesktop);
+
+ if ( shutDown && m_xDesktop.is() )
+ {
+ m_xDesktop->removeTerminateListener( this );
+ m_xDesktop.clear();
+ }
+
+ if ( !shutDown )
+ return;
+
+ if ( dp_misc::office_is_running() )
+ {
+ const SolarMutexGuard guard;
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ assert(!m_xExtMgrDialog);
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ assert(!m_xUpdReqDialog);
+ }
+ s_ExtMgr.clear();
+}
+
+// XTerminateListener
+void TheExtensionManager::queryTermination( ::lang::EventObject const & )
+{
+ DialogHelper *pDialogHelper = getDialogHelper();
+
+ if ( m_xExecuteCmdQueue->isBusy() || ( pDialogHelper && pDialogHelper->isBusy() ) )
+ {
+ ToTop();
+ throw frame::TerminationVetoException(
+ "The office cannot be closed while the Extension Manager is running",
+ static_cast<frame::XTerminateListener*>(this));
+ }
+ else
+ {
+ clearModified();
+ if (m_xExtMgrDialog)
+ {
+ if (m_bExtMgrDialogExecuting)
+ m_xExtMgrDialog->response(RET_CANCEL);
+ else
+ {
+ m_xExtMgrDialog->Close();
+ m_xExtMgrDialog.reset();
+ }
+ }
+ if (m_xUpdReqDialog)
+ m_xUpdReqDialog->response(RET_CANCEL);
+ }
+}
+
+void TheExtensionManager::notifyTermination( ::lang::EventObject const & rEvt )
+{
+ disposing( rEvt );
+}
+
+// XModifyListener
+void TheExtensionManager::modified( ::lang::EventObject const & /*rEvt*/ )
+{
+ m_bModified = true;
+ DialogHelper *pDialogHelper = getDialogHelper();
+ if (!pDialogHelper)
+ return;
+ pDialogHelper->prepareChecking();
+ createPackageList();
+ pDialogHelper->checkEntries();
+}
+
+
+::rtl::Reference< TheExtensionManager > TheExtensionManager::get( const uno::Reference< uno::XComponentContext > &xContext,
+ const uno::Reference< awt::XWindow > &xParent,
+ const OUString & extensionURL )
+{
+ if ( s_ExtMgr.is() )
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ if ( !extensionURL.isEmpty() )
+ s_ExtMgr->installPackage( extensionURL, true );
+ return s_ExtMgr;
+ }
+
+ ::rtl::Reference<TheExtensionManager> that( new TheExtensionManager( xParent, xContext ) );
+
+ const SolarMutexGuard guard;
+ if ( ! s_ExtMgr.is() )
+ {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ s_ExtMgr = that;
+ }
+
+ if ( !extensionURL.isEmpty() )
+ s_ExtMgr->installPackage( extensionURL, true );
+
+ return s_ExtMgr;
+}
+
+} //namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_theextmgr.hxx b/desktop/source/deployment/gui/dp_gui_theextmgr.hxx
new file mode 100644
index 000000000..13c329d6d
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_theextmgr.hxx
@@ -0,0 +1,127 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/frame/XDesktop2.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include "dp_gui.h"
+#include "dp_gui_dialog2.hxx"
+
+
+namespace dp_gui {
+
+
+class ExtensionCmdQueue;
+
+
+class TheExtensionManager :
+ public ::cppu::WeakImplHelper< css::frame::XTerminateListener,
+ css::util::XModifyListener >
+{
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::frame::XDesktop2 > m_xDesktop;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+ css::uno::Reference< css::container::XNameAccess > m_xNameAccessNodes;
+ css::uno::Reference< css::awt::XWindow > m_xParent;
+ std::shared_ptr<ExtMgrDialog> m_xExtMgrDialog;
+ std::unique_ptr<UpdateRequiredDialog> m_xUpdReqDialog;
+ std::unique_ptr<ExtensionCmdQueue> m_xExecuteCmdQueue;
+
+ OUString m_sGetExtensionsURL;
+ bool m_bModified;
+ bool m_bExtMgrDialogExecuting;
+
+public:
+ static ::rtl::Reference<TheExtensionManager> s_ExtMgr;
+
+ TheExtensionManager( css::uno::Reference< css::awt::XWindow > xParent,
+ const css::uno::Reference< css::uno::XComponentContext > &xContext );
+ virtual ~TheExtensionManager() override;
+
+ void createDialog( const bool bCreateUpdDlg );
+ sal_Int16 execute();
+
+ bool isModified() const { return m_bModified; }
+ void clearModified() { m_bModified = false; }
+
+ weld::Window* getDialog()
+ {
+ if (m_xExtMgrDialog)
+ return m_xExtMgrDialog->getDialog();
+ if (m_xUpdReqDialog)
+ return m_xUpdReqDialog->getDialog();
+ return nullptr;
+ }
+ DialogHelper* getDialogHelper()
+ {
+ if (m_xExtMgrDialog)
+ return m_xExtMgrDialog.get();
+ return m_xUpdReqDialog.get();
+ }
+ ExtensionCmdQueue* getCmdQueue() const { return m_xExecuteCmdQueue.get(); }
+
+ void SetText( const OUString &rTitle );
+ void Show();
+ void ToTop();
+ void Close();
+ bool isVisible();
+
+
+ void checkUpdates();
+ bool installPackage( const OUString &rPackageURL, bool bWarnUser = false );
+ void createPackageList();
+
+ void terminateDialog();
+
+ // Tools
+ bool supportsOptions( const css::uno::Reference< css::deployment::XPackage > &xPackage ) const;
+ static PackageState getPackageState( const css::uno::Reference< css::deployment::XPackage > &xPackage );
+ const css::uno::Reference< css::uno::XComponentContext >& getContext() const { return m_xContext; }
+ const css::uno::Reference< css::deployment::XExtensionManager >& getExtensionManager() const { return m_xExtensionManager; }
+ bool isReadOnly( const css::uno::Reference< css::deployment::XPackage > &xPackage ) const;
+
+
+ static ::rtl::Reference<TheExtensionManager> get(
+ css::uno::Reference< css::uno::XComponentContext> const & xContext,
+ css::uno::Reference< css::awt::XWindow> const & xParent = nullptr,
+ OUString const & view = OUString() );
+
+ // XEventListener
+ virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( css::lang::EventObject const & evt ) override;
+ virtual void SAL_CALL notifyTermination( css::lang::EventObject const & evt ) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( css::lang::EventObject const & evt ) override;
+};
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedata.hxx b/desktop/source/deployment/gui/dp_gui_updatedata.hxx
new file mode 100644
index 000000000..efac4c587
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedata.hxx
@@ -0,0 +1,74 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <utility>
+
+namespace com::sun::star::deployment {
+ class XPackage;
+}
+namespace com::sun::star::xml::dom {
+ class XNode;
+}
+
+
+namespace dp_gui {
+
+struct UpdateData
+{
+ explicit UpdateData( css::uno::Reference< css::deployment::XPackage > xExt):
+ bIsShared(false), aInstalledPackage(std::move(xExt)) {};
+
+ //When entries added to the listbox then there can be one for the user update and one
+ //for the shared update. However, both list entries will contain the same UpdateData.
+ //isShared is used to indicate which one is used for the shared entry.
+ bool bIsShared;
+
+ //The currently installed extension which is going to be updated. If the extension exist in
+ //multiple repositories then it is the one with the highest version.
+ css::uno::Reference< css::deployment::XPackage > aInstalledPackage;
+
+ //The version of the update
+ OUString updateVersion;
+
+ //For online update
+
+ // The content of the update information.
+ //Only if aUpdateInfo is set then there is an online update available with a better version
+ //than any of the currently installed extensions with the same identifier.
+ css::uno::Reference< css::xml::dom::XNode > aUpdateInfo;
+ //The URL of the locally downloaded extension. It will only be set if there were no errors
+ //during the download
+ OUString sLocalURL;
+ //The URL of the website where the download can be obtained.
+ OUString sWebsiteURL;
+
+ //For local update
+
+ //The locale extension which is used as update for the user or shared repository.
+ //If set then the data for the online update (aUpdateInfo, sLocalURL, sWebsiteURL)
+ //are to be ignored.
+ css::uno::Reference< css::deployment::XPackage > aUpdateSource;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedialog.cxx b/desktop/source/deployment/gui/dp_gui_updatedialog.cxx
new file mode 100644
index 000000000..2861d7785
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedialog.cxx
@@ -0,0 +1,994 @@
+/* -*- 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 <utility>
+#include <vector>
+
+
+#include <optional>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.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/Sequence.hxx>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <salhelper/thread.hxx>
+#include <tools/gen.hxx>
+#include <tools/link.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/svapp.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_misc.h>
+#include <dp_update.hxx>
+
+#include <strings.hrc>
+#include "dp_gui_updatedata.hxx"
+#include "dp_gui_updatedialog.hxx"
+#include <dp_shared.hxx>
+
+class KeyEvent;
+class MouseEvent;
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+using namespace ::com::sun::star;
+using dp_gui::UpdateDialog;
+
+namespace {
+
+sal_Unicode const LF = 0x000A;
+sal_Unicode const CR = 0x000D;
+
+constexpr OUStringLiteral IGNORED_UPDATES = u"/org.openoffice.Office.ExtensionManager/ExtensionUpdateData/IgnoredUpdates";
+constexpr OUStringLiteral PROPERTY_VERSION = u"Version";
+
+enum Kind { ENABLED_UPDATE, DISABLED_UPDATE, SPECIFIC_ERROR };
+
+OUString confineToParagraph(OUString const & text) {
+ // Confine arbitrary text to a single paragraph in a VclMultiLineEdit
+ // This assumes that U+000A and U+000D are the only paragraph separators in
+ // a VclMultiLineEdit, and that replacing them with a single space
+ // each is acceptable:
+ return text.replace(LF, ' ').replace(CR, ' ');
+}
+}
+
+struct UpdateDialog::DisabledUpdate {
+ OUString name;
+ uno::Sequence< OUString > unsatisfiedDependencies;
+ // We also want to show release notes and publisher for disabled updates
+ css::uno::Reference< css::xml::dom::XNode > aUpdateInfo;
+};
+
+struct UpdateDialog::SpecificError {
+ OUString name;
+ OUString message;
+};
+
+
+struct UpdateDialog::IgnoredUpdate {
+ OUString sExtensionID;
+ OUString sVersion;
+
+ IgnoredUpdate( OUString aExtensionID, OUString aVersion );
+};
+
+
+UpdateDialog::IgnoredUpdate::IgnoredUpdate( OUString aExtensionID, OUString aVersion ):
+ sExtensionID(std::move( aExtensionID )),
+ sVersion(std::move( aVersion ))
+{}
+
+
+struct UpdateDialog::Index
+{
+ Kind m_eKind;
+ bool m_bIgnored;
+ sal_uInt16 m_nIndex;
+ OUString m_aName;
+
+ Index( Kind theKind, sal_uInt16 nIndex, OUString aName ) :
+ m_eKind( theKind ),
+ m_bIgnored( false ),
+ m_nIndex( nIndex ),
+ m_aName(std::move( aName )) {}
+};
+
+
+class UpdateDialog::Thread: public salhelper::Thread {
+public:
+ Thread(
+ uno::Reference< uno::XComponentContext > const & context,
+ UpdateDialog & dialog,
+ std::vector< uno::Reference< deployment::XPackage > > && vExtensionList);
+
+ void stop();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+
+ void handleSpecificError(
+ uno::Reference< deployment::XPackage > const & package,
+ uno::Any const & exception) const;
+
+ OUString getUpdateDisplayString(
+ dp_gui::UpdateData const & data, std::u16string_view version = std::u16string_view()) const;
+
+ void prepareUpdateData(
+ css::uno::Reference< css::xml::dom::XNode > const & updateInfo,
+ UpdateDialog::DisabledUpdate & out_du,
+ dp_gui::UpdateData & out_data) const;
+
+ bool update(
+ UpdateDialog::DisabledUpdate const & du,
+ dp_gui::UpdateData const & data) const;
+
+ uno::Reference< uno::XComponentContext > m_context;
+ UpdateDialog & m_dialog;
+ std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList;
+ uno::Reference< deployment::XUpdateInformationProvider > m_updateInformation;
+ uno::Reference< task::XInteractionHandler > m_xInteractionHdl;
+
+ // guarded by Application::GetSolarMutex():
+ bool m_stop;
+};
+
+UpdateDialog::Thread::Thread(
+ uno::Reference< uno::XComponentContext > const & context,
+ UpdateDialog & dialog,
+ std::vector< uno::Reference< deployment::XPackage > >&& vExtensionList):
+ salhelper::Thread("dp_gui_updatedialog"),
+ m_context(context),
+ m_dialog(dialog),
+ m_vExtensionList(std::move(vExtensionList)),
+ m_updateInformation(
+ deployment::UpdateInformationProvider::create(context)),
+ m_stop(false)
+{
+ if( m_context.is() )
+ {
+ m_xInteractionHdl =
+ task::InteractionHandler::createWithParent(m_context, dialog.getDialog()->GetXWindow());
+ m_updateInformation->setInteractionHandler( m_xInteractionHdl );
+ }
+}
+
+void UpdateDialog::Thread::stop() {
+ {
+ SolarMutexGuard g;
+ m_stop = true;
+ }
+ m_updateInformation->cancel();
+}
+
+UpdateDialog::Thread::~Thread()
+{
+ if ( m_xInteractionHdl.is() )
+ m_updateInformation->setInteractionHandler( uno::Reference< task::XInteractionHandler > () );
+}
+
+void UpdateDialog::Thread::execute()
+{
+ {
+ SolarMutexGuard g;
+ if ( m_stop ) {
+ return;
+ }
+ }
+ uno::Reference<deployment::XExtensionManager> extMgr =
+ deployment::ExtensionManager::get(m_context);
+
+ std::vector<std::pair<uno::Reference<deployment::XPackage>, uno::Any > > errors;
+
+ dp_misc::UpdateInfoMap updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ m_context, extMgr, m_updateInformation, &m_vExtensionList, errors);
+
+ for (auto const& elem : errors)
+ handleSpecificError(elem.first, elem.second);
+
+ for (auto const& updateInfo : updateInfoMap)
+ {
+ dp_misc::UpdateInfo const & info = updateInfo.second;
+ UpdateData updateData(info.extension);
+ DisabledUpdate disableUpdate;
+ //determine if online updates meet the requirements
+ prepareUpdateData(info.info, disableUpdate, updateData);
+
+ //determine if the update is installed in the user or shared repository
+ OUString sOnlineVersion;
+ if (info.info.is())
+ sOnlineVersion = info.version;
+ OUString sVersionUser;
+ OUString sVersionShared;
+ OUString sVersionBundled;
+ uno::Sequence< uno::Reference< deployment::XPackage> > extensions;
+ try {
+ extensions = extMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(info.extension), info.extension->getName(),
+ uno::Reference<ucb::XCommandEnvironment>());
+ } catch ( const lang::IllegalArgumentException& ) {
+ OSL_ASSERT(false);
+ continue;
+ } catch ( const css::ucb::CommandFailedException& ) {
+ OSL_ASSERT(false);
+ continue;
+ }
+ OSL_ASSERT(extensions.getLength() == 3);
+ if (extensions[0].is() )
+ sVersionUser = extensions[0]->getVersion();
+ if (extensions[1].is() )
+ sVersionShared = extensions[1]->getVersion();
+ if (extensions[2].is() )
+ sVersionBundled = extensions[2]->getVersion();
+
+ bool bSharedReadOnly = extMgr->isReadOnlyRepository("shared");
+
+ dp_misc::UPDATE_SOURCE sourceUser = dp_misc::isUpdateUserExtension(
+ bSharedReadOnly, sVersionUser, sVersionShared, sVersionBundled, sOnlineVersion);
+ dp_misc::UPDATE_SOURCE sourceShared = dp_misc::isUpdateSharedExtension(
+ bSharedReadOnly, sVersionShared, sVersionBundled, sOnlineVersion);
+
+ if (sourceUser != dp_misc::UPDATE_SOURCE_NONE)
+ {
+ if (sourceUser == dp_misc::UPDATE_SOURCE_SHARED)
+ {
+ updateData.aUpdateSource = extensions[1];
+ updateData.updateVersion = extensions[1]->getVersion();
+ }
+ else if (sourceUser == dp_misc::UPDATE_SOURCE_BUNDLED)
+ {
+ updateData.aUpdateSource = extensions[2];
+ updateData.updateVersion = extensions[2]->getVersion();
+ }
+ if (!update(disableUpdate, updateData))
+ return;
+ }
+
+ if (sourceShared != dp_misc::UPDATE_SOURCE_NONE)
+ {
+ if (sourceShared == dp_misc::UPDATE_SOURCE_BUNDLED)
+ {
+ updateData.aUpdateSource = extensions[2];
+ updateData.updateVersion = extensions[2]->getVersion();
+ }
+ updateData.bIsShared = true;
+ if (!update(disableUpdate, updateData))
+ return;
+ }
+ }
+
+
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.checkingDone();
+ }
+}
+
+//Parameter package can be null
+void UpdateDialog::Thread::handleSpecificError(
+ uno::Reference< deployment::XPackage > const & package,
+ uno::Any const & exception) const
+{
+ UpdateDialog::SpecificError data;
+ if (package.is())
+ data.name = package->getDisplayName();
+ uno::Exception e;
+ if (exception >>= e) {
+ data.message = e.Message;
+ }
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addSpecificError(data);
+ }
+}
+
+OUString UpdateDialog::Thread::getUpdateDisplayString(
+ dp_gui::UpdateData const & data, std::u16string_view version) const
+{
+ OSL_ASSERT(data.aInstalledPackage.is());
+ OUStringBuffer b(data.aInstalledPackage->getDisplayName());
+ b.append(' ');
+ {
+ SolarMutexGuard g;
+ if(!m_stop)
+ b.append(m_dialog.m_version);
+ }
+ b.append(' ');
+ if (!version.empty())
+ b.append(version);
+ else
+ b.append(data.updateVersion);
+
+ if (!data.sWebsiteURL.isEmpty())
+ {
+ b.append(' ');
+ {
+ SolarMutexGuard g;
+ if(!m_stop)
+ b.append(m_dialog.m_browserbased);
+ }
+ }
+ return b.makeStringAndClear();
+}
+
+/** out_data will only be filled if all dependencies are ok.
+ */
+void UpdateDialog::Thread::prepareUpdateData(
+ uno::Reference< xml::dom::XNode > const & updateInfo,
+ UpdateDialog::DisabledUpdate & out_du,
+ dp_gui::UpdateData & out_data) const
+{
+ if (!updateInfo.is())
+ return;
+ dp_misc::DescriptionInfoset infoset(m_context, updateInfo);
+ OSL_ASSERT(!infoset.getVersion().isEmpty());
+ uno::Sequence< uno::Reference< xml::dom::XElement > > ds(
+ dp_misc::Dependencies::check(infoset));
+
+ out_du.aUpdateInfo = updateInfo;
+ out_du.unsatisfiedDependencies.realloc(ds.getLength());
+ auto p_unsatisfiedDependencies = out_du.unsatisfiedDependencies.getArray();
+ for (sal_Int32 i = 0; i < ds.getLength(); ++i) {
+ p_unsatisfiedDependencies[i] = dp_misc::Dependencies::getErrorText(ds[i]);
+ }
+
+ const ::std::optional< OUString> updateWebsiteURL(infoset.getLocalizedUpdateWebsiteURL());
+
+ out_du.name = getUpdateDisplayString(out_data, infoset.getVersion());
+
+ if (!out_du.unsatisfiedDependencies.hasElements())
+ {
+ out_data.aUpdateInfo = updateInfo;
+ out_data.updateVersion = infoset.getVersion();
+ if (updateWebsiteURL)
+ out_data.sWebsiteURL = *updateWebsiteURL;
+ }
+}
+
+bool UpdateDialog::Thread::update(
+ UpdateDialog::DisabledUpdate const & du,
+ dp_gui::UpdateData const & data) const
+{
+ bool ret = false;
+ if (!du.unsatisfiedDependencies.hasElements())
+ {
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addEnabledUpdate(getUpdateDisplayString(data), data);
+ }
+ ret = !m_stop;
+ } else {
+ SolarMutexGuard g;
+ if (!m_stop) {
+ m_dialog.addDisabledUpdate(du);
+ }
+ ret = !m_stop;
+ }
+ return ret;
+}
+
+// UpdateDialog ----------------------------------------------------------
+UpdateDialog::UpdateDialog(
+ uno::Reference< uno::XComponentContext > const & context,
+ weld::Window * parent, std::vector<uno::Reference< deployment::XPackage > > && vExtensionList,
+ std::vector< dp_gui::UpdateData > * updateData)
+ : GenericDialogController(parent, "desktop/ui/updatedialog.ui", "UpdateDialog")
+ , m_context(context)
+ , m_none(DpResId(RID_DLG_UPDATE_NONE))
+ , m_noInstallable(DpResId(RID_DLG_UPDATE_NOINSTALLABLE))
+ , m_failure(DpResId(RID_DLG_UPDATE_FAILURE))
+ , m_unknownError(DpResId(RID_DLG_UPDATE_UNKNOWNERROR))
+ , m_noDescription(DpResId(RID_DLG_UPDATE_NODESCRIPTION))
+ , m_noInstall(DpResId(RID_DLG_UPDATE_NOINSTALL))
+ , m_noDependency(DpResId(RID_DLG_UPDATE_NODEPENDENCY))
+ , m_noDependencyCurVer(DpResId(RID_DLG_UPDATE_NODEPENDENCY_CUR_VER))
+ , m_browserbased(DpResId(RID_DLG_UPDATE_BROWSERBASED))
+ , m_version(DpResId(RID_DLG_UPDATE_VERSION))
+ , m_ignoredUpdate(DpResId(RID_DLG_UPDATE_IGNORED_UPDATE))
+ , m_updateData(*updateData)
+ , m_thread(new UpdateDialog::Thread(context, *this, std::move(vExtensionList)))
+ , m_xChecking(m_xBuilder->weld_label("UPDATE_CHECKING"))
+ , m_xThrobber(m_xBuilder->weld_spinner("THROBBER"))
+ , m_xUpdate(m_xBuilder->weld_label("UPDATE_LABEL"))
+ , m_xUpdates(m_xBuilder->weld_tree_view("checklist"))
+ , m_xAll(m_xBuilder->weld_check_button("UPDATE_ALL"))
+ , m_xDescription(m_xBuilder->weld_label("DESCRIPTION_LABEL"))
+ , m_xPublisherLabel(m_xBuilder->weld_label("PUBLISHER_LABEL"))
+ , m_xPublisherLink(m_xBuilder->weld_link_button("PUBLISHER_LINK"))
+ , m_xReleaseNotesLabel(m_xBuilder->weld_label("RELEASE_NOTES_LABEL"))
+ , m_xReleaseNotesLink(m_xBuilder->weld_link_button("RELEASE_NOTES_LINK"))
+ , m_xDescriptions(m_xBuilder->weld_text_view("DESCRIPTIONS"))
+ , m_xOk(m_xBuilder->weld_button("ok"))
+ , m_xClose(m_xBuilder->weld_button("close"))
+ , m_xHelp(m_xBuilder->weld_button("help"))
+{
+ auto nWidth = m_xDescriptions->get_approximate_digit_width() * 62;
+ auto nHeight = m_xDescriptions->get_height_rows(8);
+ m_xDescriptions->set_size_request(nWidth, nHeight);
+ m_xUpdates->set_size_request(nWidth, nHeight);
+
+ m_xUpdates->enable_toggle_buttons(weld::ColumnToggleType::Check);
+
+ OSL_ASSERT(updateData != nullptr);
+
+ m_xExtensionManager = deployment::ExtensionManager::get( context );
+
+ m_xUpdates->connect_changed(LINK(this, UpdateDialog, selectionHandler));
+ m_xUpdates->connect_toggled(LINK(this, UpdateDialog, entryToggled));
+ m_xAll->connect_toggled(LINK(this, UpdateDialog, allHandler));
+ m_xOk->connect_clicked(LINK(this, UpdateDialog, okHandler));
+ m_xClose->connect_clicked(LINK(this, UpdateDialog, closeHandler));
+ if (!dp_misc::office_is_running())
+ m_xHelp->set_sensitive(false);
+
+ initDescription();
+ getIgnoredUpdates();
+}
+
+UpdateDialog::~UpdateDialog()
+{
+}
+
+short UpdateDialog::run() {
+ m_xThrobber->start();
+ m_thread->launch();
+ short nRet = GenericDialogController::run();
+ m_thread->stop();
+ return nRet;
+}
+
+IMPL_LINK(UpdateDialog, entryToggled, const weld::TreeView::iter_col&, rRowCol, void)
+{
+ // error's can't be enabled
+ const UpdateDialog::Index* p = weld::fromId<UpdateDialog::Index const *>(m_xUpdates->get_id(rRowCol.first));
+ if (p->m_eKind == SPECIFIC_ERROR)
+ m_xUpdates->set_toggle(rRowCol.first, TRISTATE_FALSE);
+
+ enableOk();
+}
+
+void UpdateDialog::insertItem(const UpdateDialog::Index *pEntry, bool bEnabledCheckBox)
+{
+ int nEntry = m_xUpdates->n_children();
+ m_xUpdates->append();
+ m_xUpdates->set_toggle(nEntry, bEnabledCheckBox ? TRISTATE_TRUE : TRISTATE_FALSE);
+ m_xUpdates->set_text(nEntry, pEntry->m_aName, 0);
+ m_xUpdates->set_id(nEntry, weld::toId(pEntry));
+}
+
+void UpdateDialog::addAdditional(const UpdateDialog::Index * index, bool bEnabledCheckBox)
+{
+ m_xAll->set_sensitive(true);
+ if (m_xAll->get_active())
+ {
+ insertItem(index, bEnabledCheckBox);
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+ }
+}
+
+void UpdateDialog::addEnabledUpdate( OUString const & name,
+ dp_gui::UpdateData const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_enabledUpdates.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( ENABLED_UPDATE, nIndex, name );
+
+ m_enabledUpdates.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ if (!isIgnoredUpdate(pEntry))
+ {
+ insertItem(pEntry, true);
+ }
+ else
+ addAdditional(pEntry, false);
+
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+}
+
+void UpdateDialog::addDisabledUpdate( UpdateDialog::DisabledUpdate const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_disabledUpdates.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( DISABLED_UPDATE, nIndex, data.name );
+
+ m_disabledUpdates.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ isIgnoredUpdate( pEntry );
+ addAdditional(pEntry, false);
+}
+
+void UpdateDialog::addSpecificError( UpdateDialog::SpecificError const & data )
+{
+ sal_uInt16 nIndex = sal::static_int_cast< sal_uInt16 >( m_specificErrors.size() );
+ UpdateDialog::Index *pEntry = new UpdateDialog::Index( SPECIFIC_ERROR, nIndex, data.name );
+
+ m_specificErrors.push_back( data );
+ m_ListboxEntries.emplace_back( pEntry );
+
+ addAdditional(pEntry, false);
+}
+
+void UpdateDialog::checkingDone() {
+ m_xChecking->hide();
+ m_xThrobber->stop();
+ m_xThrobber->hide();
+ if (m_xUpdates->n_children() == 0)
+ {
+ clearDescription();
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+
+ if ( m_disabledUpdates.empty() && m_specificErrors.empty() && m_ignoredUpdates.empty() )
+ showDescription( m_none );
+ else
+ showDescription( m_noInstallable );
+ }
+
+ enableOk();
+}
+
+void UpdateDialog::enableOk() {
+ if (!m_xChecking->get_visible()) {
+ int nChecked = 0;
+ for (int i = 0, nCount = m_xUpdates->n_children(); i < nCount; ++i) {
+ if (m_xUpdates->get_toggle(i) == TRISTATE_TRUE)
+ ++nChecked;
+ }
+ m_xOk->set_sensitive(nChecked != 0);
+ }
+}
+
+// *********************************************************************************
+void UpdateDialog::createNotifyJob( bool bPrepareOnly,
+ uno::Sequence< uno::Sequence< OUString > > const &rItemList )
+{
+ if ( !dp_misc::office_is_running() )
+ return;
+
+ // notify update check job
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
+ configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext()));
+
+ uno::Sequence< uno::Any > aArgumentList{ uno::Any(comphelper::makePropertyValue(
+ "nodepath",
+ OUString("org.openoffice.Office.Addons/AddonUI/OfficeHelp/UpdateCheckJob"))) };
+
+ uno::Reference< container::XNameAccess > xNameAccess(
+ xConfigProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArgumentList ),
+ uno::UNO_QUERY_THROW );
+
+ util::URL aURL;
+ xNameAccess->getByName("URL") >>= aURL.Complete;
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference < util::XURLTransformer > xTransformer = util::URLTransformer::create(xContext);
+
+ xTransformer->parseStrict(aURL);
+
+ uno::Reference < frame::XDesktop2 > xDesktop = frame::Desktop::create( xContext );
+ uno::Reference< frame::XDispatchProvider > xDispatchProvider( xDesktop->getCurrentFrame(),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< frame::XDispatch > xDispatch = xDispatchProvider->queryDispatch(aURL, OUString(), 0);
+
+ if( xDispatch.is() )
+ {
+ uno::Sequence aPropList{ comphelper::makePropertyValue("updateList", rItemList),
+ comphelper::makePropertyValue("prepareOnly", bPrepareOnly) };
+
+ xDispatch->dispatch(aURL, aPropList );
+ }
+ }
+ catch( const uno::Exception& e )
+ {
+ dp_misc::TRACE( "Caught exception: "
+ + e.Message + "\n thread terminated.\n\n");
+ }
+}
+
+// *********************************************************************************
+void UpdateDialog::notifyMenubar( bool bPrepareOnly, bool bRecheckOnly )
+{
+ if ( !dp_misc::office_is_running() )
+ return;
+
+ uno::Sequence< uno::Sequence< OUString > > aItemList;
+
+ if ( ! bRecheckOnly )
+ {
+ sal_Int32 nCount = 0;
+ for (sal_uInt16 i = 0, nItemCount = m_xUpdates->n_children(); i < nItemCount; ++i)
+ {
+
+ UpdateDialog::Index const * p = weld::fromId<UpdateDialog::Index const*>(m_xUpdates->get_id(i));
+
+ if ( p->m_eKind == ENABLED_UPDATE )
+ {
+ dp_gui::UpdateData aUpdData = m_enabledUpdates[ p->m_nIndex ];
+
+ dp_misc::DescriptionInfoset aInfoset( m_context, aUpdData.aUpdateInfo );
+ uno::Sequence< OUString > aItem
+ {
+ dp_misc::getIdentifier( aUpdData.aInstalledPackage ),
+ aInfoset.getVersion()
+ };
+ aItemList.realloc( nCount + 1 );
+ aItemList.getArray()[ nCount ] = aItem;
+ nCount += 1;
+ }
+ else
+ continue;
+ }
+ }
+
+ createNotifyJob( bPrepareOnly, aItemList );
+}
+
+// *********************************************************************************
+
+void UpdateDialog::initDescription()
+{
+ m_xPublisherLabel->hide();
+ m_xPublisherLink->hide();
+ m_xReleaseNotesLabel->hide();
+ m_xReleaseNotesLink->hide();
+}
+
+void UpdateDialog::clearDescription()
+{
+ m_xPublisherLabel->hide();
+ m_xPublisherLink->hide();
+ m_xPublisherLink->set_label("");
+ m_xPublisherLink->set_uri("");
+ m_xReleaseNotesLabel->hide();
+ m_xReleaseNotesLink->hide();
+ m_xReleaseNotesLink->set_uri( "" );
+ m_xDescriptions->set_text("");
+}
+
+bool UpdateDialog::showDescription(uno::Reference< xml::dom::XNode > const & aUpdateInfo)
+{
+ dp_misc::DescriptionInfoset infoset(m_context, aUpdateInfo);
+ return showDescription(infoset.getLocalizedPublisherNameAndURL(),
+ infoset.getLocalizedReleaseNotesURL());
+}
+
+bool UpdateDialog::showDescription(uno::Reference< deployment::XPackage > const & aExtension)
+{
+ OSL_ASSERT(aExtension.is());
+ beans::StringPair pubInfo = aExtension->getPublisherInfo();
+ return showDescription(std::make_pair(pubInfo.First, pubInfo.Second),
+ "");
+}
+
+bool UpdateDialog::showDescription(std::pair< OUString, OUString > const & pairPublisher,
+ OUString const & sReleaseNotes)
+{
+ OUString sPub = pairPublisher.first;
+ OUString sURL = pairPublisher.second;
+
+ if ( sPub.isEmpty() && sURL.isEmpty() && sReleaseNotes.isEmpty() )
+ // nothing to show
+ return false;
+
+ if ( !sPub.isEmpty() )
+ {
+ m_xPublisherLabel->show();
+ m_xPublisherLink->show();
+ m_xPublisherLink->set_label(sPub);
+ m_xPublisherLink->set_uri(sURL);
+ }
+
+ if ( !sReleaseNotes.isEmpty() )
+ {
+ m_xReleaseNotesLabel->show();
+ m_xReleaseNotesLink->show();
+ m_xReleaseNotesLink->set_uri( sReleaseNotes );
+ }
+ return true;
+}
+
+bool UpdateDialog::showDescription( const OUString& rDescription)
+{
+ if ( rDescription.isEmpty() )
+ // nothing to show
+ return false;
+
+ m_xDescriptions->set_text(rDescription);
+ return true;
+}
+
+void UpdateDialog::getIgnoredUpdates()
+{
+ uno::Reference< lang::XMultiServiceFactory > xConfig(
+ configuration::theDefaultProvider::get(m_context));
+ beans::NamedValue aValue( "nodepath", uno::Any( OUString(IGNORED_UPDATES) ) );
+ uno::Sequence< uno::Any > args{ uno::Any(aValue) };
+
+ uno::Reference< container::XNameAccess > xNameAccess( xConfig->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", args), uno::UNO_QUERY_THROW );
+ const uno::Sequence< OUString > aElementNames = xNameAccess->getElementNames();
+
+ for ( OUString const & aIdentifier : aElementNames )
+ {
+ OUString aVersion;
+
+ uno::Any aPropValue( uno::Reference< beans::XPropertySet >( xNameAccess->getByName( aIdentifier ), uno::UNO_QUERY_THROW )->getPropertyValue( PROPERTY_VERSION ) );
+ aPropValue >>= aVersion;
+ IgnoredUpdate *pData = new IgnoredUpdate( aIdentifier, aVersion );
+ m_ignoredUpdates.emplace_back( pData );
+ }
+}
+
+
+bool UpdateDialog::isIgnoredUpdate( UpdateDialog::Index * index )
+{
+ bool bIsIgnored = false;
+
+ if (! m_ignoredUpdates.empty() )
+ {
+ OUString aExtensionID;
+ OUString aVersion;
+
+ if ( index->m_eKind == ENABLED_UPDATE )
+ {
+ dp_gui::UpdateData aUpdData = m_enabledUpdates[ index->m_nIndex ];
+ aExtensionID = dp_misc::getIdentifier( aUpdData.aInstalledPackage );
+ aVersion = aUpdData.updateVersion;
+ }
+ else if ( index->m_eKind == DISABLED_UPDATE )
+ {
+ DisabledUpdate &rData = m_disabledUpdates[ index->m_nIndex ];
+ dp_misc::DescriptionInfoset aInfoset( m_context, rData.aUpdateInfo );
+ ::std::optional< OUString > aID( aInfoset.getIdentifier() );
+ if ( aID )
+ aExtensionID = *aID;
+ aVersion = aInfoset.getVersion();
+ }
+
+ for (auto const& ignoredUpdate : m_ignoredUpdates)
+ {
+ if ( ignoredUpdate->sExtensionID == aExtensionID )
+ {
+ if ( ( !ignoredUpdate->sVersion.isEmpty() ) || ( ignoredUpdate->sVersion == aVersion ) )
+ {
+ bIsIgnored = true;
+ index->m_bIgnored = true;
+ }
+ break;
+ }
+ }
+ }
+
+ return bIsIgnored;
+}
+
+
+IMPL_LINK_NOARG(UpdateDialog, selectionHandler, weld::TreeView&, void)
+{
+ OUStringBuffer b;
+ int nSelectedPos = m_xUpdates->get_selected_index();
+ clearDescription();
+
+ const UpdateDialog::Index* p = nullptr;
+ if (nSelectedPos != -1)
+ p = weld::fromId<UpdateDialog::Index const*>(m_xUpdates->get_id(nSelectedPos));
+ if (p != nullptr)
+ {
+ sal_uInt16 pos = p->m_nIndex;
+
+ switch (p->m_eKind)
+ {
+ case ENABLED_UPDATE:
+ {
+ if ( m_enabledUpdates[ pos ].aUpdateSource.is() )
+ showDescription( m_enabledUpdates[ pos ].aUpdateSource );
+ else
+ showDescription( m_enabledUpdates[ pos ].aUpdateInfo );
+
+ if ( p->m_bIgnored )
+ b.append( m_ignoredUpdate );
+
+ break;
+ }
+ case DISABLED_UPDATE:
+ {
+ if ( !m_disabledUpdates.empty() )
+ showDescription( m_disabledUpdates[pos].aUpdateInfo );
+
+ if ( p->m_bIgnored )
+ b.append( m_ignoredUpdate );
+
+ if ( m_disabledUpdates.empty() )
+ break;
+
+ UpdateDialog::DisabledUpdate & data = m_disabledUpdates[ pos ];
+ if (data.unsatisfiedDependencies.hasElements())
+ {
+ // create error string for version mismatch
+ OUString sVersion( "%VERSION" );
+ OUString sProductName( "%PRODUCTNAME" );
+ sal_Int32 nPos = m_noDependencyCurVer.indexOf( sVersion );
+ if ( nPos >= 0 )
+ {
+ m_noDependencyCurVer = m_noDependencyCurVer.replaceAt( nPos, sVersion.getLength(), utl::ConfigManager::getAboutBoxProductVersion() );
+ }
+ nPos = m_noDependencyCurVer.indexOf( sProductName );
+ if ( nPos >= 0 )
+ {
+ m_noDependencyCurVer = m_noDependencyCurVer.replaceAt( nPos, sProductName.getLength(), utl::ConfigManager::getProductName() );
+ }
+ nPos = m_noDependency.indexOf( sProductName );
+ if ( nPos >= 0 )
+ {
+ m_noDependency = m_noDependency.replaceAt( nPos, sProductName.getLength(), utl::ConfigManager::getProductName() );
+ }
+
+ b.append(m_noInstall);
+ b.append(LF);
+ b.append(m_noDependency);
+ for (sal_Int32 i = 0;
+ i < data.unsatisfiedDependencies.getLength(); ++i)
+ {
+ b.append(LF);
+ b.append(" ");
+ // U+2003 EM SPACE would be better than two spaces,
+ // but some fonts do not contain it
+ b.append(
+ confineToParagraph(
+ data.unsatisfiedDependencies[i]));
+ }
+ b.append(LF);
+ b.append(" ");
+ b.append(m_noDependencyCurVer);
+ }
+ break;
+ }
+ case SPECIFIC_ERROR:
+ {
+ UpdateDialog::SpecificError & data = m_specificErrors[ pos ];
+ b.append(m_failure);
+ b.append(LF);
+ b.append( data.message.isEmpty() ? m_unknownError : data.message );
+ break;
+ }
+ default:
+ OSL_ASSERT(false);
+ break;
+ }
+ }
+
+ if ( b.isEmpty() )
+ b.append( m_noDescription );
+
+ showDescription( b.makeStringAndClear() );
+}
+
+IMPL_LINK_NOARG(UpdateDialog, allHandler, weld::Toggleable&, void)
+{
+ if (m_xAll->get_active())
+ {
+ m_xUpdate->set_sensitive(true);
+ m_xUpdates->set_sensitive(true);
+ m_xDescription->set_sensitive(true);
+ m_xDescriptions->set_sensitive(true);
+
+ for (auto const& listboxEntry : m_ListboxEntries)
+ {
+ if ( listboxEntry->m_bIgnored || ( listboxEntry->m_eKind != ENABLED_UPDATE ) )
+ insertItem(listboxEntry.get(), false);
+ }
+ }
+ else
+ {
+ for (sal_uInt16 i = m_xUpdates->n_children(); i != 0 ;)
+ {
+ i -= 1;
+ UpdateDialog::Index const * p = weld::fromId<UpdateDialog::Index const*>(m_xUpdates->get_id(i));
+ if ( p->m_bIgnored || ( p->m_eKind != ENABLED_UPDATE ) )
+ {
+ m_xUpdates->remove(i);
+ }
+ }
+
+ if (m_xUpdates->n_children() == 0)
+ {
+ clearDescription();
+ m_xUpdate->set_sensitive(false);
+ m_xUpdates->set_sensitive(false);
+ if (m_xChecking->get_visible())
+ m_xDescription->set_sensitive(false);
+ else
+ showDescription(m_noInstallable);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(UpdateDialog, okHandler, weld::Button&, void)
+{
+ //If users are going to update a shared extension then we need
+ //to warn them
+ for (auto const& enableUpdate : m_enabledUpdates)
+ {
+ OSL_ASSERT(enableUpdate.aInstalledPackage.is());
+ //If the user has no write access to the shared folder then the update
+ //for a shared extension is disable, that is it cannot be in m_enabledUpdates
+ }
+
+
+ for (sal_uInt16 i = 0, nCount = m_xUpdates->n_children(); i < nCount; ++i)
+ {
+ UpdateDialog::Index const * p =
+ weld::fromId<UpdateDialog::Index const*>(m_xUpdates->get_id(i));
+ if (p->m_eKind == ENABLED_UPDATE && m_xUpdates->get_toggle(i) == TRISTATE_TRUE) {
+ m_updateData.push_back( m_enabledUpdates[ p->m_nIndex ] );
+ }
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(UpdateDialog, closeHandler, weld::Button&, void)
+{
+ m_thread->stop();
+ m_xDialog->response(RET_CANCEL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updatedialog.hxx b/desktop/source/deployment/gui/dp_gui_updatedialog.hxx
new file mode 100644
index 000000000..cbf376955
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updatedialog.hxx
@@ -0,0 +1,168 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <vector>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/link.hxx>
+#include <vcl/weld.hxx>
+
+#include "dp_gui_updatedata.hxx"
+
+/// @HTML
+
+class Image;
+class KeyEvent;
+class MouseEvent;
+class ResId;
+
+namespace com::sun::star {
+ namespace deployment { class XExtensionManager;
+ class XPackage; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_gui {
+/**
+ The modal &ldquo;Check for Updates&rdquo; dialog.
+*/
+class UpdateDialog: public weld::GenericDialogController {
+public:
+ /**
+ Create an instance.
+
+ <p>Exactly one of <code>selectedPackages</code> and
+ <code>packageManagers</code> must be non-null.</p>
+
+ @param context
+ a non-null component context
+
+ @param parent
+ the parent window, may be null
+
+ @param vExtensionList
+ check for updates for the contained extensions. There must only be one extension with
+ a particular identifier. If one extension is installed in several repositories, then the
+ one with the highest version must be used, because it contains the latest known update
+ information.
+ */
+ UpdateDialog(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ weld::Window * parent,
+ std::vector< css::uno::Reference< css::deployment::XPackage > > && vExtensionList,
+ std::vector< dp_gui::UpdateData > * updateData);
+
+ virtual ~UpdateDialog() override;
+
+ virtual short run() override;
+
+ void notifyMenubar( bool bPrepareOnly, bool bRecheckOnly );
+ static void createNotifyJob( bool bPrepareOnly,
+ css::uno::Sequence< css::uno::Sequence< OUString > > const &rItemList );
+
+private:
+ UpdateDialog(UpdateDialog const &) = delete;
+ UpdateDialog& operator =(UpdateDialog const &) = delete;
+
+ struct DisabledUpdate;
+ struct SpecificError;
+ struct IgnoredUpdate;
+ struct Index;
+ friend struct Index;
+ class Thread;
+ friend class Thread;
+
+ friend class CheckListBox;
+
+ void insertItem(const UpdateDialog::Index *pIndex, bool bEnableCheckBox);
+ void addAdditional(const UpdateDialog::Index *pIndex, bool bEnableCheckBox);
+ bool isIgnoredUpdate( UpdateDialog::Index *pIndex );
+
+ void addEnabledUpdate( OUString const & name, dp_gui::UpdateData const & data );
+ void addDisabledUpdate( UpdateDialog::DisabledUpdate const & data );
+ void addSpecificError( UpdateDialog::SpecificError const & data );
+
+ void checkingDone();
+
+ void enableOk();
+
+ void getIgnoredUpdates();
+
+ void initDescription();
+ void clearDescription();
+ bool showDescription(css::uno::Reference<
+ css::deployment::XPackage > const & aExtension);
+ bool showDescription(std::pair< OUString, OUString > const & pairPublisher,
+ OUString const & sReleaseNotes);
+ bool showDescription( css::uno::Reference<
+ css::xml::dom::XNode > const & aUpdateInfo);
+ bool showDescription( const OUString& rDescription);
+
+ DECL_LINK(selectionHandler, weld::TreeView&, void);
+ DECL_LINK(allHandler, weld::Toggleable&, void);
+ DECL_LINK(okHandler, weld::Button&, void);
+ DECL_LINK(closeHandler, weld::Button&, void);
+ DECL_LINK(entryToggled, const weld::TreeView::iter_col&, void);
+
+ css::uno::Reference< css::uno::XComponentContext > m_context;
+ OUString m_none;
+ OUString m_noInstallable;
+ OUString m_failure;
+ OUString m_unknownError;
+ OUString m_noDescription;
+ OUString m_noInstall;
+ OUString m_noDependency;
+ OUString m_noDependencyCurVer;
+ OUString m_browserbased;
+ OUString m_version;
+ OUString m_ignoredUpdate;
+ std::vector< dp_gui::UpdateData > m_enabledUpdates;
+ std::vector< UpdateDialog::DisabledUpdate > m_disabledUpdates;
+ std::vector< UpdateDialog::SpecificError > m_specificErrors;
+ std::vector< std::unique_ptr<UpdateDialog::IgnoredUpdate> > m_ignoredUpdates;
+ std::vector< std::unique_ptr<Index> > m_ListboxEntries;
+ std::vector< dp_gui::UpdateData > & m_updateData;
+ rtl::Reference< UpdateDialog::Thread > m_thread;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+
+ std::unique_ptr<weld::Label> m_xChecking;
+ std::unique_ptr<weld::Spinner> m_xThrobber;
+ std::unique_ptr<weld::Label> m_xUpdate;
+ std::unique_ptr<weld::TreeView> m_xUpdates;
+ std::unique_ptr<weld::CheckButton> m_xAll;
+ std::unique_ptr<weld::Label> m_xDescription;
+ std::unique_ptr<weld::Label> m_xPublisherLabel;
+ std::unique_ptr<weld::LinkButton> m_xPublisherLink;
+ std::unique_ptr<weld::Label> m_xReleaseNotesLabel;
+ std::unique_ptr<weld::LinkButton> m_xReleaseNotesLink;
+ std::unique_ptr<weld::TextView> m_xDescriptions;
+ std::unique_ptr<weld::Button> m_xOk;
+ std::unique_ptr<weld::Button> m_xClose;
+ std::unique_ptr<weld::Button> m_xHelp;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx
new file mode 100644
index 000000000..9b1aa423d
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.cxx
@@ -0,0 +1,665 @@
+/* -*- 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 "dp_gui_updatedata.hxx"
+
+#include <sal/config.h>
+#include <osl/file.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+
+#include <dp_descriptioninfoset.hxx>
+#include <strings.hrc>
+#include "dp_gui_updateinstalldialog.hxx"
+#include <dp_shared.hxx>
+#include <dp_ucb.h>
+#include <dp_misc.h>
+#include "dp_gui_extensioncmdqueue.hxx"
+#include <ucbhelper/content.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/thread.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <comphelper/anytostring.hxx>
+
+#include <string_view>
+#include <vector>
+
+using dp_misc::StrTitle;
+
+namespace dp_gui {
+
+class UpdateInstallDialog::Thread: public salhelper::Thread {
+ friend class UpdateCommandEnv;
+public:
+ Thread(css::uno::Reference< css::uno::XComponentContext > const & ctx,
+ UpdateInstallDialog & dialog, std::vector< dp_gui::UpdateData > & aVecUpdateData);
+
+ void stop();
+
+private:
+ virtual ~Thread() override;
+
+ virtual void execute() override;
+ void downloadExtensions();
+ bool download(OUString const & aUrls, UpdateData & aUpdatData);
+ void installExtensions();
+ void removeTempDownloads();
+
+ UpdateInstallDialog & m_dialog;
+
+ // guarded by Application::GetSolarMutex():
+ css::uno::Reference< css::task::XAbortChannel > m_abort;
+ css::uno::Reference< css::uno::XComponentContext > m_xComponentContext;
+ std::vector< dp_gui::UpdateData > & m_aVecUpdateData;
+ ::rtl::Reference<UpdateCommandEnv> m_updateCmdEnv;
+
+ //A folder which is created in the temp directory in which then the updates are downloaded
+ OUString m_sDownloadFolder;
+
+ bool m_stop;
+
+};
+
+class UpdateCommandEnv
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler,
+ css::ucb::XProgressHandler >
+{
+ friend class UpdateInstallDialog::Thread;
+
+ ::rtl::Reference<UpdateInstallDialog::Thread> m_installThread;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+public:
+ UpdateCommandEnv( css::uno::Reference< css::uno::XComponentContext > xCtx,
+ ::rtl::Reference<UpdateInstallDialog::Thread> thread);
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+UpdateInstallDialog::Thread::Thread(
+ css::uno::Reference< css::uno::XComponentContext> const & xCtx,
+ UpdateInstallDialog & dialog,
+ std::vector< dp_gui::UpdateData > & aVecUpdateData):
+ salhelper::Thread("dp_gui_updateinstalldialog"),
+ m_dialog(dialog),
+ m_xComponentContext(xCtx),
+ m_aVecUpdateData(aVecUpdateData),
+ m_updateCmdEnv(new UpdateCommandEnv(xCtx, this)),
+ m_stop(false)
+{}
+
+void UpdateInstallDialog::Thread::stop() {
+ css::uno::Reference< css::task::XAbortChannel > abort;
+ {
+ SolarMutexGuard g;
+ abort = m_abort;
+ m_stop = true;
+ }
+ if (abort.is()) {
+ abort->sendAbort();
+ }
+}
+
+UpdateInstallDialog::Thread::~Thread() {}
+
+void UpdateInstallDialog::Thread::execute()
+{
+ try {
+ downloadExtensions();
+ installExtensions();
+ }
+ catch (...)
+ {
+ }
+
+ //clean up the temp directories
+ try {
+ removeTempDownloads();
+ } catch( ... ) {
+ }
+
+ {
+ //make sure m_dialog is still alive
+ SolarMutexGuard g;
+ if (! m_stop)
+ m_dialog.updateDone();
+ }
+ //UpdateCommandEnv keeps a reference to Thread and prevents destruction. Therefore remove it.
+ m_updateCmdEnv->m_installThread.clear();
+}
+
+UpdateInstallDialog::UpdateInstallDialog(
+ weld::Window* pParent,
+ std::vector<dp_gui::UpdateData> & aVecUpdateData,
+ css::uno::Reference< css::uno::XComponentContext > const & xCtx)
+ : GenericDialogController(pParent, "desktop/ui/updateinstalldialog.ui",
+ "UpdateInstallDialog")
+ , m_thread(new Thread(xCtx, *this, aVecUpdateData))
+ , m_bError(false)
+ , m_bNoEntry(true)
+ , m_sInstalling(DpResId(RID_DLG_UPDATE_INSTALL_INSTALLING))
+ , m_sFinished(DpResId(RID_DLG_UPDATE_INSTALL_FINISHED))
+ , m_sNoErrors(DpResId(RID_DLG_UPDATE_INSTALL_NO_ERRORS))
+ , m_sErrorDownload(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_DOWNLOAD))
+ , m_sErrorInstallation(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_INSTALLATION))
+ , m_sErrorLicenseDeclined(DpResId(RID_DLG_UPDATE_INSTALL_ERROR_LIC_DECLINED))
+ , m_sNoInstall(DpResId(RID_DLG_UPDATE_INSTALL_EXTENSION_NOINSTALL))
+ , m_sThisErrorOccurred(DpResId(RID_DLG_UPDATE_INSTALL_THIS_ERROR_OCCURRED))
+ , m_xFt_action(m_xBuilder->weld_label("DOWNLOADING"))
+ , m_xStatusbar(m_xBuilder->weld_progress_bar("STATUSBAR"))
+ , m_xFt_extension_name(m_xBuilder->weld_label("EXTENSION_NAME"))
+ , m_xMle_info(m_xBuilder->weld_text_view("INFO"))
+ , m_xHelp(m_xBuilder->weld_button("help"))
+ , m_xOk(m_xBuilder->weld_button("ok"))
+ , m_xCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xMle_info->set_size_request(m_xMle_info->get_approximate_digit_width() * 52,
+ m_xMle_info->get_height_rows(5));
+
+ m_xExtensionManager = css::deployment::ExtensionManager::get( xCtx );
+
+ m_xCancel->connect_clicked(LINK(this, UpdateInstallDialog, cancelHandler));
+ if ( ! dp_misc::office_is_running())
+ m_xHelp->set_sensitive(false);
+}
+
+UpdateInstallDialog::~UpdateInstallDialog()
+{
+}
+
+short UpdateInstallDialog::run()
+{
+ m_thread->launch();
+ short nRet = GenericDialogController::run();
+ m_thread->stop();
+ return nRet;
+}
+
+// make sure the solar mutex is locked before calling
+void UpdateInstallDialog::updateDone()
+{
+ if (!m_bError)
+ m_xMle_info->set_text(m_xMle_info->get_text() + m_sNoErrors);
+ m_xOk->set_sensitive(true);
+ m_xOk->grab_focus();
+ m_xCancel->set_sensitive(false);
+}
+
+// make sure the solar mutex is locked before calling
+//sets an error message in the text area
+void UpdateInstallDialog::setError(INSTALL_ERROR err, std::u16string_view sExtension,
+ std::u16string_view exceptionMessage)
+{
+ OUString sError;
+ m_bError = true;
+
+ switch (err)
+ {
+ case ERROR_DOWNLOAD:
+ sError = m_sErrorDownload;
+ break;
+ case ERROR_INSTALLATION:
+ sError = m_sErrorInstallation;
+ break;
+ case ERROR_LICENSE_DECLINED:
+ sError = m_sErrorLicenseDeclined;
+ break;
+
+ default:
+ OSL_ASSERT(false);
+ }
+
+ OUString sMsg(m_xMle_info->get_text());
+ sError = sError.replaceFirst("%NAME", sExtension);
+ //We want to have an empty line between the error messages. However,
+ //there shall be no empty line after the last entry.
+ if (m_bNoEntry)
+ m_bNoEntry = false;
+ else
+ sMsg += "\n";
+ sMsg += sError;
+ //Insert more information about the error
+ if (!exceptionMessage.empty())
+ sMsg += m_sThisErrorOccurred + exceptionMessage + "\n";
+
+ sMsg += m_sNoInstall + "\n";
+
+ m_xMle_info->set_text(sMsg);
+}
+
+void UpdateInstallDialog::setError(std::u16string_view exceptionMessage)
+{
+ m_bError = true;
+ m_xMle_info->set_text(m_xMle_info->get_text() + exceptionMessage + "\n");
+}
+
+IMPL_LINK_NOARG(UpdateInstallDialog, cancelHandler, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+void UpdateInstallDialog::Thread::downloadExtensions()
+{
+ try
+ {
+ //create the download directory in the temp folder
+ OUString sTempDir;
+ if (::osl::FileBase::getTempDirURL(sTempDir) != ::osl::FileBase::E_None)
+ throw css::uno::Exception("Could not get URL for the temp directory. No extensions will be installed.", nullptr);
+
+ //create a unique name for the directory
+ OUString tempEntry, destFolder;
+ if (::osl::File::createTempFile(&sTempDir, nullptr, &tempEntry ) != ::osl::File::E_None)
+ throw css::uno::Exception("Could not create a temporary file in " + sTempDir +
+ ". No extensions will be installed", nullptr );
+
+ tempEntry = tempEntry.copy( tempEntry.lastIndexOf( '/' ) + 1 );
+
+ destFolder = dp_misc::makeURL( sTempDir, tempEntry ) + "_";
+ m_sDownloadFolder = destFolder;
+ try
+ {
+ dp_misc::create_folder(nullptr, destFolder, m_updateCmdEnv );
+ } catch (const css::uno::Exception & e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException( e.Message + " No extensions will be installed",
+ nullptr, anyEx );
+ }
+
+
+ sal_uInt16 count = 0;
+ for (auto & updateData : m_aVecUpdateData)
+ {
+ if (!updateData.aUpdateInfo.is() || updateData.aUpdateSource.is())
+ continue;
+ //We assume that m_aVecUpdateData contains only information about extensions which
+ //can be downloaded directly.
+ OSL_ASSERT(updateData.sWebsiteURL.isEmpty());
+
+ //update the name of the extension which is to be downloaded
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xFt_extension_name->set_label(updateData.aInstalledPackage->getDisplayName());
+ sal_uInt16 prog = (sal::static_int_cast<sal_uInt16>(100) * ++count) /
+ sal::static_int_cast<sal_uInt16>(m_aVecUpdateData.size());
+ m_dialog.m_xStatusbar->set_percentage(prog);
+ }
+ dp_misc::DescriptionInfoset info(m_xComponentContext, updateData.aUpdateInfo);
+ //remember occurring exceptions in case we need to print out error information
+ std::vector< std::pair<OUString, css::uno::Exception> > vecExceptions;
+ css::uno::Sequence<OUString> seqDownloadURLs = info.getUpdateDownloadUrls();
+ OSL_ENSURE(seqDownloadURLs.hasElements(), "No download URL provided!");
+ for (sal_Int32 j = 0; j < seqDownloadURLs.getLength(); j++)
+ {
+ try
+ {
+ OSL_ENSURE(!seqDownloadURLs[j].isEmpty(), "Download URL is empty!");
+ bool bCancelled = download(seqDownloadURLs[j], updateData);
+ if (bCancelled || !updateData.sLocalURL.isEmpty())
+ break;
+ }
+ catch ( css::uno::Exception & e )
+ {
+ vecExceptions.emplace_back(seqDownloadURLs[j], e);
+ //There can be several different errors, for example, the URL is wrong, webserver cannot be reached,
+ //name cannot be resolved. The UCB helper API does not specify different special exceptions for these
+ //cases. Therefore ignore and continue.
+ continue;
+ }
+ }
+ //update the progress and display download error
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ if (updateData.sLocalURL.isEmpty())
+ {
+ //Construct a string of all messages contained in the exceptions plus the respective download URLs
+ OUStringBuffer buf(256);
+ size_t nPos = 0;
+ for (auto const& elem : vecExceptions)
+ {
+ if (nPos)
+ buf.append("\n");
+ buf.append("Could not download ");
+ buf.append(elem.first);
+ buf.append(". ");
+ buf.append(elem.second.Message);
+ ++nPos;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_DOWNLOAD, updateData.aInstalledPackage->getDisplayName(),
+ buf);
+ }
+ }
+
+ }
+ }
+ catch (const css::uno::Exception & e)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(e.Message);
+ }
+}
+
+void UpdateInstallDialog::Thread::installExtensions()
+{
+ //Update the fix text in the dialog to "Installing extensions..."
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xFt_action->set_label(m_dialog.m_sInstalling);
+ m_dialog.m_xStatusbar->set_percentage(0);
+ }
+
+ sal_uInt16 count = 0;
+ for (auto const& updateData : m_aVecUpdateData)
+ {
+ //update the name of the extension which is to be installed
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ //we only show progress after an extension has been installed.
+ if (count > 0) {
+ m_dialog.m_xStatusbar->set_percentage(
+ (sal::static_int_cast<sal_uInt16>(100) * count) /
+ sal::static_int_cast<sal_uInt16>(m_aVecUpdateData.size()));
+ }
+ m_dialog.m_xFt_extension_name->set_label(updateData.aInstalledPackage->getDisplayName());
+ }
+ bool bError = false;
+ bool bLicenseDeclined = false;
+ css::uno::Reference<css::deployment::XPackage> xExtension;
+ css::uno::Exception exc;
+ try
+ {
+ css::uno::Reference< css::task::XAbortChannel > xAbortChannel(
+ updateData.aInstalledPackage->createAbortChannel() );
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_abort = xAbortChannel;
+ }
+ if (!updateData.aUpdateSource.is() && !updateData.sLocalURL.isEmpty())
+ {
+ css::beans::NamedValue prop("EXTENSION_UPDATE", css::uno::Any(OUString("1")));
+ if (!updateData.bIsShared)
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.sLocalURL, css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "user", xAbortChannel, m_updateCmdEnv);
+ else
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.sLocalURL, css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "shared", xAbortChannel, m_updateCmdEnv);
+ }
+ else if (updateData.aUpdateSource.is())
+ {
+ OSL_ASSERT(updateData.aUpdateSource.is());
+ //I am not sure if we should obtain the install properties and pass them into
+ //add extension. Currently it contains only "SUPPRESS_LICENSE". So it could happen
+ //that a license is displayed when updating from the shared repository, although the
+ //shared extension was installed using "SUPPRESS_LICENSE".
+ css::beans::NamedValue prop("EXTENSION_UPDATE", css::uno::Any(OUString("1")));
+ if (!updateData.bIsShared)
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.aUpdateSource->getURL(), css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "user", xAbortChannel, m_updateCmdEnv);
+ else
+ xExtension = m_dialog.getExtensionManager()->addExtension(
+ updateData.aUpdateSource->getURL(), css::uno::Sequence<css::beans::NamedValue>(&prop, 1),
+ "shared", xAbortChannel, m_updateCmdEnv);
+ }
+ }
+ catch (css::deployment::DeploymentException & de)
+ {
+ if (de.Cause.has<css::deployment::LicenseException>())
+ {
+ bLicenseDeclined = true;
+ }
+ else
+ {
+ exc = de.Cause.get<css::uno::Exception>();
+ bError = true;
+ }
+ }
+ catch (css::uno::Exception& e)
+ {
+ exc = e;
+ bError = true;
+ }
+
+ if (bLicenseDeclined)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_LICENSE_DECLINED,
+ updateData.aInstalledPackage->getDisplayName(), std::u16string_view());
+ }
+ else if (!xExtension.is() || bError)
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.setError(UpdateInstallDialog::ERROR_INSTALLATION,
+ updateData.aInstalledPackage->getDisplayName(), exc.Message);
+ }
+ ++count;
+ }
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return;
+ }
+ m_dialog.m_xStatusbar->set_percentage(100);
+ m_dialog.m_xFt_extension_name->set_label(OUString());
+ m_dialog.m_xFt_action->set_label(m_dialog.m_sFinished);
+ }
+}
+
+void UpdateInstallDialog::Thread::removeTempDownloads()
+{
+ if (!m_sDownloadFolder.isEmpty())
+ {
+ dp_misc::erase_path(m_sDownloadFolder,
+ css::uno::Reference<css::ucb::XCommandEnvironment>(),false /* no throw: ignore errors */ );
+ //remove also the temp file which we have used to create the unique name
+ OUString tempFile = m_sDownloadFolder.copy(0, m_sDownloadFolder.getLength() - 1);
+ dp_misc::erase_path(tempFile, css::uno::Reference<css::ucb::XCommandEnvironment>(),false);
+ m_sDownloadFolder.clear();
+ }
+}
+
+bool UpdateInstallDialog::Thread::download(OUString const & sDownloadURL, UpdateData & aUpdateData)
+{
+ {
+ SolarMutexGuard g;
+ if (m_stop) {
+ return m_stop;
+ }
+ }
+
+ OSL_ASSERT(m_sDownloadFolder.getLength());
+ OUString destFolder, tempEntry;
+ if (::osl::File::createTempFile(
+ &m_sDownloadFolder,
+ nullptr, &tempEntry ) != ::osl::File::E_None)
+ {
+ //ToDo feedback in window that download of this component failed
+ throw css::uno::Exception("Could not create temporary file in folder " + destFolder + ".", nullptr);
+ }
+ tempEntry = tempEntry.copy( tempEntry.lastIndexOf( '/' ) + 1 );
+
+ destFolder = dp_misc::makeURL( m_sDownloadFolder, tempEntry ) + "_";
+
+ ::ucbhelper::Content destFolderContent;
+ dp_misc::create_folder( &destFolderContent, destFolder, m_updateCmdEnv );
+
+ ::ucbhelper::Content sourceContent;
+ (void)dp_misc::create_ucb_content(&sourceContent, sDownloadURL, m_updateCmdEnv);
+
+ const OUString sTitle( StrTitle::getTitle( sourceContent ) );
+
+ destFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ sTitle, css::ucb::NameClash::OVERWRITE );
+
+ {
+ //the user may have cancelled the dialog because downloading took too long
+ SolarMutexGuard g;
+ if (m_stop) {
+ return m_stop;
+ }
+ //all errors should be handled by the command environment.
+ aUpdateData.sLocalURL = destFolder + "/" + sTitle;
+ }
+
+ return m_stop;
+}
+
+UpdateCommandEnv::UpdateCommandEnv( css::uno::Reference< css::uno::XComponentContext > xCtx,
+ ::rtl::Reference<UpdateInstallDialog::Thread> thread)
+ : m_installThread(std::move(thread)),
+ m_xContext(std::move(xCtx))
+{
+}
+
+// XCommandEnvironment
+css::uno::Reference<css::task::XInteractionHandler> UpdateCommandEnv::getInteractionHandler()
+{
+ return this;
+}
+
+css::uno::Reference<css::ucb::XProgressHandler> UpdateCommandEnv::getProgressHandler()
+{
+ return this;
+}
+
+// XInteractionHandler
+void UpdateCommandEnv::handle(
+ css::uno::Reference< css::task::XInteractionRequest> const & xRequest )
+{
+ css::uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == css::uno::TypeClass_EXCEPTION );
+ dp_misc::TRACE("[dp_gui_cmdenv.cxx] incoming request:\n"
+ + ::comphelper::anyToString(request) + "\n\n");
+
+ css::deployment::VersionException verExc;
+ bool approve = false;
+
+ if (request >>= verExc)
+ { //We must catch the version exception during the update,
+ //because otherwise the user would be confronted with the dialogs, asking
+ //them if they want to replace an already installed version of the same extension.
+ //During an update we assume that we always want to replace the old version with the
+ //new version.
+ approve = true;
+ }
+
+ if (!approve)
+ {
+ //forward to interaction handler for main dialog.
+ handleInteractionRequest( m_xContext, xRequest );
+ }
+ else
+ {
+ // select:
+ css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ css::uno::Reference< css::task::XInteractionContinuation > const * pConts =
+ conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ css::uno::Reference< css::task::XInteractionApprove > xInteractionApprove(
+ pConts[ pos ], css::uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ }
+ }
+}
+
+// XProgressHandler
+void UpdateCommandEnv::push( css::uno::Any const & /*Status*/ )
+{
+}
+
+void UpdateCommandEnv::update( css::uno::Any const & /*Status */)
+{
+}
+
+void UpdateCommandEnv::pop()
+{
+}
+
+
+} //end namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx
new file mode 100644
index 000000000..39b37ed4b
--- /dev/null
+++ b/desktop/source/deployment/gui/dp_gui_updateinstalldialog.hxx
@@ -0,0 +1,112 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <vcl/weld.hxx>
+#include <rtl/ref.hxx>
+
+#include <string_view>
+#include <vector>
+
+/// @HTML
+namespace com::sun::star::deployment {
+ class XExtensionManager;
+}
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+namespace dp_gui {
+
+ struct UpdateData;
+ class UpdateCommandEnv;
+
+
+/**
+ The modal &ldquo;Download and Installation&rdquo; dialog.
+*/
+class UpdateInstallDialog : public weld::GenericDialogController
+{
+public:
+ /**
+ Create an instance.
+
+ @param parent
+ the parent window, may be null
+ */
+ UpdateInstallDialog(weld::Window* parent, std::vector<UpdateData> & aVecUpdateData,
+ css::uno::Reference< css::uno::XComponentContext > const & xCtx);
+
+ virtual ~UpdateInstallDialog() override;
+
+ virtual short run() override;
+
+private:
+ UpdateInstallDialog(UpdateInstallDialog const &) = delete;
+ UpdateInstallDialog& operator =(UpdateInstallDialog const &) = delete;
+
+ class Thread;
+ friend class Thread;
+ friend class UpdateCommandEnv;
+
+ DECL_LINK(cancelHandler, weld::Button&, void);
+
+ //signals in the dialog that we have finished.
+ void updateDone();
+ //Writes a particular error into the info listbox.
+ enum INSTALL_ERROR
+ {
+ ERROR_DOWNLOAD,
+ ERROR_INSTALLATION,
+ ERROR_LICENSE_DECLINED
+ };
+ void setError(INSTALL_ERROR err, std::u16string_view sExtension, std::u16string_view exceptionMessage);
+ void setError(std::u16string_view exceptionMessage);
+ const css::uno::Reference< css::deployment::XExtensionManager >& getExtensionManager() const
+ { return m_xExtensionManager; }
+
+ rtl::Reference< Thread > m_thread;
+ css::uno::Reference< css::deployment::XExtensionManager > m_xExtensionManager;
+ //Signals that an error occurred during download and installation
+ bool m_bError;
+ bool m_bNoEntry;
+
+ OUString m_sInstalling;
+ OUString m_sFinished;
+ OUString m_sNoErrors;
+ OUString m_sErrorDownload;
+ OUString m_sErrorInstallation;
+ OUString m_sErrorLicenseDeclined;
+ OUString m_sNoInstall;
+ OUString m_sThisErrorOccurred;
+
+ std::unique_ptr<weld::Label> m_xFt_action;
+ std::unique_ptr<weld::ProgressBar> m_xStatusbar;
+ std::unique_ptr<weld::Label> m_xFt_extension_name;
+ std::unique_ptr<weld::TextView> m_xMle_info;
+ std::unique_ptr<weld::Button> m_xHelp;
+ std::unique_ptr<weld::Button> m_xOk;
+ std::unique_ptr<weld::Button> m_xCancel;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/license_dialog.cxx b/desktop/source/deployment/gui/license_dialog.cxx
new file mode 100644
index 000000000..23f184a33
--- /dev/null
+++ b/desktop/source/deployment/gui/license_dialog.cxx
@@ -0,0 +1,244 @@
+/* -*- 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 <comphelper/unwrapargs.hxx>
+#include <vcl/event.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/threadex.hxx>
+#include <vcl/weld.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include "license_dialog.hxx"
+
+#include <functional>
+#include <string_view>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_gui {
+
+namespace {
+
+struct LicenseDialogImpl : public weld::GenericDialogController
+{
+ bool m_bLicenseRead;
+ Idle m_aResized;
+ AutoTimer m_aRepeat;
+
+ std::unique_ptr<weld::Label> m_xFtHead;
+ std::unique_ptr<weld::Widget> m_xArrow1;
+ std::unique_ptr<weld::Widget> m_xArrow2;
+ std::unique_ptr<weld::TextView> m_xLicense;
+ std::unique_ptr<weld::Button> m_xDown;
+ std::unique_ptr<weld::Button> m_xAcceptButton;
+ std::unique_ptr<weld::Button> m_xDeclineButton;
+
+ void PageDown();
+ DECL_LINK(ScrollTimerHdl, Timer*, void);
+ DECL_LINK(ScrolledHdl, weld::TextView&, void);
+ DECL_LINK(ResizedHdl, Timer*, void);
+ DECL_LINK(CancelHdl, weld::Button&, void);
+ DECL_LINK(AcceptHdl, weld::Button&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_STATIC_LINK(LicenseDialogImpl, KeyReleaseHdl, const KeyEvent&, bool);
+ DECL_LINK(MousePressHdl, const MouseEvent&, bool);
+ DECL_LINK(MouseReleaseHdl, const MouseEvent&, bool);
+ DECL_LINK(SizeAllocHdl, const Size&, void);
+
+ LicenseDialogImpl(weld::Window * pParent,
+ std::u16string_view sExtensionName,
+ const OUString & sLicenseText);
+
+ bool IsEndReached() const;
+};
+
+}
+
+LicenseDialogImpl::LicenseDialogImpl(
+ weld::Window * pParent,
+ std::u16string_view sExtensionName,
+ const OUString & sLicenseText)
+ : GenericDialogController(pParent, "desktop/ui/licensedialog.ui", "LicenseDialog")
+ , m_bLicenseRead(false)
+ , m_aResized("desktop LicenseDialogImpl m_aResized")
+ , m_aRepeat("LicenseDialogImpl m_aRepeat")
+ , m_xFtHead(m_xBuilder->weld_label("head"))
+ , m_xArrow1(m_xBuilder->weld_widget("arrow1"))
+ , m_xArrow2(m_xBuilder->weld_widget("arrow2"))
+ , m_xLicense(m_xBuilder->weld_text_view("textview"))
+ , m_xDown(m_xBuilder->weld_button("down"))
+ , m_xAcceptButton(m_xBuilder->weld_button("ok"))
+ , m_xDeclineButton(m_xBuilder->weld_button("cancel"))
+{
+ m_xArrow1->show();
+ m_xArrow2->hide();
+
+ m_xLicense->connect_size_allocate(LINK(this, LicenseDialogImpl, SizeAllocHdl));
+ m_xLicense->set_size_request(m_xLicense->get_approximate_digit_width() * 72,
+ m_xLicense->get_height_rows(21));
+
+ m_xLicense->set_text(sLicenseText);
+ m_xFtHead->set_label(m_xFtHead->get_label() + "\n" + sExtensionName);
+
+ m_xAcceptButton->connect_clicked( LINK(this, LicenseDialogImpl, AcceptHdl) );
+ m_xDeclineButton->connect_clicked( LINK(this, LicenseDialogImpl, CancelHdl) );
+
+ m_xLicense->connect_vadjustment_changed(LINK(this, LicenseDialogImpl, ScrolledHdl));
+ m_xDown->connect_mouse_press(LINK(this, LicenseDialogImpl, MousePressHdl));
+ m_xDown->connect_mouse_release(LINK(this, LicenseDialogImpl, MouseReleaseHdl));
+ m_xDown->connect_key_press(LINK(this, LicenseDialogImpl, KeyInputHdl));
+ m_xDown->connect_key_release(LINK(this, LicenseDialogImpl, KeyReleaseHdl));
+
+ m_aRepeat.SetTimeout(Application::GetSettings().GetMouseSettings().GetButtonRepeat());
+ m_aRepeat.SetInvokeHandler(LINK(this, LicenseDialogImpl, ScrollTimerHdl));
+
+ m_aResized.SetPriority(TaskPriority::LOWEST);
+ m_aResized.SetInvokeHandler(LINK(this, LicenseDialogImpl, ResizedHdl));
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, SizeAllocHdl, const Size&, void)
+{
+ m_aResized.Start();
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, AcceptHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, CancelHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+bool LicenseDialogImpl::IsEndReached() const
+{
+ return m_xLicense->vadjustment_get_value() + m_xLicense->vadjustment_get_page_size() >= m_xLicense->vadjustment_get_upper();
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ScrolledHdl, weld::TextView&, void)
+{
+ if (IsEndReached())
+ {
+ m_xDown->set_sensitive(false);
+ m_aRepeat.Stop();
+
+ if (!m_bLicenseRead)
+ {
+ m_xAcceptButton->set_sensitive(true);
+ m_xAcceptButton->grab_focus();
+ m_xArrow1->hide();
+ m_xArrow2->show();
+ m_bLicenseRead = true;
+ }
+ }
+ else
+ m_xDown->set_sensitive(true);
+}
+
+void LicenseDialogImpl::PageDown()
+{
+ m_xLicense->vadjustment_set_value(m_xLicense->vadjustment_get_value() +
+ m_xLicense->vadjustment_get_page_size());
+ ScrolledHdl(*m_xLicense);
+}
+
+IMPL_LINK(LicenseDialogImpl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ if (aKeyCode.GetCode() == KEY_RETURN || aKeyCode.GetCode() == KEY_SPACE)
+ PageDown();
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ResizedHdl, Timer*, void)
+{
+ ScrolledHdl(*m_xLicense);
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, ScrollTimerHdl, Timer*, void)
+{
+ PageDown();
+}
+
+IMPL_STATIC_LINK_NOARG(LicenseDialogImpl, KeyReleaseHdl, const KeyEvent&, bool)
+{
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, MousePressHdl, const MouseEvent&, bool)
+{
+ PageDown();
+ m_aRepeat.Start();
+ return false;
+}
+
+IMPL_LINK_NOARG(LicenseDialogImpl, MouseReleaseHdl, const MouseEvent&, bool)
+{
+ m_aRepeat.Stop();
+ return false;
+}
+
+LicenseDialog::LicenseDialog( Sequence<Any> const& args,
+ Reference<XComponentContext> const& )
+{
+ comphelper::unwrapArgs( args, m_parent, m_sExtensionName, m_sLicenseText );
+}
+
+// XServiceInfo
+OUString LicenseDialog::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.ui.LicenseDialog";
+}
+
+sal_Bool LicenseDialog::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > LicenseDialog::getSupportedServiceNames()
+{
+ return { "com.sun.star.deployment.ui.LicenseDialog" };
+}
+
+
+// XExecutableDialog
+
+void LicenseDialog::setTitle( OUString const & )
+{
+}
+
+sal_Int16 LicenseDialog::execute()
+{
+ return vcl::solarthread::syncExecute(
+ std::bind(&LicenseDialog::solar_execute, this));
+}
+
+sal_Int16 LicenseDialog::solar_execute()
+{
+ LicenseDialogImpl dlg(Application::GetFrameWeld(m_parent), m_sExtensionName, m_sLicenseText);
+ return dlg.run();
+}
+
+} // namespace dp_gui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/gui/license_dialog.hxx b/desktop/source/deployment/gui/license_dialog.hxx
new file mode 100644
index 000000000..3c628a880
--- /dev/null
+++ b/desktop/source/deployment/gui/license_dialog.hxx
@@ -0,0 +1,53 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+namespace dp_gui
+{
+class LicenseDialog
+ : public ::cppu::WeakImplHelper<css::ui::dialogs::XExecutableDialog, css::lang::XServiceInfo>
+{
+ css::uno::Reference<css::awt::XWindow> /* const */ m_parent;
+ OUString m_sExtensionName;
+ OUString /* const */ m_sLicenseText;
+
+ sal_Int16 solar_execute();
+
+public:
+ LicenseDialog(css::uno::Sequence<css::uno::Any> const& args,
+ css::uno::Reference<css::uno::XComponentContext> const& xComponentContext);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XExecutableDialog
+ virtual void SAL_CALL setTitle(OUString const& title) override;
+ virtual sal_Int16 SAL_CALL execute() override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_dependencies.hxx b/desktop/source/deployment/inc/dp_dependencies.hxx
new file mode 100644
index 000000000..1c9872c78
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_dependencies.hxx
@@ -0,0 +1,72 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include "dp_misc_api.hxx"
+
+/// @HTML
+
+namespace com::sun::star::xml::dom {
+ class XElement;
+}
+namespace dp_misc { class DescriptionInfoset; }
+
+namespace dp_misc {
+
+/**
+ Dependency handling.
+*/
+namespace Dependencies {
+ /**
+ Check for unsatisfied dependencies.
+
+ @param infoset
+ the infoset containing the dependencies to check
+
+ @return
+ a list of the unsatisfied dependencies from <code>infoset</code> (in no
+ specific order)
+ */
+ DESKTOP_DEPLOYMENTMISC_DLLPUBLIC css::uno::Sequence<
+ css::uno::Reference< css::xml::dom::XElement > >
+ check(dp_misc::DescriptionInfoset const & infoset);
+
+ /**
+ Obtain the (human-readable) error message of a failed dependency.
+
+ @param dependency
+ a dependency represented as a non-null XML element
+
+ @return
+ the name of the dependency; will never be empty, as a localized
+ &ldquo;unknown&rdquo; is substituted for an empty/missing name
+ */
+ DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getErrorText(
+ css::uno::Reference< css::xml::dom::XElement >
+ const & dependency);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_descriptioninfoset.hxx b/desktop/source/deployment/inc/dp_descriptioninfoset.hxx
new file mode 100644
index 000000000..08d533a79
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_descriptioninfoset.hxx
@@ -0,0 +1,287 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <optional>
+#include <string_view>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sal/types.h>
+#include "dp_misc_api.hxx"
+
+/// @HTML
+
+namespace com::sun::star {
+ namespace uno { class XComponentContext; }
+ namespace xml {
+ namespace dom {
+ class XNode;
+ class XNodeList;
+ }
+ namespace xpath { class XXPathAPI; }
+ }
+}
+
+namespace dp_misc {
+
+struct DESKTOP_DEPLOYMENTMISC_DLLPUBLIC SimpleLicenseAttributes
+{
+ OUString acceptBy;
+ //Attribute suppress-on-update. Default is false.
+ bool suppressOnUpdate;
+ //Attribute suppress-if-required. Default is false.
+ bool suppressIfRequired;
+};
+
+
+/**
+ Access to the content of an XML <code>description</code> element.
+
+ <p>This works for <code>description</code> elements in both the
+ <code>description.xml</code> file and online update information formats.</p>
+*/
+class DESKTOP_DEPLOYMENTMISC_DLLPUBLIC DescriptionInfoset {
+public:
+ /**
+ Create an instance.
+
+ @param context
+ a non-null component context
+
+ @param element
+ a <code>description</code> element; may be null (equivalent to an element
+ with no content)
+ */
+ DescriptionInfoset(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ css::uno::Reference< css::xml::dom::XNode > const & element);
+
+ ~DescriptionInfoset();
+
+ /**
+ Return the identifier.
+
+ @return
+ the identifier, or an empty <code>optional</code> if none is specified
+ */
+ ::std::optional< OUString > getIdentifier() const;
+
+ /**
+ Return the textual version representation.
+
+ @return
+ textual version representation
+ */
+ OUString getVersion() const;
+
+ /**
+ Returns a list of supported platforms.
+
+ If the extension does not specify a platform by leaving out the platform element
+ then we assume that the extension supports all platforms. In this case the returned
+ sequence will have one element, which is &quot;all&quot;.
+ If the platform element is present but does not specify a platform then an empty
+ sequence is returned. Examples for invalid platform elements:
+ <pre>
+ <platform />, <platform value="" />, <platform value=",">
+ </pre>
+
+ The value attribute can contain various platform tokens. They must be separated by
+ commas.Each token will be stripped from leading and trailing white space (trim()).
+ */
+ css::uno::Sequence< OUString > getSupportedPlatforms() const;
+
+ /**
+ Returns the localized publisher name and the corresponding URL.
+
+ In case there is no publisher element then a pair of two empty strings is returned.
+ */
+ std::pair< OUString, OUString > getLocalizedPublisherNameAndURL() const;
+
+ /**
+ Returns the URL for the release notes corresponding to the office's locale.
+
+ In case there is no release-notes element then an empty string is returned.
+ */
+ OUString getLocalizedReleaseNotesURL() const;
+
+ /** returns the relative path to the license file.
+
+ In case there is no simple-license element then an empty string is returned.
+ */
+ OUString getLocalizedLicenseURL() const;
+
+ /** returns the attributes of the simple-license element
+
+ As long as there is a simple-license element, the function will return
+ the structure. If it does not exist, then the optional object is uninitialized.
+ */
+ ::std::optional<SimpleLicenseAttributes> getSimpleLicenseAttributes() const;
+
+ /** returns the localized display name of the extensions.
+
+ In case there is no localized display-name then an empty string is returned.
+ */
+ OUString getLocalizedDisplayName() const;
+
+ /**
+ returns the download website URL from the update information.
+
+ There can be multiple URLs where each is assigned to a particular locale.
+ The function returns the URL which locale matches best the one used in the office.
+
+ The return value is an optional because it may be necessary to find out if there
+ was a value provided or not. This is necessary to flag the extension in the update dialog
+ properly as "browser based update". The return value will only then not be initialized
+ if there is no <code>&lt;update-website&gt;</code>. If the element exists, then it must
+ have at least one child element containing a URL.
+
+ The <code>&lt;update-website&gt;</code> and <code>&lt;update-download&gt;</code>
+ elements are mutually exclusive.
+
+ @return
+ the download website URL, or an empty <code>optional</code> if none is
+ specified
+ */
+ ::std::optional< OUString > getLocalizedUpdateWebsiteURL() const;
+
+ /** returns the relative URL to the description.
+
+ The URL is relative to the root directory of the extensions.
+ */
+ OUString getLocalizedDescriptionURL() const;
+ /**
+ Return the dependencies.
+
+ @return
+ dependencies; will never be null
+ */
+ css::uno::Reference< css::xml::dom::XNodeList >
+ getDependencies() const;
+
+ /**
+ Return the update information URLs.
+
+ @return
+ update information URLs
+ */
+ css::uno::Sequence< OUString > getUpdateInformationUrls() const;
+
+ /**
+ Return the download URLs from the update information.
+
+ Because the <code>&lt;update-download&gt;</code> and the <code>&lt;update-website&gt;</code>
+ elements are mutually exclusive one may need to determine exactly if the element
+ was provided.
+
+ @return
+ download URLs
+ */
+ css::uno::Sequence< OUString > getUpdateDownloadUrls() const;
+
+ /**
+ Returns the URL for the icon image.
+ */
+ OUString getIconURL( bool bHighContrast ) const;
+
+ bool hasDescription() const;
+
+private:
+ SAL_DLLPRIVATE ::std::optional< OUString > getOptionalValue(
+ OUString const & expression) const;
+
+ SAL_DLLPRIVATE css::uno::Sequence< OUString > getUrls(
+ OUString const & expression) const;
+
+ /** Retrieves a child element which as lang attribute which matches the office locale.
+
+ Only top-level children are taken into account. It is also assumed that they are all
+ of the same element type and have a lang attribute. The matching algorithm is according
+ to RFC 3066, with the exception that only one variant is allowed.
+ @param parent
+ the expression used to obtain the parent of the localized children. It can be null.
+ Then a null reference is returned.
+ */
+ SAL_DLLPRIVATE css::uno::Reference< css::xml::dom::XNode >
+ getLocalizedChild( OUString const & sParent) const;
+ SAL_DLLPRIVATE css::uno::Reference< css::xml::dom::XNode>
+ matchLanguageTag(
+ css::uno::Reference< css::xml::dom::XNode > const & xParent,
+ std::u16string_view rTag) const;
+
+ /** If there is no child element with a locale matching the office locale, then we use
+ the first child. In the case of the simple-license we also use the former default locale, which
+ was determined by the default-license-id (/description/registration/simple-license/@default-license-id)
+ and the license-id attributes (/description/registration/simple-license/license-text/@license-id).
+ However, since OOo 2.4 we use also the first child as default for the license
+ unless the two attributes are present.
+ */
+ SAL_DLLPRIVATE css::uno::Reference< css::xml::dom::XNode>
+ getChildWithDefaultLocale(
+ css::uno::Reference< css::xml::dom::XNode > const & xParent) const;
+ /**
+ @param out_bParentExists
+ indicates if the element node specified in sXPathParent exists.
+ */
+ SAL_DLLPRIVATE OUString getLocalizedHREFAttrFromChild(
+ OUString const & sXPathParent, bool * out_bParentExists) const;
+
+ /** Gets the node value for a given expression. The expression is used in
+ m_xpath-selectSingleNode. The value of the returned node is return value
+ of this function.
+ */
+ SAL_DLLPRIVATE OUString
+ getNodeValueFromExpression(OUString const & expression) const;
+
+ /** Check the extensions denylist if additional extension meta data (e.g. dependencies)
+ are defined for this extension and have to be taken into account.
+ */
+ SAL_DLLPRIVATE void
+ checkDenylist() const;
+
+ /** Helper method to compare the versions with the current version
+ */
+ SAL_DLLPRIVATE static bool
+ checkDenylistVersion(std::u16string_view currentversion,
+ css::uno::Sequence< OUString > const & versions);
+
+ css::uno::Reference< css::uno::XComponentContext > m_context;
+ css::uno::Reference< css::xml::dom::XNode > m_element;
+ css::uno::Reference< css::xml::xpath::XXPathAPI > m_xpath;
+};
+
+inline bool DescriptionInfoset::hasDescription() const
+{
+ return m_element.is();
+}
+
+/** creates a DescriptionInfoset object.
+
+ The argument sExtensionFolderURL is a file URL to extension folder containing
+ the description.xml.
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+DescriptionInfoset getDescriptionInfoset(std::u16string_view sExtensionFolderURL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_identifier.hxx b/desktop/source/deployment/inc/dp_identifier.hxx
new file mode 100644
index 000000000..bd11170b6
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_identifier.hxx
@@ -0,0 +1,84 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <optional>
+#include <string_view>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include "dp_misc_api.hxx"
+
+namespace com::sun::star::deployment {
+ class XPackage;
+}
+
+namespace dp_misc {
+
+/**
+ Generates an identifier from an optional identifier.
+
+ @param optional
+ an optional identifier
+
+ @param fileName
+ a file name
+
+ @return
+ the given optional identifier if present, otherwise a legacy identifier based
+ on the given file name
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateIdentifier(
+ ::std::optional< OUString > const & optional,
+ std::u16string_view fileName);
+
+/**
+ Gets the identifier of a package.
+
+ @param package
+ a non-null package
+
+ @return
+ the explicit identifier of the given package if present, otherwise the
+ implicit legacy identifier of the given package
+
+ @throws css::uno::RuntimeException
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getIdentifier(
+ css::uno::Reference< css::deployment::XPackage >
+ const & package);
+
+/**
+ Generates a legacy identifier based on a file name.
+
+ @param fileName
+ a file name
+
+ @return
+ a legacy identifier based on the given file name
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString generateLegacyIdentifier(
+ std::u16string_view fileName);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_interact.h b/desktop/source/deployment/inc/dp_interact.h
new file mode 100644
index 000000000..6a041d99f
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_interact.h
@@ -0,0 +1,140 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <config_options.h>
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include <utility>
+#include "dp_misc_api.hxx"
+
+namespace dp_misc
+{
+
+inline void progressUpdate(
+ OUString const & status,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ if (xCmdEnv.is()) {
+ css::uno::Reference<css::ucb::XProgressHandler> xProgressHandler(
+ xCmdEnv->getProgressHandler() );
+ if (xProgressHandler.is()) {
+ xProgressHandler->update( css::uno::Any(status) );
+ }
+ }
+}
+
+
+class ProgressLevel
+{
+ css::uno::Reference<css::ucb::XProgressHandler> m_xProgressHandler;
+
+public:
+ inline ~ProgressLevel();
+ inline ProgressLevel(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ OUString const & status );
+
+ inline void update( OUString const & status ) const;
+ inline void update( css::uno::Any const & status ) const;
+};
+
+
+inline ProgressLevel::ProgressLevel(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
+ OUString const & status )
+{
+ if (xCmdEnv.is())
+ m_xProgressHandler = xCmdEnv->getProgressHandler();
+ if (m_xProgressHandler.is())
+ m_xProgressHandler->push( css::uno::Any(status) );
+}
+
+
+inline ProgressLevel::~ProgressLevel()
+{
+ if (m_xProgressHandler.is())
+ m_xProgressHandler->pop();
+}
+
+
+inline void ProgressLevel::update( OUString const & status ) const
+{
+ if (m_xProgressHandler.is())
+ m_xProgressHandler->update( css::uno::Any(status) );
+}
+
+
+inline void ProgressLevel::update( css::uno::Any const & status ) const
+{
+ if (m_xProgressHandler.is())
+ m_xProgressHandler->update( status );
+}
+
+
+
+/** @return true if ia handler is present and any selection has been chosen
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool interactContinuation(
+ css::uno::Any const & request,
+ css::uno::Type const & continuation,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool * pcont, bool * pabort );
+
+
+
+
+class UNLESS_MERGELIBS(DESKTOP_DEPLOYMENTMISC_DLLPUBLIC) AbortChannel :
+ public ::cppu::WeakImplHelper<css::task::XAbortChannel>
+{
+ bool m_aborted;
+ css::uno::Reference<css::task::XAbortChannel> m_xNext;
+
+public:
+ AbortChannel() : m_aborted( false ) {}
+ static AbortChannel * get(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel )
+ { return static_cast<AbortChannel *>(xAbortChannel.get()); }
+
+ bool isAborted() const { return m_aborted; }
+
+ // XAbortChannel
+ virtual void SAL_CALL sendAbort() override;
+
+ class SAL_DLLPRIVATE Chain
+ {
+ const ::rtl::Reference<AbortChannel> m_abortChannel;
+ public:
+ Chain(
+ ::rtl::Reference<AbortChannel> abortChannel,
+ css::uno::Reference<css::task::XAbortChannel> const & xNext )
+ : m_abortChannel(std::move( abortChannel ))
+ { if (m_abortChannel.is()) m_abortChannel->m_xNext = xNext; }
+ ~Chain()
+ { if (m_abortChannel.is()) m_abortChannel->m_xNext.clear(); }
+ };
+ friend class Chain;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_misc_api.hxx b/desktop/source/deployment/inc/dp_misc_api.hxx
new file mode 100644
index 000000000..ecb07dada
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_misc_api.hxx
@@ -0,0 +1,31 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <sal/types.h>
+
+#if defined DESKTOP_DEPLOYMENTMISC_DLLIMPLEMENTATION
+#define DESKTOP_DEPLOYMENTMISC_DLLPUBLIC SAL_DLLPUBLIC_EXPORT
+#else
+#define DESKTOP_DEPLOYMENTMISC_DLLPUBLIC SAL_DLLPUBLIC_IMPORT
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_package.hxx b/desktop/source/deployment/inc/dp_package.hxx
new file mode 100644
index 000000000..f287990f2
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_package.hxx
@@ -0,0 +1,42 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star {
+ namespace deployment { class XPackageRegistry; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_registry::backend::bundle {
+
+css::uno::Reference<css::deployment::XPackageRegistry> create(
+ css::uno::Reference<css::deployment::XPackageRegistry> const &
+ xRootRegistry,
+ OUString const & context, OUString const & cachePath,
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_persmap.h b/desktop/source/deployment/inc/dp_persmap.h
new file mode 100644
index 000000000..c36979807
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_persmap.h
@@ -0,0 +1,60 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <osl/file.hxx>
+#include <unordered_map>
+
+namespace dp_misc
+{
+typedef std::unordered_map<OString, OString> t_string2string_map;
+
+// Class to read obsolete registered extensions
+// should be removed for LibreOffice 4.0
+class PersistentMap final
+{
+ ::osl::File m_MapFile;
+ t_string2string_map m_entries;
+ bool m_bIsOpen;
+ bool m_bToBeCreated;
+ bool m_bIsDirty;
+
+public:
+ ~PersistentMap();
+ PersistentMap(OUString const& url);
+ /** in mem db */
+ PersistentMap();
+
+ bool has(OString const& key) const;
+ bool get(OString* value, OString const& key) const;
+ const t_string2string_map& getEntries() const { return m_entries; }
+ void put(OString const& key, OString const& value);
+ bool erase(OString const& key);
+
+private:
+ void open();
+ void readAll();
+ void add(OString const& key, OString const& value);
+ void flush();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_platform.hxx b/desktop/source/deployment/inc/dp_platform.hxx
new file mode 100644
index 000000000..5e454700d
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_platform.hxx
@@ -0,0 +1,41 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include "dp_misc_api.hxx"
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustring.hxx>
+
+namespace dp_misc
+{
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString const& getPlatformString();
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+bool platform_fits(std::u16string_view platform_string);
+
+/** determines if the current platform corresponds to one of the platform strings.
+
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+bool hasValidPlatform(css::uno::Sequence<OUString> const& platformStrings);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_registry.hxx b/desktop/source/deployment/inc/dp_registry.hxx
new file mode 100644
index 000000000..76742587e
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_registry.hxx
@@ -0,0 +1,40 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star {
+ namespace deployment { class XPackageRegistry; }
+ namespace uno { class XComponentContext; }
+}
+
+namespace dp_registry {
+
+css::uno::Reference<css::deployment::XPackageRegistry> create(
+ OUString const & context, OUString const & cachePath,
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_resource.h b/desktop/source/deployment/inc/dp_resource.h
new file mode 100644
index 000000000..471960e8d
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_resource.h
@@ -0,0 +1,30 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <i18nlangtag/languagetag.hxx>
+#include "dp_misc_api.hxx"
+
+namespace dp_misc
+{
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC const LanguageTag& getOfficeLanguageTag();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_ucb.h b/desktop/source/deployment/inc/dp_ucb.h
new file mode 100644
index 000000000..291b28b9b
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_ucb.h
@@ -0,0 +1,94 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include "dp_misc_api.hxx"
+#include <ucbhelper/content.hxx>
+
+namespace ucbhelper
+{
+class Content;
+}
+
+namespace dp_misc {
+
+struct DESKTOP_DEPLOYMENTMISC_DLLPUBLIC StrTitle
+{
+ static css::uno::Sequence< OUString > getTitleSequence()
+ {
+ css::uno::Sequence<OUString> aSeq { "Title" };
+ return aSeq;
+ }
+ static OUString getTitle( ::ucbhelper::Content &rContent )
+ {
+ return rContent.getPropertyValue("Title").get<OUString>();
+ }
+ // just return titles - the ucbhelper should have a simpler API for this [!]
+ static css::uno::Reference< css::sdbc::XResultSet >
+ createCursor( ::ucbhelper::Content &rContent,
+ ucbhelper::ResultSetInclude eInclude )
+ {
+ return rContent.createCursor( StrTitle::getTitleSequence(), eInclude );
+ }
+};
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool create_ucb_content(
+ ::ucbhelper::Content * ucb_content,
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc = true );
+
+
+/** @return true if previously non-existing folder has been created
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool create_folder(
+ ::ucbhelper::Content * ucb_content,
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc = true );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool erase_path(
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc = true );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+std::vector<sal_Int8> readFile( ::ucbhelper::Content & ucb_content );
+
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+bool readLine( OUString * res, OUString const & startingWith,
+ ::ucbhelper::Content & ucb_content, rtl_TextEncoding textenc );
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+bool readProperties( std::vector< std::pair< OUString, OUString> > & out_result,
+ ::ucbhelper::Content & ucb_content);
+
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_update.hxx b/desktop/source/deployment/inc/dp_update.hxx
new file mode 100644
index 000000000..8dce9b583
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_update.hxx
@@ -0,0 +1,136 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+
+#include "dp_misc_api.hxx"
+
+#include <map>
+#include <vector>
+
+namespace dp_misc {
+
+/** returns the default update URL (for the update information) which
+ is used when an extension does not provide its own URL.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+OUString getExtensionDefaultUpdateURL();
+
+enum UPDATE_SOURCE
+{
+ UPDATE_SOURCE_NONE,
+ UPDATE_SOURCE_SHARED,
+ UPDATE_SOURCE_BUNDLED,
+ UPDATE_SOURCE_ONLINE
+};
+
+/* determine if an update is available which is installed in the
+ user repository.
+
+ If the return value is UPDATE_SOURCE_NONE, then no update is
+ available, otherwise the return value determine from which the
+ repository the update is used.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+UPDATE_SOURCE isUpdateUserExtension(
+ bool bReadOnlyShared,
+ OUString const & userVersion,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion);
+
+/* determine if an update is available which is installed in the
+ shared repository.
+
+ If the return value is UPDATE_SOURCE_NONE, then no update is
+ available, otherwise the return value determine from which the
+ repository the update is used.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+UPDATE_SOURCE isUpdateSharedExtension(
+ bool bReadOnlyShared,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion);
+
+/* determines the extension with the highest identifier and returns it
+
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+css::uno::Reference< css::deployment::XPackage>
+getExtensionWithHighestVersion(
+ css::uno::Sequence<
+ css::uno::Reference<
+ css::deployment::XPackage> > const & seqExtensionsWithSameId);
+
+
+struct UpdateInfo
+{
+ UpdateInfo( css::uno::Reference< css::deployment::XPackage> const & ext);
+
+ css::uno::Reference< css::deployment::XPackage> extension;
+ //version of the update
+ OUString version;
+ css::uno::Reference< css::xml::dom::XNode > info;
+};
+
+typedef std::map< OUString, UpdateInfo > UpdateInfoMap;
+
+/*
+ @param extensionList
+ List of extension for which online update information is to be obtained. If NULL, then
+ for update information is obtained for all installed extension. There may be only one extension
+ with a particular identifier contained in the list. If one extension is installed
+ in several repositories, then the one with the highest version must be used, because it contains
+ the more recent URLs for getting the update information (if at all).
+ @param out_errors
+ the first member of the pair is the extension and the second the exception that was produced
+ when processing the extension.
+
+ @return
+ A map of UpdateInfo instances. If the parameter extensionList was given, then the map contains
+ at only information for those extensions.
+ */
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+UpdateInfoMap getOnlineUpdateInfos(
+ css::uno::Reference< css::uno::XComponentContext> const &xContext,
+ css::uno::Reference< css::deployment::XExtensionManager> const & xExtMgr,
+ css::uno::Reference< css::deployment::XUpdateInformationProvider > const & updateInformation,
+ std::vector< css::uno::Reference< css::deployment::XPackage > > const * extensionList,
+ std::vector< std::pair< css::uno::Reference<
+ css::deployment::XPackage>, css::uno::Any> > & out_errors);
+
+/* returns the highest version from the provided arguments.
+*/
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
+OUString getHighestVersion(
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_version.hxx b/desktop/source/deployment/inc/dp_version.hxx
new file mode 100644
index 000000000..342e2bf8c
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_version.hxx
@@ -0,0 +1,36 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+#include "dp_misc_api.hxx"
+
+
+namespace dp_misc {
+
+enum Order { LESS, EQUAL, GREATER };
+
+DESKTOP_DEPLOYMENTMISC_DLLPUBLIC Order compareVersions(
+ OUString const & version1, OUString const & version2);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/dp_xml.h b/desktop/source/deployment/inc/dp_xml.h
new file mode 100644
index 000000000..608073328
--- /dev/null
+++ b/desktop/source/deployment/inc/dp_xml.h
@@ -0,0 +1,42 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+
+namespace ucbhelper
+{
+class Content;
+}
+
+namespace dp_misc
+{
+
+
+void xml_parse(
+ css::uno::Reference< css::xml::sax::XDocumentHandler > const & xDocHandler,
+ ::ucbhelper::Content & ucb_content,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/inc/lockfile.hxx b/desktop/source/deployment/inc/lockfile.hxx
new file mode 100644
index 000000000..982a0c2f0
--- /dev/null
+++ b/desktop/source/deployment/inc/lockfile.hxx
@@ -0,0 +1,89 @@
+/* -*- 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 .
+ */
+
+/* Information:
+ * This class implements a mechanism to lock a users installation directory,
+ * which is necessary because instances of staroffice could be running on
+ * different hosts while using the same directory thus causing data
+ * inconsistency.
+ * When an existing lock is detected, the user will be asked whether he wants
+ * to continue anyway, thus removing the lock and replacing it with a new one
+ *
+ * ideas:
+ * - store information about user and host and time in the lockfile and display
+ * these when asking whether to remove the lockfile.
+ * - periodically check the lockfile and warn the user when it gets replaced
+ *
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+#include "dp_misc_api.hxx"
+
+#define LOCKFILE_GROUP "Lockdata"
+#define LOCKFILE_USERKEY "User"
+#define LOCKFILE_HOSTKEY "Host"
+#define LOCKFILE_STAMPKEY "Stamp"
+#define LOCKFILE_TIMEKEY "Time"
+#define LOCKFILE_IPCKEY "IPCServer"
+
+namespace desktop {
+
+ class Lockfile;
+ bool Lockfile_execWarning( Lockfile const * that );
+
+ class DESKTOP_DEPLOYMENTMISC_DLLPUBLIC Lockfile
+ {
+ public:
+
+ // constructs a new lockfile object
+ Lockfile( bool bIPCserver = true );
+
+ // separating GUI code:
+ typedef bool (* fpExecWarning)( Lockfile const * that );
+
+ // checks the lockfile, asks user when lockfile is
+ // found (iff gui) and returns false when we may not continue
+ bool check( fpExecWarning execWarning );
+
+ // removes the lockfile
+ ~Lockfile();
+
+ private:
+ bool m_bIPCserver;
+ // full qualified name (file://-url) of the lockfile
+ OUString m_aLockname;
+ // flag whether the d'tor should delete the lock
+ bool m_bRemove;
+ bool m_bIsLocked;
+ // ID
+ OUString m_aId;
+ OUString m_aDate;
+ // access to data in file
+ void syncToFile() const;
+ bool isStale() const;
+ friend bool Lockfile_execWarning( Lockfile const * that );
+
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_activepackages.cxx b/desktop/source/deployment/manager/dp_activepackages.cxx
new file mode 100644
index 000000000..c1c5f2b28
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_activepackages.cxx
@@ -0,0 +1,217 @@
+/* -*- 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 <config_extensions.h>
+
+#include <sal/config.h>
+
+#include <string_view>
+#include <utility>
+
+#include <osl/diagnose.h>
+#include <rtl/string.hxx>
+#include <rtl/textenc.h>
+#include <rtl/ustring.hxx>
+
+#include <dp_identifier.hxx>
+#include "dp_activepackages.hxx"
+
+// Old format of database entry:
+// key: UTF8(filename)
+// value: UTF8(tempname ";" mediatype)
+// New format of database entry:
+// key: 0xFF UTF8(identifier)
+// value: UTF8(tempname) 0xFF UTF8(filename) 0xFF UTF8(mediatype)
+
+#if HAVE_FEATURE_EXTENSIONS
+
+namespace {
+
+constexpr const char separator[] = "\xff";
+
+OString oldKey(std::u16string_view fileName) {
+ return OUStringToOString(fileName, RTL_TEXTENCODING_UTF8);
+}
+
+OString newKey(std::u16string_view id) {
+ return separator + OUStringToOString(id, RTL_TEXTENCODING_UTF8);
+}
+
+::dp_manager::ActivePackages::Data decodeOldData(
+ OUString const & fileName, OString const & value)
+{
+ ::dp_manager::ActivePackages::Data d;
+ sal_Int32 i = value.indexOf(';');
+ OSL_ASSERT(i >= 0);
+ d.temporaryName = OUString(value.getStr(), i, RTL_TEXTENCODING_UTF8);
+ d.fileName = fileName;
+ d.mediaType = OUString(
+ value.getStr() + i + 1, value.getLength() - i - 1,
+ RTL_TEXTENCODING_UTF8);
+ return d;
+}
+
+::dp_manager::ActivePackages::Data decodeNewData(OString const & value) {
+ ::dp_manager::ActivePackages::Data d;
+ sal_Int32 i1 = value.indexOf(separator);
+ OSL_ASSERT(i1 >= 0);
+ d.temporaryName = OUString(
+ value.getStr(), i1, RTL_TEXTENCODING_UTF8);
+ sal_Int32 i2 = value.indexOf(separator, i1 + 1);
+ OSL_ASSERT(i2 >= 0);
+ d.fileName = OUString(
+ value.getStr() + i1 + 1, i2 - i1 - 1, RTL_TEXTENCODING_UTF8);
+ sal_Int32 i3 = value.indexOf(separator, i2 + 1);
+
+ if (i3 < 0)
+ {
+ //Before ActivePackages::Data::version was added
+ d.mediaType = OUString(
+ value.getStr() + i2 + 1, value.getLength() - i2 - 1,
+ RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+ sal_Int32 i4 = value.indexOf(separator, i3 + 1);
+ d.mediaType = OUString(
+ value.getStr() + i2 + 1, i3 - i2 -1, RTL_TEXTENCODING_UTF8);
+ d.version = OUString(
+ value.getStr() + i3 + 1, i4 - i3 - 1,
+ RTL_TEXTENCODING_UTF8);
+ d.failedPrerequisites = OUString(
+ value.getStr() + i4 + 1, value.getLength() - i4 - 1,
+ RTL_TEXTENCODING_UTF8);
+ }
+ return d;
+}
+
+}
+#endif
+
+namespace dp_manager {
+
+ActivePackages::ActivePackages() {}
+
+ActivePackages::ActivePackages(OUString const & url)
+#if HAVE_FEATURE_EXTENSIONS
+ : m_map(url)
+#endif
+{
+#if !HAVE_FEATURE_EXTENSIONS
+ (void) url;
+#endif
+}
+
+ActivePackages::~ActivePackages() {}
+
+bool ActivePackages::has(
+ OUString const & id, OUString const & fileName) const
+{
+ return get(nullptr, id, fileName);
+}
+
+bool ActivePackages::get(
+ Data * data, OUString const & id, OUString const & fileName)
+ const
+{
+#if HAVE_FEATURE_EXTENSIONS
+ OString v;
+ if (m_map.get(&v, newKey(id))) {
+ if (data != nullptr) {
+ *data = decodeNewData(v);
+ }
+ return true;
+ } else if (m_map.get(&v, oldKey(fileName))) {
+ if (data != nullptr) {
+ *data = decodeOldData(fileName, v);
+ }
+ return true;
+ } else {
+ return false;
+ }
+#else
+ (void) data;
+ (void) id;
+ (void) fileName;
+ (void) this;
+ return false;
+#endif
+}
+
+ActivePackages::Entries ActivePackages::getEntries() const {
+ Entries es;
+#if HAVE_FEATURE_EXTENSIONS
+ ::dp_misc::t_string2string_map m(m_map.getEntries());
+ for (auto const& elem : m)
+ {
+ if (!elem.first.isEmpty() && elem.first[0] == separator[0]) {
+ es.emplace_back(
+ OUString(
+ elem.first.getStr() + 1, elem.first.getLength() - 1,
+ RTL_TEXTENCODING_UTF8),
+ decodeNewData(elem.second));
+ } else {
+ OUString fn(
+ OStringToOUString(elem.first, RTL_TEXTENCODING_UTF8));
+ es.emplace_back(
+ ::dp_misc::generateLegacyIdentifier(fn),
+ decodeOldData(fn, elem.second));
+ }
+ }
+#else
+ (void) this;
+#endif
+ return es;
+}
+
+void ActivePackages::put(OUString const & id, Data const & data) {
+#if HAVE_FEATURE_EXTENSIONS
+ OString b =
+ OUStringToOString(data.temporaryName, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.fileName, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.mediaType, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.version, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.failedPrerequisites, RTL_TEXTENCODING_UTF8);
+ m_map.put(newKey(id), b);
+#else
+ (void) id;
+ (void) data;
+ (void) this;
+#endif
+}
+
+void ActivePackages::erase(
+ OUString const & id, OUString const & fileName)
+{
+#if HAVE_FEATURE_EXTENSIONS
+ m_map.erase(newKey(id)) || m_map.erase(oldKey(fileName));
+#else
+ (void) id;
+ (void) fileName;
+ (void) this;
+#endif
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_activepackages.hxx b/desktop/source/deployment/manager/dp_activepackages.hxx
new file mode 100644
index 000000000..fae938019
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_activepackages.hxx
@@ -0,0 +1,95 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <config_extensions.h>
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+
+#include <utility>
+#include <vector>
+
+#if HAVE_FEATURE_EXTENSIONS
+#include <dp_persmap.h>
+#endif
+
+
+namespace dp_manager {
+
+class ActivePackages {
+public:
+ struct Data {
+ Data(): failedPrerequisites("0")
+ {}
+ /* name of the temporary file (shared, user extension) or the name of
+ the folder of the bundled extension.
+ It does not contain the trailing '_' of the folder.
+ UTF-8 encoded
+ */
+ OUString temporaryName;
+ /* The file name (shared, user) or the folder name (bundled)
+ If the key is the file name, then file name is not encoded.
+ If the key is the identifier then the file name is UTF-8 encoded.
+ */
+ OUString fileName;
+ OUString mediaType;
+ OUString version;
+ /* If this string contains the value according to
+ css::deployment::Prerequisites or "0". That is, if
+ the value is > 0 then
+ the call to XPackage::checkPrerequisites failed.
+ In this case the extension must not be registered.
+ */
+ OUString failedPrerequisites;
+ };
+
+ typedef std::vector< std::pair< OUString, Data > > Entries;
+
+ ActivePackages();
+
+ explicit ActivePackages(OUString const & url);
+
+ ~ActivePackages();
+
+ bool has(OUString const & id, OUString const & fileName)
+ const;
+
+ bool get(
+ Data * data, OUString const & id,
+ OUString const & fileName) const;
+
+ Entries getEntries() const;
+
+ void put(OUString const & id, Data const & value);
+
+ void erase(OUString const & id, OUString const & fileName);
+
+private:
+ ActivePackages(ActivePackages const &) = delete;
+ ActivePackages& operator =(ActivePackages const &) = delete;
+#if HAVE_FEATURE_EXTENSIONS
+ ::dp_misc::PersistentMap m_map;
+#endif
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_commandenvironments.cxx b/desktop/source/deployment/manager/dp_commandenvironments.cxx
new file mode 100644
index 000000000..0a25e042f
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_commandenvironments.cxx
@@ -0,0 +1,246 @@
+/* -*- 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 <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <utility>
+#include "dp_commandenvironments.hxx"
+#include <osl/diagnose.h>
+
+namespace deployment = com::sun::star::deployment;
+namespace task = com::sun::star::task;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+
+using ::com::sun::star::uno::Reference;
+
+namespace dp_manager {
+
+BaseCommandEnv::BaseCommandEnv()
+{
+}
+
+BaseCommandEnv::BaseCommandEnv(
+ Reference< task::XInteractionHandler> const & handler)
+ : m_forwardHandler(handler)
+{
+}
+
+BaseCommandEnv::~BaseCommandEnv()
+{
+}
+// XCommandEnvironment
+
+Reference<task::XInteractionHandler> BaseCommandEnv::getInteractionHandler()
+{
+ return this;
+}
+
+
+Reference<ucb::XProgressHandler> BaseCommandEnv::getProgressHandler()
+{
+ return this;
+}
+
+void BaseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & /*xRequest*/ )
+{
+}
+
+void BaseCommandEnv::handle_(bool approve,
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ if (!approve)
+ {
+ //not handled so far -> forwarding
+ if (m_forwardHandler.is())
+ m_forwardHandler->handle(xRequest);
+ else
+ return; //cannot handle
+ }
+ else
+ {
+ // select:
+ uno::Sequence< Reference< task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ Reference< task::XInteractionContinuation > const * pConts =
+ conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ Reference< task::XInteractionApprove > xInteractionApprove(
+ pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ }
+ }
+
+}
+
+// XProgressHandler
+void BaseCommandEnv::push( uno::Any const & /*Status*/ )
+{
+}
+
+void BaseCommandEnv::update( uno::Any const & /*Status */)
+{
+}
+
+void BaseCommandEnv::pop()
+{
+}
+
+
+TmpRepositoryCommandEnv::TmpRepositoryCommandEnv()
+{
+}
+
+TmpRepositoryCommandEnv::TmpRepositoryCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler):
+ BaseCommandEnv(handler)
+{
+}
+// XInteractionHandler
+void TmpRepositoryCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::VersionException verExc;
+ deployment::LicenseException licExc;
+ deployment::InstallException instExc;
+
+ bool approve = false;
+
+ if ((request >>= verExc)
+ || (request >>= licExc)
+ || (request >>= instExc))
+ {
+ approve = true;
+ }
+
+ handle_(approve, xRequest);
+}
+
+
+LicenseCommandEnv::LicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler,
+ bool bSuppressLicense,
+ OUString repository):
+ BaseCommandEnv(handler), m_repository(std::move(repository)),
+ m_bSuppressLicense(bSuppressLicense)
+{
+}
+// XInteractionHandler
+void LicenseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+
+ bool approve = false;
+
+ if (request >>= licExc)
+ {
+ if (m_bSuppressLicense
+ || m_repository == "bundled"
+ || licExc.AcceptBy == "admin")
+ {
+ //always approve in bundled case, because we do not support
+ //showing licenses anyway.
+ //The "admin" already accepted the license when installing the
+ // shared extension
+ approve = true;
+ }
+ }
+
+ handle_(approve, xRequest);
+}
+
+
+NoLicenseCommandEnv::NoLicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler):
+ BaseCommandEnv(handler)
+{
+}
+// XInteractionHandler
+void NoLicenseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+
+ bool approve = false;
+
+ if (request >>= licExc)
+ {
+ approve = true;
+ }
+ handle_(approve, xRequest);
+}
+
+SilentCheckPrerequisitesCommandEnv::SilentCheckPrerequisitesCommandEnv()
+{
+}
+
+void SilentCheckPrerequisitesCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+ deployment::PlatformException platformExc;
+ deployment::DependencyException depExc;
+
+ if (request >>= licExc)
+ {
+ handle_(true, xRequest); // approve = true
+ }
+ else if ((request >>= platformExc)
+ || (request >>= depExc))
+ {
+ m_Exception = request;
+ }
+ else
+ {
+ m_UnknownException = request;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_commandenvironments.hxx b/desktop/source/deployment/manager/dp_commandenvironments.hxx
new file mode 100644
index 000000000..6533d45b4
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_commandenvironments.hxx
@@ -0,0 +1,139 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+
+namespace dp_manager {
+
+/**
+ This command environment is to be used when an extension is temporarily
+ stored in the "tmp" repository. It prevents all kind of user interaction.
+ */
+class BaseCommandEnv
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler,
+ css::ucb::XProgressHandler >
+{
+ css::uno::Reference< css::task::XInteractionHandler> m_forwardHandler;
+protected:
+ void handle_(bool approve,
+ css::uno::Reference< css::task::XInteractionRequest> const & xRequest );
+public:
+ virtual ~BaseCommandEnv() override;
+ BaseCommandEnv();
+ explicit BaseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+class TmpRepositoryCommandEnv : public BaseCommandEnv
+{
+public:
+ TmpRepositoryCommandEnv();
+ explicit TmpRepositoryCommandEnv(css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/** this class is for use in XPackageManager::synchronize.
+
+ It handles particular license cases.
+ */
+class LicenseCommandEnv : public BaseCommandEnv
+{
+private:
+ OUString m_repository;
+ bool m_bSuppressLicense;
+public:
+ LicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler,
+ bool bSuppressLicense,
+ OUString repository);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/** this class is for use in XPackageManager::checkPrerequisites
+
+ It always prohibits a license interaction
+ */
+class NoLicenseCommandEnv : public BaseCommandEnv
+{
+
+public:
+ explicit NoLicenseCommandEnv(css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/* For use in XExtensionManager::addExtension in the call to
+ XPackage::checkPrerequisites
+ It prevents all user interactions. The license is always accepted.
+ It remembers if there was a platform or a dependency exception in
+ the member m_bException. if there was any other exception then m_bUnknownException
+ is set.
+
+ */
+class SilentCheckPrerequisitesCommandEnv : public BaseCommandEnv
+{
+public:
+ SilentCheckPrerequisitesCommandEnv();
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // Set to true if a PlatformException or a DependencyException were handled.
+ css::uno::Any m_Exception;
+ // Set to true if an unknown exception was handled.
+ css::uno::Any m_UnknownException;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_extensionmanager.cxx b/desktop/source/deployment/manager/dp_extensionmanager.cxx
new file mode 100644
index 000000000..fc7816f6a
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_extensionmanager.cxx
@@ -0,0 +1,1432 @@
+/* -*- 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 <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <rtl/bootstrap.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/deployment/thePackageManagerFactory.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/beans/Ambiguous.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <xmlscript/xml_helper.hxx>
+#include <osl/diagnose.h>
+#include <dp_interact.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <dp_identifier.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include "dp_extensionmanager.hxx"
+#include "dp_commandenvironments.hxx"
+#include "dp_properties.hxx"
+
+#include <vector>
+#include <algorithm>
+#include <set>
+#include <string_view>
+
+namespace lang = com::sun::star::lang;
+namespace task = com::sun::star::task;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+namespace beans = com::sun::star::beans;
+namespace util = com::sun::star::util;
+
+using ::com::sun::star::uno::Reference;
+
+namespace {
+
+struct CompIdentifiers
+{
+ bool operator() (std::vector<Reference<css::deployment::XPackage> > const & a,
+ std::vector<Reference<css::deployment::XPackage> > const & b)
+ {
+ return getName(a).compareTo(getName(b)) < 0;
+ }
+
+ static OUString getName(std::vector<Reference<css::deployment::XPackage> > const & a);
+};
+
+OUString CompIdentifiers::getName(std::vector<Reference<css::deployment::XPackage> > const & a)
+{
+ OSL_ASSERT(a.size() == 3);
+ //get the first non-null reference
+ Reference<css::deployment::XPackage> extension;
+ for (auto const& elem : a)
+ {
+ if (elem.is())
+ {
+ extension = elem;
+ break;
+ }
+ }
+ OSL_ASSERT(extension.is());
+ return extension->getDisplayName();
+}
+
+void writeLastModified(OUString & url, Reference<ucb::XCommandEnvironment> const & xCmdEnv, Reference< uno::XComponentContext > const & xContext)
+{
+ //Write the lastmodified file
+ try {
+ ::rtl::Bootstrap::expandMacros(url);
+ ::ucbhelper::Content ucbStamp(url, xCmdEnv, xContext);
+ dp_misc::erase_path( url, xCmdEnv );
+ OString stamp("1" );
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ ucbStamp.writeStream( xData, true /* replace existing */ );
+ }
+ catch(...)
+ {
+ uno::Any exc(::cppu::getCaughtException());
+ throw css::deployment::DeploymentException("Failed to update" + url, nullptr, exc);
+ }
+}
+
+class ExtensionRemoveGuard
+{
+ css::uno::Reference<css::deployment::XPackage> m_extension;
+ css::uno::Reference<css::deployment::XPackageManager> m_xPackageManager;
+
+public:
+ ExtensionRemoveGuard(){};
+ ExtensionRemoveGuard(
+ css::uno::Reference<css::deployment::XPackage> extension,
+ css::uno::Reference<css::deployment::XPackageManager> xPackageManager):
+ m_extension(std::move(extension)), m_xPackageManager(std::move(xPackageManager)) {}
+ ~ExtensionRemoveGuard();
+
+ void set(css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageManager) {
+ m_extension = extension;
+ m_xPackageManager = xPackageManager;
+ }
+};
+
+ExtensionRemoveGuard::~ExtensionRemoveGuard()
+{
+ try {
+ OSL_ASSERT(!(m_extension.is() && !m_xPackageManager.is()));
+ if (m_xPackageManager.is() && m_extension.is())
+ m_xPackageManager->removePackage(
+ dp_misc::getIdentifier(m_extension), OUString(),
+ css::uno::Reference<css::task::XAbortChannel>(),
+ css::uno::Reference<css::ucb::XCommandEnvironment>());
+ } catch (...) {
+ OSL_ASSERT(false);
+ }
+}
+
+}
+
+namespace dp_manager {
+
+//ToDo: bundled extension
+ExtensionManager::ExtensionManager( Reference< uno::XComponentContext > const& xContext) :
+ ::cppu::WeakComponentImplHelper< css::deployment::XExtensionManager, css::lang::XServiceInfo >(m_aMutex)
+ , m_xContext(xContext)
+{
+ m_xPackageManagerFactory = css::deployment::thePackageManagerFactory::get(m_xContext);
+ OSL_ASSERT(m_xPackageManagerFactory.is());
+
+ m_repositoryNames.emplace_back("user");
+ m_repositoryNames.emplace_back("shared");
+ m_repositoryNames.emplace_back("bundled");
+}
+
+ExtensionManager::~ExtensionManager()
+{
+}
+
+// XServiceInfo
+OUString ExtensionManager::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.ExtensionManager";
+}
+
+sal_Bool ExtensionManager::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > ExtensionManager::getSupportedServiceNames()
+{
+ // a private one:
+ return { "com.sun.star.comp.deployment.ExtensionManager" };
+}
+
+Reference<css::deployment::XPackageManager> ExtensionManager::getUserRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("user");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getSharedRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("shared");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getBundledRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("bundled");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getTmpRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("tmp");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getBakRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("bak");
+}
+
+Reference<task::XAbortChannel> ExtensionManager::createAbortChannel()
+{
+ return new dp_misc::AbortChannel;
+}
+
+css::uno::Reference<css::deployment::XPackageManager>
+ExtensionManager::getPackageManager(std::u16string_view repository)
+{
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ if (repository == u"user")
+ xPackageManager = getUserRepository();
+ else if (repository == u"shared")
+ xPackageManager = getSharedRepository();
+ else if (repository == u"bundled")
+ xPackageManager = getBundledRepository();
+ else if (repository == u"tmp")
+ xPackageManager = getTmpRepository();
+ else if (repository == u"bak")
+ xPackageManager = getBakRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+ return xPackageManager;
+}
+
+/*
+ Enters the XPackage objects into a map. They must be all from the
+ same repository. The value type of the map is a vector, where each vector
+ represents an extension with a particular identifier. The first member
+ represents the user extension, the second the shared extension and the
+ third the bundled extension.
+ */
+void ExtensionManager::addExtensionsToMap(
+ id2extensions & mapExt,
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt,
+ std::u16string_view repository)
+{
+ //Determine the index in the vector where these extensions are to be
+ //added.
+ int index = 0;
+ for (auto const& repositoryName : m_repositoryNames)
+ {
+ if (repositoryName == repository)
+ break;
+ ++index;
+ }
+
+ for (const Reference<css::deployment::XPackage>& xExtension : seqExt)
+ {
+ OUString id = dp_misc::getIdentifier(xExtension);
+ id2extensions::iterator ivec = mapExt.find(id);
+ if (ivec == mapExt.end())
+ {
+ std::vector<Reference<css::deployment::XPackage> > vec(3);
+ vec[index] = xExtension;
+ mapExt[id] = vec;
+ }
+ else
+ {
+ ivec->second[index] = xExtension;
+ }
+ }
+}
+
+/*
+ returns a list containing extensions with the same identifier from
+ all repositories (user, shared, bundled). If one repository does not
+ have this extension, then the list contains an empty Reference. The list
+ is ordered according to the priority of the repositories:
+ 1. user
+ 2. shared
+ 3. bundled
+
+ The number of elements is always three, unless the number of repository
+ changes.
+ */
+std::vector<Reference<css::deployment::XPackage> >
+ ExtensionManager::getExtensionsWithSameId(
+ OUString const & identifier, OUString const & fileName)
+
+{
+ std::vector<Reference<css::deployment::XPackage> > extensionList;
+ Reference<css::deployment::XPackageManager> lRepos[] = {
+ getUserRepository(), getSharedRepository(), getBundledRepository() };
+ for (int i(0); i != SAL_N_ELEMENTS(lRepos); ++i)
+ {
+ Reference<css::deployment::XPackage> xPackage;
+ try
+ {
+ xPackage = lRepos[i]->getDeployedPackage(
+ identifier, fileName, Reference<ucb::XCommandEnvironment>());
+ }
+ catch(const lang::IllegalArgumentException &)
+ {
+ // thrown if the extension does not exist in this repository
+ }
+ extensionList.push_back(xPackage);
+ }
+ OSL_ASSERT(extensionList.size() == 3);
+ return extensionList;
+}
+
+uno::Sequence<Reference<css::deployment::XPackage> >
+ExtensionManager::getExtensionsWithSameIdentifier(
+ OUString const & identifier,
+ OUString const & fileName,
+ Reference< ucb::XCommandEnvironment> const & /*xCmdEnv*/ )
+{
+ try
+ {
+ std::vector<Reference<css::deployment::XPackage> > listExtensions =
+ getExtensionsWithSameId(identifier, fileName);
+ bool bHasExtension = false;
+
+ //throw an IllegalArgumentException if there is no extension at all.
+ for (auto const& extension : listExtensions)
+ bHasExtension |= extension.is();
+ if (!bHasExtension)
+ throw lang::IllegalArgumentException(
+ "Could not find extension: " + identifier + ", " + fileName,
+ static_cast<cppu::OWeakObject*>(this), -1);
+
+ return comphelper::containerToSequence(listExtensions);
+ }
+ catch ( const css::deployment::DeploymentException & )
+ {
+ throw;
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ throw;
+ }
+ catch (css::uno::RuntimeException &)
+ {
+ throw;
+ }
+ catch (...)
+ {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during getExtensionsWithSameIdentifier",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+bool ExtensionManager::isUserDisabled(
+ OUString const & identifier, OUString const & fileName)
+{
+ std::vector<Reference<css::deployment::XPackage> > listExtensions;
+
+ try {
+ listExtensions = getExtensionsWithSameId(identifier, fileName);
+ } catch ( const lang::IllegalArgumentException & ) {
+ }
+ OSL_ASSERT(listExtensions.size() == 3);
+
+ return isUserDisabled( ::comphelper::containerToSequence(listExtensions) );
+}
+
+bool ExtensionManager::isUserDisabled(
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExtSameId)
+{
+ OSL_ASSERT(seqExtSameId.getLength() == 3);
+ Reference<css::deployment::XPackage> const & userExtension = seqExtSameId[0];
+ if (userExtension.is())
+ {
+ beans::Optional<beans::Ambiguous<sal_Bool> > reg =
+ userExtension->isRegistered(Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ //If the value is ambiguous, then we assume that the extension
+ //is enabled, but something went wrong during enabling. We do not
+ //automatically disable user extensions.
+ if (reg.IsPresent &&
+ ! reg.Value.IsAmbiguous && ! reg.Value.Value)
+ return true;
+ }
+ return false;
+}
+
+/*
+ This method determines the active extension (XPackage.registerPackage) with a
+ particular identifier.
+
+ The parameter bUserDisabled determines if the user extension is disabled.
+
+ When the user repository contains an extension with the given identifier and
+ it is not disabled by the user, then it is always registered. Otherwise an
+ extension is only registered when there is no registered extension in one of
+ the repositories with a higher priority. That is, if the extension is from
+ the shared repository and an active extension with the same identifier is in
+ the user repository, then the extension is not registered. Similarly a
+ bundled extension is not registered if there is an active extension with the
+ same identifier in the shared or user repository.
+*/
+void ExtensionManager::activateExtension(
+ OUString const & identifier, OUString const & fileName,
+ bool bUserDisabled,
+ bool bStartup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ std::vector<Reference<css::deployment::XPackage> > listExtensions;
+ try {
+ listExtensions = getExtensionsWithSameId(identifier, fileName);
+ } catch (const lang::IllegalArgumentException &) {
+ }
+ OSL_ASSERT(listExtensions.size() == 3);
+
+ activateExtension(
+ ::comphelper::containerToSequence(listExtensions),
+ bUserDisabled, bStartup, xAbortChannel, xCmdEnv);
+
+ fireModified();
+}
+
+void ExtensionManager::activateExtension(
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt,
+ bool bUserDisabled,
+ bool bStartup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ bool bActive = false;
+ sal_Int32 len = seqExt.getLength();
+ for (sal_Int32 i = 0; i < len; i++)
+ {
+ Reference<css::deployment::XPackage> const & aExt = seqExt[i];
+ if (aExt.is())
+ {
+ //get the registration value of the current iteration
+ beans::Optional<beans::Ambiguous<sal_Bool> > optReg =
+ aExt->isRegistered(xAbortChannel, xCmdEnv);
+ //If nothing can be registered then break
+ if (!optReg.IsPresent)
+ break;
+
+ //Check if this is a disabled user extension,
+ if (i == 0 && bUserDisabled)
+ {
+ aExt->revokePackage(bStartup, xAbortChannel, xCmdEnv);
+ continue;
+ }
+
+ //If we have already determined an active extension then we must
+ //make sure to unregister all extensions with the same id in
+ //repositories with a lower priority
+ if (bActive)
+ {
+ aExt->revokePackage(bStartup, xAbortChannel, xCmdEnv);
+ }
+ else
+ {
+ //This is the first extension in the ordered list, which becomes
+ //the active extension
+ bActive = true;
+ //Register if not already done.
+ //reregister if the value is ambiguous, which indicates that
+ //something went wrong during last registration.
+ aExt->registerPackage(bStartup, xAbortChannel, xCmdEnv);
+ }
+ }
+ }
+}
+
+Reference<css::deployment::XPackage> ExtensionManager::backupExtension(
+ OUString const & identifier, OUString const & fileName,
+ Reference<css::deployment::XPackageManager> const & xPackageManager,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Reference<css::deployment::XPackage> xBackup;
+ Reference<ucb::XCommandEnvironment> tmpCmdEnv(
+ new TmpRepositoryCommandEnv(xCmdEnv->getInteractionHandler()));
+ Reference<css::deployment::XPackage> xOldExtension = xPackageManager->getDeployedPackage(
+ identifier, fileName, tmpCmdEnv);
+
+ if (xOldExtension.is())
+ {
+ xBackup = getTmpRepository()->addPackage(
+ xOldExtension->getURL(), uno::Sequence<beans::NamedValue>(),
+ OUString(), Reference<task::XAbortChannel>(), tmpCmdEnv);
+
+ OSL_ENSURE(xBackup.is(), "Failed to backup extension");
+ }
+ return xBackup;
+}
+
+//The supported package types are actually determined by the registry. However
+//creating a registry
+//(desktop/source/deployment/registry/dp_registry.cxx:PackageRegistryImpl) will
+//create all the backends, so that the registry can obtain from them the package
+//types. Creating the registry will also set up the registry folder containing
+//all the subfolders for the respective backends.
+//Because all repositories support the same backends, we can just delegate this
+//call to one of the repositories.
+uno::Sequence< Reference<css::deployment::XPackageTypeInfo> >
+ExtensionManager::getSupportedPackageTypes()
+{
+ return getUserRepository()->getSupportedPackageTypes();
+}
+//Do some necessary checks and user interaction. This function does not
+//acquire the extension manager mutex and that mutex must not be acquired
+//when this function is called. doChecksForAddExtension does synchronous
+//user interactions which may require acquiring the solar mutex.
+//Returns true if the extension can be installed.
+bool ExtensionManager::doChecksForAddExtension(
+ Reference<css::deployment::XPackageManager> const & xPackageMgr,
+ uno::Sequence<beans::NamedValue> const & properties,
+ css::uno::Reference<css::deployment::XPackage> const & xTmpExtension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<css::deployment::XPackage> & out_existingExtension )
+{
+ try
+ {
+ Reference<css::deployment::XPackage> xOldExtension;
+ const OUString sIdentifier = dp_misc::getIdentifier(xTmpExtension);
+ const OUString sFileName = xTmpExtension->getName();
+ const OUString sDisplayName = xTmpExtension->getDisplayName();
+ const OUString sVersion = xTmpExtension->getVersion();
+
+ try
+ {
+ xOldExtension = xPackageMgr->getDeployedPackage(
+ sIdentifier, sFileName, xCmdEnv);
+ out_existingExtension = xOldExtension;
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ }
+ bool bCanInstall = false;
+
+ //This part is not guarded against other threads removing, adding, disabling ...
+ //etc. the same extension.
+ //checkInstall is safe because it notifies the user if the extension is not yet
+ //installed in the same repository. Because addExtension has its own guard
+ //(m_addMutex), another thread cannot add the extension in the meantime.
+ //checkUpdate is called if the same extension exists in the same
+ //repository. The user is asked if they want to replace it. Another
+ //thread
+ //could already remove the extension. So asking the user was not
+ //necessary. No harm is done. The other thread may also ask the user
+ //if he wants to remove the extension. This depends on the
+ //XCommandEnvironment which it passes to removeExtension.
+ if (xOldExtension.is())
+ {
+ //throws a CommandFailedException if the user cancels
+ //the action.
+ checkUpdate(sVersion, sDisplayName,xOldExtension, xCmdEnv);
+ }
+ else
+ {
+ //throws a CommandFailedException if the user cancels
+ //the action.
+ checkInstall(sDisplayName, xCmdEnv);
+ }
+ //Prevent showing the license if requested.
+ Reference<ucb::XCommandEnvironment> _xCmdEnv(xCmdEnv);
+ ExtensionProperties props(std::u16string_view(), properties, Reference<ucb::XCommandEnvironment>(), m_xContext);
+
+ dp_misc::DescriptionInfoset info(dp_misc::getDescriptionInfoset(xTmpExtension->getURL()));
+ const ::std::optional<dp_misc::SimpleLicenseAttributes> licenseAttributes =
+ info.getSimpleLicenseAttributes();
+
+ if (licenseAttributes && licenseAttributes->suppressIfRequired
+ && props.isSuppressedLicense())
+ _xCmdEnv.set(new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler()));
+
+ bCanInstall = xTmpExtension->checkPrerequisites(
+ xAbortChannel, _xCmdEnv, xOldExtension.is() || props.isExtensionUpdate()) == 0;
+
+ return bCanInstall;
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception in doChecksForAddExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ } catch (...) {
+ throw uno::RuntimeException(
+ "Extension Manager: unexpected exception in doChecksForAddExtension",
+ static_cast<OWeakObject*>(this));
+ }
+}
+
+// Only add to shared and user repository
+Reference<css::deployment::XPackage> ExtensionManager::addExtension(
+ OUString const & url, uno::Sequence<beans::NamedValue> const & properties,
+ OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Reference<css::deployment::XPackage> xNewExtension;
+ //Determine the repository to use
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ if (repository == "user")
+ xPackageManager = getUserRepository();
+ else if (repository == "shared")
+ xPackageManager = getSharedRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+ //We must make sure that the xTmpExtension is not create twice, because this
+ //would remove the first one.
+ ::osl::MutexGuard addGuard(m_addMutex);
+
+ Reference<css::deployment::XPackageManager> xTmpRepository(getTmpRepository());
+ // make sure xTmpRepository is alive as long as xTmpExtension is; as
+ // the "tmp" manager is only held weakly by m_xPackageManagerFactory, it
+ // could otherwise be disposed early, which would in turn dispose
+ // xTmpExtension's PackageRegistryBackend behind its back
+ Reference<css::deployment::XPackage> xTmpExtension(
+ xTmpRepository->addPackage(
+ url, uno::Sequence<beans::NamedValue>(), OUString(), xAbortChannel,
+ new TmpRepositoryCommandEnv()));
+ if (!xTmpExtension.is()) {
+ throw css::deployment::DeploymentException(
+ ("Extension Manager: Failed to create temporary XPackage for url: "
+ + url),
+ static_cast<OWeakObject*>(this), uno::Any());
+ }
+
+ //Make sure the extension is removed from the tmp repository in case
+ //of an exception
+ ExtensionRemoveGuard tmpExtensionRemoveGuard(xTmpExtension, getTmpRepository());
+ ExtensionRemoveGuard bakExtensionRemoveGuard;
+ const OUString sIdentifier = dp_misc::getIdentifier(xTmpExtension);
+ const OUString sFileName = xTmpExtension->getName();
+ Reference<css::deployment::XPackage> xOldExtension;
+ Reference<css::deployment::XPackage> xExtensionBackup;
+
+ uno::Any excOccurred2;
+ bool bCanInstall = doChecksForAddExtension(
+ xPackageManager,
+ properties,
+ xTmpExtension,
+ xAbortChannel,
+ xCmdEnv,
+ xOldExtension );
+
+ {
+ bool bUserDisabled = false;
+ // In this guarded section (getMutex) we must not use the argument xCmdEnv
+ // because it may bring up dialogs (XInteractionHandler::handle) this
+ // may potentially deadlock. See issue
+ // http://qa.openoffice.org/issues/show_bug.cgi?id=114933
+ // By not providing xCmdEnv the underlying APIs will throw an exception if
+ // the XInteractionRequest cannot be handled.
+ ::osl::MutexGuard guard(m_aMutex);
+
+ if (bCanInstall)
+ {
+ try
+ {
+ bUserDisabled = isUserDisabled(sIdentifier, sFileName);
+ if (xOldExtension.is())
+ {
+ try
+ {
+ xOldExtension->revokePackage(
+ false, xAbortChannel, Reference<ucb::XCommandEnvironment>());
+ //save the old user extension in case the user aborts
+ xExtensionBackup = getBakRepository()->importExtension(
+ xOldExtension, Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ bakExtensionRemoveGuard.set(xExtensionBackup, getBakRepository());
+ }
+ catch (const lang::DisposedException &)
+ {
+ //Another thread might have removed the extension meanwhile
+ }
+ }
+ //check again dependencies but prevent user interaction,
+ //We can disregard the license, because the user must have already
+ //accepted it, when we called checkPrerequisites the first time
+ rtl::Reference<SilentCheckPrerequisitesCommandEnv> pSilentCommandEnv =
+ new SilentCheckPrerequisitesCommandEnv();
+
+ sal_Int32 failedPrereq = xTmpExtension->checkPrerequisites(
+ xAbortChannel, pSilentCommandEnv, true);
+ if (failedPrereq == 0)
+ {
+ xNewExtension = xPackageManager->addPackage(
+ url, properties, OUString(), xAbortChannel,
+ Reference<ucb::XCommandEnvironment>());
+ //If we add a user extension and there is already one which was
+ //disabled by a user, then the newly installed one is enabled. If we
+ //add to another repository then the user extension remains
+ //disabled.
+ bool bUserDisabled2 = bUserDisabled;
+ if (repository == "user")
+ bUserDisabled2 = false;
+
+ // pass the two values via variables to workaround gcc-4.3.4 specific bug (bnc#655912)
+ OUString sNewExtensionIdentifier = dp_misc::getIdentifier(xNewExtension);
+ OUString sNewExtensionFileName = xNewExtension->getName();
+
+ activateExtension(
+ sNewExtensionIdentifier, sNewExtensionFileName,
+ bUserDisabled2, false, xAbortChannel,
+ Reference<ucb::XCommandEnvironment>());
+
+ // if reached this section,
+ // this means that either the licensedialog.ui didn't popup,
+ // or user accepted the license agreement. otherwise
+ // no need to call fireModified() because user declined
+ // the license agreement therefore no change made.
+ try
+ {
+ fireModified();
+
+ }catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: Exception on fireModified() "
+ "in the scope of 'if (failedPrereq == 0)'",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ } catch (...) {
+ throw uno::RuntimeException(
+ "Extension Manager: RuntimeException on fireModified() "
+ "in the scope of 'if (failedPrereq == 0)'",
+ static_cast<OWeakObject*>(this));
+ }
+ }
+ else
+ {
+ if (pSilentCommandEnv->m_Exception.hasValue())
+ ::cppu::throwException(pSilentCommandEnv->m_Exception);
+ else if ( pSilentCommandEnv->m_UnknownException.hasValue())
+ ::cppu::throwException(pSilentCommandEnv->m_UnknownException);
+ else
+ throw css::deployment::DeploymentException (
+ "Extension Manager: exception during addExtension, ckeckPrerequisites failed",
+ static_cast<OWeakObject*>(this), uno::Any());
+ }
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred2 = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during addExtension, url: "
+ + url, static_cast<OWeakObject*>(this), excOccurred2);
+ excOccurred2 <<= exc;
+ }
+ }
+
+ if (excOccurred2.hasValue())
+ {
+ //It does not matter what exception is thrown. We try to
+ //recover the original status.
+ //If the user aborted installation then a ucb::CommandAbortedException
+ //is thrown.
+ //Use a private AbortChannel so the user cannot interrupt.
+ try
+ {
+ if (xExtensionBackup.is())
+ {
+ xPackageManager->importExtension(
+ xExtensionBackup, Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ }
+ activateExtension(
+ sIdentifier, sFileName, bUserDisabled, false,
+ Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred2);
+ }
+ } // leaving the guarded section (getMutex())
+
+ return xNewExtension;
+}
+
+void ExtensionManager::removeExtension(
+ OUString const & identifier, OUString const & fileName,
+ OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ uno::Any excOccurred1;
+ Reference<css::deployment::XPackage> xExtensionBackup;
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ bool bUserDisabled = false;
+ ::osl::MutexGuard guard(m_aMutex);
+ try
+ {
+//Determine the repository to use
+ if (repository == "user")
+ xPackageManager = getUserRepository();
+ else if (repository == "shared")
+ xPackageManager = getSharedRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ bUserDisabled = isUserDisabled(identifier, fileName);
+ //Backup the extension, in case the user cancels the action
+ xExtensionBackup = backupExtension(
+ identifier, fileName, xPackageManager, xCmdEnv);
+
+ //revoke the extension if it is active
+ Reference<css::deployment::XPackage> xOldExtension =
+ xPackageManager->getDeployedPackage(
+ identifier, fileName, xCmdEnv);
+ xOldExtension->revokePackage(false, xAbortChannel, xCmdEnv);
+
+ xPackageManager->removePackage(
+ identifier, fileName, xAbortChannel, xCmdEnv);
+ activateExtension(identifier, fileName, bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ fireModified();
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred1 = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during removeExtension",
+ static_cast<OWeakObject*>(this), excOccurred1);
+ excOccurred1 <<= exc;
+ }
+
+ if (excOccurred1.hasValue())
+ {
+ //User aborted installation, restore the previous situation.
+ //Use a private AbortChannel so the user cannot interrupt.
+ try
+ {
+ Reference<ucb::XCommandEnvironment> tmpCmdEnv(
+ new TmpRepositoryCommandEnv(xCmdEnv->getInteractionHandler()));
+ if (xExtensionBackup.is())
+ {
+ xPackageManager->importExtension(
+ xExtensionBackup, Reference<task::XAbortChannel>(),
+ tmpCmdEnv);
+ activateExtension(
+ identifier, fileName, bUserDisabled, false,
+ Reference<task::XAbortChannel>(),
+ tmpCmdEnv);
+
+ getTmpRepository()->removePackage(
+ dp_misc::getIdentifier(xExtensionBackup),
+ xExtensionBackup->getName(), xAbortChannel, xCmdEnv);
+ fireModified();
+ }
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred1);
+ }
+
+ if (xExtensionBackup.is())
+ getTmpRepository()->removePackage(
+ dp_misc::getIdentifier(xExtensionBackup),
+ xExtensionBackup->getName(), xAbortChannel, xCmdEnv);
+}
+
+// Only enable extensions from shared and user repository
+void ExtensionManager::enableExtension(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ ::osl::MutexGuard guard(m_aMutex);
+ bool bUserDisabled = false;
+ uno::Any excOccurred;
+ try
+ {
+ if (!extension.is())
+ return;
+ OUString repository = extension->getRepositoryName();
+ if (repository != "user")
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ bUserDisabled = isUserDisabled(dp_misc::getIdentifier(extension),
+ extension->getName());
+
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), false, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ excOccurred <<= exc;
+ }
+
+ if (!excOccurred.hasValue())
+ return;
+
+ try
+ {
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred);
+}
+
+sal_Int32 ExtensionManager::checkPrerequisitesAndEnable(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ try
+ {
+ if (!extension.is())
+ return 0;
+ ::osl::MutexGuard guard(m_aMutex);
+ sal_Int32 ret = 0;
+ Reference<css::deployment::XPackageManager> mgr =
+ getPackageManager(extension->getRepositoryName());
+ ret = mgr->checkPrerequisites(extension, xAbortChannel, xCmdEnv);
+ if (ret)
+ {
+ //There are some unfulfilled prerequisites, try to revoke
+ extension->revokePackage(false, xAbortChannel, xCmdEnv);
+ }
+ const OUString id(dp_misc::getIdentifier(extension));
+ activateExtension(id, extension->getName(),
+ isUserDisabled(id, extension->getName()), false,
+ xAbortChannel, xCmdEnv);
+ return ret;
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during disableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ }
+}
+
+void ExtensionManager::disableExtension(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ ::osl::MutexGuard guard(m_aMutex);
+ uno::Any excOccurred;
+ bool bUserDisabled = false;
+ try
+ {
+ if (!extension.is())
+ return;
+ const OUString repository( extension->getRepositoryName());
+ if (repository != "user")
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ const OUString id(dp_misc::getIdentifier(extension));
+ bUserDisabled = isUserDisabled(id, extension->getName());
+
+ activateExtension(id, extension->getName(), true, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during disableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ excOccurred <<= exc;
+ }
+
+ if (!excOccurred.hasValue())
+ return;
+
+ try
+ {
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred);
+}
+
+uno::Sequence< Reference<css::deployment::XPackage> >
+ ExtensionManager::getDeployedExtensions(
+ OUString const & repository,
+ Reference<task::XAbortChannel> const &xAbort,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ return getPackageManager(repository)->getDeployedPackages(
+ xAbort, xCmdEnv);
+}
+
+Reference<css::deployment::XPackage>
+ ExtensionManager::getDeployedExtension(
+ OUString const & repository,
+ OUString const & identifier,
+ OUString const & filename,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ return getPackageManager(repository)->getDeployedPackage(
+ identifier, filename, xCmdEnv);
+}
+
+uno::Sequence< uno::Sequence<Reference<css::deployment::XPackage> > >
+ ExtensionManager::getAllExtensions(
+ Reference<task::XAbortChannel> const & xAbort,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ id2extensions mapExt;
+
+ uno::Sequence<Reference<css::deployment::XPackage> > userExt =
+ getUserRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, userExt, u"user");
+ uno::Sequence<Reference<css::deployment::XPackage> > sharedExt =
+ getSharedRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, sharedExt, u"shared");
+ uno::Sequence<Reference<css::deployment::XPackage> > bundledExt =
+ getBundledRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, bundledExt, u"bundled");
+
+ // Create the tmp repository to trigger its clean up (deletion
+ // of old temporary data.)
+ getTmpRepository();
+
+ //copy the values of the map to a vector for sorting
+ std::vector< std::vector<Reference<css::deployment::XPackage> > >
+ vecExtensions;
+ for (auto const& elem : mapExt)
+ vecExtensions.push_back(elem.second);
+
+ //sort the element according to the identifier
+ std::sort(vecExtensions.begin(), vecExtensions.end(), CompIdentifiers());
+
+ sal_Int32 j = 0;
+ uno::Sequence< uno::Sequence<Reference<css::deployment::XPackage> > > seqSeq(vecExtensions.size());
+ auto seqSeqRange = asNonConstRange(seqSeq);
+ for (auto const& elem : vecExtensions)
+ {
+ seqSeqRange[j++] = comphelper::containerToSequence(elem);
+ }
+ return seqSeq;
+
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+// Only to be called from unopkg or soffice bootstrap (with force=true in the
+// latter case):
+void ExtensionManager::reinstallDeployedExtensions(
+ sal_Bool force, OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ Reference<css::deployment::XPackageManager>
+ xPackageManager = getPackageManager(repository);
+
+ std::set< OUString > disabledExts;
+ {
+ const uno::Sequence< Reference<css::deployment::XPackage> > extensions(
+ xPackageManager->getDeployedPackages(xAbortChannel, xCmdEnv));
+ for ( const Reference<css::deployment::XPackage>& package : extensions )
+ {
+ try
+ {
+ beans::Optional< beans::Ambiguous< sal_Bool > > registered(
+ package->isRegistered(xAbortChannel, xCmdEnv));
+ if (registered.IsPresent &&
+ !(registered.Value.IsAmbiguous ||
+ registered.Value.Value))
+ {
+ const OUString id = dp_misc::getIdentifier(package);
+ OSL_ASSERT(!id.isEmpty());
+ disabledExts.insert(id);
+ }
+ }
+ catch (const lang::DisposedException &)
+ {
+ }
+ }
+ }
+
+ ::osl::MutexGuard guard(m_aMutex);
+ xPackageManager->reinstallDeployedPackages(
+ force, xAbortChannel, xCmdEnv);
+ //We must sync here, otherwise we will get exceptions when extensions
+ //are removed.
+ dp_misc::syncRepositories(force, xCmdEnv);
+ const uno::Sequence< Reference<css::deployment::XPackage> > extensions(
+ xPackageManager->getDeployedPackages(xAbortChannel, xCmdEnv));
+
+ for ( const Reference<css::deployment::XPackage>& package : extensions )
+ {
+ try
+ {
+ const OUString id = dp_misc::getIdentifier(package);
+ const OUString fileName = package->getName();
+ OSL_ASSERT(!id.isEmpty());
+ activateExtension(
+ id, fileName, disabledExts.find(id) != disabledExts.end(),
+ true, xAbortChannel, xCmdEnv );
+ }
+ catch (const lang::DisposedException &)
+ {
+ }
+ }
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+sal_Bool ExtensionManager::synchronize(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ ::osl::MutexGuard guard(m_aMutex);
+ OUString sSynchronizingShared(StrSyncRepository());
+ sSynchronizingShared = sSynchronizingShared.replaceAll("%NAME", "shared");
+ dp_misc::ProgressLevel progressShared(xCmdEnv, sSynchronizingShared);
+ bool bModified = getSharedRepository()->synchronize(xAbortChannel, xCmdEnv);
+ progressShared.update("\n\n");
+
+ OUString sSynchronizingBundled(StrSyncRepository());
+ sSynchronizingBundled = sSynchronizingBundled.replaceAll("%NAME", "bundled");
+ dp_misc::ProgressLevel progressBundled(xCmdEnv, sSynchronizingBundled);
+ bModified |= static_cast<bool>(getBundledRepository()->synchronize(xAbortChannel, xCmdEnv));
+ progressBundled.update("\n\n");
+
+ //Always determine the active extension.
+ //TODO: Is this still necessary? (It used to be necessary for the
+ // first-start optimization: The setup created the registration data
+ // for the bundled extensions (share/prereg/bundled) which was copied to
+ // the user installation when a user started OOo for the first time
+ // after running setup. All bundled extensions were registered at that
+ // moment. However, extensions with the same identifier could be in the
+ // shared or user repository, in which case the respective bundled
+ // extensions had to be revoked.)
+ try
+ {
+ const uno::Sequence<uno::Sequence<Reference<css::deployment::XPackage> > >
+ seqSeqExt = getAllExtensions(xAbortChannel, xCmdEnv);
+ for (uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt : seqSeqExt)
+ {
+ activateExtension(seqExt, isUserDisabled(seqExt), true,
+ xAbortChannel, xCmdEnv);
+ }
+ }
+ catch (...)
+ {
+ //We catch the exception, so we can write the lastmodified file
+ //so we will no repeat this every time OOo starts.
+ OSL_FAIL("Extensions Manager: synchronize");
+ }
+ OUString lastSyncBundled("$BUNDLED_EXTENSIONS_USER/lastsynchronized");
+ writeLastModified(lastSyncBundled, xCmdEnv, m_xContext);
+ OUString lastSyncShared("$SHARED_EXTENSIONS_USER/lastsynchronized");
+ writeLastModified(lastSyncShared, xCmdEnv, m_xContext);
+ return bModified;
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception in synchronize",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+// Notify the user when a new extension is to be installed. This is only the
+// case when one uses the system integration to install an extension (double
+// clicking on .oxt file etc.)). The function must only be called if there is no
+// extension with the same identifier already deployed. Then the checkUpdate
+// function will inform the user that the extension is about to be installed In
+// case the user cancels the installation a CommandFailed exception is
+// thrown.
+void ExtensionManager::checkInstall(
+ OUString const & displayName,
+ Reference<ucb::XCommandEnvironment> const & cmdEnv)
+{
+ uno::Any request(
+ css::deployment::InstallException(
+ "Extension " + displayName +
+ " is about to be installed.",
+ static_cast<OWeakObject *>(this), displayName));
+ bool approve = false, abort = false;
+ if (! dp_misc::interactContinuation(
+ request, cppu::UnoType<task::XInteractionApprove>::get(),
+ cmdEnv, &approve, &abort ))
+ {
+ OSL_ASSERT( !approve && !abort );
+ throw css::deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + displayName,
+ static_cast<OWeakObject *>(this), request );
+ }
+ if (abort || !approve)
+ throw ucb::CommandFailedException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + displayName,
+ static_cast<OWeakObject *>(this), request );
+}
+
+/* The function will make the user interaction in case there is an extension
+installed with the same id. This function may only be called if there is already
+an extension.
+*/
+void ExtensionManager::checkUpdate(
+ OUString const & newVersion,
+ OUString const & newDisplayName,
+ Reference<css::deployment::XPackage> const & oldExtension,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ // package already deployed, interact --force:
+ uno::Any request(
+ (css::deployment::VersionException(
+ DpResId(
+ RID_STR_PACKAGE_ALREADY_ADDED ) + newDisplayName,
+ static_cast<OWeakObject *>(this), newVersion, newDisplayName,
+ oldExtension ) ) );
+ bool replace = false, abort = false;
+ if (! dp_misc::interactContinuation(
+ request, cppu::UnoType<task::XInteractionApprove>::get(),
+ xCmdEnv, &replace, &abort )) {
+ OSL_ASSERT( !replace && !abort );
+ throw css::deployment::DeploymentException(
+ DpResId(
+ RID_STR_ERROR_WHILE_ADDING) + newDisplayName,
+ static_cast<OWeakObject *>(this), request );
+ }
+ if (abort || !replace)
+ throw ucb::CommandFailedException(
+ DpResId(
+ RID_STR_PACKAGE_ALREADY_ADDED) + newDisplayName,
+ static_cast<OWeakObject *>(this), request );
+}
+
+uno::Sequence<Reference<css::deployment::XPackage> > SAL_CALL
+ExtensionManager::getExtensionsWithUnacceptedLicenses(
+ OUString const & repository,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ Reference<css::deployment::XPackageManager>
+ xPackageManager = getPackageManager(repository);
+ ::osl::MutexGuard guard(m_aMutex);
+ return xPackageManager->getExtensionsWithUnacceptedLicenses(xCmdEnv);
+}
+
+sal_Bool ExtensionManager::isReadOnlyRepository(OUString const & repository)
+{
+ return getPackageManager(repository)->isReadOnly();
+}
+
+
+// XModifyBroadcaster
+
+void ExtensionManager::addModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void ExtensionManager::removeModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void ExtensionManager::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "ExtensionManager instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+void ExtensionManager::fireModified()
+{
+ ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer(
+ cppu::UnoType<util::XModifyListener>::get() );
+ if (pContainer != nullptr) {
+ pContainer->forEach<util::XModifyListener>(
+ [this] (uno::Reference<util::XModifyListener> const& xListener)
+ { return xListener->modified(lang::EventObject(static_cast<OWeakObject *>(this))); });
+ }
+}
+
+} // namespace dp_manager
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_ExtensionManager_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new dp_manager::ExtensionManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_extensionmanager.hxx b/desktop/source/deployment/manager/dp_extensionmanager.hxx
new file mode 100644
index 000000000..0e08314c3
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_extensionmanager.hxx
@@ -0,0 +1,227 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <strings.hrc>
+#include <dp_shared.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <com/sun/star/deployment/XPackageManagerFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <osl/mutex.hxx>
+#include <vector>
+#include <unordered_map>
+
+namespace dp_manager {
+
+typedef std::unordered_map<
+ OUString,
+ std::vector<css::uno::Reference<css::deployment::XPackage> > > id2extensions;
+
+class ExtensionManager : private cppu::BaseMutex,
+ public ::cppu::WeakComponentImplHelper< css::deployment::XExtensionManager, css::lang::XServiceInfo >
+{
+public:
+ explicit ExtensionManager( css::uno::Reference< css::uno::XComponentContext >const& xContext);
+ virtual ~ExtensionManager() override;
+
+ void check();
+ void fireModified();
+
+public:
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+// XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+ virtual void SAL_CALL removeModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+
+//XExtensionManager
+ virtual css::uno::Sequence<
+ css::uno::Reference<css::deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL addExtension(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL removeExtension(
+ OUString const & identifier,
+ OUString const & filename,
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL enableExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disableExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual sal_Int32 SAL_CALL checkPrerequisitesAndEnable(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getDeployedExtensions(
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const &,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Reference< css::deployment::XPackage>
+ SAL_CALL getDeployedExtension(
+ OUString const & repository,
+ OUString const & identifier,
+ OUString const & filename,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getExtensionsWithSameIdentifier(
+ OUString const & identifier,
+ OUString const & filename,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence< css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > >
+ SAL_CALL getAllExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const &,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL reinstallDeployedExtensions(
+ sal_Bool force, OUString const & repository,
+ css::uno::Reference< css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual sal_Bool SAL_CALL synchronize(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > SAL_CALL
+ getExtensionsWithUnacceptedLicenses(
+ OUString const & repository,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) override;
+
+ virtual sal_Bool SAL_CALL isReadOnlyRepository(OUString const & repository) override;
+
+private:
+
+ static OUString StrSyncRepository() { return DpResId(RID_STR_SYNCHRONIZING_REPOSITORY); }
+
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+ css::uno::Reference<css::deployment::XPackageManagerFactory> m_xPackageManagerFactory;
+
+ //only to be used within addExtension
+ ::osl::Mutex m_addMutex;
+ /* contains the names of all repositories (except tmp) in order of there
+ priority. That is, the first element is "user" followed by "shared" and
+ then "bundled"
+ */
+ std::vector< OUString > m_repositoryNames;
+
+ css::uno::Reference<css::deployment::XPackageManager> getUserRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getSharedRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getBundledRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getTmpRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getBakRepository();
+
+ bool isUserDisabled(OUString const & identifier,
+ OUString const & filename);
+
+ static bool isUserDisabled(
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExtSameId);
+
+ void activateExtension(
+ OUString const & identifier,
+ OUString const & fileName,
+ bool bUserDisabled, bool bStartup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ static void activateExtension(
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExt,
+ bool bUserDisabled, bool bStartup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+
+ std::vector<css::uno::Reference<css::deployment::XPackage> >
+ getExtensionsWithSameId(OUString const & identifier,
+ OUString const & fileName);
+
+ css::uno::Reference<css::deployment::XPackage> backupExtension(
+ OUString const & identifier, OUString const & fileName,
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageManager,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ void checkInstall(
+ OUString const & displayName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & cmdEnv);
+
+ void checkUpdate(
+ OUString const & newVersion,
+ OUString const & newDisplayName,
+ css::uno::Reference<css::deployment::XPackage> const & oldExtension,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ void addExtensionsToMap(
+ id2extensions & mapExt,
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExt,
+ std::u16string_view repository);
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference<css::deployment::XPackageManager>
+ getPackageManager(std::u16string_view repository);
+
+ /// @throws css::deployment::DeploymentException
+ /// @throws css::ucb::CommandFailedException
+ /// @throws css::ucb::CommandAbortedException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ bool doChecksForAddExtension(
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageMgr,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ css::uno::Reference<css::deployment::XPackage> const & xTmpExtension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ css::uno::Reference<css::deployment::XPackage> & out_existingExtension );
+
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_informationprovider.cxx b/desktop/source/deployment/manager/dp_informationprovider.cxx
new file mode 100644
index 000000000..1e7beea34
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_informationprovider.cxx
@@ -0,0 +1,338 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/XPackageInformationProvider.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <tools/diagnose_ex.h>
+#include <ucbhelper/content.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_version.hxx>
+#include <dp_update.hxx>
+
+namespace beans = com::sun::star::beans ;
+namespace deployment = com::sun::star::deployment ;
+namespace lang = com::sun::star::lang ;
+namespace task = com::sun::star::task ;
+namespace css_ucb = com::sun::star::ucb ;
+namespace uno = com::sun::star::uno ;
+namespace xml = com::sun::star::xml ;
+
+
+namespace dp_info {
+
+namespace {
+
+class PackageInformationProvider :
+ public ::cppu::WeakImplHelper< deployment::XPackageInformationProvider, lang::XServiceInfo >
+
+{
+ public:
+ explicit PackageInformationProvider( uno::Reference< uno::XComponentContext >const& xContext);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageInformationProvider
+ virtual OUString SAL_CALL getPackageLocation( const OUString& extensionId ) override;
+ virtual uno::Sequence< uno::Sequence< OUString > > SAL_CALL isUpdateAvailable( const OUString& extensionId ) override;
+ virtual uno::Sequence< uno::Sequence< OUString > > SAL_CALL getExtensionList() override;
+
+private:
+
+ uno::Reference< uno::XComponentContext> mxContext;
+
+ OUString getPackageLocation( const OUString& repository,
+ std::u16string_view _sExtensionId );
+
+ uno::Reference< deployment::XUpdateInformationProvider > mxUpdateInformation;
+};
+
+}
+
+PackageInformationProvider::PackageInformationProvider( uno::Reference< uno::XComponentContext > const& xContext) :
+ mxContext( xContext ),
+ mxUpdateInformation( deployment::UpdateInformationProvider::create( xContext ) )
+{
+}
+
+// XServiceInfo
+OUString PackageInformationProvider::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.PackageInformationProvider";
+}
+
+sal_Bool PackageInformationProvider::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > PackageInformationProvider::getSupportedServiceNames()
+{
+ // a private one:
+ return { "com.sun.star.comp.deployment.PackageInformationProvider" };
+}
+
+OUString PackageInformationProvider::getPackageLocation(
+ const OUString & repository,
+ std::u16string_view _rExtensionId )
+{
+ OUString aLocationURL;
+ uno::Reference<deployment::XExtensionManager> xManager =
+ deployment::ExtensionManager::get(mxContext);
+
+ if ( xManager.is() )
+ {
+ const uno::Sequence< uno::Reference< deployment::XPackage > > packages(
+ xManager->getDeployedExtensions(
+ repository,
+ uno::Reference< task::XAbortChannel >(),
+ uno::Reference< css_ucb::XCommandEnvironment > () ) );
+
+ for ( int pos = packages.getLength(); pos--; )
+ {
+ try
+ {
+ const beans::Optional< OUString > aID = packages[ pos ]->getIdentifier();
+ if ( aID.IsPresent && (aID.Value == _rExtensionId ) )
+ {
+ aLocationURL = packages[ pos ]->getURL();
+ break;
+ }
+ }
+ catch ( uno::RuntimeException & ) {}
+ }
+ }
+
+ return aLocationURL;
+}
+
+
+OUString SAL_CALL
+PackageInformationProvider::getPackageLocation( const OUString& _sExtensionId )
+{
+ OUString aLocationURL = getPackageLocation( "user", _sExtensionId );
+
+ if ( aLocationURL.isEmpty() )
+ {
+ aLocationURL = getPackageLocation( "shared", _sExtensionId );
+ }
+ if ( aLocationURL.isEmpty() )
+ {
+ aLocationURL = getPackageLocation( "bundled", _sExtensionId );
+ }
+ if ( !aLocationURL.isEmpty() )
+ {
+ try
+ {
+ ::ucbhelper::Content aContent( aLocationURL, nullptr, mxContext );
+ aLocationURL = aContent.getURL();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignoring");
+ }
+ }
+ return aLocationURL;
+}
+
+uno::Sequence< uno::Sequence< OUString > > SAL_CALL
+PackageInformationProvider::isUpdateAvailable( const OUString& _sExtensionId )
+{
+ uno::Sequence< uno::Sequence< OUString > > aList;
+
+ uno::Reference<deployment::XExtensionManager> extMgr =
+ deployment::ExtensionManager::get(mxContext);
+
+ if (!extMgr.is())
+ {
+ OSL_ASSERT(false);
+ return aList;
+ }
+ std::vector<std::pair<uno::Reference<deployment::XPackage>, uno::Any > > errors;
+ dp_misc::UpdateInfoMap updateInfoMap;
+ if (!_sExtensionId.isEmpty())
+ {
+ std::vector<uno::Reference<deployment::XPackage> > vecExtensions;
+ uno::Reference<deployment::XPackage> extension;
+ try
+ {
+ extension = dp_misc::getExtensionWithHighestVersion(
+ extMgr->getExtensionsWithSameIdentifier(
+ _sExtensionId, _sExtensionId, uno::Reference<css_ucb::XCommandEnvironment>()));
+ vecExtensions.push_back(extension);
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ OSL_ASSERT(false);
+ }
+ updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ mxContext, extMgr, mxUpdateInformation, &vecExtensions, errors);
+ }
+ else
+ {
+ updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ mxContext, extMgr, mxUpdateInformation, nullptr, errors);
+ }
+
+ int nCount = 0;
+ for (auto const& updateInfo : updateInfoMap)
+ {
+ dp_misc::UpdateInfo const & info = updateInfo.second;
+
+ OUString sOnlineVersion;
+ if (info.info.is())
+ {
+ // check, if there are unsatisfied dependencies and ignore this online update
+ dp_misc::DescriptionInfoset infoset(mxContext, info.info);
+ uno::Sequence< uno::Reference< xml::dom::XElement > >
+ ds( dp_misc::Dependencies::check( infoset ) );
+ if ( ! ds.hasElements() )
+ sOnlineVersion = info.version;
+ }
+
+ OUString sVersionUser;
+ OUString sVersionShared;
+ OUString sVersionBundled;
+ uno::Sequence< uno::Reference< deployment::XPackage> > extensions;
+ try {
+ extensions = extMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(info.extension), info.extension->getName(),
+ uno::Reference<css_ucb::XCommandEnvironment>());
+ } catch (const lang::IllegalArgumentException&) {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignoring");
+ continue;
+ }
+ OSL_ASSERT(extensions.getLength() == 3);
+ if (extensions[0].is() )
+ sVersionUser = extensions[0]->getVersion();
+ if (extensions[1].is() )
+ sVersionShared = extensions[1]->getVersion();
+ if (extensions[2].is() )
+ sVersionBundled = extensions[2]->getVersion();
+
+ bool bSharedReadOnly = extMgr->isReadOnlyRepository("shared");
+
+ dp_misc::UPDATE_SOURCE sourceUser = dp_misc::isUpdateUserExtension(
+ bSharedReadOnly, sVersionUser, sVersionShared, sVersionBundled, sOnlineVersion);
+ dp_misc::UPDATE_SOURCE sourceShared = dp_misc::isUpdateSharedExtension(
+ bSharedReadOnly, sVersionShared, sVersionBundled, sOnlineVersion);
+
+ OUString updateVersionUser;
+ OUString updateVersionShared;
+ if (sourceUser != dp_misc::UPDATE_SOURCE_NONE)
+ updateVersionUser = dp_misc::getHighestVersion(
+ sVersionShared, sVersionBundled, sOnlineVersion);
+ if (sourceShared != dp_misc::UPDATE_SOURCE_NONE)
+ updateVersionShared = dp_misc::getHighestVersion(
+ OUString(), sVersionBundled, sOnlineVersion);
+ OUString updateVersion;
+ if (dp_misc::compareVersions(updateVersionUser, updateVersionShared) == dp_misc::GREATER)
+ updateVersion = updateVersionUser;
+ else
+ updateVersion = updateVersionShared;
+ if (!updateVersion.isEmpty())
+ {
+
+ OUString aNewEntry[2];
+ aNewEntry[0] = updateInfo.first;
+ aNewEntry[1] = updateVersion;
+ aList.realloc( ++nCount );
+ aList.getArray()[ nCount-1 ] = ::uno::Sequence< OUString >( aNewEntry, 2 );
+ }
+ }
+ return aList;
+}
+
+
+uno::Sequence< uno::Sequence< OUString > > SAL_CALL PackageInformationProvider::getExtensionList()
+{
+ const uno::Reference<deployment::XExtensionManager> mgr =
+ deployment::ExtensionManager::get(mxContext);
+
+ if (!mgr.is())
+ return uno::Sequence< uno::Sequence< OUString > >();
+
+ const uno::Sequence< uno::Sequence< uno::Reference<deployment::XPackage > > >
+ allExt = mgr->getAllExtensions(
+ uno::Reference< task::XAbortChannel >(),
+ uno::Reference< css_ucb::XCommandEnvironment > () );
+
+ uno::Sequence< uno::Sequence< OUString > > retList;
+
+ sal_Int32 cAllIds = allExt.getLength();
+ retList.realloc(cAllIds);
+ auto pretList = retList.getArray();
+
+ for (sal_Int32 i = 0; i < cAllIds; i++)
+ {
+ //The inner sequence contains extensions with the same identifier from
+ //all the different repositories, that is user, share, bundled.
+ const uno::Sequence< uno::Reference< deployment::XPackage > > &
+ seqExtension = allExt[i];
+ sal_Int32 cExt = seqExtension.getLength();
+ OSL_ASSERT(cExt == 3);
+ for (sal_Int32 j = 0; j < cExt; j++)
+ {
+ //ToDo according to the old code the first found extension is used
+ //even if another one with the same id has a better version.
+ uno::Reference< deployment::XPackage > const & xExtension( seqExtension[j] );
+ if (xExtension.is())
+ {
+ pretList[i] = { dp_misc::getIdentifier(xExtension), xExtension->getVersion() };
+ break;
+ }
+ }
+ }
+ return retList;
+}
+
+
+} // namespace dp_info
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_PackageInformationProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new dp_info::PackageInformationProvider(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_manager.cxx b/desktop/source/deployment/manager/dp_manager.cxx
new file mode 100644
index 000000000..68765a63f
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_manager.cxx
@@ -0,0 +1,1600 @@
+/* -*- 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 <config_features.h>
+
+#include <dp_interact.h>
+#include <dp_misc.h>
+#include <dp_registry.hxx>
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include <dp_ucb.h>
+#include <dp_platform.hxx>
+#include "dp_manager.h"
+#include <dp_identifier.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/logging.hxx>
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <xmlscript/xml_helper.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/logging/FileHandler.hpp>
+#include <com/sun/star/logging/SimpleTextFormatter.hpp>
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
+#include <com/sun/star/deployment/Prerequisites.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <unotools/tempfile.hxx>
+
+#include <dp_descriptioninfoset.hxx>
+#include "dp_commandenvironments.hxx"
+#include "dp_properties.hxx"
+
+#include <vector>
+#include <algorithm>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::logging;
+
+
+namespace dp_manager {
+
+namespace {
+
+struct MatchTempDir
+{
+ OUString m_str;
+ explicit MatchTempDir( OUString str ) : m_str(std::move( str )) {}
+ bool operator () ( ActivePackages::Entries::value_type const & v ) const {
+ return v.second.temporaryName.equalsIgnoreAsciiCase( m_str );
+ }
+};
+
+OUString getExtensionFolder(OUString const & parentFolder,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext)
+{
+ ::ucbhelper::Content tempFolder( parentFolder, xCmdEnv, xContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
+
+ OUString title;
+ if (xResultSet->next())
+ {
+ title = Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(1 /* Title */ ) ;
+ }
+ return title;
+}
+}
+
+void PackageManagerImpl::initActivationLayer(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_activePackages.isEmpty())
+ {
+ OSL_ASSERT( m_registryCache.isEmpty() );
+ // documents temp activation:
+ m_activePackagesDB.reset( new ActivePackages );
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, m_context, xCmdEnv,
+ false /* no throw */ ))
+ {
+ // scan for all entries in m_packagesDir:
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (ucbContent, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) );
+
+ while (xResultSet->next())
+ {
+ Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
+ OUString title( xRow->getString( 1 /* Title */ ) );
+ // xxx todo: remove workaround for tdoc
+ if ( title == "this_is_a_dummy_stream_just_there_as_a_workaround_for_a_temporary_limitation_of_the_storage_api_implementation" )
+ continue;
+ if ( title == "META-INF" )
+ continue;
+
+ ::ucbhelper::Content sourceContent(
+ Reference<XContentAccess>(
+ xResultSet, UNO_QUERY_THROW )->queryContent(),
+ xCmdEnv, m_xComponentContext );
+
+ OUString mediaType( detectMediaType( sourceContent,
+ false /* no throw */) );
+ if (!mediaType.isEmpty())
+ {
+ ActivePackages::Data dbData;
+ insertToActivationLayer(
+ Sequence<css::beans::NamedValue>(),mediaType, sourceContent,
+ title, &dbData );
+
+ insertToActivationLayerDB( title, dbData );
+ //TODO #i73136#: insertToActivationLayerDB needs id not
+ // title, but the whole m_activePackages.getLength()==0
+ // case (i.e., document-relative deployment) currently
+ // does not work, anyway.
+ }
+ }
+ }
+ }
+ else
+ {
+ // user|share:
+ OSL_ASSERT( !m_activePackages.isEmpty() );
+ m_activePackages_expanded = expandUnoRcUrl( m_activePackages );
+ m_registrationData_expanded = expandUnoRcUrl(m_registrationData);
+ if (!m_readOnly)
+ create_folder( nullptr, m_activePackages_expanded, xCmdEnv);
+
+ OUString dbName;
+ if (m_context == "user")
+ dbName = m_activePackages_expanded + ".pmap";
+ else
+ {
+ // Create the extension data base in the user installation
+ create_folder( nullptr, m_registrationData_expanded, xCmdEnv);
+ dbName = m_registrationData_expanded + "/extensions.pmap";
+ }
+ // The data base can always be written because it is always in the user installation
+ m_activePackagesDB.reset( new ActivePackages( dbName ) );
+
+ if (! m_readOnly && m_context != "bundled")
+ {
+ // clean up activation layer, scan for zombie temp dirs:
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (tempFolder,
+ ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ) );
+
+ // get all temp directories:
+ std::vector<OUString> tempEntries;
+ std::vector<OUString> removedEntries;
+ while (xResultSet->next())
+ {
+ OUString title(
+ Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(
+ 1 /* Title */ ) );
+ if (title.endsWith("removed", &title))
+ {
+ //save the file name without the "removed" part
+ removedEntries.push_back(::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ tempEntries.push_back( ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ }
+
+ bool bShared = (m_context == "shared");
+ for (const OUString & tempEntry : tempEntries)
+ {
+ const MatchTempDir match( tempEntry );
+ if (std::none_of( id2temp.begin(), id2temp.end(), match ))
+ {
+ const OUString url(
+ makeURL(m_activePackages_expanded, tempEntry ) );
+
+ //In case of shared extensions, new entries are regarded as
+ //added extensions if there is no xxx.tmpremoved file.
+ if (bShared)
+ {
+ if (std::find(removedEntries.begin(), removedEntries.end(), tempEntry) ==
+ removedEntries.end())
+ {
+ continue;
+ }
+ else
+ {
+ //Make sure only the same user removes the extension, who
+ //previously unregistered it. This is avoid races if multiple instances
+ //of OOo are running which all have write access to the shared installation.
+ //For example, a user removes the extension, but keeps OOo
+ //running. Parts of the extension may still be loaded and used by OOo.
+ //Therefore the extension is only deleted the next time the extension manager is
+ //run after restarting OOo. While OOo is still running, another user starts OOo
+ //which would deleted the extension files. If the same user starts another
+ //instance of OOo then the lock file will prevent this.
+ OUString aUserName;
+ ::osl::Security aSecurity;
+ aSecurity.getUserName( aUserName );
+ ucbhelper::Content remFileContent(
+ url + "removed", Reference<XCommandEnvironment>(), m_xComponentContext);
+ std::vector<sal_Int8> data = dp_misc::readFile(remFileContent);
+ OString osData(reinterpret_cast<const char*>(data.data()),
+ data.size());
+ OUString sData = OStringToOUString(
+ osData, RTL_TEXTENCODING_UTF8);
+ if (sData != aUserName)
+ continue;
+ }
+ }
+ // temp entry not needed anymore:
+ erase_path( url + "_",
+ Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ erase_path( url, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ //delete the xxx.tmpremoved file
+ erase_path(url + "removed",
+ Reference<XCommandEnvironment>(), false);
+ }
+ }
+ }
+ }
+}
+
+
+void PackageManagerImpl::initRegistryBackends()
+{
+ if (!m_registryCache.isEmpty())
+ create_folder( nullptr, m_registryCache,
+ Reference<XCommandEnvironment>(), false);
+ m_xRegistry.set( ::dp_registry::create(
+ m_context, m_registryCache,
+ m_xComponentContext ) );
+}
+
+namespace {
+
+osl::FileBase::RC createDirectory(OUString const & url) {
+ auto e = osl::Directory::create(url);
+ if (e != osl::FileBase::E_NOENT) {
+ return e;
+ }
+ INetURLObject o(url);
+ if (!o.removeSegment()) {
+ return osl::FileBase::E_INVAL; // anything but E_None/E_EXIST
+ }
+ e = createDirectory(o.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ if (e != osl::FileBase::E_None && e != osl::FileBase::E_EXIST) {
+ return e;
+ }
+ return osl::Directory::create(url);
+}
+
+bool isMacroURLReadOnly( const OUString &rMacro )
+{
+ OUString aDirURL( rMacro );
+ ::rtl::Bootstrap::expandMacros( aDirURL );
+
+ ::osl::FileBase::RC aErr = createDirectory( aDirURL );
+ if ( aErr == ::osl::FileBase::E_None )
+ return false; // it will be writeable
+ if ( aErr != ::osl::FileBase::E_EXIST )
+ return true; // some serious problem creating it
+
+ bool bError;
+ sal_uInt64 nWritten = 0;
+ OUString aFileURL( aDirURL + "/stamp.sys" );
+ ::osl::File aFile( aFileURL );
+
+ bError = aFile.open( osl_File_OpenFlag_Read |
+ osl_File_OpenFlag_Write |
+ osl_File_OpenFlag_Create ) != ::osl::FileBase::E_None;
+ if (!bError)
+ bError = aFile.write( "1", 1, nWritten ) != ::osl::FileBase::E_None;
+ if (aFile.close() != ::osl::FileBase::E_None)
+ bError = true;
+ if (osl::File::remove( aFileURL ) != ::osl::FileBase::E_None)
+ bError = true;
+
+ SAL_INFO(
+ "desktop.deployment",
+ "local url '" << rMacro << "' -> '" << aFileURL << "' "
+ << (bError ? "is" : "is not") << " readonly\n");
+ return bError;
+}
+
+}
+
+Reference<deployment::XPackageManager> PackageManagerImpl::create(
+ Reference<XComponentContext> const & xComponentContext,
+ OUString const & context )
+{
+ rtl::Reference<PackageManagerImpl> that = new PackageManagerImpl(
+ xComponentContext, context );
+
+ OUString logFile, stamp;
+ if ( context == "user" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages";
+ that->m_registrationData = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE";
+ that->m_registryCache = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/registry";
+ logFile = "$UNO_USER_PACKAGES_CACHE/log.txt";
+ //We use the extension .sys for the file because on Windows Vista a sys
+ //(as well as exe and dll) file
+ //will not be written in the VirtualStore. For example if the process has no
+ //admin right once cannot write to the %programfiles% folder. However, when
+ //virtualization is used, the file will be written into the VirtualStore and
+ //it appears as if one could write to %programfiles%. When we test for write
+ //access to the office/shared folder for shared extensions then this typically
+ //fails because a normal user typically cannot write to this folder. However,
+ //using virtualization it appears that he/she can. Then a shared extension can
+ //be installed but is only visible for the user (because the extension is in
+ //the virtual store).
+ stamp = "$UNO_USER_PACKAGES_CACHE";
+ }
+ else if ( context == "shared" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages";
+ that->m_registrationData = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER";
+ that->m_registryCache = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER/registry";
+ logFile = "$SHARED_EXTENSIONS_USER/log.txt";
+#if !HAVE_FEATURE_READONLY_INSTALLSET
+ // The "shared" extensions are read-only when we have a
+ // read-only installset.
+ stamp = "$UNO_SHARED_PACKAGES_CACHE";
+#endif
+ }
+ else if ( context == "bundled" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS";
+ that->m_registrationData = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER";
+ that->m_registryCache = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER/registry";
+ logFile = "$BUNDLED_EXTENSIONS_USER/log.txt";
+ //No stamp file. We assume that bundled is always readonly. It must not be
+ //modified from ExtensionManager but only by the installer
+ }
+ else if ( context == "tmp" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$TMP_EXTENSIONS/extensions";
+ that->m_registrationData = "vnd.sun.star.expand:$TMP_EXTENSIONS";
+ that->m_registryCache = "vnd.sun.star.expand:$TMP_EXTENSIONS/registry";
+ stamp = "$TMP_EXTENSIONS";
+ }
+ else if (context == "bak") {
+ that->m_activePackages = "vnd.sun.star.expand:$BAK_EXTENSIONS/extensions";
+ that->m_registrationData = "vnd.sun.star.expand:$BAK_EXTENSIONS";
+ that->m_registryCache = "vnd.sun.star.expand:$BAK_EXTENSIONS/registry";
+ stamp = "$BAK_EXTENSIONS";
+ }
+
+ else if (! context.match("vnd.sun.star.tdoc:/")) {
+ throw lang::IllegalArgumentException(
+ "invalid context given: " + context,
+ Reference<XInterface>(), static_cast<sal_Int16>(-1) );
+ }
+
+ Reference<XCommandEnvironment> xCmdEnv;
+
+ try {
+ // There is no stamp for the bundled folder:
+ if (!stamp.isEmpty())
+ that->m_readOnly = isMacroURLReadOnly( stamp );
+
+ if (!that->m_readOnly && !logFile.isEmpty())
+ {
+ // Initialize logger which will be used in ProgressLogImpl (created below)
+ rtl::Bootstrap::expandMacros(logFile);
+ comphelper::EventLogger logger(xComponentContext, "unopkg");
+ const Reference<XLogger> xLogger(logger.getLogger());
+ Reference<XLogFormatter> xLogFormatter(SimpleTextFormatter::create(xComponentContext));
+ Sequence < beans::NamedValue > aSeq2 { { "Formatter", Any(xLogFormatter) }, {"FileURL", Any(logFile)} };
+ Reference<XLogHandler> xFileHandler(css::logging::FileHandler::createWithSettings(xComponentContext, aSeq2));
+ xFileHandler->setLevel(LogLevel::WARNING);
+ xLogger->addLogHandler(xFileHandler);
+
+ that->m_xLogFile.set(
+ that->m_xComponentContext->getServiceManager()
+ ->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.deployment.ProgressLog",
+ Sequence<Any>(),
+ that->m_xComponentContext ),
+ UNO_QUERY_THROW );
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv, that->m_xLogFile ) );
+ }
+
+ that->initRegistryBackends();
+ that->initActivationLayer( xCmdEnv );
+
+ return that;
+
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception & e) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ ("[context=\"" + context + "\"] caught unexpected "
+ + exc.getValueType().getTypeName() + ": " + e.Message),
+ Reference<XInterface>(), exc );
+ }
+}
+
+
+PackageManagerImpl::~PackageManagerImpl()
+{
+}
+
+
+void PackageManagerImpl::fireModified()
+{
+ ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer(
+ cppu::UnoType<util::XModifyListener>::get() );
+ if (pContainer != nullptr) {
+ pContainer->forEach<util::XModifyListener>(
+ [this] (uno::Reference<util::XModifyListener> const& xListener)
+ { return xListener->modified(lang::EventObject(static_cast<OWeakObject *>(this))); });
+ }
+}
+
+
+void PackageManagerImpl::disposing()
+{
+ try {
+// // xxx todo: guarding?
+// ::osl::MutexGuard guard( getMutex() );
+ try_dispose( m_xLogFile );
+ m_xLogFile.clear();
+ try_dispose( m_xRegistry );
+ m_xRegistry.clear();
+ m_activePackagesDB.reset();
+ m_xComponentContext.clear();
+
+ t_pm_helper::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 );
+ }
+}
+
+// XComponent
+
+void PackageManagerImpl::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 PackageManagerImpl::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 PackageManagerImpl::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 );
+}
+
+// XPackageManager
+
+OUString PackageManagerImpl::getContext()
+{
+ check();
+ return m_context;
+}
+
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+PackageManagerImpl::getSupportedPackageTypes()
+{
+ OSL_ASSERT( m_xRegistry.is() );
+ return m_xRegistry->getSupportedPackageTypes();
+}
+
+
+Reference<task::XAbortChannel> PackageManagerImpl::createAbortChannel()
+{
+ check();
+ return new AbortChannel;
+}
+
+// XModifyBroadcaster
+
+void PackageManagerImpl::addModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void PackageManagerImpl::removeModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+OUString PackageManagerImpl::detectMediaType(
+ ::ucbhelper::Content const & ucbContent_, bool throw_exc )
+{
+ ::ucbhelper::Content ucbContent(ucbContent_);
+ OUString url( ucbContent.getURL() );
+ OUString mediaType;
+ if (url.match( "vnd.sun.star.tdoc:" ) || url.match( "vnd.sun.star.pkg:" ))
+ {
+ try {
+ ucbContent.getPropertyValue( "MediaType" ) >>= mediaType;
+ }
+ catch (const beans::UnknownPropertyException &) {
+ }
+ OSL_ENSURE( !mediaType.isEmpty(), "### no media-type?!" );
+ }
+ if (mediaType.isEmpty())
+ {
+ try {
+ Reference<deployment::XPackage> xPackage(
+ m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), ucbContent.getCommandEnvironment() ) );
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+ }
+ catch (const lang::IllegalArgumentException &) {
+ if (throw_exc)
+ throw;
+ css::uno::Any ex( cppu::getCaughtException() );
+ SAL_WARN( "desktop", exceptionToString(ex) );
+ }
+ }
+ return mediaType;
+}
+
+
+OUString PackageManagerImpl::insertToActivationLayer(
+ Sequence<beans::NamedValue> const & properties,
+ OUString const & mediaType, ::ucbhelper::Content const & sourceContent_,
+ OUString const & title, ActivePackages::Data * dbData )
+{
+ ::ucbhelper::Content sourceContent(sourceContent_);
+ Reference<XCommandEnvironment> xCmdEnv(
+ sourceContent.getCommandEnvironment() );
+
+ OUString baseDir(m_activePackages_expanded);
+ ::utl::TempFile aTemp(&baseDir, false);
+ OUString tempEntry = aTemp.GetURL();
+ tempEntry = tempEntry.copy(tempEntry.lastIndexOf('/') + 1);
+ OUString destFolder = makeURL( m_activePackages, tempEntry) + "_";
+
+ // prepare activation folder:
+ ::ucbhelper::Content destFolderContent;
+ create_folder( &destFolderContent, destFolder, xCmdEnv );
+
+ // copy content into activation temp dir:
+ if (mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.package-bundle") ||
+ // xxx todo: more sophisticated parsing
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.legacy-package-bundle"))
+ {
+ // inflate content:
+ OUStringBuffer buf;
+ if (!sourceContent.isFolder())
+ {
+ buf.append( "vnd.sun.star.zip://" );
+ buf.append( ::rtl::Uri::encode( sourceContent.getURL(),
+ rtl_UriCharClassRegName,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ //Folder. No need to unzip, just copy
+ buf.append(sourceContent.getURL());
+ }
+ buf.append( '/' );
+ sourceContent = ::ucbhelper::Content(
+ buf.makeStringAndClear(), xCmdEnv, m_xComponentContext );
+ }
+ destFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ title, NameClash::OVERWRITE );
+
+
+ // write to DB:
+ //bundled extensions should only be added by the synchronizeAddedExtensions
+ //functions. Moreover, there is no "temporary folder" for bundled extensions.
+ OSL_ASSERT(!(m_context == "bundled"));
+ OUString sFolderUrl = makeURLAppendSysPathSegment(destFolderContent.getURL(), title);
+ DescriptionInfoset info =
+ dp_misc::getDescriptionInfoset(sFolderUrl);
+ dbData->temporaryName = tempEntry;
+ dbData->fileName = title;
+ dbData->mediaType = mediaType;
+ dbData->version = info.getVersion();
+
+ //No write the properties file next to the extension
+ ExtensionProperties props(sFolderUrl, properties, xCmdEnv, m_xComponentContext);
+ props.write();
+ return destFolder;
+}
+
+
+void PackageManagerImpl::insertToActivationLayerDB(
+ OUString const & id, ActivePackages::Data const & dbData )
+{
+ //access to the database must be guarded. See removePackage
+ const ::osl::MutexGuard guard( m_aMutex );
+ m_activePackagesDB->put( id, dbData );
+}
+
+
+/* The function returns true if there is an extension with the same id already
+ installed which needs to be uninstalled, before the new extension can be installed.
+*/
+bool PackageManagerImpl::isInstalled(
+ Reference<deployment::XPackage> const & package)
+{
+ OUString id(dp_misc::getIdentifier(package));
+ OUString fn(package->getName());
+ bool bInstalled = false;
+ if (m_activePackagesDB->has( id, fn ))
+ {
+ bInstalled = true;
+ }
+ return bInstalled;
+}
+
+// XPackageManager
+
+Reference<deployment::XPackage> PackageManagerImpl::importExtension(
+ Reference<deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ return addPackage(extension->getURL(), Sequence<beans::NamedValue>(),
+ OUString(), xAbortChannel, xCmdEnv_);
+}
+
+/* The function adds an extension but does not register it!!!
+ It may not do any user interaction. This is done in XExtensionManager::addExtension
+*/
+Reference<deployment::XPackage> PackageManagerImpl::addPackage(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType_,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ if (m_readOnly)
+ {
+ OUString message;
+ if (m_context == "shared")
+ message = "You need write permissions to install a shared extension!";
+ else
+ message = "You need write permissions to install this extension!";
+ throw deployment::DeploymentException(
+ message, static_cast<OWeakObject *>(this), Any() );
+ }
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ ::ucbhelper::Content sourceContent;
+ (void)create_ucb_content( &sourceContent, url, xCmdEnv ); // throws exc
+ const OUString title( StrTitle::getTitle( sourceContent ) );
+ const OUString title_enc( ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ OUString destFolder;
+
+ OUString mediaType(mediaType_);
+ if (mediaType.isEmpty())
+ mediaType = detectMediaType( sourceContent );
+
+ Reference<deployment::XPackage> xPackage;
+ // copy file:
+ progressUpdate(
+ DpResId(RID_STR_COPYING_PACKAGE) + title, xCmdEnv );
+ if (m_activePackages.isEmpty())
+ {
+ ::ucbhelper::Content docFolderContent;
+ create_folder( &docFolderContent, m_context, xCmdEnv );
+ // copy into document, first:
+ docFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(),
+ NameClash::ASK /* xxx todo: ASK not needed? */);
+ // set media-type:
+ ::ucbhelper::Content docContent(
+ makeURL( m_context, title_enc ), xCmdEnv, m_xComponentContext );
+ //TODO #i73136#: using title instead of id can lead to
+ // clashes, but the whole m_activePackages.getLength()==0
+ // case (i.e., document-relative deployment) currently does
+ // not work, anyway.
+ docContent.setPropertyValue("MediaType", Any(mediaType) );
+
+ // xxx todo: obsolete in the future
+ try {
+ docFolderContent.executeCommand( "flush", Any() );
+ }
+ catch (const UnsupportedCommandException &) {
+ }
+ }
+ ActivePackages::Data dbData;
+ destFolder = insertToActivationLayer(
+ properties, mediaType, sourceContent, title, &dbData );
+
+
+ // bind activation package:
+ //Because every shared/user extension will be unpacked in a folder,
+ //which was created with a unique name we will always have two different
+ //XPackage objects, even if the second extension is the same.
+ //Therefore bindPackage does not need a guard here.
+ xPackage = m_xRegistry->bindPackage(
+ makeURL( destFolder, title_enc ), mediaType, false, OUString(), xCmdEnv );
+
+ OSL_ASSERT( xPackage.is() );
+ if (xPackage.is())
+ {
+ bool install = false;
+ try
+ {
+ OUString const id = dp_misc::getIdentifier( xPackage );
+
+ ::osl::MutexGuard g(m_addMutex);
+ if (isInstalled(xPackage))
+ {
+ //Do not guard the complete function with the getMutex
+ removePackage(id, xPackage->getName(), xAbortChannel,
+ xCmdEnv);
+ }
+ install = true;
+ insertToActivationLayerDB(id, dbData);
+ }
+ catch (...)
+ {
+ deletePackageFromCache( xPackage, destFolder );
+ throw;
+ }
+ if (!install)
+ {
+ deletePackageFromCache( xPackage, destFolder );
+ }
+ //ToDo: We should notify only if the extension is registered
+ fireModified();
+ }
+ return xPackage;
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + url,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+void PackageManagerImpl::deletePackageFromCache(
+ Reference<deployment::XPackage> const & xPackage,
+ OUString const & destFolder)
+{
+ try_dispose( xPackage );
+
+ //we remove the package from the uno cache
+ //no service from the package may be loaded at this time!!!
+ erase_path( destFolder, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ //rm last character '_'
+ OUString url = destFolder.copy(0, destFolder.getLength() - 1);
+ erase_path( url, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+
+}
+
+void PackageManagerImpl::removePackage(
+ OUString const & id, OUString const & fileName,
+ Reference<task::XAbortChannel> const & /*xAbortChannel*/,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ Reference<deployment::XPackage> xPackage;
+ {
+ const ::osl::MutexGuard guard(m_aMutex);
+ //Check if this extension exist and throw an IllegalArgumentException
+ //if it does not
+ //If the files of the extension are already removed, or there is a
+ //different extension at the same place, for example after updating the
+ //extension, then the returned object is that which uses the database data.
+ xPackage = getDeployedPackage_(id, fileName, xCmdEnv );
+
+
+ //Because the extension is only removed the next time the extension
+ //manager runs after restarting OOo, we need to indicate that a
+ //shared extension was "deleted". When a user starts OOo, then it
+ //will check if something changed in the shared repository. Based on
+ //the flag file it will then recognize, that the extension was
+ //deleted and can then update the extension database of the shared
+ //extensions in the user installation.
+ if ( xPackage.is() && !m_readOnly && !xPackage->isRemoved() && (m_context == "shared"))
+ {
+ ActivePackages::Data val;
+ m_activePackagesDB->get( & val, id, fileName);
+ OSL_ASSERT(!val.temporaryName.isEmpty());
+ OUString url(makeURL(m_activePackages_expanded,
+ val.temporaryName + "removed"));
+ ::ucbhelper::Content contentRemoved(url, xCmdEnv, m_xComponentContext);
+ OUString aUserName;
+ ::osl::Security aSecurity;
+ aSecurity.getUserName( aUserName );
+
+ OString stamp = OUStringToOString(aUserName, RTL_TEXTENCODING_UTF8);
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ contentRemoved.writeStream( xData, true /* replace existing */ );
+ }
+ m_activePackagesDB->erase( id, fileName ); // to be removed upon next start
+ //remove any cached data hold by the backend
+ m_xRegistry->packageRemoved(xPackage->getURL(), xPackage->getPackageType()->getMediaType());
+ }
+ try_dispose( xPackage );
+
+ fireModified();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_REMOVING) + id,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+OUString PackageManagerImpl::getDeployPath( ActivePackages::Data const & data )
+{
+ OUStringBuffer buf;
+ buf.append( data.temporaryName );
+ //The bundled extensions are not contained in an additional folder
+ //with a unique name. data.temporaryName contains already the
+ //UTF8 encoded folder name. See PackageManagerImpl::synchronize
+ if (m_context != "bundled")
+ {
+ buf.append( "_/" );
+ buf.append( ::rtl::Uri::encode( data.fileName, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ return makeURL( m_activePackages, buf.makeStringAndClear() );
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
+ OUString const & id, OUString const & fileName,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ ActivePackages::Data val;
+ if (m_activePackagesDB->get( &val, id, fileName ))
+ {
+ return getDeployedPackage_( id, val, xCmdEnv );
+ }
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
+ std::u16string_view id, ActivePackages::Data const & data,
+ Reference<XCommandEnvironment> const & xCmdEnv, bool ignoreAlienPlatforms )
+{
+ if (ignoreAlienPlatforms)
+ {
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( data.mediaType, type, subType, &params ))
+ {
+ auto const iter = params.find(OString("platform"));
+ if (iter != params.end() && !platform_fits(iter->second.m_sValue))
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+ }
+ }
+ Reference<deployment::XPackage> xExtension;
+ try
+ {
+ //Ignore extensions where XPackage::checkPrerequisites failed.
+ //They must not be usable for this user.
+ if (data.failedPrerequisites == "0")
+ {
+ xExtension = m_xRegistry->bindPackage(
+ getDeployPath( data ), data.mediaType, false, OUString(), xCmdEnv );
+ }
+ }
+ catch (const deployment::InvalidRemovedParameterException& e)
+ {
+ xExtension = e.Extension;
+ }
+ return xExtension;
+}
+
+
+Sequence< Reference<deployment::XPackage> >
+PackageManagerImpl::getDeployedPackages_(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ std::vector< Reference<deployment::XPackage> > packages;
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+ for (auto const& elem : id2temp)
+ {
+ if (elem.second.failedPrerequisites != "0")
+ continue;
+ try {
+ packages.push_back(
+ getDeployedPackage_(
+ elem.first, elem.second, xCmdEnv,
+ true /* xxx todo: think of GUI:
+ ignore other platforms than the current one */ ) );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ // ignore
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ catch (const deployment::DeploymentException&) {
+ // ignore
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+ return comphelper::containerToSequence(packages);
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage(
+ OUString const & id, OUString const & fileName,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ const ::osl::MutexGuard guard( m_aMutex );
+ return getDeployedPackage_( id, fileName, xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ // ought never occur...
+ "error while accessing deployed package: " + id,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+Sequence< Reference<deployment::XPackage> >
+PackageManagerImpl::getDeployedPackages(
+ Reference<task::XAbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ const ::osl::MutexGuard guard( m_aMutex );
+ return getDeployedPackages_( xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ // ought never occur...
+ "error while getting all deployed packages: " + m_context,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+//ToDo: the function must not call registerPackage, do this in
+//XExtensionManager.reinstallDeployedExtensions
+void PackageManagerImpl::reinstallDeployedPackages(
+ sal_Bool force, Reference<task::XAbortChannel> const & /*xAbortChannel*/,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ if (!force && office_is_running())
+ throw RuntimeException(
+ "You must close any running Office process before reinstalling packages!",
+ static_cast<OWeakObject *>(this) );
+
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ ProgressLevel progress(
+ xCmdEnv, "Reinstalling all deployed packages..." );
+
+ try_dispose( m_xRegistry );
+ m_xRegistry.clear();
+ if (!m_registryCache.isEmpty())
+ erase_path( m_registryCache, xCmdEnv );
+ initRegistryBackends();
+ Reference<util::XUpdatable> xUpdatable( m_xRegistry, UNO_QUERY );
+ if (xUpdatable.is())
+ xUpdatable->update();
+
+ //registering is done by the ExtensionManager service.
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ "Error while reinstalling all previously deployed packages of context " + m_context,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+sal_Bool SAL_CALL PackageManagerImpl::isReadOnly( )
+{
+ return m_readOnly;
+}
+bool PackageManagerImpl::synchronizeRemovedExtensions(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+
+ //find all which are in the extension data base but which
+ //are removed already.
+ OSL_ASSERT(!(m_context == "user"));
+ bool bModified = false;
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ bool bShared = (m_context == "shared");
+
+ for (auto const& elem : id2temp)
+ {
+ try
+ {
+ //Get the URL to the extensions folder, first make the url for the
+ //shared repository including the temporary name
+ OUString url = makeURL(m_activePackages, elem.second.temporaryName);
+ if (bShared)
+ url = makeURLAppendSysPathSegment( url + "_", elem.second.fileName);
+
+ bool bRemoved = false;
+ //Check if the URL to the extension is still the same
+ ::ucbhelper::Content contentExtension;
+
+ if (!create_ucb_content(
+ &contentExtension, url,
+ Reference<XCommandEnvironment>(), false))
+ {
+ bRemoved = true;
+ }
+
+ //The folder is in the extension database, but it can still be deleted.
+ //look for the xxx.tmpremoved file
+ //There can also be the case that a different extension was installed
+ //in a "temp" folder with name that is already used.
+ if (!bRemoved && bShared)
+ {
+ ::ucbhelper::Content contentRemoved;
+
+ if (create_ucb_content(
+ &contentRemoved,
+ m_activePackages_expanded + "/" +
+ elem.second.temporaryName + "removed",
+ Reference<XCommandEnvironment>(), false))
+ {
+ bRemoved = true;
+ }
+ }
+
+ if (!bRemoved)
+ {
+ //There may be another extensions at the same place
+ dp_misc::DescriptionInfoset infoset =
+ dp_misc::getDescriptionInfoset(url);
+ OSL_ENSURE(infoset.hasDescription() && infoset.getIdentifier(),
+ "Extension Manager: bundled and shared extensions "
+ "must have an identifier and a version");
+ if (infoset.hasDescription() &&
+ infoset.getIdentifier() &&
+ ( elem.first != *(infoset.getIdentifier())
+ || elem.second.version != infoset.getVersion()))
+ {
+ bRemoved = true;
+ }
+
+ }
+ if (bRemoved)
+ {
+ Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
+ url, elem.second.mediaType, true, elem.first, xCmdEnv );
+ OSL_ASSERT(xPackage.is()); //Even if the files are removed, we must get the object.
+ xPackage->revokePackage(true, xAbortChannel, xCmdEnv);
+ removePackage(xPackage->getIdentifier().Value, xPackage->getName(),
+ xAbortChannel, xCmdEnv);
+ bModified = true;
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "");
+ }
+ }
+ return bModified;
+}
+
+
+bool PackageManagerImpl::synchronizeAddedExtensions(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ bool bModified = false;
+ OSL_ASSERT(!(m_context == "user"));
+
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+ //check if the folder exist at all. The shared extension folder
+ //may not exist for a normal user.
+ bool bOk=true;
+ try
+ {
+ bOk = create_ucb_content(
+ nullptr, m_activePackages_expanded, Reference<css::ucb::XCommandEnvironment>(), false);
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ bOk = false;
+ }
+
+ if (!bOk)
+ return bModified;
+
+ ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor( tempFolder,
+ ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
+
+ while (xResultSet->next())
+ {
+ try
+ {
+ OUString title(
+ Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(
+ 1 /* Title */ ) );
+ //The temporary folders of user and shared have an '_' at then end.
+ //But the name in ActivePackages.temporaryName is saved without.
+ OUString title2 = title;
+ bool bShared = (m_context == "shared");
+ if (bShared)
+ {
+ OSL_ASSERT(title2.endsWith("_"));
+ title2 = title2.copy(0, title2.getLength() -1);
+ }
+ OUString titleEncoded = ::rtl::Uri::encode(
+ title2, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8);
+
+ //It is sufficient to check for the folder name, because when the administrator
+ //installed the extension it was already checked if there is one with the
+ //same identifier.
+ const MatchTempDir match(titleEncoded);
+ if (std::none_of( id2temp.begin(), id2temp.end(), match ))
+ {
+
+ // The folder was not found in the data base, so it must be
+ // an added extension
+ OUString url(m_activePackages_expanded + "/" + titleEncoded);
+ OUString sExtFolder;
+ if (bShared) //that is, shared
+ {
+ //Check if the extension was not "deleted" already which is indicated
+ //by a xxx.tmpremoved file
+ ::ucbhelper::Content contentRemoved;
+ if (create_ucb_content(&contentRemoved, url + "removed",
+ Reference<XCommandEnvironment>(), false))
+ continue;
+ sExtFolder = getExtensionFolder(
+ m_activePackages_expanded + "/" + titleEncoded + "_",
+ xCmdEnv, m_xComponentContext);
+ url = makeURLAppendSysPathSegment(m_activePackages_expanded, title);
+ url = makeURLAppendSysPathSegment(url, sExtFolder);
+ }
+ Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), xCmdEnv );
+ if (xPackage.is())
+ {
+ OUString id = dp_misc::getIdentifier( xPackage );
+
+ //Prepare the database entry
+ ActivePackages::Data dbData;
+
+ dbData.temporaryName = titleEncoded;
+ if (bShared)
+ dbData.fileName = sExtFolder;
+ else
+ dbData.fileName = title;
+ dbData.mediaType = xPackage->getPackageType()->getMediaType();
+ dbData.version = xPackage->getVersion();
+ SAL_WARN_IF(
+ dbData.version.isEmpty(), "desktop.deployment",
+ "bundled/shared extension " << id << " at <" << url
+ << "> has no explicit version");
+
+ //We provide a special command environment that will prevent
+ //showing a license if simple-license/@accept-by = "admin"
+ //It will also prevent showing the license for bundled extensions
+ //which is not supported.
+ OSL_ASSERT(!(m_context == "user"));
+
+ // shall the license be suppressed?
+ DescriptionInfoset info =
+ dp_misc::getDescriptionInfoset(url);
+ ::std::optional<dp_misc::SimpleLicenseAttributes>
+ attr = info.getSimpleLicenseAttributes();
+ ExtensionProperties props(url, xCmdEnv, m_xComponentContext);
+ bool bNoLicense = false;
+ if (attr && attr->suppressIfRequired && props.isSuppressedLicense())
+ bNoLicense = true;
+
+ Reference<ucb::XCommandEnvironment> licCmdEnv(
+ new LicenseCommandEnv(xCmdEnv->getInteractionHandler(),
+ bNoLicense, m_context));
+ sal_Int32 failedPrereq = xPackage->checkPrerequisites(
+ xAbortChannel, licCmdEnv, false);
+ //Remember that this failed. For example, the user
+ //could have declined the license. Then the next time the
+ //extension folder is investigated we do not want to
+ //try to install the extension again.
+ dbData.failedPrerequisites = OUString::number(failedPrereq);
+ insertToActivationLayerDB(id, dbData);
+ bModified = true;
+ }
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ // Looks like exceptions being caught here is not an uncommon case.
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "");
+ }
+ }
+ return bModified;
+}
+
+sal_Bool PackageManagerImpl::synchronize(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ check();
+ bool bModified = false;
+ if (m_context == "user")
+ return bModified;
+ bModified |=
+ synchronizeRemovedExtensions(xAbortChannel, xCmdEnv);
+ bModified |= synchronizeAddedExtensions(xAbortChannel, xCmdEnv);
+
+ return bModified;
+}
+
+Sequence< Reference<deployment::XPackage> > PackageManagerImpl::getExtensionsWithUnacceptedLicenses(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ std::vector<Reference<deployment::XPackage> > vec;
+
+ try
+ {
+ const ::osl::MutexGuard guard( m_aMutex );
+ // clean up activation layer, scan for zombie temp dirs:
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ bool bShared = (m_context == "shared");
+
+ for (auto const& elem : id2temp)
+ {
+ //Get the database entry
+ ActivePackages::Data const & dbData = elem.second;
+ sal_Int32 failedPrereq = dbData.failedPrerequisites.toInt32();
+ //If the installation failed for other reason then the license then we
+ //ignore it.
+ if (failedPrereq ^ deployment::Prerequisites::LICENSE)
+ continue;
+
+ //Prepare the URL to the extension
+ OUString url = makeURL(m_activePackages, elem.second.temporaryName);
+ if (bShared)
+ url = makeURLAppendSysPathSegment( url + "_", elem.second.fileName);
+
+ Reference<deployment::XPackage> p = m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), xCmdEnv );
+
+ if (p.is())
+ vec.push_back(p);
+
+ }
+ return ::comphelper::containerToSequence(vec);
+ }
+ catch (const deployment::DeploymentException &)
+ {
+ throw;
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (...)
+ {
+ Any exc = ::cppu::getCaughtException();
+ deployment::DeploymentException de(
+ "PackageManagerImpl::getExtensionsWithUnacceptedLicenses",
+ static_cast<OWeakObject*>(this), exc);
+ exc <<= de;
+ ::cppu::throwException(exc);
+ }
+
+ return ::comphelper::containerToSequence(vec);
+}
+
+sal_Int32 PackageManagerImpl::checkPrerequisites(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ if (!extension.is())
+ return 0;
+ if (m_context != extension->getRepositoryName())
+ throw lang::IllegalArgumentException(
+ "PackageManagerImpl::checkPrerequisites: extension is not from this repository.",
+ nullptr, 0);
+
+ ActivePackages::Data dbData;
+ OUString id = dp_misc::getIdentifier(extension);
+ if (!m_activePackagesDB->get( &dbData, id, OUString()))
+ {
+ throw lang::IllegalArgumentException(
+ "PackageManagerImpl::checkPrerequisites: unknown extension",
+ nullptr, 0);
+
+ }
+ //If the license was already displayed, then do not show it again
+ Reference<ucb::XCommandEnvironment> _xCmdEnv = xCmdEnv;
+ sal_Int32 prereq = dbData.failedPrerequisites.toInt32();
+ if ( !(prereq & deployment::Prerequisites::LICENSE))
+ _xCmdEnv = new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler());
+
+ sal_Int32 failedPrereq = extension->checkPrerequisites(
+ xAbortChannel, _xCmdEnv, false);
+ dbData.failedPrerequisites = OUString::number(failedPrereq);
+ insertToActivationLayerDB(id, dbData);
+ return 0;
+ }
+ catch ( const deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ deployment::DeploymentException exc(
+ "PackageManagerImpl::checkPrerequisites: exception ",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ }
+}
+
+
+PackageManagerImpl::CmdEnvWrapperImpl::~CmdEnvWrapperImpl()
+{
+}
+
+
+PackageManagerImpl::CmdEnvWrapperImpl::CmdEnvWrapperImpl(
+ Reference<XCommandEnvironment> const & xUserCmdEnv,
+ Reference<XProgressHandler> const & xLogFile )
+ : m_xLogFile( xLogFile )
+{
+ if (xUserCmdEnv.is()) {
+ m_xUserProgress.set( xUserCmdEnv->getProgressHandler() );
+ m_xUserInteractionHandler.set( xUserCmdEnv->getInteractionHandler() );
+ }
+}
+
+// XCommandEnvironment
+
+Reference<task::XInteractionHandler>
+PackageManagerImpl::CmdEnvWrapperImpl::getInteractionHandler()
+{
+ return m_xUserInteractionHandler;
+}
+
+
+Reference<XProgressHandler>
+PackageManagerImpl::CmdEnvWrapperImpl::getProgressHandler()
+{
+ return this;
+}
+
+// XProgressHandler
+
+void PackageManagerImpl::CmdEnvWrapperImpl::push( Any const & Status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->push( Status );
+ if (m_xUserProgress.is())
+ m_xUserProgress->push( Status );
+}
+
+
+void PackageManagerImpl::CmdEnvWrapperImpl::update( Any const & Status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->update( Status );
+ if (m_xUserProgress.is())
+ m_xUserProgress->update( Status );
+}
+
+
+void PackageManagerImpl::CmdEnvWrapperImpl::pop()
+{
+ if (m_xLogFile.is())
+ m_xLogFile->pop();
+ if (m_xUserProgress.is())
+ m_xUserProgress->pop();
+}
+
+} // namespace dp_manager
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_manager.h b/desktop/source/deployment/manager/dp_manager.h
new file mode 100644
index 000000000..a2079da23
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_manager.h
@@ -0,0 +1,232 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include "dp_activepackages.hxx"
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+namespace dp_manager {
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::deployment::XPackageManager > t_pm_helper;
+
+
+class PackageManagerImpl final : private cppu::BaseMutex, public t_pm_helper
+{
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+ OUString m_context;
+ OUString m_registrationData;
+ OUString m_registrationData_expanded;
+ OUString m_registryCache;
+ bool m_readOnly;
+
+ OUString m_activePackages;
+ OUString m_activePackages_expanded;
+ std::unique_ptr< ActivePackages > m_activePackagesDB;
+ //This mutex is only used for synchronization in addPackage
+ ::osl::Mutex m_addMutex;
+ css::uno::Reference<css::ucb::XProgressHandler> m_xLogFile;
+ inline void logIntern( css::uno::Any const & status );
+ void fireModified();
+
+ css::uno::Reference<css::deployment::XPackageRegistry> m_xRegistry;
+
+ void initRegistryBackends();
+ void initActivationLayer(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ OUString detectMediaType(
+ ::ucbhelper::Content const & ucbContent, bool throw_exc = true );
+ OUString insertToActivationLayer(
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType,
+ ::ucbhelper::Content const & sourceContent,
+ OUString const & title, ActivePackages::Data * dbData );
+ void insertToActivationLayerDB(
+ OUString const & id, ActivePackages::Data const & dbData );
+
+ static void deletePackageFromCache(
+ css::uno::Reference<css::deployment::XPackage> const & xPackage,
+ OUString const & destFolder );
+
+ bool isInstalled(
+ css::uno::Reference<css::deployment::XPackage> const & package);
+
+ bool synchronizeRemovedExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ bool synchronizeAddedExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ class CmdEnvWrapperImpl
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::ucb::XProgressHandler >
+ {
+ css::uno::Reference<css::ucb::XProgressHandler> m_xLogFile;
+ css::uno::Reference<css::ucb::XProgressHandler> m_xUserProgress;
+ css::uno::Reference<css::task::XInteractionHandler>
+ m_xUserInteractionHandler;
+
+ public:
+ virtual ~CmdEnvWrapperImpl() override;
+ CmdEnvWrapperImpl(
+ css::uno::Reference<css::ucb::XCommandEnvironment>
+ const & xUserCmdEnv,
+ css::uno::Reference<css::ucb::XProgressHandler> const & xLogFile );
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler> SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler> SAL_CALL
+ getProgressHandler() override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+ };
+
+ inline void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageManagerImpl() override;
+ PackageManagerImpl(
+ css::uno::Reference<css::uno::XComponentContext> xComponentContext, OUString context )
+ : t_pm_helper( m_aMutex ),
+ m_xComponentContext(std::move( xComponentContext )),
+ m_context(std::move( context )),
+ m_readOnly( true )
+ {}
+
+public:
+ static css::uno::Reference<css::deployment::XPackageManager> create(
+ css::uno::Reference<css::uno::XComponentContext>
+ const & xComponentContext, OUString const & context );
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+ virtual void SAL_CALL removeEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+ virtual void SAL_CALL removeModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+
+ // XPackageManager
+ virtual OUString SAL_CALL getContext() override;
+ virtual css::uno::Sequence<
+ css::uno::Reference<css::deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL addPackage(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL importExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL removePackage(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ OUString getDeployPath( ActivePackages::Data const & data );
+ css::uno::Reference<css::deployment::XPackage> getDeployedPackage_(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ css::uno::Reference<css::deployment::XPackage> getDeployedPackage_(
+ std::u16string_view id, ActivePackages::Data const & data,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool ignoreAlienPlatforms = false );
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL
+ getDeployedPackage(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ getDeployedPackages_(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getDeployedPackages(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL reinstallDeployedPackages(
+ sal_Bool force,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Bool SAL_CALL isReadOnly( ) override;
+
+ virtual ::sal_Bool SAL_CALL synchronize(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > SAL_CALL
+ getExtensionsWithUnacceptedLicenses(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) override;
+
+ virtual sal_Int32 SAL_CALL checkPrerequisites(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ };
+
+
+inline void PackageManagerImpl::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed)
+ throw css::lang::DisposedException(
+ "PackageManager instance has already been disposed!",
+ static_cast< ::cppu::OWeakObject * >(this) );
+}
+
+
+inline void PackageManagerImpl::logIntern( css::uno::Any const & status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->update( status );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_managerfac.cxx b/desktop/source/deployment/manager/dp_managerfac.cxx
new file mode 100644
index 000000000..79e0ea358
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_managerfac.cxx
@@ -0,0 +1,186 @@
+/* -*- 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 "dp_manager.h"
+#include <dp_misc.h>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/deployment/XPackageManagerFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <unordered_map>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_manager::factory {
+
+typedef ::cppu::WeakComponentImplHelper<
+ deployment::XPackageManagerFactory, lang::XServiceInfo > t_pmfac_helper;
+
+namespace {
+
+class PackageManagerFactoryImpl : private cppu::BaseMutex, public t_pmfac_helper
+{
+ Reference<XComponentContext> m_xComponentContext;
+
+ Reference<deployment::XPackageManager> m_xUserMgr;
+ Reference<deployment::XPackageManager> m_xSharedMgr;
+ Reference<deployment::XPackageManager> m_xBundledMgr;
+ Reference<deployment::XPackageManager> m_xTmpMgr;
+ Reference<deployment::XPackageManager> m_xBakMgr;
+ typedef std::unordered_map<
+ OUString, WeakReference<deployment::XPackageManager> > t_string2weakref;
+ t_string2weakref m_managers;
+
+protected:
+ inline void check();
+ virtual void SAL_CALL disposing() override;
+
+public:
+ explicit PackageManagerFactoryImpl(
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageManagerFactory
+ virtual Reference<deployment::XPackageManager> SAL_CALL getPackageManager(
+ OUString const & context ) override;
+};
+
+}
+
+PackageManagerFactoryImpl::PackageManagerFactoryImpl(
+ Reference<XComponentContext> const & xComponentContext )
+ : t_pmfac_helper( m_aMutex ),
+ m_xComponentContext( xComponentContext )
+{
+}
+
+// XServiceInfo
+OUString PackageManagerFactoryImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.PackageManagerFactory";
+}
+
+sal_Bool PackageManagerFactoryImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > PackageManagerFactoryImpl::getSupportedServiceNames()
+{
+ // a private one:
+ return { "com.sun.star.comp.deployment.PackageManagerFactory" };
+}
+
+inline void PackageManagerFactoryImpl::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed)
+ {
+ throw lang::DisposedException(
+ "PackageManagerFactory instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+
+void PackageManagerFactoryImpl::disposing()
+{
+ // dispose all managers:
+ ::osl::MutexGuard guard( m_aMutex );
+ for (auto const& elem : m_managers)
+ try_dispose( elem.second );
+ m_managers = t_string2weakref();
+ // the below are already disposed:
+ m_xUserMgr.clear();
+ m_xSharedMgr.clear();
+ m_xBundledMgr.clear();
+ m_xTmpMgr.clear();
+ m_xBakMgr.clear();
+}
+
+// XPackageManagerFactory
+
+Reference<deployment::XPackageManager>
+PackageManagerFactoryImpl::getPackageManager( OUString const & context )
+{
+ Reference< deployment::XPackageManager > xRet;
+ ::osl::ResettableMutexGuard guard( m_aMutex );
+ check();
+ t_string2weakref::const_iterator const iFind( m_managers.find( context ) );
+ if (iFind != m_managers.end()) {
+ xRet = iFind->second;
+ if (xRet.is())
+ return xRet;
+ }
+
+ guard.clear();
+ xRet.set( PackageManagerImpl::create( m_xComponentContext, context ) );
+ guard.reset();
+ std::pair< t_string2weakref::iterator, bool > insertion(
+ m_managers.emplace( context, xRet ) );
+ if (insertion.second)
+ {
+ OSL_ASSERT( insertion.first->second.get() == xRet );
+ // hold user, shared mgrs for whole process: live deployment
+ if ( context == "user" )
+ m_xUserMgr = xRet;
+ else if ( context == "shared" )
+ m_xSharedMgr = xRet;
+ else if ( context == "bundled" )
+ m_xBundledMgr = xRet;
+ else if ( context == "tmp" )
+ m_xTmpMgr = xRet;
+ else if ( context == "bak" )
+ m_xBakMgr = xRet;
+ }
+ else
+ {
+ Reference< deployment::XPackageManager > xAlreadyIn(
+ insertion.first->second );
+ if (xAlreadyIn.is())
+ {
+ guard.clear();
+ try_dispose( xRet );
+ xRet = xAlreadyIn;
+ }
+ else
+ {
+ insertion.first->second = xRet;
+ }
+ }
+ return xRet;
+}
+
+} // namespace dp_manager::factory
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_PackageManagerFactory_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new dp_manager::factory::PackageManagerFactoryImpl(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_properties.cxx b/desktop/source/deployment/manager/dp_properties.cxx
new file mode 100644
index 000000000..b0d2245e8
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_properties.cxx
@@ -0,0 +1,145 @@
+/* -*- 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 <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <xmlscript/xml_helper.hxx>
+#include <ucbhelper/content.hxx>
+
+#include <dp_ucb.h>
+#include "dp_properties.hxx"
+
+namespace lang = com::sun::star::lang;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+
+
+using ::com::sun::star::uno::Reference;
+
+constexpr OUStringLiteral PROP_SUPPRESS_LICENSE = u"SUPPRESS_LICENSE";
+constexpr OUStringLiteral PROP_EXTENSION_UPDATE = u"EXTENSION_UPDATE";
+
+namespace dp_manager {
+
+//Reading the file
+ExtensionProperties::ExtensionProperties(
+ std::u16string_view urlExtension,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext) :
+ m_xCmdEnv(xCmdEnv), m_xContext(xContext)
+{
+ m_propFileUrl = OUString::Concat(urlExtension) + "properties";
+
+ std::vector< std::pair< OUString, OUString> > props;
+ if (! dp_misc::create_ucb_content(nullptr, m_propFileUrl, nullptr, false))
+ return;
+
+ ::ucbhelper::Content contentProps(m_propFileUrl, m_xCmdEnv, m_xContext);
+ dp_misc::readProperties(props, contentProps);
+
+ for (auto const& prop : props)
+ {
+ if (prop.first == PROP_SUPPRESS_LICENSE)
+ m_prop_suppress_license = prop.second;
+ }
+}
+
+//Writing the file
+ExtensionProperties::ExtensionProperties(
+ std::u16string_view urlExtension,
+ uno::Sequence<css::beans::NamedValue> const & properties,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext) :
+ m_xCmdEnv(xCmdEnv), m_xContext(xContext)
+{
+ m_propFileUrl = OUString::Concat(urlExtension) + "properties";
+
+ for (css::beans::NamedValue const & v : properties)
+ {
+ if (v.Name == PROP_SUPPRESS_LICENSE)
+ {
+ m_prop_suppress_license = getPropertyValue(v);
+ }
+ else if (v.Name == PROP_EXTENSION_UPDATE)
+ {
+ m_prop_extension_update = getPropertyValue(v);
+ }
+ else
+ {
+ throw lang::IllegalArgumentException(
+ "Extension Manager: unknown property", nullptr, -1);
+ }
+ }
+}
+
+OUString ExtensionProperties::getPropertyValue(css::beans::NamedValue const & v)
+{
+ OUString value("0");
+ if (! (v.Value >>= value) )
+ {
+ throw lang::IllegalArgumentException(
+ "Extension Manager: wrong property value", nullptr, -1);
+ }
+ return value;
+}
+void ExtensionProperties::write()
+{
+ ::ucbhelper::Content contentProps(m_propFileUrl, m_xCmdEnv, m_xContext);
+ OUString buf;
+
+ if (m_prop_suppress_license)
+ {
+ buf = OUString::Concat(PROP_SUPPRESS_LICENSE) + "=" + *m_prop_suppress_license;
+ }
+
+ OString stamp = OUStringToOString(buf, RTL_TEXTENCODING_UTF8);
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ contentProps.writeStream( xData, true /* replace existing */ );
+}
+
+bool ExtensionProperties::isSuppressedLicense() const
+{
+ bool ret = false;
+ if (m_prop_suppress_license)
+ {
+ if (*m_prop_suppress_license == "1")
+ ret = true;
+ }
+ return ret;
+}
+
+bool ExtensionProperties::isExtensionUpdate() const
+{
+ bool ret = false;
+ if (m_prop_extension_update)
+ {
+ if (*m_prop_extension_update == "1")
+ ret = true;
+ }
+ return ret;
+}
+
+} // namespace dp_manager
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_properties.hxx b/desktop/source/deployment/manager/dp_properties.hxx
new file mode 100644
index 000000000..06139ece3
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_properties.hxx
@@ -0,0 +1,58 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <optional>
+#include <string_view>
+
+namespace dp_manager
+{
+class ExtensionProperties final
+{
+ OUString m_propFileUrl;
+ const css::uno::Reference<css::ucb::XCommandEnvironment> m_xCmdEnv;
+ const css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ ::std::optional<OUString> m_prop_suppress_license;
+ ::std::optional<OUString> m_prop_extension_update;
+
+ static OUString getPropertyValue(css::beans::NamedValue const& v);
+
+public:
+ ExtensionProperties(std::u16string_view urlExtension,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const& xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const& xContext);
+
+ ExtensionProperties(std::u16string_view urlExtension,
+ css::uno::Sequence<css::beans::NamedValue> const& properties,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const& xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const& xContext);
+
+ void write();
+
+ bool isSuppressedLicense() const;
+
+ bool isExtensionUpdate() const;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_dependencies.cxx b/desktop/source/deployment/misc/dp_dependencies.cxx
new file mode 100644
index 000000000..e187a6974
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_dependencies.cxx
@@ -0,0 +1,196 @@
+/* -*- 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 <config_folders.h>
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <osl/diagnose.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <unotools/configmgr.hxx>
+
+#include <strings.hrc>
+#include <dp_shared.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_version.hxx>
+
+namespace {
+
+char const namespaceLibreOffice[] =
+ "http://libreoffice.org/extensions/description/2011";
+
+constexpr OUStringLiteral namespaceOpenOfficeOrg =
+ u"http://openoffice.org/extensions/description/2006";
+
+char const minimalVersionLibreOffice[] = "LibreOffice-minimal-version";
+char const maximalVersionLibreOffice[] = "LibreOffice-maximal-version";
+
+constexpr OUStringLiteral minimalVersionOpenOfficeOrg =
+ u"OpenOffice.org-minimal-version";
+
+char const maximalVersionOpenOfficeOrg[] =
+ "OpenOffice.org-maximal-version";
+
+OUString getLibreOfficeMajorMinorMicro() {
+ return utl::ConfigManager::getAboutBoxProductVersion();
+}
+
+OUString getReferenceOpenOfficeOrgMajorMinor() {
+#ifdef ANDROID
+ // just hardcode the version
+ OUString v("4.1");
+#else
+ OUString v(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version")
+ ":Version:ReferenceOOoMajorMinor}");
+ rtl::Bootstrap::expandMacros(v); //TODO: check for failure
+#endif
+ return v;
+}
+
+bool satisfiesMinimalVersion(
+ OUString const & actual, OUString const & specified)
+{
+ return dp_misc::compareVersions(actual, specified) != dp_misc::LESS;
+}
+
+bool satisfiesMaximalVersion(
+ OUString const & actual, OUString const & specified)
+{
+ return dp_misc::compareVersions(actual, specified) != dp_misc::GREATER;
+}
+
+OUString produceErrorText(
+ OUString const & reason, OUString const & version)
+{
+ return reason.replaceFirst("%VERSION",
+ (version.isEmpty()
+ ? DpResId(RID_DEPLOYMENT_DEPENDENCIES_UNKNOWN)
+ : version));
+}
+
+}
+
+namespace dp_misc::Dependencies {
+
+css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+check(dp_misc::DescriptionInfoset const & infoset) {
+ css::uno::Reference< css::xml::dom::XNodeList > deps(
+ infoset.getDependencies());
+ sal_Int32 n = deps->getLength();
+ css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+ unsatisfied(n);
+ auto unsatisfiedRange = asNonConstRange(unsatisfied);
+ sal_Int32 unsat = 0;
+ // check first if minimalVersionLibreOffice is specified -- in that case ignore the legacy OOo dependencies
+ bool bIgnoreOoo = false;
+ for (sal_Int32 i = 0; i < n; ++i) {
+ css::uno::Reference< css::xml::dom::XElement > e(
+ deps->item(i), css::uno::UNO_QUERY_THROW);
+ if ( e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == minimalVersionLibreOffice)
+ {
+ bIgnoreOoo = true;
+ break;
+ }
+ }
+ for (sal_Int32 i = 0; i < n; ++i) {
+ css::uno::Reference< css::xml::dom::XElement > e(
+ deps->item(i), css::uno::UNO_QUERY_THROW);
+ bool sat = false;
+ if ( e->getNamespaceURI() == namespaceOpenOfficeOrg && e->getTagName() == minimalVersionOpenOfficeOrg )
+ {
+ sat = bIgnoreOoo || satisfiesMinimalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttribute("value"));
+ } else if ( e->getNamespaceURI() == namespaceOpenOfficeOrg && e->getTagName() == maximalVersionOpenOfficeOrg )
+ {
+ sat = bIgnoreOoo || satisfiesMaximalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttribute("value"));
+ } else if (e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == minimalVersionLibreOffice )
+ {
+ sat = satisfiesMinimalVersion(
+ getLibreOfficeMajorMinorMicro(),
+ e->getAttribute("value"));
+ } else if (e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == maximalVersionLibreOffice )
+ {
+ sat = satisfiesMaximalVersion(getLibreOfficeMajorMinorMicro(), e->getAttribute("value"));
+ } else if (e->hasAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg))
+ {
+ sat = satisfiesMinimalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg));
+ }
+ if (!sat) {
+ unsatisfiedRange[unsat++] = e;
+ }
+ }
+ unsatisfied.realloc(unsat);
+ return unsatisfied;
+}
+
+OUString getErrorText(
+ css::uno::Reference< css::xml::dom::XElement > const & dependency)
+{
+ OSL_ASSERT(dependency.is());
+ if ( dependency->getNamespaceURI() == namespaceOpenOfficeOrg && dependency->getTagName() == minimalVersionOpenOfficeOrg )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MIN),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceOpenOfficeOrg && dependency->getTagName() == maximalVersionOpenOfficeOrg )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MAX),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceLibreOffice && dependency->getTagName() == minimalVersionLibreOffice )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_LO_MIN),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceLibreOffice && dependency->getTagName() == maximalVersionLibreOffice )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_LO_MAX),
+ dependency->getAttribute("value"));
+ } else if (dependency->hasAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg))
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MIN),
+ dependency->getAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg));
+ } else {
+ return DpResId(RID_DEPLOYMENT_DEPENDENCIES_UNKNOWN);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_descriptioninfoset.cxx b/desktop/source/deployment/misc/dp_descriptioninfoset.cxx
new file mode 100644
index 000000000..00b32c04f
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_descriptioninfoset.cxx
@@ -0,0 +1,809 @@
+/* -*- 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 <string_view>
+
+#include <dp_descriptioninfoset.hxx>
+
+#include <dp_resource.h>
+
+#include <comphelper/sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <optional>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/io/SequenceInputStream.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/xml/dom/DOMException.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/xpath/XPathException.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <ucbhelper/content.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace {
+
+using css::uno::Reference;
+
+class EmptyNodeList:
+ public cppu::WeakImplHelper<css::xml::dom::XNodeList>
+{
+public:
+ EmptyNodeList();
+
+ EmptyNodeList(const EmptyNodeList&) = delete;
+ const EmptyNodeList& operator=(const EmptyNodeList&) = delete;
+
+ virtual ::sal_Int32 SAL_CALL getLength() override;
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL
+ item(::sal_Int32 index) override;
+};
+
+EmptyNodeList::EmptyNodeList() {}
+
+::sal_Int32 EmptyNodeList::getLength() {
+ return 0;
+}
+
+css::uno::Reference< css::xml::dom::XNode > EmptyNodeList::item(::sal_Int32)
+{
+ throw css::uno::RuntimeException("bad EmptyNodeList com.sun.star.xml.dom.XNodeList.item call",
+ static_cast< ::cppu::OWeakObject * >(this));
+}
+
+OUString getNodeValue(
+ css::uno::Reference< css::xml::dom::XNode > const & node)
+{
+ OSL_ASSERT(node.is());
+ try {
+ return node->getNodeValue();
+ } catch (const css::xml::dom::DOMException & e) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "com.sun.star.xml.dom.DOMException: " + e.Message,
+ nullptr, anyEx );
+ }
+}
+
+/**The class uses the UCB to access the description.xml file in an
+ extension. The UCB must have been initialized already. It also
+ requires that the extension has already be unzipped to a particular
+ location.
+ */
+class ExtensionDescription
+{
+public:
+ /**throws an exception if the description.xml is not
+ available, cannot be read, does not contain the expected data,
+ or any other error occurred. Therefore it should only be used with
+ new extensions.
+
+ Throws css::uno::RuntimeException,
+ css::deployment::DeploymentException,
+ dp_registry::backend::bundle::NoDescriptionException.
+ */
+ ExtensionDescription(
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ std::u16string_view installDir,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
+
+ const css::uno::Reference<css::xml::dom::XNode>& getRootElement() const
+ {
+ return m_xRoot;
+ }
+
+private:
+ css::uno::Reference<css::xml::dom::XNode> m_xRoot;
+};
+
+class NoDescriptionException
+{
+};
+
+class FileDoesNotExistFilter
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler >
+
+{
+ bool m_bExist;
+ css::uno::Reference< css::ucb::XCommandEnvironment > m_xCommandEnv;
+
+public:
+ explicit FileDoesNotExistFilter(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
+
+ bool exist() { return m_bExist;}
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+};
+
+ExtensionDescription::ExtensionDescription(
+ const Reference<css::uno::XComponentContext>& xContext,
+ std::u16string_view installDir,
+ const Reference< css::ucb::XCommandEnvironment >& xCmdEnv)
+{
+ try {
+ //may throw css::ucb::ContentCreationException
+ //If there is no description.xml then ucb will start an interaction which
+ //brings up a dialog.We want to prevent this. Therefore we wrap the xCmdEnv
+ //and filter the respective exception out.
+ OUString sDescriptionUri(OUString::Concat(installDir) + "/description.xml");
+ Reference<css::ucb::XCommandEnvironment> xFilter = new FileDoesNotExistFilter(xCmdEnv);
+ ::ucbhelper::Content descContent(sDescriptionUri, xFilter, xContext);
+
+ //throws a css::uno::Exception if the file is not available
+ Reference<css::io::XInputStream> xIn;
+ try
+ { //throws com.sun.star.ucb.InteractiveIOException
+ xIn = descContent.openStream();
+ }
+ catch ( const css::uno::Exception& )
+ {
+ if ( ! static_cast<FileDoesNotExistFilter*>(xFilter.get())->exist())
+ throw NoDescriptionException();
+ throw;
+ }
+ if (!xIn.is())
+ {
+ throw css::uno::Exception(
+ "Could not get XInputStream for description.xml of extension " +
+ sDescriptionUri, nullptr);
+ }
+
+ //get root node of description.xml
+ Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
+ css::xml::dom::DocumentBuilder::create(xContext) );
+
+ if (!xDocBuilder->isNamespaceAware())
+ {
+ throw css::uno::Exception(
+ "Service com.sun.star.xml.dom.DocumentBuilder is not namespace aware.", nullptr);
+ }
+
+ Reference<css::xml::dom::XDocument> xDoc = xDocBuilder->parse(xIn);
+ if (!xDoc.is())
+ {
+ throw css::uno::Exception(sDescriptionUri + " contains data which cannot be parsed. ", nullptr);
+ }
+
+ //check for proper root element and namespace
+ Reference<css::xml::dom::XElement> xRoot = xDoc->getDocumentElement();
+ if (!xRoot.is())
+ {
+ throw css::uno::Exception(
+ sDescriptionUri + " contains no root element.", nullptr);
+ }
+
+ if ( xRoot->getTagName() != "description")
+ {
+ throw css::uno::Exception(
+ sDescriptionUri + " does not contain the root element <description>.", nullptr);
+ }
+
+ m_xRoot.set(xRoot, css::uno::UNO_QUERY_THROW);
+ OUString nsDescription = xRoot->getNamespaceURI();
+
+ //check if this namespace is supported
+ if ( nsDescription != "http://openoffice.org/extensions/description/2006")
+ {
+ throw css::uno::Exception(sDescriptionUri + " contains a root element with an unsupported namespace. ", nullptr);
+ }
+ } catch (const css::uno::RuntimeException &) {
+ throw;
+ } catch (const css::deployment::DeploymentException &) {
+ throw;
+ } catch (const css::uno::Exception & e) {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::deployment::DeploymentException(
+ e.Message, Reference< css::uno::XInterface >(), a);
+ }
+}
+
+FileDoesNotExistFilter::FileDoesNotExistFilter(
+ const Reference< css::ucb::XCommandEnvironment >& xCmdEnv):
+ m_bExist(true), m_xCommandEnv(xCmdEnv)
+{}
+
+ // XCommandEnvironment
+Reference<css::task::XInteractionHandler >
+ FileDoesNotExistFilter::getInteractionHandler()
+{
+ return static_cast<css::task::XInteractionHandler*>(this);
+}
+
+Reference<css::ucb::XProgressHandler >
+ FileDoesNotExistFilter::getProgressHandler()
+{
+ return m_xCommandEnv.is()
+ ? m_xCommandEnv->getProgressHandler()
+ : Reference<css::ucb::XProgressHandler>();
+}
+
+// XInteractionHandler
+//If the interaction was caused by a non-existing file which is specified in the ctor
+//of FileDoesNotExistFilter, then we do nothing
+void FileDoesNotExistFilter::handle(
+ Reference<css::task::XInteractionRequest > const & xRequest )
+{
+ css::uno::Any request( xRequest->getRequest() );
+
+ css::ucb::InteractiveIOException ioexc;
+ if ((request>>= ioexc)
+ && (ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING
+ || ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING_PATH))
+ {
+ m_bExist = false;
+ return;
+ }
+ Reference<css::task::XInteractionHandler> xInteraction;
+ if (m_xCommandEnv.is()) {
+ xInteraction = m_xCommandEnv->getInteractionHandler();
+ }
+ if (xInteraction.is()) {
+ xInteraction->handle(xRequest);
+ }
+}
+
+}
+
+namespace dp_misc {
+
+DescriptionInfoset getDescriptionInfoset(std::u16string_view sExtensionFolderURL)
+{
+ Reference< css::xml::dom::XNode > root;
+ Reference<css::uno::XComponentContext> context(
+ comphelper::getProcessComponentContext());
+ try {
+ root =
+ ExtensionDescription(
+ context, sExtensionFolderURL,
+ Reference< css::ucb::XCommandEnvironment >()).
+ getRootElement();
+ } catch (const NoDescriptionException &) {
+ } catch (const css::deployment::DeploymentException & e) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "com.sun.star.deployment.DeploymentException: " + e.Message,
+ nullptr, anyEx );
+ }
+ return DescriptionInfoset(context, root);
+}
+
+DescriptionInfoset::DescriptionInfoset(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ css::uno::Reference< css::xml::dom::XNode > const & element):
+ m_context(context),
+ m_element(element)
+{
+ if (m_element.is()) {
+ m_xpath = css::xml::xpath::XPathAPI::create(context);
+ m_xpath->registerNS("desc", element->getNamespaceURI());
+ m_xpath->registerNS("xlink", "http://www.w3.org/1999/xlink");
+ }
+}
+
+DescriptionInfoset::~DescriptionInfoset() {}
+
+::std::optional< OUString > DescriptionInfoset::getIdentifier() const {
+ return getOptionalValue("desc:identifier/@value");
+}
+
+OUString DescriptionInfoset::getNodeValueFromExpression(OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return n.is() ? getNodeValue(n) : OUString();
+}
+
+void DescriptionInfoset::checkDenylist() const
+{
+ if (!m_element.is())
+ return;
+
+ std::optional< OUString > id(getIdentifier());
+ if (!id)
+ return; // nothing to check
+ OUString currentversion(getVersion());
+ if (currentversion.getLength() == 0)
+ return; // nothing to check
+
+ css::uno::Sequence<css::uno::Any> args(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", css::uno::Any(OUString("/org.openoffice.Office.ExtensionDependencies/Extensions"))}
+ }));
+ css::uno::Reference< css::container::XNameAccess > denylist(
+ (css::configuration::theDefaultProvider::get(m_context)
+ ->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", args)),
+ css::uno::UNO_QUERY_THROW);
+
+ // check first if a denylist entry is available
+ if (!(denylist.is() && denylist->hasByName(*id))) return;
+
+ css::uno::Reference< css::beans::XPropertySet > extProps(
+ denylist->getByName(*id), css::uno::UNO_QUERY_THROW);
+
+ css::uno::Any anyValue = extProps->getPropertyValue("Versions");
+
+ css::uno::Sequence< OUString > blversions;
+ anyValue >>= blversions;
+
+ // check if the current version requires further dependency checks from the denylist
+ if (!checkDenylistVersion(currentversion, blversions)) return;
+
+ anyValue = extProps->getPropertyValue("Dependencies");
+ OUString udeps;
+ anyValue >>= udeps;
+
+ if (udeps.getLength() == 0)
+ return; // nothing todo
+
+ OString xmlDependencies = OUStringToOString(udeps, RTL_TEXTENCODING_UNICODE);
+
+ css::uno::Reference< css::xml::dom::XDocumentBuilder> docbuilder(
+ m_context->getServiceManager()->createInstanceWithContext("com.sun.star.xml.dom.DocumentBuilder", m_context),
+ css::uno::UNO_QUERY_THROW);
+
+ css::uno::Sequence< sal_Int8 > byteSeq(reinterpret_cast<const sal_Int8*>(xmlDependencies.getStr()), xmlDependencies.getLength());
+
+ css::uno::Reference< css::io::XInputStream> inputstream( css::io::SequenceInputStream::createStreamFromSequence(m_context, byteSeq),
+ css::uno::UNO_QUERY_THROW);
+
+ css::uno::Reference< css::xml::dom::XDocument > xDocument(docbuilder->parse(inputstream));
+ css::uno::Reference< css::xml::dom::XElement > xElement(xDocument->getDocumentElement());
+ css::uno::Reference< css::xml::dom::XNodeList > xDeps(xElement->getChildNodes());
+ sal_Int32 nLen = xDeps->getLength();
+
+ // get the parent xml document of current description info for the import
+ css::uno::Reference< css::xml::dom::XDocument > xCurrentDescInfo(m_element->getOwnerDocument());
+
+ // get dependency node of current description info to merge the new dependencies from the denylist
+ css::uno::Reference< css::xml::dom::XNode > xCurrentDeps(
+ m_xpath->selectSingleNode(m_element, "desc:dependencies"));
+
+ // if no dependency node exists, create a new one in the current description info
+ if (!xCurrentDeps.is()) {
+ css::uno::Reference< css::xml::dom::XNode > xNewDepNode(
+ xCurrentDescInfo->createElementNS(
+ "http://openoffice.org/extensions/description/2006",
+ "dependencies"), css::uno::UNO_QUERY_THROW);
+ m_element->appendChild(xNewDepNode);
+ xCurrentDeps = m_xpath->selectSingleNode(m_element, "desc:dependencies");
+ }
+
+ for (sal_Int32 i=0; i<nLen; i++) {
+ css::uno::Reference< css::xml::dom::XNode > xNode(xDeps->item(i));
+ css::uno::Reference< css::xml::dom::XElement > xDep(xNode, css::uno::UNO_QUERY);
+ if (xDep.is()) {
+ // found valid denylist dependency, import the node first and append it to the existing dependency node
+ css::uno::Reference< css::xml::dom::XNode > importedNode = xCurrentDescInfo->importNode(xNode, true);
+ xCurrentDeps->appendChild(importedNode);
+ }
+ }
+}
+
+bool DescriptionInfoset::checkDenylistVersion(
+ std::u16string_view currentversion,
+ css::uno::Sequence< OUString > const & versions)
+{
+ sal_Int32 nLen = versions.getLength();
+ for (sal_Int32 i=0; i<nLen; i++) {
+ if (currentversion == versions[i])
+ return true;
+ }
+
+ return false;
+}
+
+OUString DescriptionInfoset::getVersion() const
+{
+ return getNodeValueFromExpression( "desc:version/@value" );
+}
+
+css::uno::Sequence< OUString > DescriptionInfoset::getSupportedPlatforms() const
+{
+ //When there is no description.xml then we assume that we support all platforms
+ if (! m_element.is())
+ {
+ return { OUString("all") };
+ }
+
+ //Check if the <platform> element was provided. If not the default is "all" platforms
+ css::uno::Reference< css::xml::dom::XNode > nodePlatform(
+ m_xpath->selectSingleNode(m_element, "desc:platform"));
+ if (!nodePlatform.is())
+ {
+ return { OUString("all") };
+ }
+
+ //There is a platform element.
+ const OUString value = getNodeValueFromExpression("desc:platform/@value");
+ //parse the string, it can contained multiple strings separated by commas
+ std::vector< OUString> vec;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ const OUString aToken( o3tl::trim(o3tl::getToken(value, 0, ',', nIndex )) );
+ if (!aToken.isEmpty())
+ vec.push_back(aToken);
+
+ }
+ while (nIndex >= 0);
+
+ return comphelper::containerToSequence(vec);
+}
+
+css::uno::Reference< css::xml::dom::XNodeList >
+DescriptionInfoset::getDependencies() const {
+ if (m_element.is()) {
+ try {
+ // check the extension denylist first and expand the dependencies if applicable
+ checkDenylist();
+
+ return m_xpath->selectNodeList(m_element, "desc:dependencies/*");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return new EmptyNodeList;
+}
+
+css::uno::Sequence< OUString >
+DescriptionInfoset::getUpdateInformationUrls() const {
+ return getUrls("desc:update-information/desc:src/@xlink:href");
+}
+
+css::uno::Sequence< OUString >
+DescriptionInfoset::getUpdateDownloadUrls() const
+{
+ return getUrls("desc:update-download/desc:src/@xlink:href");
+}
+
+OUString DescriptionInfoset::getIconURL( bool bHighContrast ) const
+{
+ css::uno::Sequence< OUString > aStrList = getUrls( "desc:icon/desc:default/@xlink:href" );
+ css::uno::Sequence< OUString > aStrListHC = getUrls( "desc:icon/desc:high-contrast/@xlink:href" );
+
+ if ( bHighContrast && aStrListHC.hasElements() && !aStrListHC[0].isEmpty() )
+ return aStrListHC[0];
+
+ if ( aStrList.hasElements() && !aStrList[0].isEmpty() )
+ return aStrList[0];
+
+ return OUString();
+}
+
+::std::optional< OUString > DescriptionInfoset::getLocalizedUpdateWebsiteURL()
+ const
+{
+ bool bParentExists = false;
+ const OUString sURL (getLocalizedHREFAttrFromChild("/desc:description/desc:update-website", &bParentExists ));
+
+ if (!sURL.isEmpty())
+ return ::std::optional< OUString >(sURL);
+ else
+ return bParentExists ? ::std::optional< OUString >(OUString()) :
+ ::std::optional< OUString >();
+}
+
+::std::optional< OUString > DescriptionInfoset::getOptionalValue(
+ OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return n.is()
+ ? ::std::optional< OUString >(getNodeValue(n))
+ : ::std::optional< OUString >();
+}
+
+css::uno::Sequence< OUString > DescriptionInfoset::getUrls(
+ OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNodeList > ns;
+ if (m_element.is()) {
+ try {
+ ns = m_xpath->selectNodeList(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ css::uno::Sequence< OUString > urls(ns.is() ? ns->getLength() : 0);
+ auto urlsRange = asNonConstRange(urls);
+ for (::sal_Int32 i = 0; i < urls.getLength(); ++i) {
+ urlsRange[i] = getNodeValue(ns->item(i));
+ }
+ return urls;
+}
+
+std::pair< OUString, OUString > DescriptionInfoset::getLocalizedPublisherNameAndURL() const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild("desc:publisher");
+
+ OUString sPublisherName;
+ OUString sURL;
+ if (node.is())
+ {
+ css::uno::Reference< css::xml::dom::XNode > xPathName;
+ try {
+ xPathName = m_xpath->selectSingleNode(node, "text()");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xPathName.is());
+ if (xPathName.is())
+ sPublisherName = xPathName->getNodeValue();
+
+ css::uno::Reference< css::xml::dom::XNode > xURL;
+ try {
+ xURL = m_xpath->selectSingleNode(node, "@xlink:href");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xURL.is());
+ if (xURL.is())
+ sURL = xURL->getNodeValue();
+ }
+ return std::make_pair(sPublisherName, sURL);
+}
+
+OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:release-notes", nullptr);
+}
+
+OUString DescriptionInfoset::getLocalizedDisplayName() const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild("desc:display-name");
+ if (node.is())
+ {
+ css::uno::Reference< css::xml::dom::XNode > xtext;
+ try {
+ xtext = m_xpath->selectSingleNode(node, "text()");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (xtext.is())
+ return xtext->getNodeValue();
+ }
+ return OUString();
+}
+
+OUString DescriptionInfoset::getLocalizedLicenseURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:registration/desc:simple-license", nullptr);
+
+}
+
+::std::optional<SimpleLicenseAttributes>
+DescriptionInfoset::getSimpleLicenseAttributes() const
+{
+ //Check if the node exist
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, "/desc:description/desc:registration/desc:simple-license/@accept-by");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (n.is())
+ {
+ SimpleLicenseAttributes attributes;
+ attributes.acceptBy =
+ getNodeValueFromExpression("/desc:description/desc:registration/desc:simple-license/@accept-by");
+
+ ::std::optional< OUString > suppressOnUpdate = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-on-update");
+ if (suppressOnUpdate)
+ attributes.suppressOnUpdate = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressOnUpdate), u"true");
+ else
+ attributes.suppressOnUpdate = false;
+
+ ::std::optional< OUString > suppressIfRequired = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-if-required");
+ if (suppressIfRequired)
+ attributes.suppressIfRequired = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressIfRequired), u"true");
+ else
+ attributes.suppressIfRequired = false;
+
+ return ::std::optional<SimpleLicenseAttributes>(attributes);
+ }
+ }
+ return ::std::optional<SimpleLicenseAttributes>();
+}
+
+OUString DescriptionInfoset::getLocalizedDescriptionURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:extension-description", nullptr);
+}
+
+css::uno::Reference< css::xml::dom::XNode >
+DescriptionInfoset::getLocalizedChild( const OUString & sParent) const
+{
+ if ( ! m_element.is() || sParent.isEmpty())
+ return css::uno::Reference< css::xml::dom::XNode > ();
+
+ css::uno::Reference< css::xml::dom::XNode > xParent;
+ try {
+ xParent = m_xpath->selectSingleNode(m_element, sParent);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ css::uno::Reference<css::xml::dom::XNode> nodeMatch;
+ if (xParent.is())
+ {
+ nodeMatch = matchLanguageTag(xParent, getOfficeLanguageTag().getBcp47());
+
+ //office: en-DE, en, en-DE-altmark
+ if (! nodeMatch.is())
+ {
+ // Already tried full tag, continue with first fallback.
+ const std::vector< OUString > aFallbacks( getOfficeLanguageTag().getFallbackStrings( false));
+ for (auto const& fallback : aFallbacks)
+ {
+ nodeMatch = matchLanguageTag(xParent, fallback);
+ if (nodeMatch.is())
+ break;
+ }
+ if (! nodeMatch.is())
+ nodeMatch = getChildWithDefaultLocale(xParent);
+ }
+ }
+
+ return nodeMatch;
+}
+
+css::uno::Reference<css::xml::dom::XNode>
+DescriptionInfoset::matchLanguageTag(
+ css::uno::Reference< css::xml::dom::XNode > const & xParent, std::u16string_view rTag) const
+{
+ OSL_ASSERT(xParent.is());
+ css::uno::Reference<css::xml::dom::XNode> nodeMatch;
+
+ //first try exact match for lang
+ const OUString exp1(OUString::Concat("*[@lang=\"") + rTag + "\"]");
+ try {
+ nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+
+ //try to match in strings that also have a country and/or variant, for
+ //example en matches in en-US-montana, en-US, en-montana
+ if (!nodeMatch.is())
+ {
+ const OUString exp2(
+ OUString::Concat("*[starts-with(@lang,\"") + rTag + "-\")]");
+ try {
+ nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return nodeMatch;
+}
+
+css::uno::Reference<css::xml::dom::XNode>
+DescriptionInfoset::getChildWithDefaultLocale(css::uno::Reference< css::xml::dom::XNode >
+ const & xParent) const
+{
+ OSL_ASSERT(xParent.is());
+ if ( xParent->getNodeName() == "simple-license" )
+ {
+ css::uno::Reference<css::xml::dom::XNode> nodeDefault;
+ try {
+ nodeDefault = m_xpath->selectSingleNode(xParent, "@default-license-id");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (nodeDefault.is())
+ {
+ //The old way
+ const OUString exp1("desc:license-text[@license-id = \""
+ + nodeDefault->getNodeValue()
+ + "\"]");
+ try {
+ return m_xpath->selectSingleNode(xParent, exp1);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ }
+
+ try {
+ return m_xpath->selectSingleNode(xParent, "*[1]");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ return nullptr;
+ }
+}
+
+OUString DescriptionInfoset::getLocalizedHREFAttrFromChild(
+ OUString const & sXPathParent, bool * out_bParentExists)
+ const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild(sXPathParent);
+
+ OUString sURL;
+ if (node.is())
+ {
+ if (out_bParentExists)
+ *out_bParentExists = true;
+ css::uno::Reference< css::xml::dom::XNode > xURL;
+ try {
+ xURL = m_xpath->selectSingleNode(node, "@xlink:href");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xURL.is());
+ if (xURL.is())
+ sURL = xURL->getNodeValue();
+ }
+ else
+ {
+ if (out_bParentExists)
+ *out_bParentExists = false;
+ }
+ return sURL;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_identifier.cxx b/desktop/source/deployment/misc/dp_identifier.cxx
new file mode 100644
index 000000000..8669710c7
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_identifier.cxx
@@ -0,0 +1,56 @@
+/* -*- 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 <optional>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+
+#include <dp_identifier.hxx>
+
+namespace dp_misc {
+
+OUString generateIdentifier(
+ ::std::optional< OUString > const & optional,
+ std::u16string_view fileName)
+{
+ return optional ? *optional : generateLegacyIdentifier(fileName);
+}
+
+OUString getIdentifier(
+ css::uno::Reference< css::deployment::XPackage > const & package)
+{
+ OSL_ASSERT(package.is());
+ css::beans::Optional< OUString > id(package->getIdentifier());
+ return id.IsPresent
+ ? id.Value : generateLegacyIdentifier(package->getName());
+}
+
+OUString generateLegacyIdentifier(std::u16string_view fileName) {
+ return OUString::Concat("org.openoffice.legacy.") + fileName;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_interact.cxx b/desktop/source/deployment/misc/dp_interact.cxx
new file mode 100644
index 000000000..ae928a28e
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_interact.cxx
@@ -0,0 +1,137 @@
+/* -*- 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 <dp_interact.h>
+
+#include <comphelper/interaction.hxx>
+
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <osl/diagnose.h>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_misc {
+namespace {
+
+
+class InteractionContinuationImpl : public ::cppu::OWeakObject,
+ public task::XInteractionContinuation
+{
+ const Type m_type;
+ bool * m_pselect;
+
+public:
+ InteractionContinuationImpl( Type const & type, bool * pselect )
+ : m_type( type ),
+ m_pselect( pselect )
+ { OSL_ASSERT(
+ cppu::UnoType<task::XInteractionContinuation>::get().isAssignableFrom(m_type) ); }
+
+ // XInterface
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+ virtual Any SAL_CALL queryInterface( Type const & type ) override;
+
+ // XInteractionContinuation
+ virtual void SAL_CALL select() override;
+};
+
+// XInterface
+
+void InteractionContinuationImpl::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+void InteractionContinuationImpl::release() noexcept
+{
+ OWeakObject::release();
+}
+
+
+Any InteractionContinuationImpl::queryInterface( Type const & type )
+{
+ if (type.isAssignableFrom( m_type )) {
+ Reference<task::XInteractionContinuation> xThis(this);
+ return Any( &xThis, type );
+ }
+ else
+ return OWeakObject::queryInterface(type);
+}
+
+// XInteractionContinuation
+
+void InteractionContinuationImpl::select()
+{
+ *m_pselect = true;
+}
+
+} // anon namespace
+
+
+bool interactContinuation( Any const & request,
+ Type const & continuation,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool * pcont, bool * pabort )
+{
+ OSL_ASSERT(
+ cppu::UnoType<task::XInteractionContinuation>::get().isAssignableFrom(
+ continuation ) );
+ if (!xCmdEnv)
+ return false;
+
+ Reference<task::XInteractionHandler> xInteractionHandler(
+ xCmdEnv->getInteractionHandler() );
+ if (!xInteractionHandler)
+ return false;
+
+ bool cont = false;
+ bool abort = false;
+ std::vector< Reference<task::XInteractionContinuation> > conts {
+ new InteractionContinuationImpl(continuation, &cont ),
+ new InteractionContinuationImpl( cppu::UnoType<task::XInteractionAbort>::get(), &abort ) };
+ xInteractionHandler->handle(
+ new ::comphelper::OInteractionRequest( request, std::move(conts) ) );
+ if (cont || abort) {
+ if (pcont != nullptr)
+ *pcont = cont;
+ if (pabort != nullptr)
+ *pabort = abort;
+ return true;
+ }
+ return false;
+}
+
+// XAbortChannel
+
+void AbortChannel::sendAbort()
+{
+ m_aborted = true;
+ if (m_xNext.is())
+ m_xNext->sendAbort();
+}
+
+} // dp_misc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_misc.cxx b/desktop/source/deployment/misc/dp_misc.cxx
new file mode 100644
index 000000000..f4437711d
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_misc.cxx
@@ -0,0 +1,561 @@
+/* -*- 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 <config_folders.h>
+#include <config_features.h>
+#include <chrono>
+
+#include <dp_misc.h>
+#include <dp_interact.h>
+#include <dp_shared.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/digest.h>
+#include <rtl/random.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <unotools/bootstrap.hxx>
+#include <osl/file.hxx>
+#include <osl/pipe.hxx>
+#include <osl/security.hxx>
+#include <osl/thread.hxx>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/bridge/BridgeFactory.hpp>
+#include <com/sun/star/bridge/UnoUrlResolver.hpp>
+#include <com/sun/star/bridge/XUnoUrlResolver.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <memory>
+#include <string_view>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <salhelper/linkhelper.hxx>
+
+#ifdef _WIN32
+#include <prewin.h>
+#include <postwin.h>
+#endif
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_misc {
+namespace {
+
+struct UnoRc : public rtl::StaticWithInit<
+ std::shared_ptr<rtl::Bootstrap>, UnoRc> {
+ std::shared_ptr<rtl::Bootstrap> operator () () {
+ OUString unorc( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("louno") );
+ ::rtl::Bootstrap::expandMacros( unorc );
+ auto ret = std::make_shared<::rtl::Bootstrap>( unorc );
+ OSL_ASSERT( ret->getHandle() != nullptr );
+ return ret;
+ }
+};
+
+OUString generateOfficePipeId()
+{
+ OUString userPath;
+ ::utl::Bootstrap::PathStatus aLocateResult =
+ ::utl::Bootstrap::locateUserInstallation( userPath );
+ if (aLocateResult != ::utl::Bootstrap::PATH_EXISTS &&
+ aLocateResult != ::utl::Bootstrap::PATH_VALID)
+ {
+ throw Exception("Extension Manager: Could not obtain path for UserInstallation.", nullptr);
+ }
+
+ rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
+ if (!digest) {
+ throw RuntimeException("cannot get digest rtl_Digest_AlgorithmMD5!", nullptr );
+ }
+
+ sal_uInt8 const * data =
+ reinterpret_cast<sal_uInt8 const *>(userPath.getStr());
+ std::size_t size = userPath.getLength() * sizeof (sal_Unicode);
+ sal_uInt32 md5_key_len = rtl_digest_queryLength( digest );
+ std::unique_ptr<sal_uInt8[]> md5_buf( new sal_uInt8 [ md5_key_len ] );
+
+ rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) );
+ rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) );
+ rtl_digest_get( digest, md5_buf.get(), md5_key_len );
+ rtl_digest_destroy( digest );
+
+ // create hex-value string from the MD5 value to keep
+ // the string size minimal
+ OUStringBuffer buf;
+ buf.append( "SingleOfficeIPC_" );
+ for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) {
+ buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 );
+ }
+ return buf.makeStringAndClear();
+}
+
+bool existsOfficePipe()
+{
+ static OUString OfficePipeId = generateOfficePipeId();
+
+ OUString const & pipeId = OfficePipeId;
+ if (pipeId.isEmpty())
+ return false;
+ ::osl::Security sec;
+ ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec );
+ return pipe.is();
+}
+
+//get modification time
+bool getModifyTimeTargetFile(const OUString &rFileURL, TimeValue &rTime)
+{
+ salhelper::LinkResolver aResolver(osl_FileStatus_Mask_ModifyTime);
+
+ if (aResolver.fetchFileStatus(rFileURL) != osl::FileBase::E_None)
+ return false;
+
+ rTime = aResolver.m_aStatus.getModifyTime();
+
+ return true;
+}
+
+//Returns true if the Folder was more recently modified then
+//the lastsynchronized file. That is the repository needs to
+//be synchronized.
+bool compareExtensionFolderWithLastSynchronizedFile(
+ OUString const & folderURL, OUString const & fileURL)
+{
+ bool bNeedsSync = false;
+ ::osl::DirectoryItem itemExtFolder;
+ ::osl::File::RC err1 =
+ ::osl::DirectoryItem::get(folderURL, itemExtFolder);
+ //If it does not exist, then there is nothing to be done
+ if (err1 == ::osl::File::E_NOENT)
+ {
+ return false;
+ }
+ else if (err1 != ::osl::File::E_None)
+ {
+ OSL_FAIL("Cannot access extension folder");
+ return true; //sync just in case
+ }
+
+ //If last synchronized does not exist, then OOo is started for the first time
+ ::osl::DirectoryItem itemFile;
+ ::osl::File::RC err2 = ::osl::DirectoryItem::get(fileURL, itemFile);
+ if (err2 == ::osl::File::E_NOENT)
+ {
+ return true;
+
+ }
+ else if (err2 != ::osl::File::E_None)
+ {
+ OSL_FAIL("Cannot access file lastsynchronized");
+ return true; //sync just in case
+ }
+
+ //compare the modification time of the extension folder and the last
+ //modified file
+ TimeValue timeFolder;
+ if (getModifyTimeTargetFile(folderURL, timeFolder))
+ {
+ TimeValue timeFile;
+ if (getModifyTimeTargetFile(fileURL, timeFile))
+ {
+ if (timeFile.Seconds < timeFolder.Seconds)
+ bNeedsSync = true;
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ bNeedsSync = true;
+ }
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ bNeedsSync = true;
+ }
+
+ return bNeedsSync;
+}
+
+bool needToSyncRepository(std::u16string_view name)
+{
+ OUString folder;
+ OUString file;
+ if ( name == u"bundled" )
+ {
+ folder = "$BUNDLED_EXTENSIONS";
+ file = "$BUNDLED_EXTENSIONS_USER/lastsynchronized";
+ }
+ else if ( name == u"shared" )
+ {
+ folder = "$UNO_SHARED_PACKAGES_CACHE/uno_packages";
+ file = "$SHARED_EXTENSIONS_USER/lastsynchronized";
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ return true;
+ }
+ ::rtl::Bootstrap::expandMacros(folder);
+ ::rtl::Bootstrap::expandMacros(file);
+ return compareExtensionFolderWithLastSynchronizedFile(
+ folder, file);
+}
+
+
+} // anon namespace
+
+
+namespace {
+OUString encodeForRcFile( OUString const & str )
+{
+ // escape $\{} (=> rtl bootstrap files)
+ OUStringBuffer buf(64);
+ sal_Int32 pos = 0;
+ const sal_Int32 len = str.getLength();
+ for ( ; pos < len; ++pos ) {
+ sal_Unicode c = str[ pos ];
+ switch (c) {
+ case '$':
+ case '\\':
+ case '{':
+ case '}':
+ buf.append( '\\' );
+ break;
+ }
+ buf.append( c );
+ }
+ return buf.makeStringAndClear();
+}
+}
+
+
+OUString makeURL( OUString const & baseURL, OUString const & relPath_ )
+{
+ OUStringBuffer buf(128);
+ if (baseURL.getLength() > 1 && baseURL[ baseURL.getLength() - 1 ] == '/')
+ buf.append( baseURL.subView(0, baseURL.getLength() - 1) );
+ else
+ buf.append( baseURL );
+ OUString relPath(relPath_);
+ if( relPath.startsWith("/") )
+ relPath = relPath.copy( 1 );
+ if (!relPath.isEmpty())
+ {
+ buf.append( '/' );
+ if (baseURL.match( "vnd.sun.star.expand:" )) {
+ // encode for macro expansion: relPath is supposed to have no
+ // macros, so encode $, {} \ (bootstrap mimic)
+ relPath = encodeForRcFile(relPath);
+
+ // encode once more for vnd.sun.star.expand schema:
+ // vnd.sun.star.expand:$UNO_...
+ // will expand to file-url
+ relPath = ::rtl::Uri::encode( relPath, rtl_UriCharClassUric,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ }
+ buf.append( relPath );
+ }
+ return buf.makeStringAndClear();
+}
+
+OUString makeURLAppendSysPathSegment( OUString const & baseURL, OUString const & segment )
+{
+ OSL_ASSERT(segment.indexOf(u'/') == -1);
+
+ ::rtl::Uri::encode(
+ segment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8);
+ return makeURL(baseURL, segment);
+}
+
+
+OUString expandUnoRcTerm( OUString const & term_ )
+{
+ OUString term(term_);
+ UnoRc::get()->expandMacrosFrom( term );
+ return term;
+}
+
+OUString makeRcTerm( OUString const & url )
+{
+ OSL_ASSERT( url.match( "vnd.sun.star.expand:" ));
+ if (url.match( "vnd.sun.star.expand:" )) {
+ // cut protocol:
+ OUString rcterm( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
+ // decode uric class chars:
+ rcterm = ::rtl::Uri::decode(
+ rcterm, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ return rcterm;
+ }
+ else
+ return url;
+}
+
+
+OUString expandUnoRcUrl( OUString const & url )
+{
+ if (url.match( "vnd.sun.star.expand:" )) {
+ // cut protocol:
+ OUString rcurl( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
+ // decode uric class chars:
+ rcurl = ::rtl::Uri::decode(
+ rcurl, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ // expand macro string:
+ UnoRc::get()->expandMacrosFrom( rcurl );
+ return rcurl;
+ }
+ else {
+ return url;
+ }
+}
+
+
+bool office_is_running()
+{
+ //We need to check if we run within the office process. Then we must not use the pipe, because
+ //this could cause a deadlock. This is actually a workaround for i82778
+ OUString sFile;
+ oslProcessError err = osl_getExecutableFile(& sFile.pData);
+ bool ret = false;
+ if (osl_Process_E_None == err)
+ {
+ sFile = sFile.copy(sFile.lastIndexOf('/') + 1);
+ if (
+#if defined _WIN32
+ //osl_getExecutableFile should deliver "soffice.bin" on windows
+ //even if swriter.exe, scalc.exe etc. was started. This is a bug
+ //in osl_getExecutableFile
+ sFile == "soffice.bin" || sFile == "soffice.exe" || sFile == "soffice.com"
+ || sFile == "soffice" || sFile == "swriter.exe" || sFile == "swriter"
+ || sFile == "scalc.exe" || sFile == "scalc" || sFile == "simpress.exe"
+ || sFile == "simpress" || sFile == "sdraw.exe" || sFile == "sdraw"
+ || sFile == "sbase.exe" || sFile == "sbase"
+#elif defined MACOSX
+ sFile == "soffice"
+#elif defined UNIX
+ sFile == "soffice.bin"
+#else
+#error "Unsupported platform"
+#endif
+
+ )
+ ret = true;
+ else
+ ret = existsOfficePipe();
+ }
+ else
+ {
+ OSL_FAIL("NOT osl_Process_E_None ");
+ //if osl_getExecutable file then we take the risk of creating a pipe
+ ret = existsOfficePipe();
+ }
+ return ret;
+}
+
+
+oslProcess raiseProcess(
+ OUString const & appURL, Sequence<OUString> const & args )
+{
+ ::osl::Security sec;
+ oslProcess hProcess = nullptr;
+ oslProcessError rc = osl_executeProcess(
+ appURL.pData,
+ reinterpret_cast<rtl_uString **>(
+ const_cast<OUString *>(args.getConstArray()) ),
+ args.getLength(),
+ osl_Process_DETACHED,
+ sec.getHandle(),
+ nullptr, // => current working dir
+ nullptr, 0, // => no env vars
+ &hProcess );
+
+ switch (rc) {
+ case osl_Process_E_None:
+ break;
+ case osl_Process_E_NotFound:
+ throw RuntimeException( "image not found!", nullptr );
+ case osl_Process_E_TimedOut:
+ throw RuntimeException( "timeout occurred!", nullptr );
+ case osl_Process_E_NoPermission:
+ throw RuntimeException( "permission denied!", nullptr );
+ case osl_Process_E_Unknown:
+ throw RuntimeException( "unknown error!", nullptr );
+ case osl_Process_E_InvalidError:
+ default:
+ throw RuntimeException( "unmapped error!", nullptr );
+ }
+
+ return hProcess;
+}
+
+
+OUString generateRandomPipeId()
+{
+ // compute some good pipe id:
+ static rtlRandomPool s_hPool = rtl_random_createPool();
+ if (s_hPool == nullptr)
+ throw RuntimeException( "cannot create random pool!?", nullptr );
+ sal_uInt8 bytes[ 32 ];
+ if (rtl_random_getBytes(
+ s_hPool, bytes, SAL_N_ELEMENTS(bytes) ) != rtl_Random_E_None) {
+ throw RuntimeException( "random pool error!?", nullptr );
+ }
+ OUStringBuffer buf;
+ for (unsigned char byte : bytes) {
+ buf.append( static_cast<sal_Int32>(byte), 0x10 );
+ }
+ return buf.makeStringAndClear();
+}
+
+
+Reference<XInterface> resolveUnoURL(
+ OUString const & connectString,
+ Reference<XComponentContext> const & xLocalContext,
+ AbortChannel const * abortChannel )
+{
+ Reference<bridge::XUnoUrlResolver> xUnoUrlResolver(
+ bridge::UnoUrlResolver::create( xLocalContext ) );
+
+ for (int i = 0; i <= 40; ++i) // 20 seconds
+ {
+ if (abortChannel != nullptr && abortChannel->isAborted()) {
+ throw ucb::CommandAbortedException( "abort!" );
+ }
+ try {
+ return xUnoUrlResolver->resolve( connectString );
+ }
+ catch (const connection::NoConnectException &) {
+ if (i < 40)
+ {
+ ::osl::Thread::wait( std::chrono::milliseconds(500) );
+ }
+ else throw;
+ }
+ }
+ return nullptr; // warning C4715
+}
+
+static void writeConsoleWithStream(std::u16string_view sText, FILE * stream)
+{
+ OString s = OUStringToOString(sText, osl_getThreadTextEncoding());
+ fprintf(stream, "%s", s.getStr());
+ fflush(stream);
+}
+
+void writeConsole(std::u16string_view sText)
+{
+ writeConsoleWithStream(sText, stdout);
+}
+
+void writeConsoleError(std::u16string_view sText)
+{
+ writeConsoleWithStream(sText, stderr);
+}
+
+OUString readConsole()
+{
+ char buf[1024];
+ memset(buf, 0, 1024);
+ // read one char less so that the last char in buf is always zero
+ if (fgets(buf, 1024, stdin) != nullptr)
+ {
+ OUString value = OStringToOUString(std::string_view(buf), osl_getThreadTextEncoding());
+ return value.trim();
+ }
+ throw css::uno::RuntimeException("reading from stdin failed");
+}
+
+void TRACE(OUString const & sText)
+{
+ SAL_INFO("desktop.deployment", sText);
+}
+
+void syncRepositories(
+ bool force, Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ OUString sDisable;
+ ::rtl::Bootstrap::get( "DISABLE_EXTENSION_SYNCHRONIZATION", sDisable, OUString() );
+ if (!sDisable.isEmpty())
+ return;
+
+ Reference<deployment::XExtensionManager> xExtensionManager;
+ //synchronize shared before bundled otherwise there are
+ //more revoke and registration calls.
+ bool bModified = false;
+ if (force || needToSyncRepository(u"shared") || needToSyncRepository(u"bundled"))
+ {
+ xExtensionManager =
+ deployment::ExtensionManager::get(
+ comphelper::getProcessComponentContext());
+
+ if (xExtensionManager.is())
+ {
+ bModified = xExtensionManager->synchronize(
+ Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ }
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ if (bModified && !comphelper::LibreOfficeKit::isActive())
+ {
+ Reference<task::XRestartManager> restarter(task::OfficeRestartManager::get(comphelper::getProcessComponentContext()));
+ if (restarter.is())
+ {
+ restarter->requestRestart(xCmdEnv.is() ? xCmdEnv->getInteractionHandler() :
+ Reference<task::XInteractionHandler>());
+ }
+ }
+#endif
+}
+
+void disposeBridges(Reference<css::uno::XComponentContext> const & ctx)
+{
+ if (!ctx.is())
+ return;
+
+ Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(ctx) );
+
+ const Sequence< Reference<css::bridge::XBridge> >seqBridges = bridgeFac->getExistingBridges();
+ for (const Reference<css::bridge::XBridge>& bridge : seqBridges)
+ {
+ Reference<css::lang::XComponent> comp(bridge, UNO_QUERY);
+ if (comp.is())
+ {
+ try {
+ comp->dispose();
+ }
+ catch ( const css::lang::DisposedException& )
+ {
+ }
+ }
+ }
+}
+
+}
+
+OUString DpResId(TranslateId aId)
+{
+ static std::locale SINGLETON = Translate::Create("dkt");
+ return Translate::get(aId, SINGLETON);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_platform.cxx b/desktop/source/deployment/misc/dp_platform.cxx
new file mode 100644
index 000000000..879e617b7
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_platform.cxx
@@ -0,0 +1,207 @@
+/* -*- 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 <dp_platform.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/instance.hxx>
+#include <rtl/bootstrap.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+constexpr OUStringLiteral PLATFORM_ALL = u"all";
+
+
+namespace dp_misc
+{
+namespace
+{
+ struct StrOperatingSystem :
+ public rtl::StaticWithInit<OUString, StrOperatingSystem> {
+ OUString operator () () {
+ OUString os( "$_OS" );
+ ::rtl::Bootstrap::expandMacros( os );
+ return os;
+ }
+ };
+
+ struct StrCPU :
+ public rtl::StaticWithInit<OUString, StrCPU> {
+ OUString operator () () {
+ OUString arch( "$_ARCH" );
+ ::rtl::Bootstrap::expandMacros( arch );
+ return arch;
+ }
+ };
+
+
+ struct StrPlatform : public rtl::StaticWithInit<
+ OUString, StrPlatform> {
+ OUString operator () () {
+ return StrOperatingSystem::get() + "_" + StrCPU::get();
+ }
+ };
+
+ bool checkOSandCPU(std::u16string_view os, std::u16string_view cpu)
+ {
+ return (os == StrOperatingSystem::get())
+ && (cpu == StrCPU::get());
+ }
+
+ bool isPlatformSupported( std::u16string_view token )
+ {
+ bool ret = false;
+ if (token == PLATFORM_ALL)
+ ret = true;
+ else if (token == u"windows_x86")
+ ret = checkOSandCPU(u"Windows", u"x86");
+ else if (token == u"windows_x86_64")
+ ret = checkOSandCPU(u"Windows", u"X86_64");
+ else if (token == u"windows_aarch64")
+ ret = checkOSandCPU(u"Windows", u"AARCH64");
+ else if (token == u"solaris_sparc")
+ ret = checkOSandCPU(u"Solaris", u"SPARC");
+ else if (token == u"solaris_sparc64")
+ ret = checkOSandCPU(u"Solaris", u"SPARC64");
+ else if (token == u"solaris_x86")
+ ret = checkOSandCPU(u"Solaris", u"x86");
+ else if (token == u"aix_powerpc")
+ ret = checkOSandCPU(u"AIX", u"PowerPC");
+ else if (token == u"macosx_aarch64")
+ ret = checkOSandCPU(u"MacOSX", u"AARCH64");
+ else if (token == u"macosx_x86_64")
+ ret = checkOSandCPU(u"MacOSX", u"X86_64");
+ else if (token == u"linux_x86")
+ ret = checkOSandCPU(u"Linux", u"x86");
+ else if (token == u"linux_x86_64")
+ ret = checkOSandCPU(u"Linux", u"X86_64");
+ else if (token == u"linux_sparc")
+ ret = checkOSandCPU(u"Linux", u"SPARC");
+ else if (token == u"linux_sparc64")
+ ret = checkOSandCPU(u"Linux", u"SPARC64");
+ else if (token == u"linux_powerpc")
+ ret = checkOSandCPU(u"Linux", u"PowerPC");
+ else if (token == u"linux_powerpc64")
+ ret = checkOSandCPU(u"Linux", u"PowerPC_64");
+ else if (token == u"linux_powerpc64_le")
+ ret = checkOSandCPU(u"Linux", u"PowerPC_64_LE");
+ else if (token == u"linux_arm_eabi")
+ ret = checkOSandCPU(u"Linux", u"ARM_EABI");
+ else if (token == u"linux_arm_oabi")
+ ret = checkOSandCPU(u"Linux", u"ARM_OABI");
+ else if (token == u"linux_mips_el")
+ ret = checkOSandCPU(u"Linux", u"MIPS_EL");
+ else if (token == u"linux_mips64_el")
+ ret = checkOSandCPU(u"Linux", u"MIPS64_EL");
+ else if (token == u"linux_mips_eb")
+ ret = checkOSandCPU(u"Linux", u"MIPS_EB");
+ else if (token == u"linux_mips64_eb")
+ ret = checkOSandCPU(u"Linux", u"MIPS64_EB");
+ else if (token == u"linux_ia64")
+ ret = checkOSandCPU(u"Linux", u"IA64");
+ else if (token == u"linux_m68k")
+ ret = checkOSandCPU(u"Linux", u"M68K");
+ else if (token == u"linux_s390")
+ ret = checkOSandCPU(u"Linux", u"S390");
+ else if (token == u"linux_s390x")
+ ret = checkOSandCPU(u"Linux", u"S390x");
+ else if (token == u"linux_hppa")
+ ret = checkOSandCPU(u"Linux", u"HPPA");
+ else if (token == u"linux_alpha")
+ ret = checkOSandCPU(u"Linux", u"ALPHA");
+ else if (token == u"linux_aarch64")
+ ret = checkOSandCPU(u"Linux", u"AARCH64");
+ else if (token == u"freebsd_x86")
+ ret = checkOSandCPU(u"FreeBSD", u"x86");
+ else if (token == u"freebsd_x86_64")
+ ret = checkOSandCPU(u"FreeBSD", u"X86_64");
+ else if (token == u"freebsd_powerpc")
+ ret = checkOSandCPU(u"FreeBSD", u"PowerPC");
+ else if (token == u"freebsd_powerpc64")
+ ret = checkOSandCPU(u"FreeBSD", u"PowerPC64");
+ else if (token == u"kfreebsd_x86")
+ ret = checkOSandCPU(u"kFreeBSD", u"x86");
+ else if (token == u"kfreebsd_x86_64")
+ ret = checkOSandCPU(u"kFreeBSD", u"X86_64");
+ else if (token == u"netbsd_x86")
+ ret = checkOSandCPU(u"NetBSD", u"x86");
+ else if (token == u"netbsd_x86_64")
+ ret = checkOSandCPU(u"NetBSD", u"X86_64");
+ else if (token == u"openbsd_x86")
+ ret = checkOSandCPU(u"OpenBSD", u"x86");
+ else if (token == u"openbsd_x86_64")
+ ret = checkOSandCPU(u"OpenBSD", u"X86_64");
+ else if (token == u"dragonfly_x86")
+ ret = checkOSandCPU(u"DragonFly", u"x86");
+ else if (token == u"dragonfly_x86_64")
+ ret = checkOSandCPU(u"DragonFly", u"X86_64");
+ else
+ {
+ OSL_FAIL("Extension Manager: The extension supports an unknown platform. "
+ "Check the platform in the description.xml");
+ ret = false;
+ }
+ return ret;
+ }
+
+} // anon namespace
+
+
+OUString const & getPlatformString()
+{
+ return StrPlatform::get();
+}
+
+bool platform_fits( std::u16string_view platform_string )
+{
+ sal_Int32 index = 0;
+ for (;;)
+ {
+ const std::u16string_view token(
+ o3tl::trim(o3tl::getToken(platform_string, 0, ',', index )) );
+ // check if this platform:
+ if (o3tl::equalsIgnoreAsciiCase( token, StrPlatform::get() ) ||
+ (token.find( '_' ) == std::u16string_view::npos && /* check OS part only */
+ o3tl::equalsIgnoreAsciiCase( token, StrOperatingSystem::get() )))
+ {
+ return true;
+ }
+ if (index < 0)
+ break;
+ }
+ return false;
+}
+
+bool hasValidPlatform( css::uno::Sequence<OUString> const & platformStrings)
+{
+ bool ret = false;
+ for (const OUString& s : platformStrings)
+ {
+ if ( isPlatformSupported( s ) )
+ {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_resource.cxx b/desktop/source/deployment/misc/dp_resource.cxx
new file mode 100644
index 000000000..682c90e52
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_resource.cxx
@@ -0,0 +1,42 @@
+/* -*- 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 <dp_resource.h>
+#include <unotools/configmgr.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_misc
+{
+const LanguageTag& getOfficeLanguageTag()
+{
+ static const LanguageTag OFFICE_LANG = []() {
+ OUString slang(utl::ConfigManager::getUILocale());
+ //fallback, the locale is currently only set when the user starts the
+ //office for the first time.
+ if (slang.isEmpty())
+ slang = "en-US";
+ return LanguageTag(slang);
+ }();
+ return OFFICE_LANG;
+}
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_ucb.cxx b/desktop/source/deployment/misc/dp_ucb.cxx
new file mode 100644
index 000000000..28f4192ee
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_ucb.cxx
@@ -0,0 +1,304 @@
+/* -*- 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 <dp_misc.h>
+#include <dp_ucb.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <comphelper/processfactory.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_misc
+{
+
+
+bool create_ucb_content(
+ ::ucbhelper::Content * ret_ucbContent, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc )
+{
+ try {
+ // Existence check...
+ // content ctor/isFolder() will throw exception in case the resource
+ // does not exist.
+
+ // dilemma: no chance to use the given handler here, because it would
+ // raise no such file dialogs, else no interaction for
+ // passwords, ...? xxx todo
+ ::ucbhelper::Content ucbContent(
+ url, Reference<XCommandEnvironment>(),
+ comphelper::getProcessComponentContext() );
+
+ ucbContent.isFolder();
+
+ if (ret_ucbContent != nullptr)
+ {
+ ucbContent.setCommandEnvironment( xCmdEnv );
+ *ret_ucbContent = ucbContent;
+ }
+ return true;
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ }
+ return false;
+}
+
+
+bool create_folder(
+ ::ucbhelper::Content * ret_ucb_content, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv, bool throw_exc )
+{
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content, url_, xCmdEnv, false /* no throw */ ))
+ {
+ if (ucb_content.isFolder()) {
+ if (ret_ucb_content != nullptr)
+ *ret_ucb_content = ucb_content;
+ return true;
+ }
+ }
+
+ OUString url( url_ );
+ // xxx todo: find parent
+ sal_Int32 slash = url.lastIndexOf( '/' );
+ if (slash < 0) {
+ // fallback:
+ url = expandUnoRcUrl( url );
+ slash = url.lastIndexOf( '/' );
+ }
+ if (slash < 0) {
+ // invalid: has to be at least "auth:/..."
+ if (throw_exc)
+ throw ContentCreationException(
+ "Cannot create folder (invalid path): '" + url + "'",
+ Reference<XInterface>(), ContentCreationError_UNKNOWN );
+ return false;
+ }
+ ::ucbhelper::Content parentContent;
+ if (! create_folder(
+ &parentContent, url.copy( 0, slash ), xCmdEnv, throw_exc ))
+ return false;
+ const Any title( ::rtl::Uri::decode( url.copy( slash + 1 ),
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_UTF8 ) );
+ const Sequence<ContentInfo> infos(
+ parentContent.queryCreatableContentsInfo() );
+ for ( ContentInfo const & info : infos )
+ {
+ // look KIND_FOLDER:
+ if ((info.Attributes & ContentInfoAttribute::KIND_FOLDER) != 0)
+ {
+ // make sure the only required bootstrap property is "Title":
+ Sequence<beans::Property> const & rProps = info.Properties;
+ if ( rProps.getLength() != 1 || rProps[ 0 ].Name != "Title" )
+ continue;
+
+ try {
+ if (parentContent.insertNewContent(
+ info.Type,
+ StrTitle::getTitleSequence(),
+ Sequence<Any>( &title, 1 ),
+ ucb_content )) {
+ if (ret_ucb_content != nullptr)
+ *ret_ucb_content = ucb_content;
+ return true;
+ }
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ // Interaction Handler already handled the error
+ // that has occurred...
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ return false;
+ }
+ }
+ }
+ if (throw_exc)
+ throw ContentCreationException(
+ "Cannot create folder: '" + url + "'",
+ Reference<XInterface>(), ContentCreationError_UNKNOWN );
+ return false;
+}
+
+
+bool erase_path( OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc )
+{
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content( &ucb_content, url, xCmdEnv, false /* no throw */ ))
+ {
+ try {
+ ucb_content.executeCommand(
+ "delete", Any( true /* delete physically */ ) );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+std::vector<sal_Int8> readFile( ::ucbhelper::Content & ucb_content )
+{
+ std::vector<sal_Int8> bytes;
+ Reference<io::XOutputStream> xStream(
+ ::xmlscript::createOutputStream( &bytes ) );
+ if (! ucb_content.openStream( xStream ))
+ throw RuntimeException(
+ "::ucbhelper::Content::openStream( XOutputStream ) failed!",
+ nullptr );
+ return bytes;
+}
+
+
+bool readLine( OUString * res, OUString const & startingWith,
+ ::ucbhelper::Content & ucb_content, rtl_TextEncoding textenc )
+{
+ // read whole file:
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ OUString file( reinterpret_cast<char const *>(bytes.data()),
+ bytes.size(), textenc );
+ sal_Int32 pos = 0;
+ for (;;)
+ {
+ if (file.match( startingWith, pos ))
+ {
+ OUStringBuffer buf;
+ sal_Int32 start = pos;
+ pos += startingWith.getLength();
+ for (;;)
+ {
+ pos = file.indexOf( LF, pos );
+ if (pos < 0) { // EOF
+ buf.append( file.subView(start) );
+ }
+ else
+ {
+ if (pos > 0 && file[ pos - 1 ] == CR)
+ {
+ // consume extra CR
+ buf.append( file.subView(start, pos - start - 1) );
+ ++pos;
+ }
+ else
+ buf.append( file.subView(start, pos - start) );
+ ++pos; // consume LF
+ // check next line:
+ if (pos < file.getLength() &&
+ (file[ pos ] == ' ' || file[ pos ] == '\t'))
+ {
+ buf.append( ' ' );
+ ++pos;
+ start = pos;
+ continue;
+ }
+ }
+ break;
+ }
+ *res = buf.makeStringAndClear();
+ return true;
+ }
+ // next line:
+ sal_Int32 next_lf = file.indexOf( LF, pos );
+ if (next_lf < 0) // EOF
+ break;
+ pos = next_lf + 1;
+ }
+ return false;
+}
+
+bool readProperties( std::vector< std::pair< OUString, OUString> > & out_result,
+ ::ucbhelper::Content & ucb_content )
+{
+ // read whole file:
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ OUString file( reinterpret_cast<char const *>(bytes.data()),
+ bytes.size(), RTL_TEXTENCODING_UTF8);
+ sal_Int32 pos = 0;
+
+ for (;;)
+ {
+
+ OUStringBuffer buf;
+ sal_Int32 start = pos;
+
+ bool bEOF = false;
+ pos = file.indexOf( LF, pos );
+ if (pos < 0) { // EOF
+ buf.append( file.subView(start) );
+ bEOF = true;
+ }
+ else
+ {
+ if (pos > 0 && file[ pos - 1 ] == CR)
+ // consume extra CR
+ buf.append( file.subView(start, pos - start - 1) );
+ else
+ buf.append( file.subView(start, pos - start) );
+ pos++;
+ }
+ OUString aLine = buf.makeStringAndClear();
+
+ sal_Int32 posEqual = aLine.indexOf('=');
+ if (posEqual > 0 && (posEqual + 1) < aLine.getLength())
+ {
+ OUString name = aLine.copy(0, posEqual);
+ OUString value = aLine.copy(posEqual + 1);
+ out_result.emplace_back(name, value);
+ }
+
+ if (bEOF)
+ break;
+ }
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_update.cxx b/desktop/source/deployment/misc/dp_update.cxx
new file mode 100644
index 000000000..7116be42b
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_update.cxx
@@ -0,0 +1,408 @@
+/* -*- 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 <config_folders.h>
+
+#include <dp_update.hxx>
+#include <dp_version.hxx>
+#include <dp_identifier.hxx>
+#include <dp_descriptioninfoset.hxx>
+
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <osl/diagnose.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_misc {
+namespace {
+
+int determineHighestVersion(
+ OUString const & userVersion,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ int index = 0;
+ OUString greatest = userVersion;
+ if (dp_misc::compareVersions(sharedVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 1;
+ greatest = sharedVersion;
+ }
+ if (dp_misc::compareVersions(bundledVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 2;
+ greatest = bundledVersion;
+ }
+ if (dp_misc::compareVersions(onlineVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 3;
+ }
+ return index;
+}
+
+Sequence< Reference< xml::dom::XElement > >
+getUpdateInformation( Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ Sequence< OUString > const & urls,
+ OUString const & identifier,
+ uno::Any & out_error)
+{
+ try {
+ return updateInformation->getUpdateInformation(urls, identifier);
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const ucb::CommandFailedException & e) {
+ out_error = e.Reason;
+ } catch (const ucb::CommandAbortedException &) {
+ } catch (const uno::Exception & e) {
+ out_error <<= e;
+ }
+ return
+ Sequence<Reference< xml::dom::XElement > >();
+}
+
+void getOwnUpdateInfos(
+ Reference<uno::XComponentContext> const & xContext,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ UpdateInfoMap& inout_map, std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors,
+ bool & out_allFound)
+{
+ bool bAllHaveOwnUpdateInformation = true;
+ for (auto & inout : inout_map)
+ {
+ OSL_ASSERT(inout.second.extension.is());
+ Sequence<OUString> urls(inout.second.extension->getUpdateInformationURLs());
+ if (urls.hasElements())
+ {
+ const OUString search_id = dp_misc::getIdentifier(inout.second.extension);
+ SAL_INFO( "extensions.update", "Searching update for " << search_id );
+ uno::Any anyError;
+ //It is unclear from the idl if there can be a null reference returned.
+ //However all valid information should be the same
+ const Sequence<Reference< xml::dom::XElement > >
+ infos(getUpdateInformation(updateInformation, urls, search_id, anyError));
+ if (anyError.hasValue())
+ out_errors.emplace_back(inout.second.extension, anyError);
+
+ for (const Reference< xml::dom::XElement >& element : infos)
+ {
+ dp_misc::DescriptionInfoset infoset(
+ xContext,
+ Reference< xml::dom::XNode >(element, UNO_QUERY_THROW));
+ if (!infoset.hasDescription())
+ continue;
+ std::optional< OUString > result_id(infoset.getIdentifier());
+ if (!result_id)
+ continue;
+ SAL_INFO( "extensions.update", " found version "
+ << infoset.getVersion() << " for " << *result_id );
+ if (*result_id != search_id)
+ continue;
+ inout.second.version = infoset.getVersion();
+ inout.second.info.set(element, UNO_QUERY_THROW);
+ break;
+ }
+ }
+ else
+ {
+ bAllHaveOwnUpdateInformation = false;
+ }
+ }
+ out_allFound = bAllHaveOwnUpdateInformation;
+}
+
+void getDefaultUpdateInfos(
+ Reference<uno::XComponentContext> const & xContext,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ UpdateInfoMap& inout_map,
+ std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors)
+{
+ const OUString sDefaultURL(dp_misc::getExtensionDefaultUpdateURL());
+ OSL_ASSERT(!sDefaultURL.isEmpty());
+
+ Any anyError;
+ const Sequence< Reference< xml::dom::XElement > >
+ infos(
+ getUpdateInformation(
+ updateInformation,
+ Sequence< OUString >(&sDefaultURL, 1), OUString(), anyError));
+ if (anyError.hasValue())
+ out_errors.emplace_back(Reference<deployment::XPackage>(), anyError);
+ for (const Reference< xml::dom::XElement >& element : infos)
+ {
+ Reference< xml::dom::XNode > node(element, UNO_QUERY_THROW);
+ dp_misc::DescriptionInfoset infoset(xContext, node);
+ std::optional< OUString > id(infoset.getIdentifier());
+ if (!id) {
+ continue;
+ }
+ UpdateInfoMap::iterator j = inout_map.find(*id);
+ if (j != inout_map.end())
+ {
+ //skip those extension which provide its own update urls
+ if (j->second.extension->getUpdateInformationURLs().getLength())
+ continue;
+ OUString v(infoset.getVersion());
+ //look for the highest version in the online repository
+ if (dp_misc::compareVersions(v, j->second.version) ==
+ dp_misc::GREATER)
+ {
+ j->second.version = v;
+ j->second.info = node;
+ }
+ }
+ }
+}
+
+bool containsBundledOnly(Sequence<Reference<deployment::XPackage> > const & sameIdExtensions)
+{
+ OSL_ASSERT(sameIdExtensions.getLength() == 3);
+ return !sameIdExtensions[0].is() && !sameIdExtensions[1].is() && sameIdExtensions[2].is();
+}
+
+/** Returns true if the list of extensions are bundled extensions and there are no
+ other extensions with the same identifier in the shared or user repository.
+ If extensionList is NULL, then it is checked if there are only bundled extensions.
+*/
+bool onlyBundledExtensions(
+ Reference<deployment::XExtensionManager> const & xExtMgr,
+ std::vector< Reference<deployment::XPackage > > const * extensionList)
+{
+ OSL_ASSERT(xExtMgr.is());
+ bool bOnlyBundled = true;
+ if (extensionList)
+ {
+ for (auto const& elem : *extensionList)
+ {
+ Sequence<Reference<deployment::XPackage> > seqExt = xExtMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(elem), elem->getName(), Reference<ucb::XCommandEnvironment>());
+
+ bOnlyBundled = containsBundledOnly(seqExt);
+ if (!bOnlyBundled)
+ break;
+ }
+ }
+ else
+ {
+ const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =
+ xExtMgr->getAllExtensions(Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+
+ for (int pos(0), nLen(seqAllExt.getLength()); bOnlyBundled && pos != nLen; ++pos)
+ {
+ bOnlyBundled = containsBundledOnly(seqAllExt[pos]);
+ }
+ }
+ return bOnlyBundled;
+}
+
+} // anon namespace
+
+
+OUString getExtensionDefaultUpdateURL()
+{
+ OUString sUrl(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version")
+ ":Version:ExtensionUpdateURL}");
+ ::rtl::Bootstrap::expandMacros(sUrl);
+ return sUrl;
+}
+
+/* returns the index of the greatest version, starting with 0
+
+ */
+UPDATE_SOURCE isUpdateUserExtension(
+ bool bReadOnlyShared,
+ OUString const & userVersion,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
+ if (bReadOnlyShared)
+ {
+ if (!userVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ userVersion, sharedVersion, bundledVersion, onlineVersion);
+ if (index == 1)
+ retVal = UPDATE_SOURCE_SHARED;
+ else if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ else if (!sharedVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ OUString(), sharedVersion, bundledVersion, onlineVersion);
+ if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+
+ }
+ }
+ else
+ {
+ if (!userVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ userVersion, sharedVersion, bundledVersion, onlineVersion);
+ if (index == 1)
+ retVal = UPDATE_SOURCE_SHARED;
+ else if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ }
+
+ return retVal;
+}
+
+UPDATE_SOURCE isUpdateSharedExtension(
+ bool bReadOnlyShared,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ if (bReadOnlyShared)
+ return UPDATE_SOURCE_NONE;
+ UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
+
+ if (!sharedVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ OUString(), sharedVersion, bundledVersion, onlineVersion);
+ if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ return retVal;
+}
+
+Reference<deployment::XPackage>
+getExtensionWithHighestVersion(
+ Sequence<Reference<deployment::XPackage> > const & seqExt)
+{
+ if (!seqExt.hasElements())
+ return Reference<deployment::XPackage>();
+
+ Reference<deployment::XPackage> greatest;
+ sal_Int32 len = seqExt.getLength();
+
+ for (sal_Int32 i = 0; i < len; i++)
+ {
+ if (!greatest.is())
+ {
+ greatest = seqExt[i];
+ continue;
+ }
+ Reference<deployment::XPackage> const & current = seqExt[i];
+ //greatest has a value
+ if (! current.is())
+ continue;
+
+ if (dp_misc::compareVersions(current->getVersion(), greatest->getVersion()) == dp_misc::GREATER)
+ greatest = current;
+ }
+ return greatest;
+}
+
+UpdateInfo::UpdateInfo( Reference< deployment::XPackage> const & ext):
+extension(ext)
+{
+}
+
+
+UpdateInfoMap getOnlineUpdateInfos(
+ Reference<uno::XComponentContext> const &xContext,
+ Reference<deployment::XExtensionManager> const & xExtMgr,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ std::vector<Reference<deployment::XPackage > > const * extensionList,
+ std::vector<std::pair< Reference<deployment::XPackage>, uno::Any> > & out_errors)
+{
+ OSL_ASSERT(xExtMgr.is());
+ UpdateInfoMap infoMap;
+ if (!xExtMgr.is() || onlyBundledExtensions(xExtMgr, extensionList))
+ return infoMap;
+
+ if (!extensionList)
+ {
+ const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt = xExtMgr->getAllExtensions(
+ Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+
+ //fill the UpdateInfoMap. key = extension identifier, value = UpdateInfo
+ for (int pos = seqAllExt.getLength(); pos --; )
+ {
+ uno::Sequence<Reference<deployment::XPackage> > const & seqExt = seqAllExt[pos];
+
+ Reference<deployment::XPackage> extension = getExtensionWithHighestVersion(seqExt);
+ OSL_ASSERT(extension.is());
+
+ std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
+ dp_misc::getIdentifier(extension), UpdateInfo(extension));
+ OSL_ASSERT(insertRet.second);
+ }
+ }
+ else
+ {
+ for (auto const& elem : *extensionList)
+ {
+ OSL_ASSERT(elem.is());
+ std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
+ dp_misc::getIdentifier(elem), UpdateInfo(elem));
+ OSL_ASSERT(insertRet.second);
+ }
+ }
+
+ //Now find the update information for the extensions which provide their own
+ //URLs to update information.
+ bool bAllInfosObtained = false;
+ getOwnUpdateInfos(xContext, updateInformation, infoMap, out_errors, bAllInfosObtained);
+
+ if (!bAllInfosObtained)
+ getDefaultUpdateInfos(xContext, updateInformation, infoMap, out_errors);
+ return infoMap;
+}
+OUString getHighestVersion(
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ int index = determineHighestVersion(OUString(), sharedVersion, bundledVersion, onlineVersion);
+ switch (index)
+ {
+ case 1: return sharedVersion;
+ case 2: return bundledVersion;
+ case 3: return onlineVersion;
+ default: OSL_ASSERT(false);
+ }
+
+ return OUString();
+}
+} //namespace dp_misc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_version.cxx b/desktop/source/deployment/misc/dp_version.cxx
new file mode 100644
index 000000000..703045dde
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_version.cxx
@@ -0,0 +1,63 @@
+/* -*- 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 <rtl/ustring.hxx>
+
+#include <dp_version.hxx>
+
+namespace {
+
+OUString getElement(OUString const & version, ::sal_Int32 * index)
+{
+ while (*index < version.getLength() && version[*index] == '0') {
+ ++*index;
+ }
+ return version.getToken(0, '.', *index);
+}
+
+}
+
+namespace dp_misc {
+
+::dp_misc::Order compareVersions(
+ OUString const & version1, OUString const & version2)
+{
+ for (::sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0;) {
+ OUString e1(i1 >= 0 ? getElement(version1, &i1) : OUString());
+ OUString e2(i2 >= 0 ? getElement(version2, &i2) : OUString());
+ if (e1.getLength() < e2.getLength()) {
+ return ::dp_misc::LESS;
+ } else if (e1.getLength() > e2.getLength()) {
+ return ::dp_misc::GREATER;
+ } else if (e1 < e2) {
+ return ::dp_misc::LESS;
+ } else if (e1 > e2) {
+ return ::dp_misc::GREATER;
+ }
+ }
+ return ::dp_misc::EQUAL;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/lockfile.cxx b/desktop/source/deployment/misc/lockfile.cxx
new file mode 100644
index 000000000..206da8286
--- /dev/null
+++ b/desktop/source/deployment/misc/lockfile.cxx
@@ -0,0 +1,211 @@
+/* -*- 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 <memory>
+
+#include <time.h>
+#ifndef _WIN32
+#include <unistd.h>
+#else
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#endif
+#include <comphelper/random.hxx>
+#include <sal/types.h>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <unotools/bootstrap.hxx>
+#include <tools/config.hxx>
+
+#include <lockfile.hxx>
+
+using namespace ::osl;
+using namespace ::utl;
+
+
+static OString impl_getHostname()
+{
+ OString aHost;
+#ifdef _WIN32
+ /*
+ prevent windows from connecting to the net to get its own
+ hostname by using the netbios name
+ */
+ DWORD sz = MAX_COMPUTERNAME_LENGTH + 1;
+ TCHAR szHost[MAX_COMPUTERNAME_LENGTH + 1];
+ if (GetComputerNameA(szHost, &sz))
+ aHost = OString(szHost);
+ else
+ aHost = OString("UNKNOWN");
+#else
+ /* Don't do dns lookup on Linux either */
+ char pHostName[1024];
+
+ if ( gethostname( pHostName, sizeof( pHostName ) - 1 ) == 0 )
+ {
+ pHostName[sizeof( pHostName ) - 1] = '\0';
+ aHost = OString( pHostName );
+ }
+ else
+ aHost = OString("UNKNOWN");
+#endif
+
+ return aHost;
+}
+
+namespace desktop {
+
+ Lockfile::Lockfile( bool bIPCserver )
+ :m_bIPCserver(bIPCserver)
+ ,m_bRemove(false)
+ ,m_bIsLocked(false)
+ {
+ // build the file-url to use for the lock
+ OUString aUserPath;
+ utl::Bootstrap::locateUserInstallation( aUserPath );
+ m_aLockname = aUserPath + "/.lock";
+
+ // generate ID
+ const int nIdBytes = 16;
+ char tmpId[nIdBytes*2+1];
+ time_t t = time(nullptr);
+ for (int i = 0; i<nIdBytes; i++) {
+ int tmpByte = comphelper::rng::uniform_int_distribution(0, 0xFF);
+ sprintf( tmpId+i*2, "%02X", tmpByte );
+ }
+ tmpId[nIdBytes*2]=0x00;
+ m_aId = OUString::createFromAscii( tmpId );
+
+ // generate date string
+ char *tmpTime = ctime( &t );
+ if (tmpTime != nullptr) {
+ m_aDate = OUString::createFromAscii( tmpTime );
+ sal_Int32 i = m_aDate.indexOf('\n');
+ if (i > 0)
+ m_aDate = m_aDate.copy(0, i);
+ }
+
+
+ // try to create file
+ File aFile(m_aLockname);
+ if (aFile.open( osl_File_OpenFlag_Create ) == File::E_EXIST) {
+ m_bIsLocked = true;
+ } else {
+ // new lock created
+ aFile.close( );
+ syncToFile( );
+ m_bRemove = true;
+ }
+ }
+
+ bool Lockfile::check( fpExecWarning execWarning )
+ {
+
+ if (m_bIsLocked) {
+ // lock existed, ask user what to do
+ if (isStale() ||
+ (execWarning != nullptr && (*execWarning)( this ))) {
+ // remove file and create new
+ File::remove( m_aLockname );
+ File aFile(m_aLockname);
+ (void)aFile.open( osl_File_OpenFlag_Create );
+ aFile.close( );
+ syncToFile( );
+ m_bRemove = true;
+ return true;
+ } else {
+ //leave alone and return false
+ m_bRemove = false;
+ return false;
+ }
+ } else {
+ // lock was created by us
+ return true;
+ }
+ }
+
+ bool Lockfile::isStale() const
+ {
+ // this checks whether the lockfile was created on the same
+ // host by the same user. Should this be the case it is safe
+ // to assume that it is a stale lockfile which can be overwritten
+ OUString aLockname = m_aLockname;
+ Config aConfig(aLockname);
+ aConfig.SetGroup(LOCKFILE_GROUP);
+ OString aIPCserver = aConfig.ReadKey( LOCKFILE_IPCKEY );
+ if (!aIPCserver.equalsIgnoreAsciiCase("true"))
+ return false;
+
+ OString aHost = aConfig.ReadKey( LOCKFILE_HOSTKEY );
+ OString aUser = aConfig.ReadKey( LOCKFILE_USERKEY );
+
+ // lockfile from same host?
+ OString myHost( impl_getHostname() );
+ if (aHost == myHost) {
+ // lockfile by same UID
+ OUString myUserName;
+ Security aSecurity;
+ aSecurity.getUserName( myUserName );
+ OString myUser(OUStringToOString(myUserName, RTL_TEXTENCODING_ASCII_US));
+ if (aUser == myUser)
+ return true;
+ }
+ return false;
+ }
+
+ void Lockfile::syncToFile() const
+ {
+ OUString aLockname = m_aLockname;
+ Config aConfig(aLockname);
+ aConfig.SetGroup(LOCKFILE_GROUP);
+
+ // get information
+ OString aHost( impl_getHostname() );
+ OUString aUserName;
+ Security aSecurity;
+ aSecurity.getUserName( aUserName );
+ OString aUser = OUStringToOString( aUserName, RTL_TEXTENCODING_ASCII_US );
+ OString aTime = OUStringToOString( m_aDate, RTL_TEXTENCODING_ASCII_US );
+ OString aStamp = OUStringToOString( m_aId, RTL_TEXTENCODING_ASCII_US );
+
+ // write information
+ aConfig.WriteKey( LOCKFILE_USERKEY, aUser );
+ aConfig.WriteKey( LOCKFILE_HOSTKEY, aHost );
+ aConfig.WriteKey( LOCKFILE_STAMPKEY, aStamp );
+ aConfig.WriteKey( LOCKFILE_TIMEKEY, aTime );
+ aConfig.WriteKey(
+ LOCKFILE_IPCKEY,
+ m_bIPCserver ? OString("true") : OString("false") );
+ aConfig.Flush( );
+ }
+
+ Lockfile::~Lockfile()
+ {
+ // unlock userdata by removing file
+ if ( m_bRemove )
+ File::remove( m_aLockname );
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/component/dp_compbackenddb.cxx b/desktop/source/deployment/registry/component/dp_compbackenddb.cxx
new file mode 100644
index 000000000..fd9bb2c61
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_compbackenddb.cxx
@@ -0,0 +1,131 @@
+/* -*- 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 <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_compbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/component-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"comp";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"component-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"component";
+
+namespace dp_registry::backend::component {
+
+ComponentBackendDb::ComponentBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ComponentBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ComponentBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ComponentBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ComponentBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+void ComponentBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> componentNode = writeKeyElement(url);
+ writeSimpleElement(u"java-type-library",
+ OUString::boolean(data.javaTypeLibrary),
+ componentNode);
+
+ writeSimpleList(
+ data.implementationNames,
+ u"implementation-names",
+ u"name",
+ componentNode);
+
+ writeVectorOfPair(
+ data.singletons,
+ u"singletons",
+ u"item",
+ u"key",
+ u"value",
+ componentNode);
+
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+ComponentBackendDb::Data ComponentBackendDb::getEntry(std::u16string_view url)
+{
+ try
+ {
+ ComponentBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ bool bJava = readSimpleElement(u"java-type-library", aNode) == "true";
+ retData.javaTypeLibrary = bJava;
+
+ retData.implementationNames =
+ readList( aNode, u"implementation-names", u"name");
+
+ retData.singletons =
+ readVectorOfPair( aNode, u"singletons", u"item", u"key", u"value");
+ }
+ return retData;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+} // namespace dp_registry::backend::component
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/component/dp_compbackenddb.hxx b/desktop/source/deployment/registry/component/dp_compbackenddb.hxx
new file mode 100644
index 000000000..84153b6fa
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_compbackenddb.hxx
@@ -0,0 +1,95 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <vector>
+#include <deque>
+#include <string_view>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry::backend::component {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ The format looks like this:
+
+<?xml version="1.0"?>
+<component-backend-db xmlns="http://openoffice.org/extensionmanager/component-registry/2010">
+ <component url="vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages/5CD5.tmp_/leaves1.oxt/extensionoptions.jar">
+ <name>FileName</name>
+ <java-type-library>true</java-type-library>
+ <implementation-names>
+ <name>com.sun.star.comp.extensionoptions.OptionsEventHandler$_OptionsEventHandler</name>
+ ...
+ </implementation-names>
+ <singletons>
+ <item>
+ <key>com.sun.star.java.theJavaVirtualMachine</key>
+ <value>com.sun.star.java.JavaVirtualMachine</value>
+ </item>
+ ...
+ </singletons>
+ </component>
+
+ <component ...>
+ ...
+</component-backend-db>
+ */
+class ComponentBackendDb: public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+ virtual OUString getNSPrefix() override;
+ virtual OUString getRootElementName() override;
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ Data(): javaTypeLibrary(false) {};
+
+ std::deque< OUString> implementationNames;
+ std::vector< std::pair< OUString, OUString> >singletons;
+ // map from singleton names to implementation names
+ bool javaTypeLibrary;
+ };
+
+public:
+
+ ComponentBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+ void addEntry(OUString const & url, Data const & data);
+
+ Data getEntry(std::u16string_view url);
+
+
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/component/dp_component.cxx b/desktop/source/deployment/registry/component/dp_component.cxx
new file mode 100644
index 000000000..b9aa6518a
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_component.cxx
@@ -0,0 +1,1720 @@
+/* -*- 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 <strings.hrc>
+#include <dp_misc.h>
+#include <dp_shared.hxx>
+#include <dp_backend.h>
+#include <dp_platform.hxx>
+#include <dp_ucb.h>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <xmlscript/xml_helper.hxx>
+#include <svl/inettype.hxx>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XSet.hpp>
+#include <com/sun/star/registry/XSimpleRegistry.hpp>
+#include <com/sun/star/registry/XImplementationRegistration.hpp>
+#include <com/sun/star/loader/XImplementationLoader.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <algorithm>
+#include <deque>
+#include <memory>
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+#include "dp_compbackenddb.hxx"
+
+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::component {
+namespace {
+
+/** return a vector of bootstrap variables which have been provided
+ as command arguments.
+*/
+std::vector<OUString> getCmdBootstrapVariables()
+{
+ std::vector<OUString> ret;
+ sal_uInt32 count = osl_getCommandArgCount();
+ for (sal_uInt32 i = 0; i < count; i++)
+ {
+ OUString arg;
+ osl_getCommandArg(i, &arg.pData);
+ if (arg.startsWith("-env:"))
+ ret.push_back(arg);
+ }
+ return ret;
+}
+
+bool jarManifestHeaderPresent(
+ OUString const & url, OUString const & name,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString buf = "vnd.sun.star.zip://"
+ + ::rtl::Uri::encode(
+ url, rtl_UriCharClassRegName, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 )
+ + "/META-INF/MANIFEST.MF";
+ ::ucbhelper::Content manifestContent;
+ OUString line;
+ return
+ create_ucb_content(
+ &manifestContent, buf, xCmdEnv,
+ false /* no throw */ )
+ && readLine( &line, name, manifestContent, RTL_TEXTENCODING_ASCII_US );
+}
+
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class ComponentPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const OUString m_loader;
+
+ enum class Reg { Uninit, Void, Registered, NotRegistered, MaybeRegistered };
+ Reg m_registered;
+
+ void getComponentInfo(
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > *
+ factories,
+ Reference<XComponentContext> const & xContext );
+
+ void componentLiveInsertion(
+ ComponentBackendDb::Data const & data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > const &
+ factories);
+
+ void componentLiveRemoval(ComponentBackendDb::Data const & data);
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ Reference<registry::XSimpleRegistry> getRDB() const;
+
+ public:
+ ComponentPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ OUString loader, bool bRemoved,
+ OUString const & identifier);
+ };
+ friend class ComponentPackageImpl;
+
+ class ComponentsPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ public:
+ ComponentsPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier);
+ };
+ friend class ComponentsPackageImpl;
+
+ class TypelibraryPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const bool m_jarFile;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ TypelibraryPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool jarFile, bool bRemoved,
+ OUString const & identifier);
+ };
+ friend class TypelibraryPackageImpl;
+
+ /** Serves for unregistering packages that were registered on a
+ different platform. This can happen if one has remotely mounted
+ /home, for example.
+ */
+ class OtherPlatformPackageImpl : public ::dp_registry::backend::Package
+ {
+ public:
+ OtherPlatformPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier, OUString platform);
+
+ private:
+ BackendImpl * getMyBackend() const;
+
+ Reference<registry::XSimpleRegistry> impl_openRDB() const;
+ Reference<XInterface> impl_createInstance(OUString const& rService) const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ private:
+ OUString const m_aPlatform;
+ };
+ friend class OtherPlatformPackageImpl;
+
+ std::deque<OUString> m_jar_typelibs;
+ std::deque<OUString> m_rdb_typelibs;
+ std::deque<OUString> m_components;
+
+ enum RcItem { RCITEM_JAR_TYPELIB, RCITEM_RDB_TYPELIB, RCITEM_COMPONENTS };
+
+ std::deque<OUString> & getRcItemList( RcItem kind ) {
+ switch (kind)
+ {
+ case RCITEM_JAR_TYPELIB:
+ return m_jar_typelibs;
+ case RCITEM_RDB_TYPELIB:
+ return m_rdb_typelibs;
+ default: // case RCITEM_COMPONENTS
+ return m_components;
+ }
+ }
+
+ bool m_unorc_inited;
+ bool m_unorc_modified;
+ bool bSwitchedRdbFiles;
+
+ typedef std::unordered_map< OUString, Reference<XInterface> > t_string2object;
+ t_string2object m_backendObjects;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xDynComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xJavaComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xPythonComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xComponentsTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xRDBTypelibTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xJavaTypelibTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ OUString m_commonRDB;
+ OUString m_nativeRDB;
+
+ //URLs of the original rdbs (before any switching):
+ OUString m_commonRDB_orig;
+ OUString m_nativeRDB_orig;
+
+ std::unique_ptr<ComponentBackendDb> m_backendDb;
+
+ void addDataToDb(OUString const & url, ComponentBackendDb::Data const & data);
+ ComponentBackendDb::Data readDataFromDb(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+
+ Reference<registry::XSimpleRegistry> m_xCommonRDB;
+ Reference<registry::XSimpleRegistry> m_xNativeRDB;
+
+ void unorc_verify_init( Reference<XCommandEnvironment> const & xCmdEnv );
+ void unorc_flush( Reference<XCommandEnvironment> const & xCmdEnv );
+
+ Reference<XInterface> getObject( OUString const & id );
+ Reference<XInterface> insertObject(
+ OUString const & id, Reference<XInterface> const & xObject );
+ void releaseObject( OUString const & id );
+
+ void addToUnoRc( RcItem kind, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ void removeFromUnoRc( RcItem kind, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ bool hasInUnoRc( RcItem kind, OUString const & url );
+
+ css::uno::Reference< css::uno::XComponentContext > getRootContext() const;
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using PackageRegistryBackend::disposing;
+
+ //Will be called from ComponentPackageImpl
+ void initServiceRdbFiles();
+};
+
+
+BackendImpl::ComponentPackageImpl::ComponentPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ OUString loader, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_loader(std::move( loader )),
+ m_registered( Reg::Uninit )
+{}
+
+Reference<registry::XSimpleRegistry>
+BackendImpl::ComponentPackageImpl::getRDB() const
+{
+ BackendImpl * that = getMyBackend();
+
+ //Late "initialization" of the services rdb files
+ //This is to prevent problems when running several
+ //instances of OOo with root rights in parallel. This
+ //would otherwise cause problems when copying the rdbs.
+ //See http://qa.openoffice.org/issues/show_bug.cgi?id=99257
+ {
+ const ::osl::MutexGuard guard( m_aMutex );
+ if (!that->bSwitchedRdbFiles)
+ {
+ that->bSwitchedRdbFiles = true;
+ that->initServiceRdbFiles();
+ }
+ }
+ if ( m_loader == "com.sun.star.loader.SharedLibrary" )
+ return that->m_xNativeRDB;
+ else
+ return that->m_xCommonRDB;
+}
+
+BackendImpl * BackendImpl::ComponentPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<ComponentPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+
+void BackendImpl::disposing()
+{
+ try {
+ m_backendObjects = t_string2object();
+ if (m_xNativeRDB.is()) {
+ m_xNativeRDB->close();
+ m_xNativeRDB.clear();
+ }
+ if (m_xCommonRDB.is()) {
+ m_xCommonRDB->close();
+ m_xCommonRDB.clear();
+ }
+ unorc_flush( Reference<XCommandEnvironment>() );
+
+ PackageRegistryBackend::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 );
+ }
+}
+
+
+void BackendImpl::initServiceRdbFiles()
+{
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ ::ucbhelper::Content cacheDir( getCachePath(), xCmdEnv, m_xComponentContext );
+ ::ucbhelper::Content oldRDB;
+ // switch common rdb:
+ if (!m_commonRDB_orig.isEmpty())
+ {
+ (void)create_ucb_content(
+ &oldRDB, makeURL( getCachePath(), m_commonRDB_orig),
+ xCmdEnv, false /* no throw */ );
+ }
+ m_commonRDB = m_commonRDB_orig == "common.rdb" ? std::u16string_view(u"common_.rdb") : std::u16string_view(u"common.rdb");
+ if (oldRDB.get().is())
+ {
+ cacheDir.transferContent(
+ oldRDB, ::ucbhelper::InsertOperation::Copy,
+ m_commonRDB, NameClash::OVERWRITE );
+ oldRDB = ::ucbhelper::Content();
+ }
+ // switch native rdb:
+ if (!m_nativeRDB_orig.isEmpty())
+ {
+ (void)create_ucb_content(
+ &oldRDB, makeURL(getCachePath(), m_nativeRDB_orig),
+ xCmdEnv, false /* no throw */ );
+ }
+ const OUString plt_rdb( getPlatformString() + ".rdb" );
+ const OUString plt_rdb_( getPlatformString() + "_.rdb" );
+ m_nativeRDB = (m_nativeRDB_orig == plt_rdb ) ? plt_rdb_ : plt_rdb;
+ if (oldRDB.get().is())
+ {
+ cacheDir.transferContent(
+ oldRDB, ::ucbhelper::InsertOperation::Copy,
+ m_nativeRDB, NameClash::OVERWRITE );
+ }
+
+ // UNO is bootstrapped, flush for next process start:
+ m_unorc_modified = true;
+ unorc_flush( Reference<XCommandEnvironment>() );
+
+
+ // common rdb for java, native rdb for shared lib components
+ if (!m_commonRDB.isEmpty()) {
+ m_xCommonRDB.set(
+ m_xComponentContext->getServiceManager()
+ ->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ m_xComponentContext ), UNO_QUERY_THROW );
+ m_xCommonRDB->open(
+ makeURL( expandUnoRcUrl(getCachePath()), m_commonRDB ),
+ false, true);
+ }
+ if (!m_nativeRDB.isEmpty()) {
+ m_xNativeRDB.set(
+ m_xComponentContext->getServiceManager()
+ ->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ m_xComponentContext ), UNO_QUERY_THROW );
+ m_xNativeRDB->open(
+ makeURL( expandUnoRcUrl(getCachePath()), m_nativeRDB ),
+ false, true);
+ }
+}
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_unorc_inited( false ),
+ m_unorc_modified( false ),
+ bSwitchedRdbFiles(false),
+ m_xDynComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=native;platform=" +
+ getPlatformString(),
+ "*" SAL_DLLEXTENSION,
+ DpResId(RID_STR_DYN_COMPONENT)
+ ) ),
+ m_xJavaComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=Java",
+ "*.jar",
+ DpResId(RID_STR_JAVA_COMPONENT)
+ ) ),
+ m_xPythonComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=Python",
+ "*.py",
+ DpResId(
+ RID_STR_PYTHON_COMPONENT)
+ ) ),
+ m_xComponentsTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-components",
+ "*.components",
+ DpResId(RID_STR_COMPONENTS)
+ ) ),
+ m_xRDBTypelibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-typelibrary;type=RDB",
+ "*.rdb",
+ DpResId(RID_STR_RDB_TYPELIB)
+ ) ),
+ m_xJavaTypelibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-typelibrary;type=Java",
+ "*.jar",
+ DpResId(RID_STR_JAVA_TYPELIB)
+ ) ),
+ m_typeInfos{ m_xDynComponentTypeInfo, m_xJavaComponentTypeInfo, m_xPythonComponentTypeInfo,
+ m_xComponentsTypeInfo, m_xRDBTypelibTypeInfo, m_xJavaTypelibTypeInfo }
+{
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ if (transientMode())
+ {
+ // in-mem rdbs:
+ // common rdb for java, native rdb for shared lib components
+ m_xCommonRDB.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ xComponentContext ), UNO_QUERY_THROW );
+ m_xCommonRDB->open( OUString() /* in-mem */,
+ false /* ! read-only */, true /* create */ );
+ m_xNativeRDB.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ xComponentContext ), UNO_QUERY_THROW );
+ m_xNativeRDB->open( OUString() /* in-mem */,
+ false /* ! read-only */, true /* create */ );
+ }
+ else
+ {
+ unorc_verify_init( xCmdEnv );
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ComponentBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.component.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ComponentBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+ComponentBackendDb::Data BackendImpl::readDataFromDb(std::u16string_view url)
+{
+ ComponentBackendDb::Data data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType(mediaType_);
+ if ( mediaType.isEmpty() || mediaType == "application/vnd.sun.star.uno-component" || mediaType == "application/vnd.sun.star.uno-typelibrary" )
+ {
+ // detect exact media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv )) {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase(SAL_DLLEXTENSION))
+ {
+ mediaType = "application/vnd.sun.star.uno-component;type=native;platform=" +
+ getPlatformString();
+ }
+ else if (title.endsWithIgnoreAsciiCase(".jar"))
+ {
+ if (jarManifestHeaderPresent(
+ url, "RegistrationClassName", xCmdEnv ))
+ mediaType = "application/vnd.sun.star.uno-component;type=Java";
+ if (mediaType.isEmpty())
+ mediaType = "application/vnd.sun.star.uno-typelibrary;type=Java";
+ }
+ else if (title.endsWithIgnoreAsciiCase(".py"))
+ mediaType = "application/vnd.sun.star.uno-component;type=Python";
+ else if (title.endsWithIgnoreAsciiCase(".rdb"))
+ mediaType = "application/vnd.sun.star.uno-typelibrary;type=RDB";
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent( url, xCmdEnv, m_xComponentContext );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.uno-component"))
+ {
+ // xxx todo: probe and evaluate component xml description
+
+ auto const iter = params.find(OString("platform"));
+ bool bPlatformFits(iter == params.end());
+ OUString aPlatform;
+ if (!bPlatformFits) // platform is specified, we have to check
+ {
+ aPlatform = iter->second.m_sValue;
+ bPlatformFits = platform_fits(aPlatform);
+ }
+ // If the package is being removed, do not care whether
+ // platform fits. We won't be using it anyway.
+ if (bPlatformFits || bRemoved) {
+ auto const iterType = params.find(OString("type"));
+ if (iterType != params.end())
+ {
+ OUString const & value = iterType->second.m_sValue;
+ if (value.equalsIgnoreAsciiCase("native")) {
+ if (bPlatformFits)
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xDynComponentTypeInfo,
+ "com.sun.star.loader.SharedLibrary",
+ bRemoved, identifier);
+ else
+ return new BackendImpl::OtherPlatformPackageImpl(
+ this, url, name, m_xDynComponentTypeInfo,
+ bRemoved, identifier, aPlatform);
+ }
+ if (value.equalsIgnoreAsciiCase("Java")) {
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xJavaComponentTypeInfo,
+ "com.sun.star.loader.Java2",
+ bRemoved, identifier);
+ }
+ if (value.equalsIgnoreAsciiCase("Python")) {
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xPythonComponentTypeInfo,
+ "com.sun.star.loader.Python",
+ bRemoved, identifier);
+ }
+ }
+ }
+ }
+ else if (subType.equalsIgnoreAsciiCase("vnd.sun.star.uno-components"))
+ {
+ auto const iter = params.find(OString("platform"));
+ if (iter == params.end() || platform_fits(iter->second.m_sValue)) {
+ return new BackendImpl::ComponentsPackageImpl(
+ this, url, name, m_xComponentsTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-typelibrary"))
+ {
+ auto const iter = params.find(OString("type"));
+ if (iter != params.end()) {
+ OUString const & value = iter->second.m_sValue;
+ if (value.equalsIgnoreAsciiCase("RDB"))
+ {
+ return new BackendImpl::TypelibraryPackageImpl(
+ this, url, name, m_xRDBTypelibTypeInfo,
+ false /* rdb */, bRemoved, identifier);
+ }
+ if (value.equalsIgnoreAsciiCase("Java")) {
+ return new BackendImpl::TypelibraryPackageImpl(
+ this, url, name, m_xJavaTypelibTypeInfo,
+ true /* jar */, bRemoved, identifier);
+ }
+ }
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::unorc_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ const ::osl::MutexGuard guard( m_aMutex );
+ if ( m_unorc_inited)
+ return;
+
+ // common rc:
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), "unorc" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ OUString line;
+ if (readLine( &line, "UNO_JAVA_CLASSPATH=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ sal_Int32 index = sizeof ("UNO_JAVA_CLASSPATH=") - 1;
+ do {
+ OUString token( o3tl::trim(o3tl::getToken(line, 0, ' ', index )) );
+ if (!token.isEmpty())
+ {
+ if (create_ucb_content(
+ nullptr, expandUnoRcTerm(token), xCmdEnv,
+ false /* no throw */ ))
+ {
+ //The jar file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the unorc
+ //After running XExtensionManager::synchronize, the unorc is
+ //cleaned up
+ m_jar_typelibs.push_back( token );
+ }
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "UNO_TYPES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ sal_Int32 index = sizeof ("UNO_TYPES=") - 1;
+ do {
+ OUString token( o3tl::trim(o3tl::getToken(line, 0, ' ', index )) );
+ if (!token.isEmpty())
+ {
+ if (token[ 0 ] == '?')
+ token = token.copy( 1 );
+ if (create_ucb_content(
+ nullptr, expandUnoRcTerm(token), xCmdEnv,
+ false /* no throw */ ))
+ {
+ //The RDB file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the unorc.
+ //After running XExtensionManager::synchronize, the unorc is
+ //cleaned up
+ m_rdb_typelibs.push_back( token );
+ }
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "UNO_SERVICES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ // The UNO_SERVICES line always has the BNF form
+ // "UNO_SERVICES="
+ // ("?$ORIGIN/" <common-rdb>)? -- first
+ // "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}"? -- second
+ // ("?" ("BUNDLED_EXTENSIONS" | -- third
+ // "UNO_SHARED_PACKAGES_CACHE" | "UNO_USER_PACKAGES_CACHE")
+ // ...)*
+ // so can unambiguously be split into its three parts:
+ int state = 1;
+ for (sal_Int32 i = RTL_CONSTASCII_LENGTH("UNO_SERVICES=");
+ i >= 0;)
+ {
+ OUString token(line.getToken(0, ' ', i));
+ if (!token.isEmpty())
+ {
+ if (state == 1 && token.match("?$ORIGIN/"))
+ {
+ m_commonRDB_orig = token.copy(
+ RTL_CONSTASCII_LENGTH("?$ORIGIN/"));
+ state = 2;
+ }
+ else if ( state <= 2 && token == "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}" )
+ {
+ state = 3;
+ }
+ else
+ {
+ if (token[0] == '?')
+ {
+ token = token.copy(1);
+ }
+ m_components.push_back(token);
+ state = 3;
+ }
+ }
+ }
+ }
+
+ // native rc:
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), getPlatformString() + "rc"),
+ xCmdEnv, false /* no throw */ )) {
+ if (readLine( &line, "UNO_SERVICES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ m_nativeRDB_orig = line.copy(
+ sizeof ("UNO_SERVICES=?$ORIGIN/") - 1 );
+ }
+ }
+ }
+ m_unorc_modified = false;
+ m_unorc_inited = true;
+}
+
+
+void BackendImpl::unorc_flush( Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ if (!m_unorc_inited || !m_unorc_modified)
+ return;
+
+ OStringBuffer buf;
+
+ buf.append("ORIGIN=");
+ OUString sOrigin = dp_misc::makeRcTerm(m_cachePath);
+ OString osOrigin = OUStringToOString(sOrigin, RTL_TEXTENCODING_UTF8);
+ buf.append(osOrigin);
+ buf.append(LF);
+
+ if (! m_jar_typelibs.empty())
+ {
+ auto iPos( m_jar_typelibs.cbegin() );
+ auto const iEnd( m_jar_typelibs.cend() );
+ buf.append( "UNO_JAVA_CLASSPATH=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+ if (! m_rdb_typelibs.empty())
+ {
+ auto iPos( m_rdb_typelibs.cbegin() );
+ auto const iEnd( m_rdb_typelibs.cend() );
+ buf.append( "UNO_TYPES=" );
+ while (iPos != iEnd) {
+ buf.append( '?' );
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+
+ // If we duplicated the common or native rdb then we must use those urls
+ //otherwise we use those of the original files. That is, m_commonRDB_orig
+ //and m_nativeRDB_orig;
+ OUString sCommonRDB(m_commonRDB.isEmpty() ? m_commonRDB_orig : m_commonRDB );
+ OUString sNativeRDB(m_nativeRDB.isEmpty() ? m_nativeRDB_orig : m_nativeRDB );
+
+ if (!sCommonRDB.isEmpty() || !sNativeRDB.isEmpty() ||
+ !m_components.empty())
+ {
+ buf.append( "UNO_SERVICES=" );
+ bool space = false;
+ if (!sCommonRDB.isEmpty())
+ {
+ buf.append( "?$ORIGIN/" );
+ buf.append( OUStringToOString(
+ sCommonRDB, RTL_TEXTENCODING_ASCII_US ) );
+ space = true;
+ }
+ if (!sNativeRDB.isEmpty())
+ {
+ if (space)
+ {
+ buf.append(' ');
+ }
+ buf.append( "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}" );
+ space = true;
+
+ // write native rc:
+ OString buf2 =
+ "ORIGIN=" +
+ osOrigin +
+ OStringChar(LF) +
+ "UNO_SERVICES=?$ORIGIN/" +
+ OUStringToOString( sNativeRDB, RTL_TEXTENCODING_ASCII_US ) +
+ OStringChar(LF);
+
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf2.getStr()),
+ buf2.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), getPlatformString() + "rc" ),
+ xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+ }
+ for (auto const& component : m_components)
+ {
+ if (space)
+ {
+ buf.append(' ');
+ }
+ buf.append('?');
+ buf.append(OUStringToOString(component, RTL_TEXTENCODING_UTF8));
+ space = true;
+ }
+ buf.append(LF);
+ }
+
+ // write unorc:
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf.getStr()),
+ buf.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), "unorc" ), xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+
+ m_unorc_modified = false;
+}
+
+
+void BackendImpl::addToUnoRc( RcItem kind, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( m_aMutex );
+ unorc_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getRcItemList(kind);
+ if (std::find( rSet.begin(), rSet.end(), rcterm ) == rSet.end()) {
+ rSet.push_front( rcterm ); // prepend to list, thus overriding
+ // write immediately:
+ m_unorc_modified = true;
+ unorc_flush( xCmdEnv );
+ }
+}
+
+
+void BackendImpl::removeFromUnoRc(
+ RcItem kind, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( m_aMutex );
+ unorc_verify_init( xCmdEnv );
+ std::deque<OUString> & aRcItemList = getRcItemList(kind);
+ aRcItemList.erase(std::remove(aRcItemList.begin(), aRcItemList.end(), rcterm), aRcItemList.end());
+ // write immediately:
+ m_unorc_modified = true;
+ unorc_flush( xCmdEnv );
+}
+
+
+bool BackendImpl::hasInUnoRc(
+ RcItem kind, OUString const & url_ )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( m_aMutex );
+ std::deque<OUString> const & rSet = getRcItemList(kind);
+ return std::find( rSet.begin(), rSet.end(), rcterm ) != rSet.end();
+}
+
+css::uno::Reference< css::uno::XComponentContext > BackendImpl::getRootContext()
+ const
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getComponentContext()->getValueByName("_root"),
+ css::uno::UNO_QUERY);
+ return rootContext.is() ? rootContext : getComponentContext();
+}
+
+
+void BackendImpl::releaseObject( OUString const & id )
+{
+ const ::osl::MutexGuard guard( m_aMutex );
+ m_backendObjects.erase( id );
+}
+
+
+Reference<XInterface> BackendImpl::getObject( OUString const & id )
+{
+ const ::osl::MutexGuard guard( m_aMutex );
+ const t_string2object::const_iterator iFind( m_backendObjects.find( id ) );
+ if (iFind == m_backendObjects.end())
+ return Reference<XInterface>();
+ else
+ return iFind->second;
+}
+
+
+Reference<XInterface> BackendImpl::insertObject(
+ OUString const & id, Reference<XInterface> const & xObject )
+{
+ const ::osl::MutexGuard guard( m_aMutex );
+ const std::pair<t_string2object::iterator, bool> insertion(
+ m_backendObjects.emplace( id, xObject ) );
+ return insertion.first->second;
+}
+
+
+Reference<XComponentContext> raise_uno_process(
+ Reference<XComponentContext> const & xContext,
+ ::rtl::Reference<AbortChannel> const & abortChannel )
+{
+ OSL_ASSERT( xContext.is() );
+
+ OUString url( util::theMacroExpander::get(xContext)->expandMacros( "$URE_BIN_DIR/uno" ) );
+
+ const OUString connectStr = "uno:pipe,name=" + generateRandomPipeId() + ";urp;uno.ComponentContext";
+
+ // raise core UNO process to register/run a component,
+ // javavm service uses unorc next to executable to retrieve deployed
+ // jar typelibs
+
+ std::vector<OUString> args{
+#if OSL_DEBUG_LEVEL == 0
+ "--quiet",
+#endif
+ "--singleaccept",
+ "-u",
+ connectStr,
+ // don't inherit from unorc:
+ "-env:INIFILENAME=" };
+
+ //now add the bootstrap variables which were supplied on the command line
+ std::vector<OUString> bootvars = getCmdBootstrapVariables();
+ args.insert(args.end(), bootvars.begin(), bootvars.end());
+
+ oslProcess hProcess;
+ try {
+ hProcess = raiseProcess(
+ url, comphelper::containerToSequence(args) );
+ }
+ catch (...) {
+ OUStringBuffer sMsg = "error starting process: " + url;
+ for(const auto& arg : args)
+ sMsg.append(" " + arg);
+ throw uno::RuntimeException(sMsg.makeStringAndClear());
+ }
+ try {
+ return Reference<XComponentContext>(
+ resolveUnoURL( connectStr, xContext, abortChannel.get() ),
+ UNO_QUERY_THROW );
+ }
+ catch (...) {
+ // try to terminate process:
+ if ( osl_terminateProcess( hProcess ) != osl_Process_E_None )
+ {
+ OSL_ASSERT( false );
+ }
+ throw;
+ }
+}
+
+void extractComponentData(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ css::uno::Reference< css::registry::XRegistryKey > const & registry,
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > * factories,
+ css::uno::Reference< css::loader::XImplementationLoader > const &
+ componentLoader,
+ OUString const & componentUrl)
+{
+ OSL_ASSERT(
+ context.is() && registry.is() && data != nullptr && componentLoader.is());
+ OUString registryName(registry->getKeyName());
+ sal_Int32 prefix = registryName.getLength();
+ if (!registryName.endsWith("/")) {
+ prefix += RTL_CONSTASCII_LENGTH("/");
+ }
+ const css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > >
+ keys(registry->openKeys());
+ css::uno::Reference< css::lang::XMultiComponentFactory > smgr(
+ context->getServiceManager(), css::uno::UNO_SET_THROW);
+ for (css::uno::Reference< css::registry::XRegistryKey > const & key : keys) {
+ OUString name(key->getKeyName().copy(prefix));
+ data->implementationNames.push_back(name);
+ css::uno::Reference< css::registry::XRegistryKey > singletons(
+ key->openKey("UNO/SINGLETONS"));
+ if (singletons.is()) {
+ sal_Int32 prefix2 = key->getKeyName().getLength() +
+ RTL_CONSTASCII_LENGTH("/UNO/SINGLETONS/");
+ const css::uno::Sequence<
+ css::uno::Reference< css::registry::XRegistryKey > >
+ singletonKeys(singletons->openKeys());
+ for (css::uno::Reference< css::registry::XRegistryKey > const & singletonKey : singletonKeys) {
+ data->singletons.emplace_back(
+ singletonKey->getKeyName().copy(prefix2), name);
+ }
+ }
+ if (factories != nullptr) {
+ factories->push_back(
+ componentLoader->activate(
+ name, OUString(), componentUrl, key));
+ }
+ }
+}
+
+void BackendImpl::ComponentPackageImpl::getComponentInfo(
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > * factories,
+ Reference<XComponentContext> const & xContext )
+{
+ const Reference<loader::XImplementationLoader> xLoader(
+ xContext->getServiceManager()->createInstanceWithContext(
+ m_loader, xContext ), UNO_QUERY );
+ if (! xLoader.is())
+ {
+ throw css::deployment::DeploymentException(
+ "cannot instantiate loader " + m_loader,
+ static_cast< OWeakObject * >(this), Any());
+ }
+
+ // HACK: highly dependent on stoc/source/servicemanager
+ // and stoc/source/implreg implementation which rely on the same
+ // services.rdb format!
+ // .../UNO/LOCATION and .../UNO/ACTIVATOR appear not to be written by
+ // writeRegistryInfo, however, but are known, fixed values here, so
+ // can be passed into extractComponentData
+ OUString url(getURL());
+ const Reference<registry::XSimpleRegistry> xMemReg(
+ xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry", xContext ),
+ UNO_QUERY_THROW );
+ xMemReg->open( OUString() /* in mem */, false, true );
+ xLoader->writeRegistryInfo( xMemReg->getRootKey(), OUString(), url );
+ extractComponentData(
+ xContext, xMemReg->getRootKey(), data, factories, xLoader, url);
+}
+
+void BackendImpl::ComponentPackageImpl::componentLiveInsertion(
+ ComponentBackendDb::Data const & data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > const &
+ factories)
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getMyBackend()->getRootContext());
+ css::uno::Reference< css::container::XSet > set(
+ rootContext->getServiceManager(), css::uno::UNO_QUERY_THROW);
+ std::vector< css::uno::Reference< css::uno::XInterface > >::const_iterator
+ factory(factories.begin());
+ for (auto const& implementationName : data.implementationNames)
+ {
+ try {
+ set->insert(css::uno::Any(*factory++));
+ } catch (const container::ElementExistException &) {
+ SAL_WARN("desktop.deployment", "implementation already registered " << implementationName);
+ }
+ }
+ if (data.singletons.empty()) return;
+
+ css::uno::Reference< css::container::XNameContainer > cont(
+ rootContext, css::uno::UNO_QUERY_THROW);
+ for (auto const& singleton : data.singletons)
+ {
+ OUString name("/singletons/" + singleton.first);
+ //TODO: Update should be atomic:
+ try {
+ cont->removeByName( name + "/arguments");
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->insertByName( name + "/service", css::uno::Any(singleton.second));
+ } catch (const container::ElementExistException &) {
+ cont->replaceByName( name + "/service", css::uno::Any(singleton.second));
+ }
+ try {
+ cont->insertByName(name, css::uno::Any());
+ } catch (const container::ElementExistException &) {
+ SAL_WARN("desktop.deployment", "singleton already registered " << singleton.first);
+ cont->replaceByName(name, css::uno::Any());
+ }
+ }
+}
+
+void BackendImpl::ComponentPackageImpl::componentLiveRemoval(
+ ComponentBackendDb::Data const & data)
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getMyBackend()->getRootContext());
+ css::uno::Reference< css::container::XSet > set(
+ rootContext->getServiceManager(), css::uno::UNO_QUERY_THROW);
+ for (auto const& implementationName : data.implementationNames)
+ {
+ try {
+ set->remove(css::uno::Any(implementationName));
+ } catch (const css::container::NoSuchElementException &) {
+ // ignore if factory has not been live deployed
+ }
+ }
+ if (data.singletons.empty())
+ return;
+
+ css::uno::Reference< css::container::XNameContainer > cont(
+ rootContext, css::uno::UNO_QUERY_THROW);
+ for (auto const& singleton : data.singletons)
+ {
+ OUString name("/singletons/" + singleton.first);
+ //TODO: Removal should be atomic:
+ try {
+ cont->removeByName(name);
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->removeByName( name + "/service" );
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->removeByName( name + "/arguments" );
+ } catch (const container::NoSuchElementException &) {}
+ }
+}
+
+// Package
+
+//We could use here BackendImpl::hasActiveEntry. However, this check is just as well.
+//And it also shows the problem if another extension has overwritten an implementation
+//entry, because it contains the same service implementation
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ComponentPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & )
+{
+ if (m_registered == Reg::Uninit)
+ {
+ m_registered = Reg::NotRegistered;
+ const Reference<registry::XSimpleRegistry> xRDB( getRDB() );
+ if (xRDB.is())
+ {
+ bool bAmbiguousComponentName = false;
+ // lookup rdb for location URL:
+ const Reference<registry::XRegistryKey> xRootKey(
+ xRDB->getRootKey() );
+ const Reference<registry::XRegistryKey> xImplKey(
+ xRootKey->openKey( "IMPLEMENTATIONS" ) );
+ Sequence<OUString> implNames;
+ if (xImplKey.is() && xImplKey->isValid())
+ implNames = xImplKey->getKeyNames();
+ OUString const * pImplNames = implNames.getConstArray();
+ sal_Int32 pos = implNames.getLength();
+ for ( ; pos--; )
+ {
+ checkAborted( abortChannel );
+ const OUString key(
+ pImplNames[ pos ] + "/UNO/LOCATION" );
+ const Reference<registry::XRegistryKey> xKey(
+ xRootKey->openKey(key) );
+ if (xKey.is() && xKey->isValid())
+ {
+ const OUString location( xKey->getAsciiValue() );
+ if (location.equalsIgnoreAsciiCase( getURL() ))
+ {
+ break;
+ }
+ else
+ {
+ //try to match only the file name
+ OUString thisUrl(getURL());
+ std::u16string_view thisFileName(thisUrl.subView(thisUrl.lastIndexOf('/')));
+
+ std::u16string_view locationFileName(location.subView(location.lastIndexOf('/')));
+ if (o3tl::equalsIgnoreAsciiCase(locationFileName, thisFileName))
+ bAmbiguousComponentName = true;
+ }
+ }
+ }
+ if (pos >= 0)
+ m_registered = Reg::Registered;
+ else if (bAmbiguousComponentName)
+ m_registered = Reg::MaybeRegistered;
+ }
+ }
+
+ //Different extensions can use the same service implementations. Then the extensions
+ //which was installed last will overwrite the one from the other extension. That is
+ //the registry will contain the path (the location) of the library or jar of the
+ //second extension. In this case isRegistered called for the lib of the first extension
+ //would return "not registered". That would mean that during uninstallation
+ //XPackage::registerPackage is not called, because it just was not registered. This is,
+ //however, necessary for jar files. Registering and unregistering update
+ //uno_packages/cache/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc
+ //Therefore, we will return always "is ambiguous" if the path of this component cannot
+ //be found in the registry and if there is another path and both have the same file name (but
+ //the rest of the path is different).
+ //If the caller cannot precisely determine that this package was registered, then it must
+ //call registerPackage.
+ bool bAmbiguous = m_registered == Reg::Void // Reg::Void == we are in the progress of unregistration
+ || m_registered == Reg::MaybeRegistered;
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ m_registered == Reg::Registered, bAmbiguous) );
+}
+
+
+void BackendImpl::ComponentPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url(getURL());
+ if (doRegisterPackage) {
+ ComponentBackendDb::Data data;
+ css::uno::Reference< css::uno::XComponentContext > context;
+ if (startup) {
+ context = that->getComponentContext();
+ } else {
+ context.set(that->getObject(url), css::uno::UNO_QUERY);
+ if (!context.is()) {
+ context.set(
+ that->insertObject(
+ url,
+ raise_uno_process(
+ that->getComponentContext(), abortChannel)),
+ css::uno::UNO_QUERY_THROW);
+ }
+ }
+ css::uno::Reference< css::registry::XImplementationRegistration> impreg(
+ context->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.ImplementationRegistration",
+ context),
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::registry::XSimpleRegistry > rdb(getRDB());
+ impreg->registerImplementation(m_loader, url, rdb);
+ // Only write to unorc after successful registration; it may fail if
+ // there is no suitable java
+ if (m_loader == "com.sun.star.loader.Java2" && !jarManifestHeaderPresent(url, "UNO-Type-Path", xCmdEnv))
+ {
+ that->addToUnoRc(RCITEM_JAR_TYPELIB, url, xCmdEnv);
+ data.javaTypeLibrary = true;
+ }
+ std::vector< css::uno::Reference< css::uno::XInterface > > factories;
+ getComponentInfo(&data, startup ? nullptr : &factories, context);
+ if (!startup) {
+ try {
+ componentLiveInsertion(data, factories);
+ } catch (css::uno::Exception &) {
+ TOOLS_INFO_EXCEPTION("desktop.deployment", "caught");
+ try {
+ impreg->revokeImplementation(url, rdb);
+ } catch (css::uno::RuntimeException &) {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignored");
+ }
+ throw;
+ }
+ }
+ m_registered = Reg::Registered;
+ that->addDataToDb(url, data);
+ } else { // revoke
+ m_registered = Reg::Void;
+ ComponentBackendDb::Data data(that->readDataFromDb(url));
+ css::uno::Reference< css::uno::XComponentContext > context(
+ that->getObject(url), css::uno::UNO_QUERY);
+ bool remoteContext = context.is();
+ if (!remoteContext) {
+ context = that->getComponentContext();
+ }
+ if (!startup) {
+ componentLiveRemoval(data);
+ }
+ css::uno::Reference< css::registry::XImplementationRegistration >(
+ context->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.ImplementationRegistration",
+ context),
+ css::uno::UNO_QUERY_THROW)->revokeImplementation(url, getRDB());
+ if (data.javaTypeLibrary) {
+ that->removeFromUnoRc(RCITEM_JAR_TYPELIB, url, xCmdEnv);
+ }
+ if (remoteContext) {
+ that->releaseObject(url);
+ }
+ m_registered = Reg::NotRegistered;
+ getMyBackend()->revokeEntryFromDb(url);
+ }
+}
+
+BackendImpl::TypelibraryPackageImpl::TypelibraryPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool jarFile, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_jarFile( jarFile )
+{
+}
+
+// Package
+BackendImpl * BackendImpl::TypelibraryPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(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<TypelibraryPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::TypelibraryPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ that->hasInUnoRc(
+ m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB, getURL() ),
+ false /* IsAmbiguous */ ) );
+}
+
+
+void BackendImpl::TypelibraryPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /*startup*/,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ const OUString url( getURL() );
+
+ if (doRegisterPackage)
+ {
+ // live insertion:
+ if (m_jarFile) {
+ // xxx todo add to classpath at runtime: ???
+ //SB: It is probably not worth it to add the live inserted type
+ // library JAR to the UnoClassLoader in the soffice process. Any
+ // live inserted component JAR that might reference this type
+ // library JAR runs in its own uno process, so there is probably no
+ // Java code in the soffice process that would see any UNO types
+ // introduced by this type library JAR.
+ }
+ else // RDB:
+ {
+ css::uno::Reference< css::container::XSet >(
+ that->getComponentContext()->getValueByName(
+ "/singletons"
+ "/com.sun.star.reflection.theTypeDescriptionManager"),
+ css::uno::UNO_QUERY_THROW)->insert(
+ css::uno::Any(expandUnoRcUrl(url)));
+ }
+
+ that->addToUnoRc( m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB,
+ url, xCmdEnv );
+ }
+ else // revokePackage()
+ {
+ that->removeFromUnoRc(
+ m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB, url, xCmdEnv );
+
+ // revoking types at runtime, possible, sensible?
+ if (!m_jarFile) {
+ css::uno::Reference< css::container::XSet >(
+ that->getComponentContext()->getValueByName(
+ "/singletons"
+ "/com.sun.star.reflection.theTypeDescriptionManager"),
+ css::uno::UNO_QUERY_THROW)->remove(
+ css::uno::Any(expandUnoRcUrl(url)));
+ }
+ }
+}
+
+BackendImpl::OtherPlatformPackageImpl::OtherPlatformPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier, OUString platform)
+ : Package(myBackend, url, name, name, xPackageType, bRemoved, identifier)
+ , m_aPlatform(std::move(platform))
+{
+ OSL_PRECOND(bRemoved, "this class can only be used for removing packages!");
+}
+
+BackendImpl *
+BackendImpl::OtherPlatformPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<OtherPlatformPackageImpl*>(this)));
+ }
+ return pBackend;
+}
+
+Reference<registry::XSimpleRegistry>
+BackendImpl::OtherPlatformPackageImpl::impl_openRDB() const
+{
+ OUString const aRDB(m_aPlatform + ".rdb");
+ OUString const aRDBPath(makeURL(getMyBackend()->getCachePath(), aRDB));
+
+ Reference<registry::XSimpleRegistry> xRegistry;
+
+ try
+ {
+ xRegistry.set(
+ impl_createInstance("com.sun.star.registry.SimpleRegistry"),
+ UNO_QUERY)
+ ;
+ if (xRegistry.is())
+ xRegistry->open(expandUnoRcUrl(aRDBPath), false, false);
+ }
+ catch (registry::InvalidRegistryException const&)
+ {
+ // If the registry does not exist, we do not need to bother at all
+ xRegistry.set(nullptr);
+ }
+
+ SAL_WARN_IF( !xRegistry.is(), "desktop.deployment", "could not create registry for the package's platform");
+ return xRegistry;
+}
+
+Reference<XInterface>
+BackendImpl::OtherPlatformPackageImpl::impl_createInstance(OUString const& rService)
+const
+{
+ Reference<XComponentContext> const xContext(getMyBackend()->getComponentContext());
+ OSL_ASSERT(xContext.is());
+ Reference<XInterface> xService;
+ if (xContext.is())
+ xService.set(xContext->getServiceManager()->createInstanceWithContext(rService, xContext));
+ return xService;
+}
+
+beans::Optional<beans::Ambiguous<sal_Bool> >
+BackendImpl::OtherPlatformPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard& /* guard */,
+ ::rtl::Reference<AbortChannel> const& /* abortChannel */,
+ Reference<XCommandEnvironment> const& /* xCmdEnv */ )
+{
+ return beans::Optional<beans::Ambiguous<sal_Bool> >(true,
+ beans::Ambiguous<sal_Bool>(true, false));
+}
+
+void
+BackendImpl::OtherPlatformPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard& /* guard */,
+ bool bRegisterPackage,
+ bool /* bStartup */,
+ ::rtl::Reference<AbortChannel> const& /* abortChannel */,
+ Reference<XCommandEnvironment> const& /* xCmdEnv */)
+{
+ OSL_PRECOND(!bRegisterPackage, "this class can only be used for removing packages!");
+
+ OUString const aURL(getURL());
+
+ Reference<registry::XSimpleRegistry> const xServicesRDB(impl_openRDB());
+ Reference<registry::XImplementationRegistration> const xImplReg(
+ impl_createInstance("com.sun.star.registry.ImplementationRegistration"),
+ UNO_QUERY)
+ ;
+ if (xImplReg.is() && xServicesRDB.is())
+ xImplReg->revokeImplementation(aURL, xServicesRDB);
+ if (xServicesRDB.is())
+ xServicesRDB->close();
+
+ getMyBackend()->revokeEntryFromDb(aURL);
+}
+
+BackendImpl * BackendImpl::ComponentsPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<ComponentsPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ComponentsPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true,
+ beans::Ambiguous<sal_Bool>(
+ getMyBackend()->hasInUnoRc(RCITEM_COMPONENTS, getURL()), false));
+}
+
+void BackendImpl::ComponentsPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url(getURL());
+ if (doRegisterPackage) {
+ if (!startup) {
+ css::uno::Reference< css::uno::XComponentContext > context(
+ that->getObject(url), css::uno::UNO_QUERY);
+ if (!context.is()) {
+ context.set(
+ that->insertObject(
+ url,
+ raise_uno_process(
+ that->getComponentContext(), abortChannel)),
+ css::uno::UNO_QUERY_THROW);
+ }
+ // This relies on the root component context's service manager
+ // supporting the extended XSet semantics:
+ css::uno::Sequence< css::beans::NamedValue > args
+ {
+ { "uri", css::uno::Any(expandUnoRcUrl(url)) },
+ { "component-context", css::uno::Any(context) }
+ };
+ css::uno::Reference< css::container::XSet > smgr(
+ that->getRootContext()->getServiceManager(),
+ css::uno::UNO_QUERY_THROW);
+ smgr->insert(css::uno::Any(args));
+ }
+ that->addToUnoRc(RCITEM_COMPONENTS, url, xCmdEnv);
+ } else { // revoke
+ that->removeFromUnoRc(RCITEM_COMPONENTS, url, xCmdEnv);
+ if (!startup) {
+ // This relies on the root component context's service manager
+ // supporting the extended XSet semantics:
+ css::uno::Sequence< css::beans::NamedValue > args { { "uri", css::uno::Any(expandUnoRcUrl(url)) } };
+ css::uno::Reference< css::container::XSet > smgr(
+ that->getRootContext()->getServiceManager(),
+ css::uno::UNO_QUERY_THROW);
+ smgr->remove(css::uno::Any(args));
+ }
+ that->releaseObject(url);
+ that->revokeEntryFromDb(url); // in case it got added with old code
+ }
+}
+
+BackendImpl::ComponentsPackageImpl::ComponentsPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier)
+{}
+
+} // anon namespace
+
+} // namespace dp_registry
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_component_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::component::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configuration.cxx b/desktop/source/deployment/registry/configuration/dp_configuration.cxx
new file mode 100644
index 000000000..62d5ab88f
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configuration.cxx
@@ -0,0 +1,818 @@
+/* -*- 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 .
+ */
+
+//TODO: Large parts of this file were copied from dp_component.cxx; those parts
+// should be consolidated.
+
+#include <config_extensions.h>
+
+#include <dp_backend.h>
+#if HAVE_FEATURE_EXTENSIONS
+#include <dp_persmap.h>
+#endif
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <comphelper/lok.hxx>
+#include <svl/inettype.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/configuration/Update.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <deque>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+#include "dp_configurationbackenddb.hxx"
+
+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::configuration {
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const ;
+
+ const bool m_isSchema;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool isSchema, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_isSchema( isSchema )
+ {}
+ };
+ friend class PackageImpl;
+
+ std::deque<OUString> m_xcs_files;
+ std::deque<OUString> m_xcu_files;
+ std::deque<OUString> & getFiles( bool xcs ) {
+ return xcs ? m_xcs_files : m_xcu_files;
+ }
+
+ bool m_configmgrini_inited;
+ bool m_configmgrini_modified;
+ std::unique_ptr<ConfigurationBackendDb> m_backendDb;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+#if HAVE_FEATURE_EXTENSIONS
+ // for backwards compatibility - nil if no (compatible) back-compat db present
+ std::unique_ptr<PersistentMap> m_registeredPackages;
+#endif
+
+ virtual void SAL_CALL disposing() override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xConfDataTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xConfSchemaTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ void configmgrini_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ void configmgrini_flush( Reference<XCommandEnvironment> const & xCmdEnv );
+
+ /* The parameter isURL is false in the case of adding the conf:ini-entry
+ value from the backend db. This entry already contains the path as it
+ is used in the configmgr.ini.
+ */
+ void addToConfigmgrIni( bool isSchema, bool isURL, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+#if HAVE_FEATURE_EXTENSIONS
+ bool removeFromConfigmgrIni( bool isSchema, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+#endif
+ void addDataToDb(OUString const & url, ConfigurationBackendDb::Data const & data);
+ ::std::optional<ConfigurationBackendDb::Data> readDataFromDb(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+ bool hasActiveEntry(std::u16string_view url);
+ bool activateEntry(std::u16string_view url);
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using PackageRegistryBackend::disposing;
+};
+
+
+void BackendImpl::disposing()
+{
+ try {
+ configmgrini_flush( Reference<XCommandEnvironment>() );
+
+ PackageRegistryBackend::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 );
+ }
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_configmgrini_inited( false ),
+ m_configmgrini_modified( false ),
+ m_xConfDataTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.configuration-data",
+ "*.xcu",
+ DpResId(RID_STR_CONF_DATA)
+ ) ),
+ m_xConfSchemaTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.configuration-schema",
+ "*.xcs",
+ DpResId(RID_STR_CONF_SCHEMA)
+ ) ),
+ m_typeInfos{ m_xConfDataTypeInfo, m_xConfSchemaTypeInfo }
+{
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ if (transientMode())
+ {
+ // TODO
+ }
+ else
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ConfigurationBackendDb(getComponentContext(), dbFile));
+ //clean up data folders which are no longer used.
+ //This must not be done in the same process where the help files
+ //are still registers. Only after revoking and restarting OOo the folders
+ //can be removed. This works now, because the extension manager is a singleton
+ //and the backends are only create once per process.
+ std::vector<OUString> folders = m_backendDb->getAllDataUrls();
+ deleteUnusedFolders(folders);
+
+ configmgrini_verify_init( xCmdEnv );
+
+#if HAVE_FEATURE_EXTENSIONS
+ std::unique_ptr<PersistentMap> pMap;
+ OUString aCompatURL( makeURL( getCachePath(), "registered_packages.pmap" ) );
+
+ // Don't create it if it doesn't exist already
+ if ( ::utl::UCBContentHelper::Exists( expandUnoRcUrl( aCompatURL ) ) )
+ {
+ try
+ {
+ pMap.reset( new PersistentMap( aCompatURL ) );
+ }
+ catch (const Exception &e)
+ {
+ OUString aStr = "Exception loading legacy package database: '" +
+ e.Message +
+ "' - ignoring file, please remove it.\n";
+ dp_misc::writeConsole( aStr );
+ }
+ }
+ m_registeredPackages = std::move(pMap);
+#endif
+ }
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.configuration.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ConfigurationBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+::std::optional<ConfigurationBackendDb::Data> BackendImpl::readDataFromDb(
+ std::u16string_view url)
+{
+ ::std::optional<ConfigurationBackendDb::Data> data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+bool BackendImpl::activateEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->activateEntry(url);
+ return false;
+}
+
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ))
+ {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase( ".xcu" )) {
+ mediaType = "application/vnd.sun.star.configuration-data";
+ }
+ if (title.endsWithIgnoreAsciiCase( ".xcs" )) {
+ mediaType = "application/vnd.sun.star.configuration-schema";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent( url, xCmdEnv, m_xComponentContext );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xConfDataTypeInfo, false /* data file */,
+ bRemoved, identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-schema")) {
+ return new PackageImpl(
+ this, url, name, m_xConfSchemaTypeInfo, true /* schema file */,
+ bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::configmgrini_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ const ::osl::MutexGuard guard( m_aMutex );
+ if ( m_configmgrini_inited)
+ return;
+
+ // common rc:
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), "configmgr.ini" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ OUString line;
+ if (readLine( &line, "SCHEMA=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ sal_Int32 index = RTL_CONSTASCII_LENGTH("SCHEMA=");
+ do {
+ OUString token( o3tl::trim(o3tl::getToken(line, 0, ' ', index )) );
+ if (!token.isEmpty()) {
+ //The file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the configmgrini.
+ //After running XExtensionManager::synchronize, the configmgrini is
+ //cleaned up
+ m_xcs_files.push_back( token );
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "DATA=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ sal_Int32 index = RTL_CONSTASCII_LENGTH("DATA=");
+ do {
+ std::u16string_view token( o3tl::trim(o3tl::getToken(line, 0, ' ', index )) );
+ if (!token.empty())
+ {
+ if (token[ 0 ] == '?')
+ token = token.substr( 1 );
+ //The file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the configmgrini.
+ //After running XExtensionManager::synchronize, the configmgrini is
+ //cleaned up
+ m_xcu_files.push_back( OUString(token) );
+ }
+ }
+ while (index >= 0);
+ }
+ }
+ m_configmgrini_modified = false;
+ m_configmgrini_inited = true;
+}
+
+
+void BackendImpl::configmgrini_flush(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ if (!m_configmgrini_inited || !m_configmgrini_modified)
+ return;
+
+ OStringBuffer buf;
+ if (! m_xcs_files.empty())
+ {
+ auto iPos( m_xcs_files.cbegin() );
+ auto const iEnd( m_xcs_files.cend() );
+ buf.append( "SCHEMA=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+ if (! m_xcu_files.empty())
+ {
+ auto iPos( m_xcu_files.cbegin() );
+ auto const iEnd( m_xcu_files.cend() );
+ buf.append( "DATA=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+
+ // write configmgr.ini:
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf.getStr()),
+ buf.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), "configmgr.ini" ), xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+
+ m_configmgrini_modified = false;
+}
+
+
+void BackendImpl::addToConfigmgrIni( bool isSchema, bool isURL, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( isURL ? dp_misc::makeRcTerm(url_) : url_ );
+ const ::osl::MutexGuard guard( m_aMutex );
+ configmgrini_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getFiles(isSchema);
+ if (std::find( rSet.begin(), rSet.end(), rcterm ) == rSet.end()) {
+ rSet.push_front( rcterm ); // prepend to list, thus overriding
+ // write immediately:
+ m_configmgrini_modified = true;
+ configmgrini_flush( xCmdEnv );
+ }
+}
+
+#if HAVE_FEATURE_EXTENSIONS
+bool BackendImpl::removeFromConfigmgrIni(
+ bool isSchema, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( m_aMutex );
+ configmgrini_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getFiles(isSchema);
+ auto i(std::find(rSet.begin(), rSet.end(), rcterm));
+ if (i == rSet.end() && !isSchema)
+ {
+ //in case the xcu contained %origin% then the configmr.ini contains the
+ //url to the file in the user installation (e.g. $BUNDLED_EXTENSIONS_USER)
+ //However, m_url (getURL()) contains the URL for the file in the actual
+ //extension installation.
+ ::std::optional<ConfigurationBackendDb::Data> data = readDataFromDb(url_);
+ if (data)
+ i = std::find(rSet.begin(), rSet.end(), data->iniEntry);
+ }
+ if (i == rSet.end()) {
+ return false;
+ }
+ rSet.erase(i);
+ // write immediately:
+ m_configmgrini_modified = true;
+ configmgrini_flush( xCmdEnv );
+ return true;
+}
+#endif
+
+// Package
+
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(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<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+
+ bool bReg = false;
+ if (that->hasActiveEntry(getURL()))
+ bReg = true;
+
+#if HAVE_FEATURE_EXTENSIONS
+ const OUString url(getURL());
+ if (!bReg && that->m_registeredPackages)
+ {
+ // fallback for user extension registered in berkeley DB
+ bReg = that->m_registeredPackages->has(
+ OUStringToOString( url, RTL_TEXTENCODING_UTF8 ));
+ }
+#endif
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true, beans::Ambiguous<sal_Bool>( bReg, false ) );
+}
+
+
+OUString encodeForXml( OUString const & text )
+{
+ // encode conforming xml:
+ sal_Int32 len = text.getLength();
+ OUStringBuffer buf;
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ sal_Unicode c = text[ pos ];
+ switch (c) {
+ case '<':
+ buf.append( "&lt;" );
+ break;
+ case '>':
+ buf.append( "&gt;" );
+ break;
+ case '&':
+ buf.append( "&amp;" );
+ break;
+ case '\'':
+ buf.append( "&apos;" );
+ break;
+ case '\"':
+ buf.append( "&quot;" );
+ break;
+ default:
+ buf.append( c );
+ break;
+ }
+ }
+ return buf.makeStringAndClear();
+}
+
+
+OUString replaceOrigin(
+ OUString const & url, std::u16string_view destFolder, Reference< XCommandEnvironment > const & xCmdEnv, Reference< XComponentContext > const & xContext, bool & out_replaced)
+{
+ // looking for %origin%:
+ ::ucbhelper::Content ucb_content( url, xCmdEnv, xContext );
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ std::vector<sal_Int8> filtered( bytes.size() * 2 );
+ bool use_filtered = false;
+ OString origin;
+ char const * pBytes = reinterpret_cast<char const *>(
+ bytes.data());
+ std::size_t nBytes = bytes.size();
+ size_t write_pos = 0;
+ while (nBytes > 0)
+ {
+ sal_Int32 index = rtl_str_indexOfChar_WithLength( pBytes, nBytes, '%' );
+ if (index < 0) {
+ if (! use_filtered) // opt
+ break;
+ index = nBytes;
+ }
+
+ if ((write_pos + index) > filtered.size())
+ filtered.resize( (filtered.size() + index) * 2 );
+ memcpy( filtered.data() + write_pos, pBytes, index );
+ write_pos += index;
+ pBytes += index;
+ nBytes -= index;
+ if (nBytes == 0)
+ break;
+
+ // consume %:
+ ++pBytes;
+ --nBytes;
+ char const * pAdd = "%";
+ sal_Int32 nAdd = 1;
+ if (nBytes > 1 && pBytes[ 0 ] == '%')
+ {
+ // %% => %
+ ++pBytes;
+ --nBytes;
+ use_filtered = true;
+ }
+ else if (rtl_str_shortenedCompare_WithLength(
+ pBytes, nBytes,
+ "origin%",
+ RTL_CONSTASCII_LENGTH("origin%"),
+ RTL_CONSTASCII_LENGTH("origin%")) == 0)
+ {
+ if (origin.isEmpty()) {
+ // encode only once
+ origin = OUStringToOString(
+ encodeForXml( url.copy( 0, url.lastIndexOf( '/' ) ) ),
+ // xxx todo: encode always for UTF-8? => lookup doc-header?
+ RTL_TEXTENCODING_UTF8 );
+ }
+ pAdd = origin.getStr();
+ nAdd = origin.getLength();
+ pBytes += RTL_CONSTASCII_LENGTH("origin%");
+ nBytes -= RTL_CONSTASCII_LENGTH("origin%");
+ use_filtered = true;
+ }
+ if ((write_pos + nAdd) > filtered.size())
+ filtered.resize( (filtered.size() + nAdd) * 2 );
+ memcpy( filtered.data() + write_pos, pAdd, nAdd );
+ write_pos += nAdd;
+ }
+ if (!use_filtered)
+ return url;
+ if (write_pos < filtered.size())
+ filtered.resize( write_pos );
+ OUString newUrl(url);
+ if (!destFolder.empty())
+ {
+ //get the file name of the xcu and add it to the url of the temporary folder
+ sal_Int32 i = url.lastIndexOf('/');
+ newUrl = OUString::Concat(destFolder) + url.subView(i);
+ }
+
+ ucbhelper::Content(newUrl, xCmdEnv, xContext).writeStream(
+ xmlscript::createInputStream(std::move(filtered)), true);
+ out_replaced = true;
+ return newUrl;
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url( getURL() );
+
+ if (doRegisterPackage)
+ {
+ if (getMyBackend()->activateEntry(getURL()))
+ {
+ ::std::optional<ConfigurationBackendDb::Data> data = that->readDataFromDb(url);
+ OSL_ASSERT(data);
+ that->addToConfigmgrIni( m_isSchema, false, data->iniEntry, xCmdEnv );
+ }
+ else
+ {
+ ConfigurationBackendDb::Data data;
+ if (!m_isSchema)
+ {
+ const OUString sModFolder = that->createFolder(xCmdEnv);
+ bool out_replaced = false;
+ url = replaceOrigin(url, sModFolder, xCmdEnv, that->getComponentContext(), out_replaced);
+ if (out_replaced)
+ data.dataUrl = sModFolder;
+ else
+ deleteTempFolder(sModFolder);
+ }
+ //No need for live-deployment for bundled extension, because OOo
+ //restarts after installation
+ if ((that->m_eContext != Context::Bundled && !startup)
+ || comphelper::LibreOfficeKit::isActive())
+ {
+ if (m_isSchema)
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->insertExtensionXcsFile(
+ that->m_eContext == Context::Shared, expandUnoRcUrl(url));
+ }
+ else
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->insertExtensionXcuFile(
+ that->m_eContext == Context::Shared, expandUnoRcUrl(url));
+ }
+ }
+ that->addToConfigmgrIni( m_isSchema, true, url, xCmdEnv );
+ data.iniEntry = dp_misc::makeRcTerm(url);
+ that->addDataToDb(getURL(), data);
+ }
+ }
+ else // revoke
+ {
+#if HAVE_FEATURE_EXTENSIONS
+ if (!that->removeFromConfigmgrIni(m_isSchema, url, xCmdEnv) &&
+ that->m_registeredPackages) {
+ // Obsolete package database handling - should be removed for LibreOffice 4.0
+ t_string2string_map entries(
+ that->m_registeredPackages->getEntries());
+ for (auto const& entry : entries)
+ {
+ //If the xcu file was installed before the configmgr was changed
+ //to use the configmgr.ini, one needed to rebuild to whole directory
+ //structure containing the xcu, xcs files from all extensions. Now,
+ //we just add all other xcu/xcs files to the configmgr.ini instead of
+ //rebuilding the directory structure.
+ OUString url2(
+ OStringToOUString(entry.first, RTL_TEXTENCODING_UTF8));
+ if (url2 != url) {
+ bool schema = entry.second.equalsIgnoreAsciiCase(
+ "vnd.sun.star.configuration-schema");
+ OUString url_replaced(url2);
+ ConfigurationBackendDb::Data data;
+ if (!schema)
+ {
+ const OUString sModFolder = that->createFolder(xCmdEnv);
+ bool out_replaced = false;
+ url_replaced = replaceOrigin(
+ url2, sModFolder, xCmdEnv, that->getComponentContext(), out_replaced);
+ if (out_replaced)
+ data.dataUrl = sModFolder;
+ else
+ deleteTempFolder(sModFolder);
+ }
+ that->addToConfigmgrIni(schema, true, url_replaced, xCmdEnv);
+ data.iniEntry = dp_misc::makeRcTerm(url_replaced);
+ that->addDataToDb(url2, data);
+ }
+ that->m_registeredPackages->erase(entry.first);
+ }
+ try
+ {
+ ::ucbhelper::Content(
+ makeURL( that->getCachePath(), "registry" ),
+ xCmdEnv, that->getComponentContext() ).executeCommand(
+ "delete", Any( true /* delete physically */ ) );
+ }
+ catch(const Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+ }
+#endif
+ ::std::optional<ConfigurationBackendDb::Data> data = that->readDataFromDb(url);
+ //If an xcu file was life deployed then always a data entry is written.
+ //If the xcu file was already in the configmr.ini then there is also
+ //a data entry
+ if (!m_isSchema && data)
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->removeExtensionXcuFile(expandUnoRcTerm(data->iniEntry));
+ }
+ that->revokeEntryFromDb(url);
+ }
+}
+
+} // anon namespace
+
+} // namespace dp_registry
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_configuration_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::configuration::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx
new file mode 100644
index 000000000..afdc8112f
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx
@@ -0,0 +1,161 @@
+/* -*- 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 <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/xpath/XXPathAPI.hpp>
+
+#include "dp_configurationbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/configuration-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"conf";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"configuration-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"configuration";
+
+namespace dp_registry::backend::configuration {
+
+ConfigurationBackendDb::ConfigurationBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ConfigurationBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ConfigurationBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ConfigurationBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ConfigurationBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+void ConfigurationBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> helpNode
+ = writeKeyElement(url);
+
+ writeSimpleElement(u"data-url", data.dataUrl, helpNode);
+ writeSimpleElement(u"ini-entry", data.iniEntry, helpNode);
+ save();
+ }
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+::std::optional<ConfigurationBackendDb::Data>
+ConfigurationBackendDb::getEntry(std::u16string_view url)
+{
+ try
+ {
+ ConfigurationBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ retData.dataUrl = readSimpleElement(u"data-url", aNode);
+ retData.iniEntry = readSimpleElement(u"ini-entry", aNode);
+ }
+ else
+ {
+ return ::std::optional<Data>();
+ }
+ return ::std::optional<Data>(retData);
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> ConfigurationBackendDb::getAllDataUrls()
+{
+ try
+ {
+ std::vector<OUString> listRet;
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sPrefix = getNSPrefix();
+ OUString sExpression(
+ sPrefix + ":configuration/" + sPrefix + ":data-url/text()");
+ Reference<css::xml::dom::XNodeList> nodes =
+ xpathApi->selectNodeList(root, sExpression);
+ if (nodes.is())
+ {
+ sal_Int32 length = nodes->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ listRet.push_back(nodes->item(i)->getNodeValue());
+ }
+ return listRet;
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry::backend::configuration
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx
new file mode 100644
index 000000000..bd48aab7b
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx
@@ -0,0 +1,74 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <vector>
+#include <optional>
+#include <string_view>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+namespace dp_registry::backend::configuration
+{
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ConfigurationBackendDb : public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* the URL to the folder containing the xcu or xcs files which contained
+ %origin%
+ */
+ OUString dataUrl;
+ /* the URL of the xcu or xcs file which is written in to the configmgr.ini
+ */
+ OUString iniEntry;
+ };
+
+public:
+ ConfigurationBackendDb(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ OUString const& url);
+
+ void addEntry(OUString const& url, Data const& data);
+
+ ::std::optional<Data> getEntry(std::u16string_view url);
+ std::vector<OUString> getAllDataUrls();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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: */
diff --git a/desktop/source/deployment/registry/dp_backenddb.cxx b/desktop/source/deployment/registry/dp_backenddb.cxx
new file mode 100644
index 000000000..28effd95c
--- /dev/null
+++ b/desktop/source/deployment/registry/dp_backenddb.cxx
@@ -0,0 +1,655 @@
+/* -*- 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 <cppuhelper/exc_hlp.hxx>
+#include <osl/file.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/io/XActiveDataSource.hpp>
+#include <com/sun/star/io/XActiveDataControl.hpp>
+#include <dp_misc.h>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <dp_backenddb.hxx>
+
+
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend {
+
+BackendDb::BackendDb(
+ Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url):
+ m_xContext(xContext)
+{
+ m_urlDb = dp_misc::expandUnoRcUrl(url);
+}
+
+void BackendDb::save()
+{
+ const Reference<css::io::XActiveDataSource> xDataSource(m_doc,css::uno::UNO_QUERY_THROW);
+ std::vector<sal_Int8> bytes;
+ xDataSource->setOutputStream(::xmlscript::createOutputStream(&bytes));
+ const Reference<css::io::XActiveDataControl> xDataControl(m_doc,css::uno::UNO_QUERY_THROW);
+ xDataControl->start();
+
+ const Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(std::move(bytes)));
+ ::ucbhelper::Content ucbDb(m_urlDb, nullptr, m_xContext);
+ ucbDb.writeStream(xData, true /*replace existing*/);
+}
+
+css::uno::Reference<css::xml::dom::XDocument> const & BackendDb::getDocument()
+{
+ if (!m_doc.is())
+ {
+ const Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
+ css::xml::dom::DocumentBuilder::create(m_xContext) );
+
+ ::osl::DirectoryItem item;
+ ::osl::File::RC err = ::osl::DirectoryItem::get(m_urlDb, item);
+ if (err == ::osl::File::E_None)
+ {
+ ::ucbhelper::Content descContent(
+ m_urlDb, css::uno::Reference<css::ucb::XCommandEnvironment>(),
+ m_xContext);
+ Reference<css::io::XInputStream> xIn = descContent.openStream();
+ m_doc = xDocBuilder->parse(xIn);
+ }
+ else if (err == ::osl::File::E_NOENT)
+ {
+ //Create a new document and insert some basic stuff
+ m_doc = xDocBuilder->newDocument();
+ const Reference<css::xml::dom::XElement> rootNode =
+ m_doc->createElementNS(getDbNSName(), getNSPrefix() +
+ ":" + getRootElementName());
+
+ m_doc->appendChild(Reference<css::xml::dom::XNode>(
+ rootNode, UNO_QUERY_THROW));
+ save();
+ }
+ else
+ throw css::uno::RuntimeException(
+ "Extension manager could not access database file:"
+ + m_urlDb, nullptr);
+
+ if (!m_doc.is())
+ throw css::uno::RuntimeException(
+ "Extension manager could not get root node of data base file: "
+ + m_urlDb, nullptr);
+ }
+
+ return m_doc;
+}
+
+Reference<css::xml::xpath::XXPathAPI> const & BackendDb::getXPathAPI()
+{
+ if (!m_xpathApi.is())
+ {
+ m_xpathApi = css::xml::xpath::XPathAPI::create( m_xContext );
+
+ m_xpathApi->registerNS( getNSPrefix(), getDbNSName() );
+ }
+
+ return m_xpathApi;
+}
+
+void BackendDb::removeElement(OUString const & sXPathExpression)
+{
+ try
+ {
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ //find the extension element that is to be removed
+ const Reference<css::xml::dom::XNode> aNode =
+ xpathApi->selectSingleNode(root, sXPathExpression);
+
+ if (aNode.is())
+ {
+ root->removeChild(aNode);
+ save();
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ //There must not be any other entry with the same url
+ const Reference<css::xml::dom::XNode> nextNode =
+ xpathApi->selectSingleNode(root, sXPathExpression);
+ OSL_ASSERT(! nextNode.is());
+#endif
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+void BackendDb::removeEntry(std::u16string_view url)
+{
+ const OUString sKeyElement = getKeyElementName();
+ const OUString sPrefix = getNSPrefix();
+ OUString sExpression =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "[@url = \"" +
+ url +
+ "\"]";
+
+ removeElement(sExpression);
+}
+
+void BackendDb::revokeEntry(std::u16string_view url)
+{
+ try
+ {
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ entry->setAttribute("revoked", "true");
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to revoke data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+bool BackendDb::activateEntry(std::u16string_view url)
+{
+ try
+ {
+ bool ret = false;
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ //no attribute "active" means it is active, that is, registered.
+ entry->removeAttribute("revoked");
+ save();
+ ret = true;
+ }
+ return ret;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to revoke data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+bool BackendDb::hasActiveEntry(std::u16string_view url)
+{
+ try
+ {
+ bool ret = false;
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ OUString sActive = entry->getAttribute("revoked");
+ if (!(sActive == "true"))
+ ret = true;
+ }
+ return ret;
+
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to determine an active entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+Reference<css::xml::dom::XNode> BackendDb::getKeyElement(
+ std::u16string_view url)
+{
+ try
+ {
+ const OUString sPrefix = getNSPrefix();
+ const OUString sKeyElement = getKeyElementName();
+ OUString sExpression =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "[@url = \"" +
+ url +
+ "\"]";
+
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ return xpathApi->selectSingleNode(root, sExpression);
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read key element in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Only writes the data if there is at least one entry
+void BackendDb::writeVectorOfPair(
+ std::vector< std::pair< OUString, OUString > > const & vecPairs,
+ std::u16string_view sVectorTagName,
+ std::u16string_view sPairTagName,
+ std::u16string_view sFirstTagName,
+ std::u16string_view sSecondTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent)
+{
+ try{
+ if (vecPairs.empty())
+ return;
+ const OUString sNameSpace = getDbNSName();
+ OSL_ASSERT(!sNameSpace.isEmpty());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+
+ const Reference<css::xml::dom::XElement> vectorNode(
+ doc->createElementNS(sNameSpace, sPrefix + sVectorTagName));
+
+ xParent->appendChild(
+ Reference<css::xml::dom::XNode>(
+ vectorNode, css::uno::UNO_QUERY_THROW));
+ for (auto const& vecPair : vecPairs)
+ {
+ const Reference<css::xml::dom::XElement> pairNode(
+ doc->createElementNS(sNameSpace, sPrefix + sPairTagName));
+
+ vectorNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ pairNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XElement> firstNode(
+ doc->createElementNS(sNameSpace, sPrefix + sFirstTagName));
+
+ pairNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ firstNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XText> firstTextNode(
+ doc->createTextNode( vecPair.first));
+
+ firstNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ firstTextNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XElement> secondNode(
+ doc->createElementNS(sNameSpace, sPrefix + sSecondTagName));
+
+ pairNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ secondNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XText> secondTextNode(
+ doc->createTextNode( vecPair.second));
+
+ secondNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ secondTextNode, css::uno::UNO_QUERY_THROW));
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector< std::pair< OUString, OUString > >
+BackendDb::readVectorOfPair(
+ Reference<css::xml::dom::XNode> const & parent,
+ std::u16string_view sListTagName,
+ std::u16string_view sPairTagName,
+ std::u16string_view sFirstTagName,
+ std::u16string_view sSecondTagName)
+{
+ try
+ {
+ OSL_ASSERT(parent.is());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sExprPairs(
+ sPrefix + sListTagName + "/" + sPrefix + sPairTagName);
+ const Reference<css::xml::dom::XNodeList> listPairs =
+ xpathApi->selectNodeList(parent, sExprPairs);
+
+ std::vector< std::pair< OUString, OUString > > retVector;
+ sal_Int32 length = listPairs->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ {
+ const Reference<css::xml::dom::XNode> aPair = listPairs->item(i);
+ const OUString sExprFirst(sPrefix + sFirstTagName + "/text()");
+ const Reference<css::xml::dom::XNode> first =
+ xpathApi->selectSingleNode(aPair, sExprFirst);
+
+ const OUString sExprSecond(sPrefix + sSecondTagName + "/text()");
+ const Reference<css::xml::dom::XNode> second =
+ xpathApi->selectSingleNode(aPair, sExprSecond);
+ OSL_ASSERT(first.is() && second.is());
+
+ retVector.emplace_back(
+ first->getNodeValue(), second->getNodeValue());
+ }
+ return retVector;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Only writes the data if there is at least one entry
+void BackendDb::writeSimpleList(
+ std::deque< OUString> const & list,
+ std::u16string_view sListTagName,
+ std::u16string_view sMemberTagName,
+ Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ if (list.empty())
+ return;
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+
+ const Reference<css::xml::dom::XElement> listNode(
+ doc->createElementNS(sNameSpace, sPrefix + sListTagName));
+
+ xParent->appendChild(
+ Reference<css::xml::dom::XNode>(
+ listNode, css::uno::UNO_QUERY_THROW));
+
+ for (auto const& elem : list)
+ {
+ const Reference<css::xml::dom::XNode> memberNode(
+ doc->createElementNS(sNameSpace, sPrefix + sMemberTagName), css::uno::UNO_QUERY_THROW);
+
+ listNode->appendChild(memberNode);
+
+ const Reference<css::xml::dom::XNode> textNode(
+ doc->createTextNode(elem), css::uno::UNO_QUERY_THROW);
+
+ memberNode->appendChild(textNode);
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Writes only the element if is has a value.
+//The prefix is automatically added to the element name
+void BackendDb::writeSimpleElement(
+ std::u16string_view sElementName, OUString const & value,
+ Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ if (value.isEmpty())
+ return;
+ const OUString sPrefix = getNSPrefix();
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const OUString sNameSpace = getDbNSName();
+ const Reference<css::xml::dom::XNode> dataNode(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName),
+ UNO_QUERY_THROW);
+ xParent->appendChild(dataNode);
+
+ const Reference<css::xml::dom::XNode> dataValue(
+ doc->createTextNode(value), UNO_QUERY_THROW);
+ dataNode->appendChild(dataValue);
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry(writeSimpleElement) in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+
+}
+
+/// The key elements have a url attribute and are always children of the root element.
+Reference<css::xml::dom::XNode> BackendDb::writeKeyElement(
+ OUString const & url)
+{
+ try
+ {
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sElementName = getKeyElementName();
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ //Check if there are an entry with the same url. This can be the case if the
+ //status of an XPackage is ambiguous. In this case a call to activateExtension
+ //(dp_extensionmanager.cxx), will register the package again. See also
+ //Package::processPackage_impl in dp_backend.cxx.
+ //A package can become
+ //invalid after its successful registration, for example if a second extension with
+ //the same service is installed.
+ const OUString sExpression(
+ sPrefix + ":" + sElementName + "[@url = \"" + url + "\"]");
+ const Reference<css::xml::dom::XNode> existingNode =
+ getXPathAPI()->selectSingleNode(root, sExpression);
+ if (existingNode.is())
+ {
+ OSL_ASSERT(false);
+ //replace the existing entry.
+ removeEntry(url);
+ }
+
+ const Reference<css::xml::dom::XElement> keyElement(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName));
+
+ keyElement->setAttribute("url", url);
+
+ const Reference<css::xml::dom::XNode> keyNode(
+ keyElement, UNO_QUERY_THROW);
+ root->appendChild(keyNode);
+ return keyNode;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write key element in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+OUString BackendDb::readSimpleElement(
+ std::u16string_view sElementName, Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ const OUString sPrefix = getNSPrefix();
+ const OUString sExpr(sPrefix + ":" + sElementName + "/text()");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const Reference<css::xml::dom::XNode> val =
+ xpathApi->selectSingleNode(xParent, sExpr);
+ if (val.is())
+ return val->getNodeValue();
+ return OUString();
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data (readSimpleElement) in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+std::deque< OUString> BackendDb::readList(
+ Reference<css::xml::dom::XNode> const & parent,
+ std::u16string_view sListTagName,
+ std::u16string_view sMemberTagName)
+{
+ try
+ {
+ OSL_ASSERT(parent.is());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sExprList(
+ sPrefix + sListTagName + "/" + sPrefix + sMemberTagName + "/text()");
+ const Reference<css::xml::dom::XNodeList> list =
+ xpathApi->selectNodeList(parent, sExprList);
+
+ std::deque<OUString > retList;
+ sal_Int32 length = list->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ {
+ const Reference<css::xml::dom::XNode> member = list->item(i);
+ retList.push_back(member->getNodeValue());
+ }
+ return retList;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> BackendDb::getOneChildFromAllEntries(
+ std::u16string_view name)
+{
+ try
+ {
+ std::vector<OUString> listRet;
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sKeyElement = getKeyElementName();
+ OUString sNodeSelectExpr =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "/" +
+ sPrefix +
+ ":" +
+ name +
+ "/text()";
+
+ Reference<css::xml::dom::XNodeList> nodes =
+ xpathApi->selectNodeList(root, sNodeSelectExpr);
+ if (nodes.is())
+ {
+ sal_Int32 length = nodes->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ listRet.push_back(nodes->item(i)->getNodeValue());
+ }
+ return listRet;
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+RegisteredDb::RegisteredDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+}
+
+void RegisteredDb::addEntry(OUString const & url)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sEntry = getKeyElementName();
+
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+#if OSL_DEBUG_LEVEL > 0
+ //There must not be yet an entry with the same url
+ OUString sExpression(
+ sPrefix + ":" + sEntry + "[@url = \"" + url + "\"]");
+ Reference<css::xml::dom::XNode> _extensionNode =
+ getXPathAPI()->selectSingleNode(root, sExpression);
+ OSL_ASSERT(! _extensionNode.is());
+#endif
+ Reference<css::xml::dom::XElement> helpElement(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sEntry));
+
+ helpElement->setAttribute("url", url);
+
+ Reference<css::xml::dom::XNode> helpNode(
+ helpElement, UNO_QUERY_THROW);
+ root->appendChild(helpNode);
+
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/dp_registry.cxx b/desktop/source/deployment/registry/dp_registry.cxx
new file mode 100644
index 000000000..961afe7bb
--- /dev/null
+++ b/desktop/source/deployment/registry/dp_registry.cxx
@@ -0,0 +1,528 @@
+/* -*- 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 <sal/log.hxx>
+
+#include <dp_shared.hxx>
+#include <dp_package.hxx>
+#include <strings.hrc>
+#include <dp_registry.hxx>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/sequence.hxx>
+#include <ucbhelper/content.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/DeploymentException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/deployment/XPackageTypeInfo.hpp>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <set>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+
+namespace dp_registry {
+
+namespace {
+
+typedef ::cppu::WeakComponentImplHelper<
+ deployment::XPackageRegistry, util::XUpdatable > t_helper;
+
+
+class PackageRegistryImpl : private cppu::BaseMutex, public t_helper
+{
+ struct ci_string_hash {
+ std::size_t operator () ( OUString const & str ) const {
+ return str.toAsciiLowerCase().hashCode();
+ }
+ };
+ struct ci_string_equals {
+ bool operator () ( std::u16string_view str1, std::u16string_view str2 ) const{
+ return o3tl::equalsIgnoreAsciiCase( str1, str2 );
+ }
+ };
+ typedef std::unordered_map<
+ OUString, Reference<deployment::XPackageRegistry>,
+ ci_string_hash, ci_string_equals > t_string2registry;
+ typedef std::unordered_map<
+ OUString, OUString,
+ ci_string_hash, ci_string_equals > t_string2string;
+ typedef std::set<
+ Reference<deployment::XPackageRegistry> > t_registryset;
+
+ t_string2registry m_mediaType2backend;
+ t_string2string m_filter2mediaType;
+ t_registryset m_ambiguousBackends;
+ t_registryset m_allBackends;
+ std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
+
+ void insertBackend(
+ Reference<deployment::XPackageRegistry> const & xBackend );
+
+protected:
+ void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageRegistryImpl() override;
+ PackageRegistryImpl() : t_helper( m_aMutex ) {}
+
+
+public:
+ static Reference<deployment::XPackageRegistry> create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XUpdatable
+ virtual void SAL_CALL update() override;
+
+ // XPackageRegistry
+ virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
+ OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+void PackageRegistryImpl::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "PackageRegistry instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+
+void PackageRegistryImpl::disposing()
+{
+ // dispose all backends:
+ for (auto const& backend : m_allBackends)
+ {
+ try_dispose(backend);
+ }
+ m_mediaType2backend = t_string2registry();
+ m_ambiguousBackends = t_registryset();
+ m_allBackends = t_registryset();
+
+ t_helper::disposing();
+}
+
+
+PackageRegistryImpl::~PackageRegistryImpl()
+{
+}
+
+
+OUString normalizeMediaType( std::u16string_view mediaType )
+{
+ OUStringBuffer buf;
+ sal_Int32 index = 0;
+ for (;;) {
+ buf.append( o3tl::trim(o3tl::getToken(mediaType, 0, '/', index )) );
+ if (index < 0)
+ break;
+ buf.append( '/' );
+ }
+ return buf.makeStringAndClear();
+}
+
+
+void PackageRegistryImpl::packageRemoved(
+ OUString const & url, OUString const & mediaType)
+{
+ const t_string2registry::const_iterator i =
+ m_mediaType2backend.find(mediaType);
+
+ if (i != m_mediaType2backend.end())
+ {
+ i->second->packageRemoved(url, mediaType);
+ }
+}
+
+void PackageRegistryImpl::insertBackend(
+ Reference<deployment::XPackageRegistry> const & xBackend )
+{
+ m_allBackends.insert( xBackend );
+ std::unordered_set<OUString> ambiguousFilters;
+
+ const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
+ xBackend->getSupportedPackageTypes() );
+ for ( Reference<deployment::XPackageTypeInfo> const & xPackageType : packageTypes )
+ {
+ m_typesInfos.push_back( xPackageType );
+
+ const OUString mediaType( normalizeMediaType(
+ xPackageType->getMediaType() ) );
+ std::pair<t_string2registry::iterator, bool> a_insertion(
+ m_mediaType2backend.emplace( mediaType, xBackend ) );
+ if (a_insertion.second)
+ {
+ // add parameterless media-type, too:
+ sal_Int32 semi = mediaType.indexOf( ';' );
+ if (semi >= 0) {
+ m_mediaType2backend.emplace( mediaType.copy( 0, semi ), xBackend );
+ }
+ const OUString fileFilter( xPackageType->getFileFilter() );
+ //The package backend shall also be called to determine the mediatype
+ //(XPackageRegistry.bindPackage) when the URL points to a directory.
+ const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
+ if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
+ {
+ m_ambiguousBackends.insert( xBackend );
+ }
+ else
+ {
+ sal_Int32 nIndex = 0;
+ do {
+ OUString token( fileFilter.getToken( 0, ';', nIndex ) );
+ if (token.match( "*." ))
+ token = token.copy( 1 );
+ if (token.isEmpty())
+ continue;
+ // mark any further wildcards ambig:
+ bool ambig = (token.indexOf('*') >= 0 ||
+ token.indexOf('?') >= 0);
+ if (! ambig) {
+ std::pair<t_string2string::iterator, bool> ins(
+ m_filter2mediaType.emplace(
+ token, mediaType ) );
+ ambig = !ins.second;
+ if (ambig) {
+ // filter has already been in: add previously
+ // added backend to ambig set
+ const t_string2registry::const_iterator iFind(
+ m_mediaType2backend.find(
+ /* media-type of pr. added backend */
+ ins.first->second ) );
+ OSL_ASSERT(
+ iFind != m_mediaType2backend.end() );
+ if (iFind != m_mediaType2backend.end())
+ m_ambiguousBackends.insert( iFind->second );
+ }
+ }
+ if (ambig) {
+ m_ambiguousBackends.insert( xBackend );
+ // mark filter to be removed later from filters map:
+ ambiguousFilters.insert( token );
+ }
+ }
+ while (nIndex >= 0);
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
+ << mediaType
+ << "\" => "
+ << Reference<lang::XServiceInfo>(
+ xBackend, UNO_QUERY_THROW )->getImplementationName()
+ << "\"!" );
+ }
+#endif
+ }
+
+ // cut out ambiguous filters:
+ for (auto const& ambiguousFilter : ambiguousFilters)
+ {
+ m_filter2mediaType.erase(ambiguousFilter);
+ }
+}
+
+
+Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ rtl::Reference<PackageRegistryImpl> that = new PackageRegistryImpl;
+
+ // auto-detect all registered package registries:
+ Reference<container::XEnumeration> xEnum(
+ Reference<container::XContentEnumerationAccess>(
+ xComponentContext->getServiceManager(),
+ UNO_QUERY_THROW )->createContentEnumeration(
+ "com.sun.star.deployment.PackageRegistryBackend" ) );
+ if (xEnum.is())
+ {
+ while (xEnum->hasMoreElements())
+ {
+ Any element( xEnum->nextElement() );
+ Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
+ auto pregistryArgs = registryArgs.getArray();
+ pregistryArgs[ 0 ] <<= context;
+ if (!cachePath.isEmpty())
+ {
+ Reference<lang::XServiceInfo> xServiceInfo(
+ element, UNO_QUERY_THROW );
+ OUString registryCachePath(
+ makeURL( cachePath,
+ ::rtl::Uri::encode(
+ xServiceInfo->getImplementationName(),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+ pregistryArgs[ 1 ] <<= registryCachePath;
+ pregistryArgs[ 2 ] <<= false; // readOnly;
+ create_folder( nullptr, registryCachePath,
+ Reference<XCommandEnvironment>() );
+ }
+
+ Reference<deployment::XPackageRegistry> xBackend;
+ Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
+ if (xFac.is()) {
+ xBackend.set(
+ xFac->createInstanceWithArgumentsAndContext(
+ registryArgs, xComponentContext ), UNO_QUERY );
+ }
+ else {
+ Reference<lang::XSingleServiceFactory> xSingleServiceFac(
+ element, UNO_QUERY_THROW );
+ xBackend.set(
+ xSingleServiceFac->createInstanceWithArguments(
+ registryArgs ), UNO_QUERY );
+ }
+ if (! xBackend.is()) {
+ throw DeploymentException(
+ "cannot instantiate PackageRegistryBackend service: "
+ + Reference<lang::XServiceInfo>(
+ element, UNO_QUERY_THROW )->getImplementationName(),
+ static_cast<OWeakObject *>(that.get()) );
+ }
+
+ that->insertBackend( xBackend );
+ }
+ }
+
+ // Insert bundle back-end.
+ // Always register as last, because we want to add extensions also as folders
+ // and as a default we accept every folder, which was not recognized by the other
+ // backends.
+ Reference<deployment::XPackageRegistry> extensionBackend =
+ ::dp_registry::backend::bundle::create(
+ that, context, cachePath, xComponentContext);
+ that->insertBackend(extensionBackend);
+
+ Reference<lang::XServiceInfo> xServiceInfo(
+ extensionBackend, UNO_QUERY_THROW );
+
+ OSL_ASSERT(xServiceInfo.is());
+ OUString registryCachePath(
+ makeURL( cachePath,
+ ::rtl::Uri::encode(
+ xServiceInfo->getImplementationName(),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+ create_folder( nullptr, registryCachePath, Reference<XCommandEnvironment>());
+
+
+#if OSL_DEBUG_LEVEL > 0
+ // dump tables:
+ {
+ t_registryset allBackends;
+ dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
+ for (auto const& elem : that->m_filter2mediaType)
+ {
+ const Reference<deployment::XPackageRegistry> xBackend(
+ that->m_mediaType2backend.find( elem.second )->second );
+ allBackends.insert( xBackend );
+ dp_misc::TRACE(
+ "extension \"" + elem.first + "\" maps to media-type \"" + elem.second
+ + "\" maps to backend "
+ + Reference<lang::XServiceInfo>(
+ xBackend, UNO_QUERY_THROW )
+ ->getImplementationName()
+ + "\n");
+ }
+ dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
+ for (auto const& ambiguousBackend : that->m_ambiguousBackends)
+ {
+ OUStringBuffer buf;
+ buf.append(
+ Reference<lang::XServiceInfo>(
+ ambiguousBackend, UNO_QUERY_THROW )->getImplementationName() );
+ buf.append( ": " );
+ const Sequence< Reference<deployment::XPackageTypeInfo> > types(
+ ambiguousBackend->getSupportedPackageTypes() );
+ for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
+ Reference<deployment::XPackageTypeInfo> const & xInfo =
+ types[ pos ];
+ buf.append( xInfo->getMediaType() );
+ const OUString filter( xInfo->getFileFilter() );
+ if (!filter.isEmpty()) {
+ buf.append( " (" );
+ buf.append( filter );
+ buf.append( ")" );
+ }
+ if (pos < (types.getLength() - 1))
+ buf.append( ", " );
+ }
+ dp_misc::TRACE(buf + "\n\n");
+ }
+ allBackends.insert( that->m_ambiguousBackends.begin(),
+ that->m_ambiguousBackends.end() );
+ OSL_ASSERT( allBackends == that->m_allBackends );
+ }
+#endif
+
+ return that;
+}
+
+// XUpdatable: broadcast to backends
+
+void PackageRegistryImpl::update()
+{
+ check();
+ for (auto const& backend : m_allBackends)
+ {
+ const Reference<util::XUpdatable> xUpdatable(backend, UNO_QUERY);
+ if (xUpdatable.is())
+ xUpdatable->update();
+ }
+}
+
+// XPackageRegistry
+
+Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
+ OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ check();
+ OUString mediaType(mediaType_);
+ if (mediaType.isEmpty())
+ {
+ ::ucbhelper::Content ucbContent;
+ bool bOk=true;
+
+ try
+ {
+ bOk = create_ucb_content(
+ &ucbContent, url, xCmdEnv, false /* no throw */ )
+ && !ucbContent.isFolder();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ bOk = false;
+ }
+
+ if (bOk)
+ {
+ OUString title( StrTitle::getTitle( ucbContent ) );
+ for (;;)
+ {
+ const t_string2string::const_iterator iFind(
+ m_filter2mediaType.find(title) );
+ if (iFind != m_filter2mediaType.end()) {
+ mediaType = iFind->second;
+ break;
+ }
+ sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
+ if (point < 0)
+ break;
+ title = title.copy(point);
+ }
+ }
+ }
+ if (mediaType.isEmpty())
+ {
+ // try ambiguous backends:
+ for (auto const& ambiguousBackend : m_ambiguousBackends)
+ {
+ try {
+ return ambiguousBackend->bindPackage( url, mediaType, bRemoved,
+ identifier, xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ }
+ }
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+ else
+ {
+ // get backend by media-type:
+ t_string2registry::const_iterator iFind(
+ m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
+ if (iFind == m_mediaType2backend.end()) {
+ // xxx todo: more sophisticated media-type argument parsing...
+ sal_Int32 q = mediaType.indexOf( ';' );
+ if (q >= 0) {
+ iFind = m_mediaType2backend.find(
+ normalizeMediaType(
+ // cut parameters:
+ mediaType.subView( 0, q ) ) );
+ }
+ }
+ if (iFind == m_mediaType2backend.end()) {
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+ return iFind->second->bindPackage( url, mediaType, bRemoved,
+ identifier, xCmdEnv );
+ }
+}
+
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+PackageRegistryImpl::getSupportedPackageTypes()
+{
+ return comphelper::containerToSequence(m_typesInfos);
+}
+} // anon namespace
+
+
+Reference<deployment::XPackageRegistry> create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ return PackageRegistryImpl::create(
+ context, cachePath, xComponentContext );
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executable.cxx b/desktop/source/deployment/registry/executable/dp_executable.cxx
new file mode 100644
index 000000000..40b253587
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executable.cxx
@@ -0,0 +1,336 @@
+/* -*- 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 <memory>
+#include <string_view>
+
+#include <dp_misc.h>
+#include <dp_backend.h>
+#include <dp_ucb.h>
+#include <dp_interact.h>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <osl/file.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include "dp_executablebackenddb.hxx"
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace dp_misc;
+
+namespace dp_registry::backend::executable {
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class ExecutablePackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ bool getFileAttributes(sal_uInt64& out_Attributes);
+ bool isUrlTargetInExtension() const;
+
+ public:
+ ExecutablePackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier)
+ {}
+ };
+ friend class ExecutablePackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void addDataToDb(OUString const & url);
+ bool hasActiveEntry(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+
+ Reference<deployment::XPackageTypeInfo> m_xExecutableTypeInfo;
+ std::unique_ptr<ExecutableBackendDb> m_backendDb;
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xExecutableTypeInfo(new Package::TypeInfo(
+ "application/vnd.sun.star.executable",
+ "", "Executable" ) )
+{
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ExecutableBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.executable.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+void BackendImpl::addDataToDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url);
+}
+
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+
+// XPackageRegistry
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return Sequence<Reference<deployment::XPackageTypeInfo> >(
+ & m_xExecutableTypeInfo, 1);
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (mediaType.isEmpty())
+ {
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.executable"))
+ {
+ return new BackendImpl::ExecutablePackageImpl(
+ this, url, name, m_xExecutableTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ }
+ return Reference<deployment::XPackage>();
+}
+
+
+// Package
+BackendImpl * BackendImpl::ExecutablePackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(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<ExecutablePackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ExecutablePackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<dp_misc::AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ bool registered = getMyBackend()->hasActiveEntry(getURL());
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ registered, false /* IsAmbiguous */ ) );
+}
+
+void BackendImpl::ExecutablePackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /*startup*/,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & /*xCmdEnv*/ )
+{
+ checkAborted(abortChannel);
+ if (doRegisterPackage)
+ {
+ if (!isUrlTargetInExtension())
+ {
+ OSL_ASSERT(false);
+ return;
+ }
+ sal_uInt64 attributes = 0;
+ //Setting the executable attribute does not affect executables on Windows
+ if (getFileAttributes(attributes))
+ {
+ if(getMyBackend()->m_context == "user")
+ attributes |= osl_File_Attribute_OwnExe;
+ else if (getMyBackend()->m_context == "shared")
+ attributes |= (osl_File_Attribute_OwnExe | osl_File_Attribute_GrpExe
+ | osl_File_Attribute_OthExe);
+ else if (getMyBackend()->m_context != "bundled")
+ //Bundled extensions are required to be in the properly
+ //installed. That is an executable must have the right flags
+ OSL_ASSERT(false);
+
+ //This won't have effect on Windows
+ osl::File::setAttributes(
+ dp_misc::expandUnoRcUrl(m_url), attributes);
+ }
+ getMyBackend()->addDataToDb(getURL());
+ }
+ else
+ {
+ getMyBackend()->revokeEntryFromDb(getURL());
+ }
+}
+
+//We currently cannot check if this XPackage represents a content of a particular extension
+//But we can check if we are within $UNO_USER_PACKAGES_CACHE etc.
+//Done for security reasons. For example an extension manifest could contain a path to
+//an executable outside the extension.
+bool BackendImpl::ExecutablePackageImpl::isUrlTargetInExtension() const
+{
+ bool bSuccess = false;
+ OUString sExtensionDir;
+ if(getMyBackend()->m_context == "user")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$UNO_USER_PACKAGES_CACHE");
+ else if (getMyBackend()->m_context == "shared")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$UNO_SHARED_PACKAGES_CACHE");
+ else if (getMyBackend()->m_context == "bundled")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$BUNDLED_EXTENSIONS");
+ else
+ OSL_ASSERT(false);
+ //remove file ellipses
+ if (osl::File::E_None == osl::File::getAbsoluteFileURL(OUString(), sExtensionDir, sExtensionDir))
+ {
+ OUString sFile;
+ if (osl::File::E_None == osl::File::getAbsoluteFileURL(
+ OUString(), dp_misc::expandUnoRcUrl(m_url), sFile))
+ {
+ if (sFile.match(sExtensionDir))
+ bSuccess = true;
+ }
+ }
+ return bSuccess;
+}
+
+bool BackendImpl::ExecutablePackageImpl::getFileAttributes(sal_uInt64& out_Attributes)
+{
+ bool bSuccess = false;
+ const OUString url(dp_misc::expandUnoRcUrl(m_url));
+ osl::DirectoryItem item;
+ if (osl::FileBase::E_None == osl::DirectoryItem::get(url, item))
+ {
+ osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
+ if( osl::FileBase::E_None == item.getFileStatus(aStatus))
+ {
+ out_Attributes = aStatus.getAttributes();
+ bSuccess = true;
+ }
+ }
+ return bSuccess;
+}
+
+
+} // anon namespace
+
+
+} // namespace dp_registry::backend::executable
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_executable_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::executable::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx b/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx
new file mode 100644
index 000000000..29cbf85b3
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx
@@ -0,0 +1,64 @@
+/* -*- 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 <com/sun/star/uno/XComponentContext.hpp>
+#include "dp_executablebackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/executable-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"exe";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"executable-backend-db";
+constexpr OUStringLiteral ENTRY_NAME = u"executable";
+
+namespace dp_registry::backend::executable {
+
+ExecutableBackendDb::ExecutableBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):RegisteredDb(xContext, url)
+{
+
+}
+
+OUString ExecutableBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ExecutableBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ExecutableBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ExecutableBackendDb::getKeyElementName()
+{
+ return ENTRY_NAME;
+}
+
+
+} // namespace dp_registry::backend::executable
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx b/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx
new file mode 100644
index 000000000..0561a8c54
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx
@@ -0,0 +1,55 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+namespace dp_registry::backend::executable
+{
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ The format looks like this:
+
+<?xml version="1.0"?>
+ */
+class ExecutableBackendDb : public dp_registry::backend::RegisteredDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+ ExecutableBackendDb(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ OUString const& url);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/README.md b/desktop/source/deployment/registry/help/README.md
new file mode 100644
index 000000000..24ea19518
--- /dev/null
+++ b/desktop/source/deployment/registry/help/README.md
@@ -0,0 +1 @@
+Support for help integrated in extensions. Also see /README.help.md.
diff --git a/desktop/source/deployment/registry/help/dp_help.cxx b/desktop/source/deployment/registry/help/dp_help.cxx
new file mode 100644
index 000000000..9e1a75fb9
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_help.cxx
@@ -0,0 +1,621 @@
+/* -*- 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 <memory>
+#include <config_features.h>
+
+#include <strings.hrc>
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include "dp_helpbackenddb.hxx"
+#include <dp_ucb.h>
+#include <rtl/uri.hxx>
+#include <osl/file.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include <unotools/pathoptions.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/string_view.hxx>
+
+#if HAVE_FEATURE_XMLHELP
+#include <helpcompiler/compilehelp.hxx>
+#include <helpcompiler/HelpIndexer.hxx>
+#endif
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/util/XMacroExpander.hpp>
+#include <optional>
+#include <string_view>
+
+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::help {
+namespace {
+
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier);
+
+ bool extensionContainsCompiledHelp();
+
+ //XPackage
+ virtual css::beans::Optional< OUString > SAL_CALL getRegistrationDataURL() override;
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void implProcessHelp( PackageImpl * package, bool doRegisterPackage,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv);
+ void implCollectXhpFiles( const OUString& aDir,
+ std::vector< OUString >& o_rXhpFileVector );
+
+ ::std::optional<HelpBackendDb::Data> readDataFromDb(std::u16string_view url);
+ bool hasActiveEntry(std::u16string_view url);
+ bool activateEntry(std::u16string_view url);
+
+ Reference< ucb::XSimpleFileAccess3 > const & getFileAccess();
+ Reference< ucb::XSimpleFileAccess3 > m_xSFA;
+
+ const Reference<deployment::XPackageTypeInfo> m_xHelpTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+ std::unique_ptr<HelpBackendDb> m_backendDb;
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xHelpTypeInfo( new Package::TypeInfo("application/vnd.sun.star.help",
+ OUString(),
+ DpResId(RID_STR_HELP)
+ ) ),
+ m_typeInfos{ m_xHelpTypeInfo }
+{
+ if (transientMode())
+ return;
+
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new HelpBackendDb(getComponentContext(), dbFile));
+
+ //clean up data folders which are no longer used.
+ //This must not be done in the same process where the help files
+ //are still registers. Only after revoking and restarting OOo the folders
+ //can be removed. This works now, because the extension manager is a singleton
+ //and the backends are only create once per process.
+ std::vector<OUString> folders = m_backendDb->getAllDataUrls();
+ deleteUnusedFolders(folders);
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.help.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ // we don't support auto detection:
+ if (mediaType_.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType_, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.help"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xHelpTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType_,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+::std::optional<HelpBackendDb::Data> BackendImpl::readDataFromDb(
+ std::u16string_view url)
+{
+ ::std::optional<HelpBackendDb::Data> data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+bool BackendImpl::hasActiveEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+bool BackendImpl::activateEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->activateEntry(url);
+ return false;
+}
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name, xPackageType, bRemoved,
+ identifier)
+{
+}
+
+// Package
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(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<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+bool BackendImpl::PackageImpl::extensionContainsCompiledHelp()
+{
+ bool bCompiled = true;
+ OUString aExpandedHelpURL = dp_misc::expandUnoRcUrl(getURL());
+
+ ::osl::Directory helpFolder(aExpandedHelpURL);
+ if ( helpFolder.open() == ::osl::File::E_None)
+ {
+ //iterate over the contents of the help folder
+ //We assume that all folders within the help folder contain language specific
+ //help files. If just one of them does not contain compiled help then this
+ //function returns false.
+ ::osl::DirectoryItem item;
+ ::osl::File::RC errorNext = ::osl::File::E_None;
+ while ((errorNext = helpFolder.getNextItem(item)) == ::osl::File::E_None)
+ {
+ //No find the language folders
+ ::osl::FileStatus stat(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |osl_FileStatus_Mask_FileURL);
+ if (item.getFileStatus(stat) == ::osl::File::E_None)
+ {
+ if (stat.getFileType() != ::osl::FileStatus::Directory)
+ continue;
+
+ //look if there is the folder help.idxl in the language folder
+ OUString compUrl(stat.getFileURL() + "/help.idxl");
+ ::osl::Directory compiledFolder(compUrl);
+ if (compiledFolder.open() != ::osl::File::E_None)
+ {
+ bCompiled = false;
+ break;
+ }
+ }
+ else
+ {
+ //Error
+ OSL_ASSERT(false);
+ bCompiled = false;
+ break;
+ }
+ }
+ if (errorNext != ::osl::File::E_NOENT
+ && errorNext != ::osl::File::E_None)
+ {
+ //Error
+ OSL_ASSERT(false);
+ bCompiled = false;
+ }
+ }
+ return bCompiled;
+}
+
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+
+ bool bReg = false;
+ if (that->hasActiveEntry(getURL()))
+ bReg = true;
+
+ return beans::Optional< beans::Ambiguous<sal_Bool> >( true, beans::Ambiguous<sal_Bool>( bReg, false ) );
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /* startup */,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl* that = getMyBackend();
+ that->implProcessHelp( this, doRegisterPackage, xCmdEnv);
+}
+
+beans::Optional< OUString > BackendImpl::PackageImpl::getRegistrationDataURL()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ ::std::optional<HelpBackendDb::Data> data =
+ getMyBackend()->readDataFromDb(getURL());
+
+ if (data && getMyBackend()->hasActiveEntry(getURL()))
+ return beans::Optional<OUString>(true, data->dataUrl);
+
+ return beans::Optional<OUString>(true, OUString());
+}
+
+void BackendImpl::implProcessHelp(
+ PackageImpl * package, bool doRegisterPackage,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ Reference< deployment::XPackage > xPackage(package);
+ OSL_ASSERT(xPackage.is());
+ if (doRegisterPackage)
+ {
+ //revive already processed help if possible
+ if ( !activateEntry(xPackage->getURL()))
+ {
+ HelpBackendDb::Data data;
+ data.dataUrl = xPackage->getURL();
+ if (!package->extensionContainsCompiledHelp())
+ {
+#if HAVE_FEATURE_XMLHELP
+ const OUString sHelpFolder = createFolder(xCmdEnv);
+ data.dataUrl = sHelpFolder;
+
+ Reference< ucb::XSimpleFileAccess3 > xSFA = getFileAccess();
+ OUString aHelpURL = xPackage->getURL();
+ OUString aExpandedHelpURL = dp_misc::expandUnoRcUrl( aHelpURL );
+ if( !xSFA->isFolder( aExpandedHelpURL ) )
+ {
+ OUString aErrStr = DpResId( RID_STR_HELPPROCESSING_GENERAL_ERROR ) +
+ "No help folder";
+ OWeakObject* oWeakThis = this;
+ throw deployment::DeploymentException( OUString(), oWeakThis,
+ Any( uno::Exception( aErrStr, oWeakThis ) ) );
+ }
+
+ // Scan languages
+ Sequence< OUString > aLanguageFolderSeq = xSFA->getFolderContents( aExpandedHelpURL, true );
+ sal_Int32 nLangCount = aLanguageFolderSeq.getLength();
+ const OUString* pSeq = aLanguageFolderSeq.getConstArray();
+ for( sal_Int32 iLang = 0 ; iLang < nLangCount ; ++iLang )
+ {
+ OUString aLangURL = pSeq[iLang];
+ if( xSFA->isFolder( aLangURL ) )
+ {
+ std::vector< OUString > aXhpFileVector;
+
+ // calculate jar file URL
+ sal_Int32 indexStartSegment = aLangURL.lastIndexOf('/');
+ // for example "/en"
+ OUString langFolderURLSegment(
+ aLangURL.copy(
+ indexStartSegment + 1, aLangURL.getLength() - indexStartSegment - 1));
+
+ //create the folder in the "temporary folder"
+ ::ucbhelper::Content langFolderContent;
+ const OUString langFolderDest = makeURL(sHelpFolder, langFolderURLSegment);
+ const OUString langFolderDestExpanded = ::dp_misc::expandUnoRcUrl(langFolderDest);
+ ::dp_misc::create_folder(
+ &langFolderContent,
+ langFolderDest, xCmdEnv);
+
+ static const OUStringLiteral aHelpStr(u"help");
+
+ OUString aJarFile(
+ makeURL(sHelpFolder, langFolderURLSegment + "/" + aHelpStr + ".jar"));
+ aJarFile = ::dp_misc::expandUnoRcUrl(aJarFile);
+
+ OUString aEncodedJarFilePath = rtl::Uri::encode(
+ aJarFile, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ OUString aDestBasePath = "vnd.sun.star.zip://" +
+ aEncodedJarFilePath + "/" ;
+
+ sal_Int32 nLenLangFolderURL = aLangURL.getLength() + 1;
+
+ Sequence< OUString > aSubLangSeq = xSFA->getFolderContents( aLangURL, true );
+ sal_Int32 nSubLangCount = aSubLangSeq.getLength();
+ const OUString* pSubLangSeq = aSubLangSeq.getConstArray();
+ for( sal_Int32 iSubLang = 0 ; iSubLang < nSubLangCount ; ++iSubLang )
+ {
+ OUString aSubFolderURL = pSubLangSeq[iSubLang];
+ if( !xSFA->isFolder( aSubFolderURL ) )
+ continue;
+
+ implCollectXhpFiles( aSubFolderURL, aXhpFileVector );
+
+ // Copy to package (later: move?)
+ std::u16string_view aPureFolderName = aSubFolderURL.subView( nLenLangFolderURL );
+ OUString aDestPath = aDestBasePath + aPureFolderName;
+ xSFA->copy( aSubFolderURL, aDestPath );
+ }
+
+ // Call compiler
+ sal_Int32 nXhpFileCount = aXhpFileVector.size();
+ std::unique_ptr<OUString[]> pXhpFiles(new OUString[nXhpFileCount]);
+ for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
+ {
+ OUString aXhpFile = aXhpFileVector[iXhp];
+ OUString aXhpRelFile = aXhpFile.copy( nLenLangFolderURL );
+ pXhpFiles[iXhp] = aXhpRelFile;
+ }
+
+ OUString aOfficeHelpPath( SvtPathOptions().GetHelpPath() );
+ OUString aOfficeHelpPathFileURL;
+ ::osl::File::getFileURLFromSystemPath( aOfficeHelpPath, aOfficeHelpPathFileURL );
+
+ HelpProcessingErrorInfo aErrorInfo;
+ bool bSuccess = compileExtensionHelp(
+ aOfficeHelpPathFileURL, aHelpStr, aLangURL,
+ nXhpFileCount, pXhpFiles.get(),
+ langFolderDestExpanded, aErrorInfo );
+
+ pXhpFiles.reset();
+
+ if( bSuccess )
+ {
+ OUString aLang;
+ sal_Int32 nLastSlash = aLangURL.lastIndexOf( '/' );
+ if( nLastSlash != -1 )
+ aLang = aLangURL.copy( nLastSlash + 1 );
+ else
+ aLang = "en";
+
+ HelpIndexer aIndexer(aLang, "help", langFolderDestExpanded, langFolderDestExpanded);
+ aIndexer.indexDocuments();
+ }
+
+ if( !bSuccess )
+ {
+ TranslateId pErrStrId;
+ switch( aErrorInfo.m_eErrorClass )
+ {
+ case HelpProcessingErrorClass::General: pErrStrId = RID_STR_HELPPROCESSING_GENERAL_ERROR; break;
+ case HelpProcessingErrorClass::XmlParsing: pErrStrId = RID_STR_HELPPROCESSING_XMLPARSING_ERROR; break;
+ default: ;
+ };
+
+ OUString aErrStr;
+ if (pErrStrId)
+ {
+ aErrStr = DpResId(pErrStrId);
+
+ // Remove CR/LF
+ OUString aErrMsg( aErrorInfo.m_aErrorMsg );
+ sal_Unicode const nCR = 13, nLF = 10;
+ sal_Int32 nSearchCR = aErrMsg.indexOf( nCR );
+ sal_Int32 nSearchLF = aErrMsg.indexOf( nLF );
+ if( nSearchCR != -1 || nSearchLF != -1 )
+ {
+ sal_Int32 nCopy;
+ if( nSearchCR == -1 )
+ nCopy = nSearchLF;
+ else if( nSearchLF == -1 )
+ nCopy = nSearchCR;
+ else
+ nCopy = ( nSearchCR < nSearchLF ) ? nSearchCR : nSearchLF;
+
+ aErrMsg = aErrMsg.copy( 0, nCopy );
+ }
+ aErrStr += aErrMsg;
+ if (pErrStrId != RID_STR_HELPPROCESSING_XMLPARSING_ERROR && !aErrorInfo.m_aXMLParsingFile.isEmpty() )
+ {
+ aErrStr += " in ";
+
+ OUString aDecodedFile = rtl::Uri::decode( aErrorInfo.m_aXMLParsingFile,
+ rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ aErrStr += aDecodedFile;
+ if( aErrorInfo.m_nXMLParsingLine != -1 )
+ {
+ aErrStr += ", line " +
+ OUString::number( aErrorInfo.m_nXMLParsingLine );
+ }
+ }
+ }
+
+ OWeakObject* oWeakThis = this;
+ throw deployment::DeploymentException( OUString(), oWeakThis,
+ Any( uno::Exception( aErrStr, oWeakThis ) ) );
+ }
+ }
+ }
+#else
+ (void) xCmdEnv;
+#endif
+ }
+ // Writing the data entry replaces writing the flag file. If we got to this
+ // point the registration was successful.
+ if (m_backendDb)
+ m_backendDb->addEntry(xPackage->getURL(), data);
+ }
+ } //if (doRegisterPackage)
+ else
+ {
+ if (m_backendDb)
+ m_backendDb->revokeEntry(xPackage->getURL());
+ }
+}
+
+void BackendImpl::implCollectXhpFiles( const OUString& aDir,
+ std::vector< OUString >& o_rXhpFileVector )
+{
+ Reference< ucb::XSimpleFileAccess3 > xSFA = getFileAccess();
+
+ // Scan xhp files recursively
+ Sequence< OUString > aSeq = xSFA->getFolderContents( aDir, true );
+ sal_Int32 nCount = aSeq.getLength();
+ const OUString* pSeq = aSeq.getConstArray();
+ for( sal_Int32 i = 0 ; i < nCount ; ++i )
+ {
+ OUString aURL = pSeq[i];
+ if( xSFA->isFolder( aURL ) )
+ {
+ implCollectXhpFiles( aURL, o_rXhpFileVector );
+ }
+ else
+ {
+ sal_Int32 nLastDot = aURL.lastIndexOf( '.' );
+ if( nLastDot != -1 )
+ {
+ std::u16string_view aExt = aURL.subView( nLastDot + 1 );
+ if( o3tl::equalsIgnoreAsciiCase( aExt, u"xhp" ) )
+ o_rXhpFileVector.push_back( aURL );
+ }
+ }
+ }
+}
+
+Reference< ucb::XSimpleFileAccess3 > const & BackendImpl::getFileAccess()
+{
+ if( !m_xSFA.is() )
+ {
+ Reference<XComponentContext> const & xContext = getComponentContext();
+ if( xContext.is() )
+ {
+ m_xSFA = ucb::SimpleFileAccess::create(xContext);
+ }
+ if( !m_xSFA.is() )
+ {
+ throw RuntimeException(
+ "dp_registry::backend::help::BackendImpl::getFileAccess(), "
+ "could not instantiate SimpleFileAccess." );
+ }
+ }
+ return m_xSFA;
+}
+
+} // anon namespace
+
+} // namespace dp_registry
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_help_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::help::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx b/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx
new file mode 100644
index 000000000..19bb3c3ce
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx
@@ -0,0 +1,126 @@
+/* -*- 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 <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_helpbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/help-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"help";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"help-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"help";
+
+namespace dp_registry::backend::help {
+
+HelpBackendDb::HelpBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString HelpBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString HelpBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString HelpBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString HelpBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+void HelpBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> helpNode
+ = writeKeyElement(url);
+
+ writeSimpleElement(u"data-url", data.dataUrl, helpNode);
+ save();
+ }
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in help backend db: " + m_urlDb, nullptr, exc);
+ }
+}
+
+
+::std::optional<HelpBackendDb::Data>
+HelpBackendDb::getEntry(std::u16string_view url)
+{
+ try
+ {
+ HelpBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ retData.dataUrl = readSimpleElement(u"data-url", aNode);
+ }
+ else
+ {
+ return ::std::optional<Data>();
+ }
+ return ::std::optional<Data>(retData);
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in help backend db: " + m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> HelpBackendDb::getAllDataUrls()
+{
+ return getOneChildFromAllEntries(u"data-url");
+}
+
+} // namespace dp_registry::backend::help
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx b/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx
new file mode 100644
index 000000000..a46bd8663
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx
@@ -0,0 +1,70 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <optional>
+#include <string_view>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+namespace dp_registry::backend::help
+{
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class HelpBackendDb : public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* the URL to the folder containing the compiled help files, etc.
+ */
+ OUString dataUrl;
+ };
+
+public:
+ HelpBackendDb(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ OUString const& url);
+
+ void addEntry(OUString const& url, Data const& data);
+
+ ::std::optional<Data> getEntry(std::u16string_view url);
+ //must also return the data urls for entries with @active="false". That is,
+ //those are currently revoked.
+ std::vector<OUString> getAllDataUrls();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/inc/dp_backend.h b/desktop/source/deployment/registry/inc/dp_backend.h
new file mode 100644
index 000000000..0fe39db45
--- /dev/null
+++ b/desktop/source/deployment/registry/inc/dp_backend.h
@@ -0,0 +1,287 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <dp_shared.hxx>
+#include <dp_interact.h>
+#include <rtl/ref.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <unordered_map>
+#include <strings.hrc>
+#include <utility>
+
+namespace dp_registry::backend
+{
+
+class PackageRegistryBackend;
+
+inline constexpr OUStringLiteral BACKEND_SERVICE_NAME = u"com.sun.star.deployment.PackageRegistryBackend";
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::deployment::XPackage > t_PackageBase;
+
+
+class Package : protected cppu::BaseMutex, public t_PackageBase
+{
+ PackageRegistryBackend * getMyBackend() const;
+ void processPackage_impl(
+ bool registerPackage,
+ bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+
+protected:
+ ::rtl::Reference<PackageRegistryBackend> m_myBackend;
+ const OUString m_url;
+ OUString m_name;
+ OUString m_displayName;
+ const css::uno::Reference<css::deployment::XPackageTypeInfo> m_xPackageType;
+ const bool m_bRemoved;
+ //Only set if m_bRemoved = true;
+ const OUString m_identifier;
+
+ void check() const;
+ void fireModified();
+ virtual void SAL_CALL disposing() override;
+
+ void checkAborted(
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel );
+
+ // @@@ to be implemented by specific backend:
+ virtual css::beans::Optional< css::beans::Ambiguous<sal_Bool> >
+ isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+
+ virtual ~Package() override;
+ Package( ::rtl::Reference<PackageRegistryBackend> myBackend,
+ OUString url,
+ OUString name,
+ OUString displayName,
+ css::uno::Reference<css::deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved,
+ OUString identifier);
+
+public:
+
+ class TypeInfo :
+ public ::cppu::WeakImplHelper<css::deployment::XPackageTypeInfo>
+ {
+ const OUString m_mediaType;
+ const OUString m_fileFilter;
+ const OUString m_shortDescr;
+ public:
+ virtual ~TypeInfo() override;
+ TypeInfo( OUString mediaType,
+ OUString fileFilter,
+ OUString shortDescr )
+ : m_mediaType(std::move(mediaType)), m_fileFilter(std::move(fileFilter)),
+ m_shortDescr(std::move(shortDescr))
+ {}
+ // XPackageTypeInfo
+ virtual OUString SAL_CALL getMediaType() override;
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getShortDescription() override;
+ virtual OUString SAL_CALL getFileFilter() override;
+ virtual css::uno::Any SAL_CALL getIcon( sal_Bool highContrast,
+ sal_Bool smallIcon ) override;
+ };
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+ virtual void SAL_CALL removeEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+ virtual void SAL_CALL removeModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+
+ // XPackage
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+ virtual css::beans::Optional< css::beans::Ambiguous<sal_Bool> >
+ SAL_CALL isRegistered(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Int32 SAL_CALL checkPrerequisites(
+ const css::uno::Reference< css::task::XAbortChannel >& xAbortChannel,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool noLicenseChecking) override;
+
+ virtual ::sal_Bool SAL_CALL checkDependencies(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv ) override;
+
+ virtual void SAL_CALL registerPackage(
+ sal_Bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void SAL_CALL revokePackage(
+ sal_Bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual sal_Bool SAL_CALL isBundle() override;
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getBundle(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getName() override;
+ virtual css::beans::Optional< OUString > SAL_CALL getIdentifier() override;
+ virtual OUString SAL_CALL getVersion() override;
+ virtual OUString SAL_CALL getURL() override;
+ virtual OUString SAL_CALL getDisplayName() override;
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getLicenseText() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getUpdateInformationURLs() override;
+ virtual css::beans::StringPair SAL_CALL getPublisherInfo() override;
+ virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL
+ getIcon( sal_Bool bHighContrast ) override;
+ virtual css::uno::Reference<css::deployment::XPackageTypeInfo> SAL_CALL
+ getPackageType() override;
+ virtual void SAL_CALL exportTo(
+ OUString const & destFolderURL,
+ OUString const & newTitle,
+ sal_Int32 nameClashAction,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getRepositoryName() override;
+ virtual css::beans::Optional< OUString > SAL_CALL getRegistrationDataURL() override;
+ virtual sal_Bool SAL_CALL isRemoved() override;
+
+};
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::lang::XEventListener,
+ css::deployment::XPackageRegistry,
+ css::lang::XServiceInfo > t_BackendBase;
+
+
+class PackageRegistryBackend
+ : protected cppu::BaseMutex, public t_BackendBase
+{
+ //The map held originally WeakReferences. The map entries are removed in the disposing
+ //function, which is called when the XPackages are destructed or they are
+ //explicitly disposed. The latter happens, for example, when an extension is
+ //removed (see dp_manager.cxx). However, because of how the help systems work, now
+ // XPackageManager::getDeployedPackages is called often. This results in a lot
+ //of bindPackage calls which are costly. Therefore we keep hard references in
+ //the map now.
+ typedef std::unordered_map<
+ OUString, css::uno::Reference<css::deployment::XPackage> > t_string2ref;
+ t_string2ref m_bound;
+
+protected:
+ OUString m_cachePath;
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+
+ OUString m_context;
+ // currently only for library containers:
+ enum class Context {
+ Unknown, User, Shared, Bundled, Tmp, Document
+ } m_eContext;
+
+ static OUString StrCannotDetectMediaType() { return DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE); }
+ static OUString StrUnsupportedMediaType() { return DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE); }
+
+ // @@@ to be implemented by specific backend:
+ virtual css::uno::Reference<css::deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+
+ void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageRegistryBackend() override;
+ PackageRegistryBackend(
+ css::uno::Sequence<css::uno::Any> const & args,
+ css::uno::Reference<css::uno::XComponentContext> const & xContext );
+
+ /* creates a folder with a unique name.
+ If url is empty then it is created in the backend folder, otherwise
+ at a location relative to that folder specified by url.
+ */
+ OUString createFolder(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+ /* deletes folders and files.
+
+ All folder all files which end with ".tmp" or ".tmp_" and which are
+ not used are deleted.
+ */
+ void deleteUnusedFolders(
+ std::vector< OUString> const & usedFolders);
+ /* deletes one folder with a "temporary" name and the corresponding
+ tmp file, which was used to derive the folder name.
+ */
+ static void deleteTempFolder(
+ OUString const & folderUrl);
+
+public:
+ static OUString StrRegisteringPackage() { return DpResId(RID_STR_REGISTERING_PACKAGE); }
+ static OUString StrRevokingPackage() { return DpResId(RID_STR_REVOKING_PACKAGE); }
+
+ css::uno::Reference<css::uno::XComponentContext> const &
+ getComponentContext() const { return m_xComponentContext; }
+
+ OUString const & getCachePath() const { return m_cachePath; }
+ bool transientMode() const { return m_cachePath.isEmpty(); }
+
+ const OUString& getContext() const {return m_context; }
+
+ // XEventListener
+ virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
+
+ // XPackageRegistry
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL bindPackage(
+ OUString const & url, OUString const & mediaType,
+ sal_Bool bRemoved, OUString const & identifier,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+// virtual void SAL_CALL packageRemoved(
+// OUString const & url, OUString const & mediaType)
+// throw (css::deployment::DeploymentException,
+// css::uno::RuntimeException);
+
+};
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/inc/dp_backenddb.hxx b/desktop/source/deployment/registry/inc/dp_backenddb.hxx
new file mode 100644
index 000000000..785201466
--- /dev/null
+++ b/desktop/source/deployment/registry/inc/dp_backenddb.hxx
@@ -0,0 +1,165 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ustring.hxx>
+#include <deque>
+#include <string_view>
+#include <vector>
+
+namespace com::sun::star {
+ namespace uno {
+ class XComponentContext;
+ }
+ namespace xml::dom {
+ class XDocument;
+ class XNode;
+ }
+ namespace xml::xpath {
+ class XXPathAPI;
+ }
+}
+
+namespace dp_registry::backend {
+
+class BackendDb
+{
+private:
+
+ css::uno::Reference<css::xml::dom::XDocument> m_doc;
+ css::uno::Reference<css::xml::xpath::XXPathAPI> m_xpathApi;
+
+ BackendDb(BackendDb const &) = delete;
+ BackendDb & operator = (BackendDb const &) = delete;
+
+protected:
+ const css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ OUString m_urlDb;
+
+protected:
+
+ /* caller must make sure that only one thread accesses the function
+ */
+ css::uno::Reference<css::xml::dom::XDocument> const & getDocument();
+
+ /* the namespace prefix is "reg" (without quotes)
+ */
+ css::uno::Reference<css::xml::xpath::XXPathAPI> const & getXPathAPI();
+ void save();
+ void removeElement(OUString const & sXPathExpression);
+
+ css::uno::Reference<css::xml::dom::XNode> getKeyElement(
+ std::u16string_view url);
+
+ void writeSimpleList(
+ std::deque< OUString> const & list,
+ std::u16string_view sListTagName,
+ std::u16string_view sMemberTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ void writeVectorOfPair(
+ std::vector< std::pair< OUString, OUString > > const & vecPairs,
+ std::u16string_view sVectorTagName,
+ std::u16string_view sPairTagName,
+ std::u16string_view sFirstTagName,
+ std::u16string_view sSecondTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ void writeSimpleElement(
+ std::u16string_view sElementName, OUString const & value,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ css::uno::Reference<css::xml::dom::XNode> writeKeyElement(
+ OUString const & url);
+
+ OUString readSimpleElement(
+ std::u16string_view sElementName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ std::vector< std::pair< OUString, OUString > >
+ readVectorOfPair(
+ css::uno::Reference<css::xml::dom::XNode> const & parent,
+ std::u16string_view sListTagName,
+ std::u16string_view sPairTagName,
+ std::u16string_view sFirstTagName,
+ std::u16string_view sSecondTagName);
+
+ std::deque< OUString> readList(
+ css::uno::Reference<css::xml::dom::XNode> const & parent,
+ std::u16string_view sListTagName,
+ std::u16string_view sMemberTagName);
+
+ /* returns the values of one particularly child element of all key elements.
+ */
+ std::vector< OUString> getOneChildFromAllEntries(
+ std::u16string_view sElementName);
+
+
+ /* returns the namespace which is to be written as xmlns attribute
+ into the root element.
+ */
+ virtual OUString getDbNSName()=0;
+ /* return the namespace prefix which is to be registered with the XPath API.
+
+ The prefix can then be used in XPath expressions.
+ */
+ virtual OUString getNSPrefix()=0;
+ /* returns the name of the root element without any namespace prefix.
+ */
+ virtual OUString getRootElementName()=0;
+ /* returns the name of xml element for each entry
+ */
+ virtual OUString getKeyElementName()=0;
+
+public:
+ BackendDb(css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+ virtual ~BackendDb() {};
+
+ void removeEntry(std::u16string_view url);
+
+ /* This is called to write the "revoked" attribute to the entry.
+ This is done when XPackage::revokePackage is called.
+ */
+ void revokeEntry(std::u16string_view url);
+
+ /* returns false if the entry does not exist yet.
+ */
+ bool activateEntry(std::u16string_view url);
+
+ bool hasActiveEntry(std::u16string_view url);
+
+};
+
+class RegisteredDb: public BackendDb
+{
+
+public:
+ RegisteredDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+
+ void addEntry(OUString const & url);
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_extbackenddb.cxx b/desktop/source/deployment/registry/package/dp_extbackenddb.cxx
new file mode 100644
index 000000000..8e656c5d7
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_extbackenddb.cxx
@@ -0,0 +1,111 @@
+/* -*- 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 <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_extbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/extension-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"ext";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"extension-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"extension";
+
+namespace dp_registry::backend::bundle {
+
+ExtensionBackendDb::ExtensionBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ExtensionBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ExtensionBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ExtensionBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ExtensionBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+void ExtensionBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ //reactive revoked entry if possible.
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> extensionNodeNode = writeKeyElement(url);
+ writeVectorOfPair( data.items, u"extension-items", u"item",
+ u"url", u"media-type", extensionNodeNode);
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+ExtensionBackendDb::Data ExtensionBackendDb::getEntry(std::u16string_view url)
+{
+ try
+ {
+ ExtensionBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+
+ if (aNode.is())
+ {
+ retData.items =
+ readVectorOfPair( aNode, u"extension-items", u"item",
+ u"url", u"media-type");
+ }
+ return retData;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry::backend::bundle
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_extbackenddb.hxx b/desktop/source/deployment/registry/package/dp_extbackenddb.hxx
new file mode 100644
index 000000000..fb736e6e2
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_extbackenddb.hxx
@@ -0,0 +1,69 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+namespace dp_registry::backend::bundle
+{
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ExtensionBackendDb : public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+ virtual OUString getNSPrefix() override;
+ virtual OUString getRootElementName() override;
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* every element consists of a pair of the url to the item (jar,rdb, etc)
+ and the media type
+ */
+ std::vector<std::pair<OUString, OUString>> items;
+ };
+
+public:
+ ExtensionBackendDb(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ OUString const& url);
+
+ void addEntry(OUString const& url, Data const& data);
+
+ Data getEntry(std::u16string_view url);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_package.cxx b/desktop/source/deployment/registry/package/dp_package.cxx
new file mode 100644
index 000000000..36b761b54
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_package.cxx
@@ -0,0 +1,1594 @@
+/* -*- 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 <strings.hrc>
+#include <dp_package.hxx>
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <dp_interact.h>
+#include <dp_dependencies.hxx>
+#include <dp_platform.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_resource.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphicProvider.hpp>
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/task/InteractionClassification.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XInteractionReplaceExistingData.hpp>
+#include <com/sun/star/ucb/NameClashResolveRequest.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/packages/manifest/ManifestReader.hpp>
+#include <com/sun/star/packages/manifest/ManifestWriter.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+#include <com/sun/star/deployment/Prerequisites.hpp>
+#include <optional>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "dp_extbackenddb.hxx"
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend::bundle {
+namespace {
+
+typedef cppu::ImplInheritanceHelper<PackageRegistryBackend> ImplBaseT;
+
+
+class BackendImpl : public ImplBaseT
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+ /** contains the old tooltip description for the Extension Manager GUI in OOo v.2.x
+ We keep it for backward compatibility.
+ */
+ OUString m_oldDescription;
+ OUString m_url_expanded;
+ const bool m_legacyBundle;
+ Sequence< Reference<deployment::XPackage> > m_bundle;
+ Sequence< Reference<deployment::XPackage> > * m_pBundle;
+
+ ExtensionBackendDb::Data m_dbData;
+
+ Reference<deployment::XPackage> bindBundleItem(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, //that is, using data base information
+ OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool notifyDetectionError = true );
+
+ typedef std::vector< Reference<deployment::XPackage> > t_packagevec;
+ void scanBundle(
+ t_packagevec & bundle,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv );
+ void scanLegacyBundle(
+ t_packagevec & bundle,
+ OUString const & url,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool skip_registration = false );
+ std::vector<Reference<deployment::XPackage> > getPackagesFromDb(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv);
+ bool checkPlatform(
+ Reference<ucb::XCommandEnvironment > const & environment);
+
+ bool checkDependencies(
+ Reference<ucb::XCommandEnvironment > const &
+ environment,
+ DescriptionInfoset const & description);
+ // throws css::uno::RuntimeException,
+ // css::deployment::DeploymentException
+
+ /// @throws deployment::DeploymentException
+ /// @throws ucb::CommandFailedException
+ /// @throws ucb::CommandAbortedException
+ /// @throws RuntimeException
+ bool checkLicense(
+ Reference< ucb::XCommandEnvironment > const & xCmdEnv,
+ DescriptionInfoset const & description, bool bNoLicenseChecking);
+ // @throws DeploymentException
+ OUString getTextFromURL(
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv,
+ const OUString& licenseUrl);
+
+ DescriptionInfoset getDescriptionInfoset() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url,
+ OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool legacyBundle,
+ bool bRemoved,
+ OUString const & identifier);
+
+ // XPackage
+ virtual sal_Bool SAL_CALL isBundle() override;
+
+ virtual Sequence< Reference<deployment::XPackage> > SAL_CALL getBundle(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getDescription() override;
+
+ virtual OUString SAL_CALL getLicenseText() override;
+
+ virtual void SAL_CALL exportTo(
+ OUString const & destFolderURL, OUString const & newTitle,
+ sal_Int32 nameClashAction,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Int32 SAL_CALL checkPrerequisites(
+ const Reference< task::XAbortChannel >& xAbortChannel,
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool noLicenseChecking) override;
+
+ virtual sal_Bool SAL_CALL checkDependencies(
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv ) override;
+
+ virtual beans::Optional<OUString> SAL_CALL getIdentifier() override;
+
+ virtual OUString SAL_CALL getVersion() override;
+
+ virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs() override;
+
+ virtual beans::StringPair SAL_CALL getPublisherInfo() override;
+
+ virtual OUString SAL_CALL getDisplayName() override;
+
+ virtual Reference< graphic::XGraphic > SAL_CALL
+ getIcon( sal_Bool bHighContrast ) override;
+ };
+ friend class PackageImpl;
+
+ Reference<deployment::XPackageRegistry> m_xRootRegistry;
+ const Reference<deployment::XPackageTypeInfo> m_xBundleTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xLegacyBundleTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ std::unique_ptr<ExtensionBackendDb> m_backendDb;
+
+ void addDataToDb(OUString const & url, ExtensionBackendDb::Data const & data);
+ ExtensionBackendDb::Data readDataFromDb(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+public:
+ BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext,
+ Reference<deployment::XPackageRegistry> const & xRootRegistry );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( OUString const& name ) override;
+ virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using ImplBaseT::disposing;
+};
+
+//Used to find a XPackage with a particular URL
+class XPackage_eq
+{
+ OUString m_URL;
+public:
+ explicit XPackage_eq(OUString s) : m_URL(std::move(s)) {}
+ bool operator() (const Reference<deployment::XPackage> & p) const
+ {
+ return m_URL == p->getURL();
+ }
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext,
+ Reference<deployment::XPackageRegistry> const & xRootRegistry )
+ : ImplBaseT( args, xComponentContext ),
+ m_xRootRegistry( xRootRegistry ),
+ m_xBundleTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.package-bundle",
+ "*.oxt;*.uno.pkg",
+ DpResId(RID_STR_PACKAGE_BUNDLE)
+ ) ),
+ m_xLegacyBundleTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.legacy-package-bundle",
+ "*.zip",
+ m_xBundleTypeInfo->getShortDescription()
+ ) ),
+ m_typeInfos{ m_xBundleTypeInfo, m_xLegacyBundleTypeInfo }
+{
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), getImplementationName());
+ dbFile = makeURL(dbFile, "backenddb.xml");
+ m_backendDb.reset(
+ new ExtensionBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+
+void BackendImpl::disposing()
+{
+ m_xRootRegistry.clear();
+ PackageRegistryBackend::disposing();
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.bundle.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence<OUString> BackendImpl::getSupportedServiceNames()
+{
+ return { OUString(BACKEND_SERVICE_NAME) };
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ //Notify the backend responsible for processing the different media
+ //types that this extension was removed.
+ ExtensionBackendDb::Data data = readDataFromDb(url);
+ for (auto const& item : data.items)
+ {
+ m_xRootRegistry->packageRemoved(item.first, item.second);
+ }
+
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ))
+ {
+ if (ucbContent.isFolder())
+ {
+ //Every .oxt, uno.pkg file must contain a META-INF folder
+ ::ucbhelper::Content metaInfContent;
+ if (create_ucb_content(
+ &metaInfContent, makeURL( url, "META-INF" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ mediaType = "application/vnd.sun.star.package-bundle";
+ }
+ //No support of legacy bundles, because every folder could be one.
+ }
+ else
+ {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase(".oxt") ||
+ title.endsWithIgnoreAsciiCase(".uno.pkg"))
+ mediaType = "application/vnd.sun.star.package-bundle";
+ else if (title.endsWithIgnoreAsciiCase(".zip"))
+ mediaType = "application/vnd.sun.star.legacy-package-bundle";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+
+ //In case a XPackage is created for a removed extension, we cannot
+ //obtain the name
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.package-bundle"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xBundleTypeInfo, false, bRemoved,
+ identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.legacy-package-bundle"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved,
+ identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ExtensionBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+ExtensionBackendDb::Data BackendImpl::readDataFromDb(
+ std::u16string_view url)
+{
+ ExtensionBackendDb::Data data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url,
+ OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool legacyBundle, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_url_expanded( expandUnoRcUrl( url ) ),
+ m_legacyBundle( legacyBundle ),
+ m_pBundle( nullptr )
+{
+ if (bRemoved)
+ m_dbData = getMyBackend()->readDataFromDb(url);
+}
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(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<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+void BackendImpl::PackageImpl::disposing()
+{
+ sal_Int32 len = m_bundle.getLength();
+ Reference<deployment::XPackage> const * p = m_bundle.getConstArray();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ try_dispose( p[ pos ] );
+ m_bundle.realloc( 0 );
+
+ Package::disposing();
+}
+
+// Package
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ //In case the object was created for a removed extension (m_bRemoved = true)
+ //but the extension is not registered, then bundle will be empty. Then
+ //the return value will be Optional<...>.IsPresent= false. Although this is
+ //not true, this does not matter. Then registerPackage or revokePackage
+ //would never be called for the items. But since the extension is removed
+ //and not registered anyway, this does not matter.
+ const Sequence< Reference<deployment::XPackage> > bundle(
+ getBundle( abortChannel, xCmdEnv ) );
+
+ bool reg = false;
+ bool present = false;
+ bool ambig = false;
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ beans::Optional< beans::Ambiguous<sal_Bool> > option(
+ xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) );
+
+ //present = true if at least one bundle item has this value.
+ //reg = true if all bundle items have an option value (option.IsPresent == 1)
+ //and all have value of true (option.Value.Value == true)
+ //If not, then the bundle has the status of not registered and ambiguous.
+ if (option.IsPresent)
+ {
+ beans::Ambiguous<sal_Bool> const & status = option.Value;
+ if (present)
+ {
+ //we never come here in the first iteration
+ if (reg != bool(status.Value)) {
+
+ ambig = true;
+ reg = false;
+ break;
+ }
+ }
+ else
+ {
+ //we always come here in the first iteration
+ reg = status.Value;
+ present = true;
+ }
+ }
+ }
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ present, beans::Ambiguous<sal_Bool>(reg, ambig) );
+}
+
+OUString BackendImpl::PackageImpl::getTextFromURL(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ const OUString& licenseUrl)
+{
+ try
+ {
+ ::ucbhelper::Content descContent(
+ licenseUrl, xCmdEnv, getMyBackend()->getComponentContext());
+ std::vector<sal_Int8> seq = dp_misc::readFile(descContent);
+ return OUString( reinterpret_cast<char const *>(
+ seq.data()), seq.size(), RTL_TEXTENCODING_UTF8);
+ }
+ catch (const css::uno::Exception&)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Could not read file " + licenseUrl, nullptr, exc);
+ }
+
+}
+
+DescriptionInfoset BackendImpl::PackageImpl::getDescriptionInfoset() const
+{
+ return dp_misc::getDescriptionInfoset(m_url_expanded);
+}
+
+bool BackendImpl::PackageImpl::checkPlatform(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & environment)
+{
+ bool ret = false;
+ DescriptionInfoset info(getDescriptionInfoset());
+ Sequence<OUString> platforms(info.getSupportedPlatforms());
+ if (hasValidPlatform(platforms))
+ {
+ ret = true;
+ }
+ else
+ {
+ ret = false;
+ OUString msg(
+ "unsupported platform");
+ Any e(
+ css::deployment::PlatformException(
+ msg, static_cast<OWeakObject *>(this), this));
+ if (!interactContinuation(
+ e, cppu::UnoType< css::task::XInteractionApprove >::get(),
+ environment, nullptr, nullptr))
+ {
+ throw css::deployment::DeploymentException(
+ msg, static_cast<OWeakObject *>(this), e);
+ }
+ }
+ return ret;
+}
+
+
+bool BackendImpl::PackageImpl::checkDependencies(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & environment,
+ DescriptionInfoset const & description)
+{
+ css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+ unsatisfied(dp_misc::Dependencies::check(description));
+
+ if (!unsatisfied.hasElements()) {
+ return true;
+ } else {
+ OUString msg(
+ "unsatisfied dependencies");
+ Any e(
+ css::deployment::DependencyException(
+ msg, static_cast<OWeakObject *>(this), unsatisfied));
+ if (!interactContinuation(
+ e, cppu::UnoType< css::task::XInteractionApprove >::get(),
+ environment, nullptr, nullptr))
+ {
+ throw css::deployment::DeploymentException(
+ msg, static_cast<OWeakObject *>(this), e);
+ }
+ return false;
+ }
+}
+
+bool BackendImpl::PackageImpl::checkLicense(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
+ DescriptionInfoset const & info, bool alreadyInstalled)
+{
+ try
+ {
+ ::std::optional<SimpleLicenseAttributes> simplLicAttr
+ = info.getSimpleLicenseAttributes();
+ if (! simplLicAttr)
+ return true;
+ OUString sLic = info.getLocalizedLicenseURL();
+ //If we do not get a localized licence then there is an error in the description.xml
+ //This should be handled by using a validating parser. Therefore we assume that no
+ //license is available.
+ if (sLic.isEmpty())
+ throw css::deployment::DeploymentException(
+ "Could not obtain path to license. Possible error in description.xml", nullptr, Any());
+ OUString sHref = m_url_expanded + "/" + sLic;
+ OUString sLicense = getTextFromURL(xCmdEnv, sHref);
+ ////determine who has to agree to the license
+ //check correct value for attribute
+ if ( simplLicAttr->acceptBy != "user" && simplLicAttr->acceptBy != "admin")
+ throw css::deployment::DeploymentException(
+ "Could not obtain attribute simple-license@accept-by or it has no valid value", nullptr, Any());
+
+
+ //Only use interaction if there is no version of this extension already installed
+ //and the suppress-on-update flag is not set for the new extension
+ // alreadyInstalled | bSuppressOnUpdate | show license
+
+ // 0 | 0 | 1
+ // 0 | 1 | 1
+ // 1 | 0 | 1
+ // 1 | 1 | 0
+
+ if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate))
+ {
+ css::deployment::LicenseException licExc(
+ OUString(), nullptr, getDisplayName(), sLicense,
+ simplLicAttr->acceptBy);
+ bool approve = false;
+ bool abort = false;
+ if (! interactContinuation(
+ Any(licExc), cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, &approve, &abort ))
+ throw css::deployment::DeploymentException(
+ "Could not interact with user.", nullptr, Any());
+
+ return approve;
+ }
+ return true;
+ } catch (const css::ucb::CommandFailedException&) {
+ throw;
+ } catch (const css::ucb::CommandAbortedException&) {
+ throw;
+ } catch (const css::deployment::DeploymentException&) {
+ throw;
+ } catch (const css::uno::RuntimeException&) {
+ throw;
+ } catch (const css::uno::Exception&) {
+ Any anyExc = cppu::getCaughtException();
+ throw css::deployment::DeploymentException("Unexpected exception", nullptr, anyExc);
+ }
+}
+
+::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites(
+ const css::uno::Reference< css::task::XAbortChannel >&,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool alreadyInstalled)
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ DescriptionInfoset info = getDescriptionInfoset();
+ if (!info.hasDescription())
+ return 0;
+
+ //always return LICENSE as long as the user did not accept the license
+ //so that XExtensionManager::checkPrerequisitesAndEnable will again
+ //check the license
+ if (!checkPlatform(xCmdEnv))
+ return deployment::Prerequisites::PLATFORM |
+ deployment::Prerequisites::LICENSE;
+ else if(!checkDependencies(xCmdEnv, info))
+ return deployment::Prerequisites::DEPENDENCIES |
+ deployment::Prerequisites::LICENSE;
+ else if(!checkLicense(xCmdEnv, info, alreadyInstalled))
+ return deployment::Prerequisites::LICENSE;
+ else
+ return 0;
+}
+
+sal_Bool BackendImpl::PackageImpl::checkDependencies(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ DescriptionInfoset info = getDescriptionInfoset();
+ if (!info.hasDescription())
+ return true;
+
+ return checkDependencies(xCmdEnv, info);
+}
+
+beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier()
+{
+ OUString identifier;
+ if (m_bRemoved)
+ identifier = m_identifier;
+ else
+ identifier = dp_misc::generateIdentifier(
+ getDescriptionInfoset().getIdentifier(), m_name);
+
+ return beans::Optional<OUString>(
+ true, identifier);
+}
+
+OUString BackendImpl::PackageImpl::getVersion()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return getDescriptionInfoset().getVersion();
+}
+
+Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return getDescriptionInfoset().getUpdateInformationUrls();
+}
+
+beans::StringPair BackendImpl::PackageImpl::getPublisherInfo()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL();
+ beans::StringPair aStrPair( aInfo.first, aInfo.second );
+ return aStrPair;
+}
+
+
+uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ uno::Reference< graphic::XGraphic > xGraphic;
+
+ OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast );
+ if ( !aIconURL.isEmpty() )
+ {
+ OUString aFullIconURL = m_url_expanded + "/" + aIconURL;
+
+ uno::Reference< XComponentContext > xContext( getMyBackend()->getComponentContext() );
+ uno::Reference< graphic::XGraphicProvider > xGraphProvider( graphic::GraphicProvider::create(xContext) );
+
+ uno::Sequence< beans::PropertyValue > aMediaProps{ comphelper::makePropertyValue(
+ "URL", aFullIconURL) };
+ xGraphic = xGraphProvider->queryGraphic( aMediaProps );
+ }
+
+ return xGraphic;
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ const Sequence< Reference<deployment::XPackage> > bundle(
+ getBundle( abortChannel, xCmdEnv ) );
+
+ if (doRegisterPackage)
+ {
+ ExtensionBackendDb::Data data;
+ const sal_Int32 len = bundle.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ checkAborted(abortChannel);
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ try {
+ xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const Exception &)
+ {
+ //We even try a rollback if the user cancelled the action (CommandAbortedException)
+ //in order to prevent invalid database entries.
+ Any exc( ::cppu::getCaughtException() );
+ // try to handle exception, notify:
+ bool approve = false, abort = false;
+ if (! interactContinuation(
+ Any( lang::WrappedTargetException(
+ "bundle item registration error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
+ &approve, &abort )) {
+ OSL_ASSERT( !approve && !abort );
+ if (m_legacyBundle) // default for legacy packages: ignore
+ continue;
+ // no selection at all, so rethrow;
+ // no C++ rethrow after getCaughtException(),
+ // see cppuhelper/exc_hlp.hxx:
+ ::cppu::throwException(exc);
+ }
+ if (approve && !abort) // ignore error, just continue
+ continue;
+
+ {
+ ProgressLevel progress( xCmdEnv, "rollback..." );
+ // try rollback
+ for ( ; pos--; )
+ {
+ try {
+ bundle[ pos ]->revokePackage(
+ startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const Exception &)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ // ignore any errors of rollback
+ }
+ }
+ progress.update( "rollback finished." );
+ }
+
+ deployment::DeploymentException dpExc;
+ if (exc >>= dpExc) {
+ throw ucb::CommandFailedException(
+ dpExc.Message, dpExc.Context, dpExc.Cause );
+ }
+ else {
+ // rethrow CommandFailedException
+ ::cppu::throwException(exc);
+ }
+ }
+ data.items.emplace_back(xPackage->getURL(),
+ xPackage->getPackageType()->getMediaType());
+ }
+ getMyBackend()->addDataToDb(getURL(), data);
+ }
+ else
+ {
+ // revoke in reverse order:
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ checkAborted(abortChannel);
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ try {
+ bundle[ pos ]->revokePackage(
+ startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandAbortedException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ // CommandFailedException, DeploymentException:
+ Any exc( ::cppu::getCaughtException() );
+ // try to handle exception, notify:
+ bool approve = false, abort = false;
+ if (! interactContinuation(
+ Any( lang::WrappedTargetException(
+ "bundle item revocation error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
+ &approve, &abort )) {
+ OSL_ASSERT( !approve && !abort );
+ if (m_legacyBundle) // default for legacy packages: ignore
+ continue;
+ // no selection at all, so rethrow
+ // no C++ rethrow after getCaughtException(),
+ // see cppuhelper/exc_hlp.hxx:
+ ::cppu::throwException(exc);
+ }
+ // ignore errors when revoking, although abort may have been
+ // selected
+ }
+ }
+ getMyBackend()->revokeEntryFromDb(getURL());
+ }
+}
+
+
+OUString BackendImpl::PackageImpl::getDescription()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL());
+ OUString sDescription;
+ if (!sRelativeURL.isEmpty())
+ {
+ OUString sURL = m_url_expanded + "/" + sRelativeURL;
+
+ try
+ {
+ sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL );
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+
+ if (!sDescription.isEmpty())
+ return sDescription;
+ return m_oldDescription;
+}
+
+
+OUString BackendImpl::PackageImpl::getLicenseText()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ OUString sLicense;
+ DescriptionInfoset aInfo = getDescriptionInfoset();
+
+ ::std::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes();
+ if ( aSimplLicAttr )
+ {
+ OUString aLicenseURL = aInfo.getLocalizedLicenseURL();
+
+ if ( !aLicenseURL.isEmpty() )
+ {
+ OUString aFullURL = m_url_expanded + "/" + aLicenseURL;
+ sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL);
+ }
+ }
+
+ return sLicense;
+}
+
+
+void BackendImpl::PackageImpl::exportTo(
+ OUString const & destFolderURL, OUString const & newTitle,
+ sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ ::ucbhelper::Content sourceContent(
+ m_url_expanded, xCmdEnv, getMyBackend()->getComponentContext() );
+ OUString title(newTitle);
+ if (title.isEmpty())
+ sourceContent.getPropertyValue( "Title" ) >>= title;
+ OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+
+ if (nameClashAction == ucb::NameClash::ASK)
+ {
+ if (create_ucb_content(
+ nullptr, destURL, xCmdEnv, false /* no throw */ )) {
+ bool replace = false, abort = false;
+ if (! interactContinuation(
+ Any( ucb::NameClashResolveRequest(
+ "file already exists: " + title,
+ static_cast<OWeakObject *>(this),
+ task::InteractionClassification_QUERY,
+ destFolderURL, title, OUString() ) ),
+ cppu::UnoType<ucb::XInteractionReplaceExistingData>::get(), xCmdEnv,
+ &replace, &abort ) || !replace) {
+ return;
+ }
+ }
+ }
+ else if (nameClashAction != ucb::NameClash::OVERWRITE) {
+ throw ucb::CommandFailedException("unsupported nameClashAction!",
+ static_cast<OWeakObject *>(this), Any() );
+ }
+ erase_path( destURL, xCmdEnv );
+
+ OUString destFolder =
+ "vnd.sun.star.zip://" +
+ ::rtl::Uri::encode( destURL,
+ rtl_UriCharClassRegName,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) +
+ "/";
+
+ ::ucbhelper::Content destFolderContent(
+ destFolder, xCmdEnv, getMyBackend()->getComponentContext() );
+ {
+ // transfer every item of folder into zip:
+ Reference<sdbc::XResultSet> xResultSet(
+ sourceContent.createCursor( Sequence<OUString>() ) );
+ ProgressLevel progress( xCmdEnv, OUString() );
+ while (xResultSet->next())
+ {
+ ::ucbhelper::Content subContent(
+ Reference<ucb::XContentAccess>(
+ xResultSet, UNO_QUERY_THROW )->queryContent(),
+ xCmdEnv, getMyBackend()->getComponentContext() );
+ destFolderContent.transferContent(
+ subContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(), ucb::NameClash::OVERWRITE );
+ progress.update( Any() ); // animating progress bar
+ }
+ }
+
+ // assure META-INF folder:
+ ::ucbhelper::Content metainfFolderContent;
+ create_folder( &metainfFolderContent,
+ makeURL( destFolderContent.getURL(), "META-INF" ),
+ xCmdEnv );
+
+ if (m_legacyBundle)
+ {
+ // easy to migrate legacy bundles to new format:
+ // just export them once using a .oxt name!
+ // set detected media-types of any bundle item:
+
+ // collect all manifest entries:
+ Sequence< Reference<deployment::XPackage> > bundle;
+ try {
+ bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv );
+ }
+ // xxx todo: think about exception specs:
+ catch (const deployment::DeploymentException &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+
+ std::vector< Sequence<beans::PropertyValue> > manifest;
+ manifest.reserve( bundle.getLength() );
+ sal_Int32 baseURLlen = m_url_expanded.getLength();
+ Reference<deployment::XPackage> const *pbundle = bundle.getConstArray();
+ static const OUStringLiteral strMediaType( u"MediaType" );
+ static const OUStringLiteral strFullPath( u"FullPath" );
+ static const OUStringLiteral strIsFolder( u"IsFolder" );
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ Reference<deployment::XPackage> const & xPackage = pbundle[ pos ];
+ OUString url_( expandUnoRcUrl( xPackage->getURL() ) );
+ OSL_ASSERT( url_.getLength() >= baseURLlen );
+ OUString fullPath;
+ if (url_.getLength() > baseURLlen)
+ fullPath = url_.copy( baseURLlen + 1 );
+ ::ucbhelper::Content ucbContent(
+ url_, xCmdEnv, getMyBackend()->getComponentContext() );
+ if (ucbContent.getPropertyValue(strIsFolder).get<bool>())
+ fullPath += "/";
+ Sequence<beans::PropertyValue> attribs( 2 );
+ beans::PropertyValue * pattribs = attribs.getArray();
+ pattribs[ 0 ].Name = strFullPath;
+ pattribs[ 0 ].Value <<= fullPath;
+ pattribs[ 1 ].Name = strMediaType;
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OUString mediaType;
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+ else
+ mediaType = "unknown";
+ pattribs[ 1 ].Value <<= mediaType;
+ manifest.push_back( attribs );
+ }
+
+ // write into pipe:
+ Reference<XComponentContext> xContext(
+ getMyBackend()->getComponentContext() );
+ Reference<packages::manifest::XManifestWriter> xManifestWriter =
+ packages::manifest::ManifestWriter::create( xContext );
+ Reference<io::XOutputStream> xPipe( io::Pipe::create(xContext), UNO_QUERY_THROW );
+ xManifestWriter->writeManifestSequence(
+ xPipe, comphelper::containerToSequence(manifest) );
+
+ // write buffered pipe data to content:
+ ::ucbhelper::Content manifestContent(
+ makeURL( metainfFolderContent.getURL(), "manifest.xml" ),
+ xCmdEnv, getMyBackend()->getComponentContext() );
+ manifestContent.writeStream(
+ Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ),
+ true /* replace existing */ );
+ }
+ else
+ {
+ bool bSuccess = false;
+ try
+ {
+ // overwrite manifest.xml:
+ ::ucbhelper::Content manifestContent;
+ if ( ! create_ucb_content(
+ &manifestContent,
+ makeURL( m_url_expanded, "META-INF/manifest.xml" ),
+ xCmdEnv, false ) )
+ {
+ OSL_FAIL( "### missing META-INF/manifest.xml file!" );
+ return;
+ }
+
+ metainfFolderContent.transferContent(
+ manifestContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(), ucb::NameClash::OVERWRITE );
+ bSuccess = true;
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "exception on overwriting manifest");
+ }
+
+ if (!bSuccess)
+ throw RuntimeException( "UCB transferContent() failed!",
+ static_cast<OWeakObject *>(this) );
+ }
+
+ // xxx todo: maybe obsolete in the future
+ try {
+ destFolderContent.executeCommand( "flush", Any() );
+ }
+ catch (const ucb::UnsupportedCommandException &) {
+ }
+}
+
+
+sal_Bool BackendImpl::PackageImpl::isBundle()
+{
+ return true;
+}
+
+
+Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle;
+ if (pBundle == nullptr)
+ {
+ t_packagevec bundle;
+ if (m_bRemoved)
+ {
+ bundle = getPackagesFromDb(xCmdEnv);
+ }
+ else
+ {
+ try {
+ if (m_legacyBundle)
+ {
+ // .zip legacy packages allow script.xlb, dialog.xlb in bundle
+ // root folder:
+ OUString mediaType;
+ // probe for script.xlb:
+ if (create_ucb_content(
+ nullptr, makeURL( m_url_expanded, "script.xlb" ),
+ xCmdEnv, false /* no throw */ )) {
+ mediaType = "application/vnd.sun.star.basic-library";
+ }
+ // probe for dialog.xlb:
+ else if (create_ucb_content(
+ nullptr, makeURL( m_url_expanded, "dialog.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.dialog-library";
+
+ if (!mediaType.isEmpty()) {
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( getURL(), mediaType, false, OUString(),
+ xCmdEnv ) );
+ if (xPackage.is())
+ bundle.push_back( xPackage );
+ // continue scanning:
+ }
+ scanLegacyBundle( bundle, getURL(),
+ AbortChannel::get(xAbortChannel), xCmdEnv );
+ }
+ else
+ {
+ // .oxt:
+ scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv );
+ }
+
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandFailedException &) {
+ throw;
+ }
+ catch (const ucb::CommandAbortedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ "error scanning bundle: " + getURL(),
+ static_cast<OWeakObject *>(this), exc );
+ }
+ }
+
+ // sort: schema before config data, typelibs before components:
+ Sequence< Reference<deployment::XPackage> > ret( bundle.size() );
+ Reference<deployment::XPackage> * pret = ret.getArray();
+ sal_Int32 lower_end = 0;
+ sal_Int32 upper_end = ret.getLength();
+ for (auto const& elem : bundle)
+ {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ elem->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ {
+ const OUString mediaType( xPackageType->getMediaType() );
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ) &&
+ type.equalsIgnoreAsciiCase("application") &&
+ (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-component") ||
+ subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data")))
+ {
+ --upper_end;
+ pret[ upper_end ] = elem;
+ continue;
+ }
+ }
+ pret[ lower_end ] = elem;
+ ++lower_end;
+ }
+ OSL_ASSERT( lower_end == upper_end );
+
+ const ::osl::MutexGuard guard( m_aMutex );
+ pBundle = m_pBundle;
+ if (pBundle == nullptr) {
+ m_bundle = ret;
+ pBundle = &m_bundle;
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ m_pBundle = pBundle;
+ }
+ }
+ else {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return *pBundle;
+}
+
+bool isBundle_( std::u16string_view mediaType )
+{
+ // xxx todo: additional parsing?
+ return !mediaType.empty() &&
+ (o3tl::matchIgnoreAsciiCase( mediaType, u"application/vnd.sun.star.package-bundle") ||
+ o3tl::matchIgnoreAsciiCase( mediaType, u"application/vnd.sun.star.legacy-package-bundle"));
+}
+
+
+Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool notifyDetectionError )
+{
+ // ignore any nested bundles:
+ if (isBundle_(mediaType))
+ return Reference<deployment::XPackage>();
+
+ Reference<deployment::XPackage>xPackage;
+ try {
+ try {
+ xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage(
+ url, mediaType, bRemoved, identifier, xCmdEnv ) );
+ OSL_ASSERT( xPackage.is() );
+ } catch (css::lang::IllegalArgumentException & e) {
+ css::uno::Any exc(cppu::getCaughtException());
+ throw css::lang::WrappedTargetException(
+ "wrapped: " + e.Message, e.Context, exc);
+ }
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandFailedException &) {
+ // ignore already handled error
+ }
+ catch (const Exception &) {
+ const Any exc( ::cppu::getCaughtException() );
+ if (notifyDetectionError ||
+ !exc.isExtractableTo( cppu::UnoType<lang::IllegalArgumentException>::get()) )
+ {
+ (void)interactContinuation(
+ Any( lang::WrappedTargetException("bundle item error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, nullptr, nullptr );
+ }
+ }
+
+ if (xPackage.is()) {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ // ignore any nested bundles:
+ if (xPackageType.is() && isBundle_( xPackageType->getMediaType() ))
+ xPackage.clear();
+ }
+ return xPackage;
+}
+
+
+void BackendImpl::PackageImpl::scanBundle(
+ t_packagevec & bundle,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ OSL_ASSERT( !m_legacyBundle );
+
+ OUString mfUrl( makeURL( m_url_expanded, "META-INF/manifest.xml" ) );
+ ::ucbhelper::Content manifestContent;
+ if (! create_ucb_content(
+ &manifestContent, mfUrl, xCmdEnv, false /* no throw */ ))
+ {
+ SAL_WARN(
+ "desktop.deployment",
+ "cannot create UCB Content for <" << mfUrl << ">" );
+ return;
+ }
+
+
+ const LanguageTag& officeLocale = getOfficeLanguageTag();
+ const std::vector< OUString > officeFallbacks( officeLocale.getFallbackStrings( true));
+ const size_t nPenaltyMax = std::numeric_limits<size_t>::max();
+ size_t descrPenalty = nPenaltyMax;
+ OUString descrFile;
+
+ const Reference<XComponentContext> xContext(
+ getMyBackend()->getComponentContext() );
+ Reference<packages::manifest::XManifestReader> xManifestReader =
+ packages::manifest::ManifestReader::create( xContext );
+ const Sequence< Sequence<beans::PropertyValue> > manifestSeq(
+ xManifestReader->readManifestSequence( manifestContent.openStream() ) );
+ const OUString packageRootURL( getURL() );
+ for ( sal_Int32 pos = manifestSeq.getLength(); pos--; )
+ {
+ OUString fullPath, mediaType;
+ Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ];
+ for ( sal_Int32 i = attribs.getLength(); i--; )
+ {
+ if (!(fullPath.isEmpty() || mediaType.isEmpty()))
+ break;
+ if ( attribs[i].Name == "FullPath" )
+ attribs[i].Value >>= fullPath;
+ else if ( attribs[i].Name == "MediaType" )
+ attribs[i].Value >>= mediaType;
+ }
+
+ if ( fullPath.isEmpty() || mediaType.isEmpty() || mediaType == "text/xml" )// opt: exclude common text/xml
+ continue;
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (! INetContentTypes::parse( mediaType, type, subType, &params ))
+ continue;
+
+ {
+ auto const iter = params.find("platform");
+ if (iter != params.end() && !platform_fits(iter->second.m_sValue))
+ continue;
+ }
+ const OUString url( makeURL( packageRootURL, fullPath ) );
+
+ // check for bundle description:
+ if (type.equalsIgnoreAsciiCase("application") &&
+ subType.equalsIgnoreAsciiCase( "vnd.sun.star.package-bundle-description"))
+ {
+ // check locale:
+ auto const iter = params.find("locale");
+ if (iter == params.end())
+ {
+ if (descrFile.isEmpty())
+ descrFile = url;
+ }
+ else {
+ // match best locale:
+ LanguageTag descrTag(iter->second.m_sValue);
+ if (officeLocale.getLanguage() == descrTag.getLanguage())
+ {
+ size_t nPenalty = nPenaltyMax;
+ const std::vector< OUString > descrFallbacks( descrTag.getFallbackStrings( true));
+ for (size_t o=0; o < officeFallbacks.size() && nPenalty == nPenaltyMax; ++o)
+ {
+ for (size_t d=0; d < descrFallbacks.size() && nPenalty == nPenaltyMax; ++d)
+ {
+ if (officeFallbacks[o] == descrFallbacks[d])
+ {
+ // The last fallbacks are always language-only
+ // fallbacks, so we _will_ have _some_ match if
+ // we ever entered the overall if() condition.
+ nPenalty = o * 1000 + d;
+ if (descrPenalty > nPenalty)
+ {
+ descrPenalty = nPenalty;
+ descrFile = url;
+ }
+ }
+ }
+ }
+ }
+ // TODO: we could break here if descrPenalty==0 for an exact
+ // match of officeLocale, but the previous code didn't; are
+ // there side effects?
+ }
+ continue;
+ }
+
+ checkAborted( abortChannel );
+
+ //We make sure that we only create one XPackage for a particular URL.
+ //Sometime programmers insert the same URL several times in the manifest
+ //which may lead to DisposedExceptions.
+ if (std::none_of(bundle.begin(), bundle.end(), XPackage_eq(url)))
+ {
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) );
+ if (xPackage.is())
+ bundle.push_back( xPackage );
+ }
+ else
+ {
+ SAL_WARN("desktop.deployment", "manifest.xml contains a duplicate entry (from " << url << ")");
+ }
+ }
+
+ if (descrFile.isEmpty())
+ return;
+
+ ::ucbhelper::Content descrFileContent;
+ if (!create_ucb_content( &descrFileContent, descrFile,
+ xCmdEnv, false /* no throw */ ))
+ return;
+
+ // patch description:
+ std::vector<sal_Int8> bytes( readFile( descrFileContent ) );
+ OUStringBuffer buf;
+ if ( !bytes.empty() )
+ {
+ buf.append( OUString( reinterpret_cast<char const *>(
+ bytes.data() ),
+ bytes.size(), RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ buf.append( Package::getDescription() );
+ }
+ m_oldDescription = buf.makeStringAndClear();
+}
+
+
+void BackendImpl::PackageImpl::scanLegacyBundle(
+ t_packagevec & bundle,
+ OUString const & url,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool skip_registration )
+{
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getMyBackend()->getComponentContext() );
+
+ // check for platform paths:
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase( ".plt" ) &&
+ !platform_fits( title.subView( 0, title.getLength() - 4 ) )) {
+ return;
+ }
+ if (title.endsWithIgnoreAsciiCase("skip_registration") )
+ skip_registration = true;
+
+ Sequence<OUString> ar { OUString("Title"), OUString("IsFolder") };
+ Reference<sdbc::XResultSet> xResultSet( ucbContent.createCursor( ar ) );
+ while (xResultSet->next())
+ {
+ checkAborted( abortChannel );
+
+ const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
+ const OUString title_enc( ::rtl::Uri::encode(
+ xRow->getString( 1 /* Title */ ),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ const OUString path( makeURL( url, title_enc ) );
+
+ OUString mediaType;
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( path, OUString() /* detect */, false, OUString(),
+ xCmdEnv, false /* ignore detection errors */ ) );
+ if (xPackage.is()) {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+
+ if (skip_registration &&
+ // xxx todo: additional parsing?
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.uno-component"))
+ continue;
+
+ bundle.push_back( xPackage );
+ }
+
+ if (mediaType.isEmpty() ||
+ // script.xlb, dialog.xlb can be met everywhere:
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.basic-library") ||
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.dialog-library"))
+ {
+ if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder:
+ scanLegacyBundle(
+ bundle, path, abortChannel, xCmdEnv, skip_registration );
+ }
+ }
+ }
+}
+
+OUString BackendImpl::PackageImpl::getDisplayName()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ OUString sName = getDescriptionInfoset().getLocalizedDisplayName();
+ if (sName.isEmpty())
+ return m_displayName;
+ else
+ return sName;
+}
+
+std::vector<Reference<deployment::XPackage> >
+BackendImpl::PackageImpl::getPackagesFromDb(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ std::vector<Reference<deployment::XPackage> > retVector;
+
+ for (auto const& item : m_dbData.items)
+ {
+ Reference<deployment::XPackage> xExtension =
+ bindBundleItem(item.first, item.second, true, m_identifier, xCmdEnv);
+ OSL_ASSERT(xExtension.is());
+ if (xExtension.is())
+ retVector.push_back(xExtension);
+ }
+
+ return retVector;
+}
+
+} // anon namespace
+
+
+Reference<deployment::XPackageRegistry> create(
+ Reference<deployment::XPackageRegistry> const & xRootRegistry,
+ OUString const & context, OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ Sequence<Any> args(cachePath.isEmpty() ? 1 : 3 );
+ auto pArgs = args.getArray();
+ pArgs[ 0 ] <<= context;
+ if (!cachePath.isEmpty()) {
+ pArgs[ 1 ] <<= cachePath;
+ pArgs[ 2 ] <<= false; // readOnly
+ }
+ return new BackendImpl( args, xComponentContext, xRootRegistry );
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_lib_container.cxx b/desktop/source/deployment/registry/script/dp_lib_container.cxx
new file mode 100644
index 000000000..3a6f30253
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_lib_container.cxx
@@ -0,0 +1,64 @@
+/* -*- 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 <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+#include <strings.hrc>
+#include <dp_resource.h>
+#include <dp_shared.hxx>
+#include <dp_xml.h>
+#include "dp_lib_container.h"
+
+#include <rtl/ustring.hxx>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xmllib_imexp.hxx>
+
+
+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::script {
+
+namespace {
+ OUString StrCannotDetermineLibName() { return DpResId(RID_STR_CANNOT_DETERMINE_LIBNAME); }
+}
+
+OUString LibraryContainer::get_libname(
+ OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ Reference<XComponentContext> const & xContext )
+{
+ ::xmlscript::LibDescriptor import;
+ ::ucbhelper::Content ucb_content( url, xCmdEnv, xContext );
+ xml_parse( ::xmlscript::importLibrary( import ), ucb_content, xContext );
+
+ if (import.aName.isEmpty()) {
+ throw Exception( StrCannotDetermineLibName(),
+ Reference<XInterface>() );
+ }
+ return import.aName;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_lib_container.h b/desktop/source/deployment/registry/script/dp_lib_container.h
new file mode 100644
index 000000000..fbbedf866
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_lib_container.h
@@ -0,0 +1,48 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star {
+ namespace uno {
+ class XComponentContext;
+ }
+ namespace ucb {
+ class XCommandEnvironment;
+ }
+}
+
+
+namespace dp_registry::backend::script {
+
+
+class LibraryContainer
+{
+public:
+ static OUString get_libname(
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const & xContext );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_script.cxx b/desktop/source/deployment/registry/script/dp_script.cxx
new file mode 100644
index 000000000..47e41a364
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_script.cxx
@@ -0,0 +1,480 @@
+/* -*- 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 <strings.hrc>
+#include "dp_lib_container.h"
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <ucbhelper/content.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/script/XLibraryContainer3.hpp>
+#include <memory>
+#include <string_view>
+
+#include "dp_scriptbackenddb.hxx"
+#include <cppuhelper/supportsservice.hxx>
+
+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::script {
+namespace {
+
+typedef ::cppu::ImplInheritanceHelper<
+ ::dp_registry::backend::PackageRegistryBackend, util::XUpdatable > t_helper;
+
+class BackendImpl : public t_helper
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const OUString m_scriptURL;
+ const OUString m_dialogURL;
+ OUString m_dialogName;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url,
+ Reference<XCommandEnvironment> const &xCmdEnv,
+ OUString const & scriptURL, OUString const & dialogURL,
+ bool bRemoved, OUString const & identifier);
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void addDataToDb(OUString const & url);
+ bool hasActiveEntry(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+
+ const Reference<deployment::XPackageTypeInfo> m_xBasicLibTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xDialogLibTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+ std::unique_ptr<ScriptBackendDb> m_backendDb;
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XUpdatable
+ virtual void SAL_CALL update() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url,
+ Reference<XCommandEnvironment> const &xCmdEnv,
+ OUString const & scriptURL, OUString const & dialogURL, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend, url,
+ OUString(), OUString(), // will be late-initialized
+ !scriptURL.isEmpty() ? myBackend->m_xBasicLibTypeInfo
+ : myBackend->m_xDialogLibTypeInfo, bRemoved, identifier),
+ m_scriptURL( scriptURL ),
+ m_dialogURL( dialogURL )
+{
+ // name, displayName:
+ if (!dialogURL.isEmpty()) {
+ m_dialogName = LibraryContainer::get_libname(
+ dialogURL, xCmdEnv, myBackend->getComponentContext() );
+ }
+ if (!scriptURL.isEmpty()) {
+ assert(m_name.pData);
+ m_name = LibraryContainer::get_libname(
+ scriptURL, xCmdEnv, myBackend->getComponentContext() );
+ }
+ else
+ m_name = m_dialogName;
+ m_displayName = m_name;
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : t_helper( args, xComponentContext ),
+ m_xBasicLibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.basic-library",
+ OUString() /* no file filter */,
+ DpResId(RID_STR_BASIC_LIB)
+ ) ),
+ m_xDialogLibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.dialog-library",
+ OUString() /* no file filter */,
+ DpResId(RID_STR_DIALOG_LIB)
+ ) ),
+ m_typeInfos{ m_xBasicLibTypeInfo, m_xDialogLibTypeInfo }
+{
+ OSL_ASSERT( ! transientMode() );
+
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ScriptBackendDb(getComponentContext(), dbFile));
+ }
+
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.script.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+void BackendImpl::addDataToDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+// XUpdatable
+
+void BackendImpl::update()
+{
+ // Nothing to do here after fixing i70283!?
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
+ ucbContent.isFolder())
+ {
+ // probe for script.xlb:
+ if (create_ucb_content(
+ nullptr, makeURL( url, "script.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.basic-library";
+ // probe for dialog.xlb:
+ else if (create_ucb_content(
+ nullptr, makeURL( url, "dialog.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.dialog-library";
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString dialogURL( makeURL( url, "dialog.xlb" ) );
+ if (! create_ucb_content(
+ nullptr, dialogURL, xCmdEnv, false /* no throw */ )) {
+ dialogURL.clear();
+ }
+
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.basic-library"))
+ {
+ OUString scriptURL( makeURL( url, "script.xlb"));
+ if (! create_ucb_content(
+ nullptr, scriptURL, xCmdEnv, false /* no throw */ )) {
+ scriptURL.clear();
+ }
+
+ return new PackageImpl(
+ this, url, xCmdEnv, scriptURL,
+ dialogURL, bRemoved, identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase(
+ "vnd.sun.star.dialog-library")) {
+ return new PackageImpl(
+ this, url, xCmdEnv,
+ OUString() /* no script lib */,
+ dialogURL,
+ bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+// Package
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(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<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard & /* guard */,
+ ::rtl::Reference<AbortChannel> const & /* abortChannel */,
+ Reference<XCommandEnvironment> const & /* xCmdEnv */ )
+{
+ BackendImpl * that = getMyBackend();
+ Reference< deployment::XPackage > xThisPackage( this );
+
+ bool registered = that->hasActiveEntry(getURL());
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>( registered, false /* IsAmbiguous */ ) );
+}
+
+void
+lcl_maybeRemoveScript(
+ bool const bExists,
+ OUString const& rName,
+ std::u16string_view rScriptURL,
+ Reference<css::script::XLibraryContainer3> const& xScriptLibs)
+{
+ if (bExists && xScriptLibs.is() && xScriptLibs->hasByName(rName))
+ {
+ const OUString sScriptUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
+ if (sScriptUrl == rScriptURL)
+ xScriptLibs->removeLibrary(rName);
+ }
+}
+
+bool
+lcl_maybeAddScript(
+ bool const bExists,
+ OUString const& rName,
+ OUString const& rScriptURL,
+ Reference<css::script::XLibraryContainer3> const& xScriptLibs)
+{
+ if (!bExists || !xScriptLibs)
+ return false;
+
+ bool bCanAdd = true;
+ if (xScriptLibs->hasByName(rName))
+ {
+ const OUString sOriginalUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
+ //We assume here that library names in extensions are unique, which may not be the case
+ //ToDo: If the script exist in another extension, then both extensions must have the
+ //same id
+ if (sOriginalUrl.match("vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE")
+ || sOriginalUrl.match("vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE")
+ || sOriginalUrl.match("vnd.sun.star.expand:$BUNDLED_EXTENSIONS")
+ || sOriginalUrl.match("$(INST)/share/basic/Access2Base/"))
+ {
+ xScriptLibs->removeLibrary(rName);
+ bCanAdd = true;
+ }
+ else
+ {
+ bCanAdd = false;
+ }
+ }
+
+ if (bCanAdd)
+ {
+ xScriptLibs->createLibraryLink(rName, rScriptURL, false);
+ return xScriptLibs->hasByName(rName);
+ }
+
+ return false;
+}
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard & /* guard */,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & /* abortChannel */,
+ Reference<XCommandEnvironment> const & /* xCmdEnv */ )
+{
+ BackendImpl * that = getMyBackend();
+
+ Reference< deployment::XPackage > xThisPackage( this );
+ Reference<XComponentContext> const & xComponentContext = that->getComponentContext();
+
+ bool bScript = !m_scriptURL.isEmpty();
+ Reference<css::script::XLibraryContainer3> xScriptLibs;
+
+ bool bDialog = !m_dialogURL.isEmpty();
+ Reference<css::script::XLibraryContainer3> xDialogLibs;
+
+ bool bRunning = !startup && office_is_running();
+ if( bRunning )
+ {
+ if( bScript )
+ {
+ xScriptLibs.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.script.ApplicationScriptLibraryContainer",
+ xComponentContext ), UNO_QUERY_THROW );
+ }
+
+ if( bDialog )
+ {
+ xDialogLibs.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.script.ApplicationDialogLibraryContainer",
+ xComponentContext ), UNO_QUERY_THROW );
+ }
+ }
+ bool bRegistered = getMyBackend()->hasActiveEntry(getURL());
+ if( !doRegisterPackage )
+ {
+ //We cannot just call removeLibrary(name) because this could remove a
+ //script which was added by an extension in a different repository. For
+ //example, extension foo is contained in the bundled repository and then
+ //the user adds it to the user repository. The extension manager will
+ //then register the new script and revoke the script from the bundled
+ //extension. removeLibrary(name) would now remove the script from the
+ //user repository. That is, the script of the newly added user extension does
+ //not work anymore. Therefore we must check if the currently active
+ //script comes in fact from the currently processed extension.
+
+ if (bRegistered)
+ {
+ //we also prevent and live deployment at startup
+ if (!isRemoved() && !startup)
+ {
+ lcl_maybeRemoveScript(bScript, m_name, m_scriptURL, xScriptLibs);
+ lcl_maybeRemoveScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
+ }
+ getMyBackend()->revokeEntryFromDb(getURL());
+ return;
+ }
+ }
+ if (bRegistered)
+ return; // Already registered
+
+ // Update LibraryContainer
+ bool bScriptSuccess = false;
+ bool bDialogSuccess = false;
+ if (!startup)
+ {
+ //If there is a bundled extension, and the user installs the same extension
+ //then the script from the bundled extension must be removed. If this does not work
+ //then live deployment does not work for scripts.
+ bScriptSuccess = lcl_maybeAddScript(bScript, m_name, m_scriptURL, xScriptLibs);
+ bDialogSuccess = lcl_maybeAddScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
+ }
+ bool bSuccess = bScript || bDialog; // Something must have happened
+ if( bRunning )
+ if( (bScript && !bScriptSuccess) || (bDialog && !bDialogSuccess) )
+ bSuccess = false;
+
+ if (bSuccess)
+ getMyBackend()->addDataToDb(getURL());
+}
+
+} // anon namespace
+
+} // namespace dp_registry::backend::script
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_script_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::script::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx b/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx
new file mode 100644
index 000000000..476f43953
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx
@@ -0,0 +1,64 @@
+/* -*- 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 <com/sun/star/uno/XComponentContext.hpp>
+#include "dp_scriptbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/script-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"script";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"script-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"script";
+
+namespace dp_registry::backend::script {
+
+ScriptBackendDb::ScriptBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):RegisteredDb(xContext, url)
+{
+
+}
+
+OUString ScriptBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ScriptBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ScriptBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ScriptBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+} // namespace dp_registry::backend::script
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx b/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx
new file mode 100644
index 000000000..21d4b1f6b
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx
@@ -0,0 +1,55 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <dp_backenddb.hxx>
+#include <optional>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry::backend::script {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ScriptBackendDb: public dp_registry::backend::RegisteredDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+
+public:
+
+ ScriptBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx b/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx
new file mode 100644
index 000000000..530924a07
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx
@@ -0,0 +1,103 @@
+/* -*- 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 <dp_misc.h>
+#include "dp_parceldesc.hxx"
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend::sfwk
+{
+
+
+// XDocumentHandler
+void SAL_CALL
+ParcelDescDocHandler::startDocument()
+{
+ m_bIsParsed = false;
+}
+
+void SAL_CALL
+ParcelDescDocHandler::endDocument()
+{
+ m_bIsParsed = true;
+}
+
+void SAL_CALL
+ParcelDescDocHandler::characters( const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::ignorableWhitespace( const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::processingInstruction(
+ const OUString &, const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::setDocumentLocator(
+ const Reference< xml::sax::XLocator >& )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::startElement( const OUString& aName,
+ const Reference< xml::sax::XAttributeList > & xAttribs )
+{
+
+ dp_misc::TRACE("ParcelDescDocHandler::startElement() for " +
+ aName + "\n");
+ if ( !skipIndex )
+ {
+ if ( aName == "parcel" )
+ {
+ m_sLang = xAttribs->getValueByName( "language" );
+ }
+ ++skipIndex;
+ }
+ else
+ {
+ dp_misc::TRACE("ParcelDescDocHandler::startElement() skipping for "
+ + aName + "\n");
+ }
+
+}
+
+void SAL_CALL ParcelDescDocHandler::endElement( const OUString & aName )
+{
+ if ( skipIndex )
+ {
+ --skipIndex;
+ dp_misc::TRACE("ParcelDescDocHandler::endElement() skipping for "
+ + aName + "\n");
+ }
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx b/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx
new file mode 100644
index 000000000..6b5bde8bd
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx
@@ -0,0 +1,64 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/xml/sax/XAttributeList.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+namespace dp_registry::backend::sfwk
+{
+
+class ParcelDescDocHandler : public ::cppu::WeakImplHelper< css::xml::sax::XDocumentHandler >
+{
+private:
+ bool m_bIsParsed;
+ OUString m_sLang;
+ sal_Int32 skipIndex;
+public:
+ ParcelDescDocHandler():m_bIsParsed( false ), skipIndex( 0 ){}
+ const OUString& getParcelLanguage() const { return m_sLang; }
+ bool isParsed() const { return m_bIsParsed; }
+ // XDocumentHandler
+ virtual void SAL_CALL startDocument() override;
+
+ virtual void SAL_CALL endDocument() override;
+
+ virtual void SAL_CALL startElement( const OUString& aName,
+ const css::uno::Reference< css::xml::sax::XAttributeList > & xAttribs ) override;
+
+ virtual void SAL_CALL endElement( const OUString & aName ) override;
+
+ virtual void SAL_CALL characters( const OUString & aChars ) override;
+
+ virtual void SAL_CALL ignorableWhitespace( const OUString & aWhitespaces ) override;
+
+ virtual void SAL_CALL processingInstruction(
+ const OUString & aTarget, const OUString & aData ) override;
+
+ virtual void SAL_CALL setDocumentLocator(
+ const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override;
+};
+}
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx b/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx
new file mode 100644
index 000000000..b617d7fa4
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx
@@ -0,0 +1,378 @@
+/* -*- 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 <strings.hrc>
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include "dp_parceldesc.hxx"
+#include <rtl/uri.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::script;
+
+
+namespace dp_registry::backend::sfwk
+{
+
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ Reference< container::XNameContainer > m_xNameCntrPkgHandler;
+ OUString m_descr;
+
+ void initPackageHandler();
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url, OUString libType, bool bRemoved,
+ OUString const & identifier);
+ // XPackage
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getLicenseText() override;
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xTypeInfo;
+
+
+public:
+ BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+};
+
+}
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(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<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+OUString BackendImpl::PackageImpl::getDescription()
+{
+ if (m_descr.isEmpty())
+ return Package::getDescription();
+ else
+ return m_descr;
+}
+
+OUString BackendImpl::PackageImpl::getLicenseText()
+{
+ return Package::getDescription();
+}
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url, OUString libType, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend, url, OUString(), OUString(),
+ myBackend->m_xTypeInfo, bRemoved, identifier),
+ m_descr(std::move(libType))
+{
+ initPackageHandler();
+
+ sal_Int32 segmEnd = url.getLength();
+ if ( url.endsWith("/") )
+ --segmEnd;
+ sal_Int32 segmStart = url.lastIndexOf( '/', segmEnd ) + 1;
+ if (segmStart < 0)
+ segmStart = 0;
+ // name and display name default the same:
+ m_displayName = ::rtl::Uri::decode(
+ url.copy( segmStart, segmEnd - segmStart ),
+ rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ m_name = m_displayName;
+
+ dp_misc::TRACE("PackageImpl displayName is " + m_displayName);
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.framework-script",
+ OUString() /* no file filter */,
+ "Scripting Framework Script Library"
+ ) )
+{
+}
+
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.sfwk.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return Sequence< Reference<deployment::XPackageTypeInfo> >(&m_xTypeInfo, 1);
+}
+
+void BackendImpl::packageRemoved(OUString const & /*url*/, OUString const & /*mediaType*/)
+{
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
+ ucbContent.isFolder())
+ {
+ // probe for parcel-descriptor.xml:
+ if (create_ucb_content(
+ nullptr, makeURL( url, "parcel-descriptor.xml" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ mediaType = "application/vnd.sun.star.framework-script";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.framework-script"))
+ {
+ OUString lang = "Script";
+ OUString sParcelDescURL = makeURL(
+ url, "parcel-descriptor.xml" );
+
+ ::ucbhelper::Content ucb_content;
+
+ if (create_ucb_content( &ucb_content, sParcelDescURL,
+ xCmdEnv, false /* no throw */ ))
+ {
+ rtl::Reference<ParcelDescDocHandler> pHandler =
+ new ParcelDescDocHandler();
+
+ Reference<XComponentContext>
+ xContext( getComponentContext() );
+
+ Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext);
+
+ xParser->setDocumentHandler( pHandler );
+ xml::sax::InputSource source;
+ source.aInputStream = ucb_content.openStream();
+ source.sSystemId = ucb_content.getURL();
+ xParser->parseStream( source );
+
+ if ( pHandler->isParsed() )
+ {
+ lang = pHandler->getParcelLanguage();
+ }
+ }
+
+ OUString sfwkLibType = DpResId( RID_STR_SFWK_LIB );
+ // replace %MACRONAME placeholder with language name
+ OUString MACRONAME( "%MACROLANG" );
+ sal_Int32 startOfReplace = sfwkLibType.indexOf( MACRONAME );
+ sal_Int32 charsToReplace = MACRONAME.getLength();
+ sfwkLibType = sfwkLibType.replaceAt( startOfReplace, charsToReplace, lang );
+ dp_misc::TRACE("******************************\n");
+ dp_misc::TRACE(" BackEnd detected lang = " + lang + "\n");
+ dp_misc::TRACE(" for url " + sParcelDescURL + "\n");
+ dp_misc::TRACE("******************************\n");
+ return new PackageImpl( this, url, sfwkLibType, bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::PackageImpl:: initPackageHandler()
+{
+ if (m_xNameCntrPkgHandler.is())
+ return;
+
+ BackendImpl * that = getMyBackend();
+ Any aContext;
+
+ if ( that->m_eContext == Context::User )
+ {
+ aContext <<= OUString("user");
+ }
+ else if ( that->m_eContext == Context::Shared )
+ {
+ aContext <<= OUString("share");
+ }
+ else if ( that->m_eContext == Context::Bundled )
+ {
+ aContext <<= OUString("bundled");
+ }
+ else
+ {
+ OSL_ASSERT( false );
+ // NOT supported at the moment // TODO
+ }
+
+ Reference< provider::XScriptProviderFactory > xFac =
+ provider::theMasterScriptProviderFactory::get( that->getComponentContext() );
+
+ Reference< container::XNameContainer > xName( xFac->createScriptProvider( aContext ), UNO_QUERY );
+ if ( xName.is() )
+ {
+ m_xNameCntrPkgHandler.set( xName );
+ }
+ // TODO what happens if above fails??
+}
+
+// Package
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ m_xNameCntrPkgHandler.is() && m_xNameCntrPkgHandler->hasByName(
+ m_url ),
+ false /* IsAmbiguous */ ) );
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /* startup */,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ if ( !m_xNameCntrPkgHandler.is() )
+ {
+ dp_misc::TRACE("no package handler!!!!\n");
+ throw RuntimeException( "No package Handler " );
+ }
+
+ if (doRegisterPackage)
+ {
+ // will throw if it fails
+ m_xNameCntrPkgHandler->insertByName( m_url, Any( Reference< XPackage >(this) ) );
+
+ }
+ else // revokePackage()
+ {
+ m_xNameCntrPkgHandler->removeByName( m_url );
+ }
+}
+
+} // namespace dp_registry::backend::sfwk
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_sfwk_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::sfwk::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */