/* -*- 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 <comphelper/diagnose_ex.hxx>
#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, std::u16string_view 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(
            u"Failed to get the BackendImpl"_ustr,
            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(
            u"caught unexpected exception while disposing..."_ustr,
            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(
            u"com.sun.star.registry.SimpleRegistry"_ustr,
            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(
            u"com.sun.star.registry.SimpleRegistry"_ustr,
            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(),
                                   u"*" SAL_DLLEXTENSION ""_ustr,
                                   DpResId(RID_STR_DYN_COMPONENT)
                                   ) ),
      m_xJavaComponentTypeInfo( new Package::TypeInfo(
             u"application/vnd.sun.star.uno-component;type=Java"_ustr,
                                    u"*.jar"_ustr,
                                    DpResId(RID_STR_JAVA_COMPONENT)
                                    ) ),
      m_xPythonComponentTypeInfo( new Package::TypeInfo(
             u"application/vnd.sun.star.uno-component;type=Python"_ustr,
                                      u"*.py"_ustr,
                                      DpResId(
                                          RID_STR_PYTHON_COMPONENT)
                                      ) ),
      m_xComponentsTypeInfo( new Package::TypeInfo(
                                 u"application/vnd.sun.star.uno-components"_ustr,
                                 u"*.components"_ustr,
                                 DpResId(RID_STR_COMPONENTS)
                                 ) ),
      m_xRDBTypelibTypeInfo( new Package::TypeInfo(
             u"application/vnd.sun.star.uno-typelibrary;type=RDB"_ustr,
                                 u"*.rdb"_ustr,
                                 DpResId(RID_STR_RDB_TYPELIB)
                                 ) ),
      m_xJavaTypelibTypeInfo( new Package::TypeInfo(
             u"application/vnd.sun.star.uno-typelibrary;type=Java"_ustr,
                                  u"*.jar"_ustr,
                                  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(
                u"com.sun.star.registry.SimpleRegistry"_ustr,
                xComponentContext ), UNO_QUERY_THROW );
        m_xCommonRDB->open( OUString() /* in-mem */,
                            false /* ! read-only */, true /* create */ );
        m_xNativeRDB.set(
            xComponentContext->getServiceManager()->createInstanceWithContext(
                u"com.sun.star.registry.SimpleRegistry"_ustr,
                xComponentContext ), UNO_QUERY_THROW );
        m_xNativeRDB->open( OUString() /* in-mem */,
                            false /* ! read-only */, true /* create */ );
    }
    else
    {
        unorc_verify_init( xCmdEnv );
        OUString dbFile = makeURL(getCachePath(), u"backenddb.xml"_ustr);
        m_backendDb.reset(
            new ComponentBackendDb(getComponentContext(), dbFile));
    }
}
 
// XServiceInfo
OUString BackendImpl::getImplementationName()
{
    return u"com.sun.star.comp.deployment.component.PackageRegistryBackend"_ustr;
}
 
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, u"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("platform"_ostr);
                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("type"_ostr);
                    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,
                                    u"com.sun.star.loader.SharedLibrary"_ustr,
                                    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,
                                u"com.sun.star.loader.Java2"_ustr,
                                bRemoved, identifier);
                        }
                        if (value.equalsIgnoreAsciiCase("Python")) {
                            return new BackendImpl::ComponentPackageImpl(
                                this, url, name, m_xPythonComponentTypeInfo,
                                u"com.sun.star.loader.Python"_ustr,
                                bRemoved, identifier);
                        }
                    }
                }
            }
            else if (subType.equalsIgnoreAsciiCase("vnd.sun.star.uno-components"))
            {
                auto const iter = params.find("platform"_ostr);
                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("type"_ostr);
                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(), u"unorc"_ustr ),
            xCmdEnv, false /* no throw */ ))
    {
        OUString line;
        if (readLine( &line, u"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, u"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, u"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, u"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;
 
    OUString sOrigin = dp_misc::makeRcTerm(m_cachePath);
    OString osOrigin = OUStringToOString(sOrigin, RTL_TEXTENCODING_UTF8);
    OStringBuffer buf("ORIGIN=" + osOrigin + OStringChar(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/"
                + 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("?" + 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(), u"unorc"_ustr ), 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);
    std::erase(aRcItemList, rcterm);
    // 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(u"_root"_ustr),
        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( u"$URE_BIN_DIR/uno"_ustr ) );
 
    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
        u"--singleaccept"_ustr,
        u"-u"_ustr,
        connectStr,
        // don't inherit from unorc:
        u"-env:INIFILENAME="_ustr };
 
    //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)
{
    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(u"UNO/SINGLETONS"_ustr));
        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(
            u"com.sun.star.registry.SimpleRegistry"_ustr, 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( u"IMPLEMENTATIONS"_ustr ) );
            Sequence<OUString> implNames;
            if (xImplKey.is() && xImplKey->isValid())
                implNames = xImplKey->getKeyNames();
            sal_Int32 pos = implNames.getLength();
            for ( ; pos--; )
            {
                checkAborted( abortChannel );
                const OUString key(implNames[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(
                u"com.sun.star.registry.ImplementationRegistration"_ustr,
                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, u"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(
                u"com.sun.star.registry.ImplementationRegistration"_ustr,
                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( u"Failed to get the BackendImpl"_ustr,
            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(
                    u"/singletons"
                    "/com.sun.star.reflection.theTypeDescriptionManager"_ustr),
                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(
                    u"/singletons"
                    "/com.sun.star.reflection.theTypeDescriptionManager"_ustr),
                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(u"Failed to get the BackendImpl"_ustr,
            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(u"com.sun.star.registry.SimpleRegistry"_ustr),
                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.clear();
    }
 
    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(u"com.sun.star.registry.ImplementationRegistration"_ustr),
            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(u"Failed to get the BackendImpl"_ustr,
            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
            {
                { u"uri"_ustr, css::uno::Any(expandUnoRcUrl(url)) },
                { u"component-context"_ustr, 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 { { u"uri"_ustr, 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: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.