Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/package/source/xstor/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 196 kB image not shown  

Quelle  xstorage.cxx   Sprache: C

 
/* -*- 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 <sal/config.h>
#include <sal/log.hxx>

#include <cassert>
#include <string_view>

#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/InvalidStorageException.hpp>
#include <com/sun/star/embed/UseBackupException.hpp>
#include <com/sun/star/embed/StorageFormats.hpp>
#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
#include <com/sun/star/packages/NoEncryptionException.hpp>
#include <com/sun/star/packages/NoRawFormatException.hpp>
#include <com/sun/star/packages/WrongPasswordException.hpp>
#include <com/sun/star/io/TempFile.hpp>
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/util/XChangesBatch.hpp>

#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/beans/NamedValue.hpp>

#include <PackageConstants.hxx>

#include <comphelper/sequence.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <cppuhelper/exc_hlp.hxx>

#include <comphelper/servicehelper.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/ofopxmlhelper.hxx>
#include <utility>
#include <comphelper/diagnose_ex.hxx>

#include "xstorage.hxx"
#include "owriteablestream.hxx"
#include "switchpersistencestream.hxx"

using namespace ::com::sun::star;

#if OSL_DEBUG_LEVEL > 0
#define THROW_WHERE SAL_WHERE
#else
#define THROW_WHERE ""
#endif

// static
void OStorage_Impl::completeStorageStreamCopy_Impl(
                            const uno::Reference< io::XStream >& xSource,
                            const uno::Reference< io::XStream >& xDest,
                            sal_Int32 nStorageType,
                            const uno::Sequence< uno::Sequence< beans::StringPair > >& aRelInfo )
{
        uno::Reference< beans::XPropertySet > xSourceProps( xSource, uno::UNO_QUERY_THROW );
        uno::Reference< beans::XPropertySet > xDestProps( xDest, uno::UNO_QUERY_THROW );

        uno::Reference< io::XOutputStream > xDestOutStream = xDest->getOutputStream();
        if ( !xDestOutStream.is() )
            throw io::IOException( THROW_WHERE );

        uno::Reference< io::XInputStream > xSourceInStream = xSource->getInputStream();
        if ( !xSourceInStream.is() )
            throw io::IOException( THROW_WHERE );

        // TODO: headers of encrypted streams should be copied also
        ::comphelper::OStorageHelper::CopyInputToOutput( xSourceInStream, xDestOutStream );

        uno::Sequence<OUString> aPropNames { u"Compressed"_ustr, u"MediaType"_ustr,
                                             u"UseCommonStoragePasswordEncryption"_ustr };

        if ( nStorageType == embed::StorageFormats::OFOPXML )
        {
            // TODO/LATER: in future it might make sense to provide the stream if there is one
            uno::Reference< embed::XRelationshipAccess > xRelAccess( xDest, uno::UNO_QUERY_THROW );
            xRelAccess->clearRelationships();
            xRelAccess->insertRelationships( aRelInfo, false );

            aPropNames.realloc( 2 );
        }
        else if ( nStorageType != embed::StorageFormats::PACKAGE )
        {
            aPropNames.realloc( 1 );
        }

        for (const auto& rPropName : aPropNames)
            xDestProps->setPropertyValue( rPropName, xSourceProps->getPropertyValue( rPropName ) );
}

static uno::Reference< io::XInputStream > GetSeekableTempCopy( const uno::Reference< io::XInputStream >& xInStream )
{
    rtl::Reference < utl::TempFileFastService > xTempFile = new utl::TempFileFastService;
    uno::Reference < io::XOutputStream > xTempOut = xTempFile->getOutputStream();
    uno::Reference < io::XInputStream > xTempIn = xTempFile->getInputStream();

    if ( !xTempOut.is() || !xTempIn.is() )
        throw io::IOException( THROW_WHERE );

    ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOut );
    xTempOut->closeOutput();

    return xTempIn;
}

SotElement_Impl::SotElement_Impl(OUString aName, bool bStor, bool bNew)
    : m_aOriginalName(std::move(aName))
    , m_bIsRemoved(false)
    , m_bIsInserted(bNew)
    , m_bIsStorage(bStor)
{
}

// most of properties are holt by the storage but are not used
OStorage_Impl::OStorage_Impl(   uno::Reference< io::XInputStream > const & xInputStream,
                                sal_Int32 nMode,
                                const uno::Sequence< beans::PropertyValue >& xProperties,
                                uno::Reference< uno::XComponentContext > const & xContext,
                                sal_Int32 nStorageType )
: m_xMutex( new comphelper::RefCountedMutex )
, m_pAntiImpl( nullptr )
, m_nStorageMode( nMode & ~embed::ElementModes::SEEKABLE )
, m_bIsModified( ( nMode & ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) == ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) )
, m_bBroadcastModified( false )
, m_bCommited( false )
, m_bIsRoot( true )
, m_bListCreated( false )
, m_nModifiedListenerCount( 0 )
, m_xContext( xContext )
, m_xProperties( xProperties )
, m_bHasCommonEncryptionData( false )
, m_pParent( nullptr )
, m_bControlMediaType( false )
, m_bMTFallbackUsed( false )
, m_bControlVersion( false )
, m_nStorageType( nStorageType )
, m_pRelStorElement( nullptr )
, m_nRelInfoStatus( RELINFO_NO_INIT )
{
    // all the checks done below by assertion statements must be done by factory
    SAL_WARN_IF( !xInputStream.is(), "package.xstor""No input stream is provided!" );
    assert(xContext.is());

    m_pSwitchStream = new SwitchablePersistenceStream(xInputStream);
    m_xInputStream = m_pSwitchStream->getInputStream();

    if ( m_nStorageMode & embed::ElementModes::WRITE )
    {
        // check that the stream allows to write
        SAL_WARN( "package.xstor""No stream for writing is provided!" );
    }
}

// most of properties are holt by the storage but are not used
OStorage_Impl::OStorage_Impl(   uno::Reference< io::XStream > const & xStream,
                                sal_Int32 nMode,
                                const uno::Sequence< beans::PropertyValue >& xProperties,
                                uno::Reference< uno::XComponentContext > const & xContext,
                                sal_Int32 nStorageType )
: m_xMutex( new comphelper::RefCountedMutex )
, m_pAntiImpl( nullptr )
, m_nStorageMode( nMode & ~embed::ElementModes::SEEKABLE )
, m_bIsModified( ( nMode & ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) == ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) )
, m_bBroadcastModified( false )
, m_bCommited( false )
, m_bIsRoot( true )
, m_bListCreated( false )
, m_nModifiedListenerCount( 0 )
, m_xContext( xContext )
, m_xProperties( xProperties )
, m_bHasCommonEncryptionData( false )
, m_pParent( nullptr )
, m_bControlMediaType( false )
, m_bMTFallbackUsed( false )
, m_bControlVersion( false )
, m_nStorageType( nStorageType )
, m_pRelStorElement( nullptr )
, m_nRelInfoStatus( RELINFO_NO_INIT )
{
    // all the checks done below by assertion statements must be done by factory
    SAL_WARN_IF( !xStream.is(), "package.xstor""No stream is provided!" );
    assert(xContext.is());

    if ( m_nStorageMode & embed::ElementModes::WRITE )
    {
        m_pSwitchStream = new SwitchablePersistenceStream(xStream);
        m_xStream = m_pSwitchStream.get();
    }
    else
    {
        m_pSwitchStream = new SwitchablePersistenceStream(xStream->getInputStream());
        m_xInputStream = m_pSwitchStream->getInputStream();
    }
}

OStorage_Impl::OStorage_Impl(   OStorage_Impl* pParent,
                                sal_Int32 nMode,
                                uno::Reference< container::XNameContainer > const & xPackageFolder,
                                uno::Reference< lang::XSingleServiceFactory > xPackage,
                                uno::Reference< uno::XComponentContext > const & xContext,
                                sal_Int32 nStorageType )
: m_xMutex( new comphelper::RefCountedMutex )
, m_pAntiImpl( nullptr )
, m_nStorageMode( nMode & ~embed::ElementModes::SEEKABLE )
, m_bIsModified( ( nMode & ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) == ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) )
, m_bBroadcastModified( false )
, m_bCommited( false )
, m_bIsRoot( false )
, m_bListCreated( false )
, m_nModifiedListenerCount( 0 )
, m_xPackageFolder( xPackageFolder )
, m_xPackage(std::move( xPackage ))
, m_xContext( xContext )
, m_bHasCommonEncryptionData( false )
, m_pParent( pParent ) // can be empty in case of temporary readonly substorages and relation storage
, m_bControlMediaType( false )
, m_bMTFallbackUsed( false )
, m_bControlVersion( false )
, m_nStorageType( nStorageType )
, m_pRelStorElement( nullptr )
, m_nRelInfoStatus( RELINFO_NO_INIT )
{
    SAL_WARN_IF( !xPackageFolder.is(), "package.xstor""No package folder!" );
    assert(xContext.is());
}

OStorage_Impl::~OStorage_Impl()
{
    {
        ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
        if ( m_pAntiImpl ) // root storage wrapper must set this member to NULL before destruction of object
        {
            SAL_WARN_IF( m_bIsRoot, "package.xstor""The root storage wrapper must be disposed already" );

            try {
                m_pAntiImpl->InternalDispose( false );
            }
            catch ( const uno::Exception& )
            {
                TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");
            }
            m_pAntiImpl = nullptr;
        }
        else if ( !m_aReadOnlyWrapVector.empty() )
        {
            for ( auto& rStorage : m_aReadOnlyWrapVector )
            {
                uno::Reference< embed::XStorage > xTmp = rStorage.m_xWeakRef;
                if ( xTmp.is() )
                    try {
                        rStorage.m_pPointer->InternalDispose( false );
                    } catchconst uno::Exception& )
                    {
                        TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");
                    }
            }

            m_aReadOnlyWrapVector.clear();
        }

        m_pParent = nullptr;
    }

    for (const auto & pair : m_aChildrenMap)
        for (auto pElement : pair.second)
            delete pElement;
    m_aChildrenMap.clear();

    std::for_each(m_aDeletedVector.begin(), m_aDeletedVector.end(), std::default_delete<SotElement_Impl>());
    m_aDeletedVector.clear();

    if ( m_nStorageType == embed::StorageFormats::OFOPXML && m_pRelStorElement )
    {
        delete m_pRelStorElement;
        m_pRelStorElement = nullptr;
    }

    m_xPackageFolder.clear();
    m_xPackage.clear();

    OUString aPropertyName = u"URL"_ustr;
    for (const auto& rProp : m_xProperties)
    {
        if ( rProp.Name == aPropertyName )
        {
            // the storage is URL based so all the streams are opened by factory and should be closed
            try
            {
                if ( m_xInputStream.is() )
                {
                    m_xInputStream->closeInput();
                    m_xInputStream.clear();
                }

                if ( m_xStream.is() )
                {
                    uno::Reference< io::XInputStream > xInStr = m_xStream->getInputStream();
                    if ( xInStr.is() )
                        xInStr->closeInput();

                    uno::Reference< io::XOutputStream > xOutStr = m_xStream->getOutputStream();
                    if ( xOutStr.is() )
                        xOutStr->closeOutput();

                    m_xStream.clear();
                }
            }
            catch (const uno::Exception&)
            {
                TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");
            }
        }
    }
}

void OStorage_Impl::SetReadOnlyWrap( OStorage& aStorage )
{
    // Weak reference is used inside the holder so the refcount must not be zero at this point
    OSL_ENSURE( aStorage.GetRefCount_Impl(), "There must be a reference alive to use this method!" );
    m_aReadOnlyWrapVector.emplace_back( &aStorage );
}

void OStorage_Impl::RemoveReadOnlyWrap( const OStorage& aStorage )
{
    for ( StorageHoldersType::iterator pStorageIter = m_aReadOnlyWrapVector.begin();
      pStorageIter != m_aReadOnlyWrapVector.end();)
    {
        uno::Reference< embed::XStorage > xTmp = pStorageIter->m_xWeakRef;
        if ( !xTmp.is() || pStorageIter->m_pPointer == &aStorage )
        {
            try {
                pStorageIter->m_pPointer->InternalDispose( false );
            } catchconst uno::Exception& )
            {
                TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");
            }

            pStorageIter = m_aReadOnlyWrapVector.erase(pStorageIter);
        }
        else
            ++pStorageIter;
    }
}

void OStorage_Impl::OpenOwnPackage()
{
    SAL_WARN_IF( !m_bIsRoot, "package.xstor""Opening of the package has no sense!" );

    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    if ( !m_xPackageFolder.is() )
    {
        if ( !m_xPackage.is() )
        {
            uno::Sequence< uno::Any > aArguments( 2 );
            auto pArguments = aArguments.getArray();
            if ( m_nStorageMode & embed::ElementModes::WRITE )
                pArguments[ 0 ] <<= css::uno::Reference< css::io::XStream >(m_xStream);
            else
            {
                SAL_WARN_IF( !m_xInputStream.is(), "package.xstor""Input stream must be set for readonly access!" );
                pArguments[ 0 ] <<= m_xInputStream;
                // TODO: if input stream is not seekable or XSeekable interface is supported
                // on XStream object a wrapper must be used
            }

            // do not allow elements to remove themself from the old container in case of insertion to another container
            pArguments[ 1 ] <<= beans::NamedValue( u"AllowRemoveOnInsert"_ustr,
                                                    uno::Any( false ) );

            sal_Int32 nArgNum = 2;
            for (const auto& rProp : m_xProperties)
            {
                if ( rProp.Name == "RepairPackage"
                  || rProp.Name == "ProgressHandler"
                  || rProp.Name == "NoFileSync" )
                {
                    // Forward these to the package.
                    beans::NamedValue aNamedValue( rProp.Name, rProp.Value );
                    aArguments.realloc( ++nArgNum );
                    pArguments = aArguments.getArray();
                    pArguments[nArgNum-1] <<= aNamedValue;
                    if (rProp.Name == "RepairPackage")
                        rProp.Value >>= m_bRepairPackage;
                }
                else if ( rProp.Name == "Password" )
                {
                    // TODO: implement password setting for documents
                    // the password entry must be removed after setting
                }
            }

            if ( m_nStorageType == embed::StorageFormats::ZIP )
            {
                // let the package support only plain zip format
                beans::NamedValue aNamedValue;
                aNamedValue.Name = "StorageFormat";
                aNamedValue.Value <<= u"ZipFormat"_ustr;
                aArguments.realloc( ++nArgNum );
                pArguments = aArguments.getArray();
                pArguments[nArgNum-1] <<= aNamedValue;
            }
            else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
            {
                // let the package support OFOPXML media type handling
                beans::NamedValue aNamedValue;
                aNamedValue.Name = "StorageFormat";
                aNamedValue.Value <<= u"OFOPXMLFormat"_ustr;
                aArguments.realloc( ++nArgNum );
                pArguments = aArguments.getArray();
                pArguments[nArgNum-1] <<= aNamedValue;
            }

            m_xPackage.set( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
                               u"com.sun.star.packages.comp.ZipPackage"_ustr, aArguments, m_xContext),
                            uno::UNO_QUERY );
        }

        uno::Reference< container::XHierarchicalNameAccess > xHNameAccess( m_xPackage, uno::UNO_QUERY );
        SAL_WARN_IF( !xHNameAccess.is(), "package.xstor""The package could not be created!" );

        if ( xHNameAccess.is() )
        {
            uno::Any aFolder = xHNameAccess->getByHierarchicalName(u"/"_ustr);
            aFolder >>= m_xPackageFolder;
        }
    }

    SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor""The package root folder can not be opened!" );
    if ( !m_xPackageFolder.is() )
        throw embed::InvalidStorageException( THROW_WHERE );
}

bool OStorage_Impl::HasChildren()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    ReadContents();
    return !m_aChildrenMap.empty();
}

void OStorage_Impl::GetStorageProperties()
{
    if ( m_nStorageType != embed::StorageFormats::PACKAGE )
        return;

    uno::Reference< beans::XPropertySet > xProps( m_xPackageFolder, uno::UNO_QUERY_THROW );

    if ( !m_bControlMediaType )
    {
        uno::Reference< beans::XPropertySet > xPackageProps( m_xPackage, uno::UNO_QUERY_THROW );
        xPackageProps->getPropertyValue( MEDIATYPE_FALLBACK_USED_PROPERTY ) >>= m_bMTFallbackUsed;

        static constexpr OUStringLiteral sMediaType(u"MediaType");
        xProps->getPropertyValue( sMediaType ) >>= m_aMediaType;
        m_bControlMediaType = true;
    }

    if ( !m_bControlVersion )
    {
        xProps->getPropertyValue( u"Version"_ustr ) >>= m_aVersion;
        m_bControlVersion = true;
    }

    // the properties of OFOPXML will be handled directly
}

void OStorage_Impl::ReadRelInfoIfNecessary()
{
    if ( m_nStorageType != embed::StorageFormats::OFOPXML )
        return;

    if ( m_nRelInfoStatus == RELINFO_NO_INIT )
    {
        // Init from original stream
        uno::Reference< io::XInputStream > xRelInfoStream
            = GetRelInfoStreamForName( std::u16string_view() );
        try
        {
            if ( xRelInfoStream.is() )
                m_aRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(
                                    xRelInfoStream,
                                    u"_rels/.rels",
                                    m_xContext );
            m_nRelInfoStatus = RELINFO_READ;
        }
        catch (css::uno::Exception &)
        {
            TOOLS_INFO_EXCEPTION("package.xstor""");
        }
    }
    else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
    {
        // Init from the new stream
        try
        {
            if ( m_xNewRelInfoStream.is() )
                m_aRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(
                                        m_xNewRelInfoStream,
                                        u"_rels/.rels",
                                        m_xContext );

            m_nRelInfoStatus = RELINFO_CHANGED_STREAM_READ;
        }
        catchconst uno::Exception& )
        {
            m_nRelInfoStatus = RELINFO_CHANGED_BROKEN;
        }
    }
}

void OStorage_Impl::ReadContents()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    if ( m_bListCreated )
        return;

    if ( m_bIsRoot )
        OpenOwnPackage();

    uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xPackageFolder, uno::UNO_QUERY_THROW );
    uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
    if ( !xEnum.is() )
        throw uno::RuntimeException( THROW_WHERE );

    m_bListCreated = true;

    while( xEnum->hasMoreElements() )
    {
        try {
            uno::Reference< container::XNamed > xNamed;
            xEnum->nextElement() >>= xNamed;

            if ( !xNamed.is() )
            {
                SAL_WARN( "package.xstor""XNamed is not supported!" );
                throw uno::RuntimeException( THROW_WHERE );
            }

            OUString aName = xNamed->getName();
            SAL_WARN_IF( aName.isEmpty(), "package.xstor""Empty name!" );

            uno::Reference< container::XNameContainer > xNameContainer( xNamed, uno::UNO_QUERY );

            std::unique_ptr<SotElement_Impl> xNewElement(new SotElement_Impl(aName, xNameContainer.is(), false));
            if ( m_nStorageType == embed::StorageFormats::OFOPXML && aName == "_rels" )
            {
                if (!xNewElement->m_bIsStorage)
                    throw io::IOException( THROW_WHERE ); // TODO: Unexpected format

                m_pRelStorElement = xNewElement.release();
                CreateRelStorage();
            }
            else
            {
                if ( ( m_nStorageMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE )
                {
                    // if a storage is truncated all of it elements are marked as deleted
                    xNewElement->m_bIsRemoved = true;
                }

                m_aChildrenMap[aName].push_back(xNewElement.release());
            }
        }
        catchconst container::NoSuchElementException& )
        {
            TOOLS_WARN_EXCEPTION( "package.xstor""hasMoreElements() implementation has problems!");
            break;
        }
    }
    if ( ( m_nStorageMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE )
    {
        // if a storage is truncated the relations information should be cleaned
        m_xNewRelInfoStream.clear();
        m_aRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
        m_nRelInfoStatus = RELINFO_CHANGED;
    }

    // cache changeable folder properties
    GetStorageProperties();
}

void OStorage_Impl::CopyToStorage( const uno::Reference< embed::XStorage >& xDest, bool bDirect )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    uno::Reference< beans::XPropertySet > xPropSet( xDest, uno::UNO_QUERY );
    if ( !xPropSet.is() )
        throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );

    sal_Int32 nDestMode = embed::ElementModes::READ;
    xPropSet->getPropertyValue( u"OpenMode"_ustr ) >>= nDestMode;

    if ( !( nDestMode & embed::ElementModes::WRITE ) )
        throw io::IOException( THROW_WHERE ); // TODO: access_denied

    ReadContents();

    if ( !m_xPackageFolder.is() )
        throw embed::InvalidStorageException( THROW_WHERE );

    for ( const auto& pair : m_aChildrenMap )
        for (auto pElement : pair.second)
        {
            if ( !pElement->m_bIsRemoved )
                CopyStorageElement( pElement, xDest, /*aName*/pair.first, bDirect );
        }

    // move storage properties to the destination one ( means changeable properties )
    if ( m_nStorageType == embed::StorageFormats::PACKAGE )
    {
        xPropSet->setPropertyValue( u"MediaType"_ustr, uno::Any( m_aMediaType ) );
        xPropSet->setPropertyValue( u"Version"_ustr, uno::Any( m_aVersion ) );
    }

    if ( m_nStorageType == embed::StorageFormats::PACKAGE )
    {
        // if this is a root storage, the common key from current one should be moved there
        bool bIsRoot = false;
        if ( ( xPropSet->getPropertyValue( u"IsRoot"_ustr ) >>= bIsRoot ) && bIsRoot )
        {
            try
            {
                uno::Reference< embed::XEncryptionProtectedStorage > xEncr( xDest, uno::UNO_QUERY );
                if ( xEncr.is() )
                {
                    xEncr->setEncryptionData( GetCommonRootEncryptionData().getAsConstNamedValueList() );

                    uno::Sequence< beans::NamedValue > aAlgorithms;
                    uno::Reference< beans::XPropertySet > xPackPropSet( m_xPackage, uno::UNO_QUERY_THROW );
                    xPackPropSet->getPropertyValue( ENCRYPTION_ALGORITHMS_PROPERTY )
                        >>= aAlgorithms;
                    xEncr->setEncryptionAlgorithms( aAlgorithms );
                }
            }
            catchconst packages::NoEncryptionException& )
            {
                TOOLS_INFO_EXCEPTION("package.xstor""No Encryption");
            }
        }
    }
    else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
    {

        // TODO/LATER: currently the optimization is not active
        // uno::Reference< io::XInputStream > xRelInfoStream = GetRelInfoStreamForName( OUString() ); // own stream
        // if ( xRelInfoStream.is() )
        // {
        //  // Relations info stream is a writeonly property, introduced only to optimize copying
        //  // Should be used carefully since no check for stream consistency is done, and the stream must not stay locked

        //  OUString aRelInfoString = "RelationsInfoStream";
        //  xPropSet->setPropertyValue( aRelInfoString, uno::makeAny( GetSeekableTempCopy( xRelInfoStream, m_xFactory ) ) );
        // }

        uno::Reference< embed::XRelationshipAccess > xRels( xDest, uno::UNO_QUERY );
        if ( !xRels.is() )
            throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 );

        xRels->insertRelationships( GetAllRelationshipsIfAny(), false );
    }

    // if possible the destination storage should be committed after successful copying
    uno::Reference< embed::XTransactedObject > xObjToCommit( xDest, uno::UNO_QUERY );
    if ( xObjToCommit.is() )
        xObjToCommit->commit();
}

void OStorage_Impl::CopyStorageElement( SotElement_Impl* pElement,
                                        const uno::Reference< embed::XStorage >& xDest,
                                        const OUString& aName,
                                        bool bDirect )
{
    SAL_WARN_IF( !xDest.is(), "package.xstor""No destination storage!" );
    SAL_WARN_IF( aName.isEmpty(), "package.xstor""Empty element name!" );

    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    uno::Reference< container::XNameAccess > xDestAccess( xDest, uno::UNO_QUERY_THROW );
    if ( xDestAccess->hasByName( aName )
      && !( pElement->m_bIsStorage && xDest->isStorageElement( aName ) ) )
        xDest->removeElement( aName );

    if ( pElement->m_bIsStorage )
    {
        uno::Reference< embed::XStorage > xSubDest =
                                    xDest->openStorageElement(  aName,
                                                                embed::ElementModes::WRITE );

        SAL_WARN_IF( !xSubDest.is(), "package.xstor""No destination substorage!" );

        if (!pElement->m_xStorage)
        {
            OpenSubStorage( pElement, embed::ElementModes::READ );
            if (!pElement->m_xStorage)
                throw io::IOException( THROW_WHERE );
        }

        pElement->m_xStorage->CopyToStorage(xSubDest, bDirect);
    }
    else
    {
        if (!pElement->m_xStream)
        {
            OpenSubStream( pElement );
            if (!pElement->m_xStream)
                throw io::IOException( THROW_WHERE );
        }

        if (!pElement->m_xStream->IsEncrypted())
        {
            if ( bDirect )
            {
                // fill in the properties for the stream
                uno::Sequence< beans::PropertyValue > aStrProps(0);
                const uno::Sequence< beans::PropertyValue > aSrcPkgProps = pElement->m_xStream->GetStreamProperties();
                sal_Int32 nNum = 0;
                for ( const auto& rSrcPkgProp : aSrcPkgProps )
                {
                    if ( rSrcPkgProp.Name == "MediaType" || rSrcPkgProp.Name == "Compressed" )
                    {
                        aStrProps.realloc( ++nNum );
                        auto pStrProps = aStrProps.getArray();
                        pStrProps[nNum-1].Name = rSrcPkgProp.Name;
                        pStrProps[nNum-1].Value = rSrcPkgProp.Value;
                    }
                }

                if ( m_nStorageType == embed::StorageFormats::PACKAGE )
                {
                    aStrProps.realloc( ++nNum );
                    auto pStrProps = aStrProps.getArray();
                    pStrProps[nNum-1].Name = "UseCommonStoragePasswordEncryption";
                    pStrProps[nNum-1].Value <<= pElement->m_xStream->UsesCommonEncryption_Impl();
                }
                else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
                {
                    // TODO/LATER: currently the optimization is not active
                    // uno::Reference< io::XInputStream > xInStream = GetRelInfoStreamForName( OUString() ); // own rels stream
                    // if ( xInStream.is() )
                    // {
                    //  aStrProps.realloc( ++nNum );
                    //  aStrProps[nNum-1].Name = "RelationsInfoStream";
                    //  aStrProps[nNum-1].Value <<= GetSeekableTempCopy( xInStream, m_xFactory );
                    // }

                    uno::Reference< embed::XRelationshipAccess > xRels( xDest, uno::UNO_QUERY );
                    if ( !xRels.is() )
                        throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );

                    xRels->insertRelationships( GetAllRelationshipsIfAny(), false );
                }

                uno::Reference< embed::XOptimizedStorage > xOptDest( xDest, uno::UNO_QUERY_THROW );
                uno::Reference < io::XInputStream > xInputToInsert;

                if (pElement->m_xStream->HasTempFile_Impl() || !pElement->m_xStream->m_xPackageStream.is())
                {
                    SAL_WARN_IF(!pElement->m_xStream->m_xPackageStream.is(), "package.xstor""No package stream!");

                    // if the stream is modified - the temporary file must be used for insertion
                    xInputToInsert = pElement->m_xStream->GetTempFileAsInputStream();
                }
                else
                {
                    // for now get just nonseekable access to the stream
                    // TODO/LATER: the raw stream can be used

                    xInputToInsert = pElement->m_xStream->m_xPackageStream->getDataStream();
                }

                if ( !xInputToInsert.is() )
                        throw io::IOException( THROW_WHERE );

                xOptDest->insertStreamElementDirect( aName, xInputToInsert, aStrProps );
            }
            else
            {
                uno::Reference< io::XStream > xSubStr =
                                            xDest->openStreamElement( aName,
                                            embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
                SAL_WARN_IF( !xSubStr.is(), "package.xstor""No destination substream!" );

                pElement->m_xStream->CopyInternallyTo_Impl(xSubStr);
            }
        }
        else if ( m_nStorageType != embed::StorageFormats::PACKAGE )
        {
            SAL_WARN( "package.xstor""Encryption is only supported in package storage!" );
            throw io::IOException( THROW_WHERE );
        }
        else if ( pElement->m_xStream->HasCachedEncryptionData()
             && ( pElement->m_xStream->IsModified() || pElement->m_xStream->HasWriteOwner_Impl() ) )
        {
            ::comphelper::SequenceAsHashMap aCommonEncryptionData;
            bool bHasCommonEncryptionData = false;
            try
            {
                aCommonEncryptionData = GetCommonRootEncryptionData();
                bHasCommonEncryptionData = true;
            }
            catchconst packages::NoEncryptionException& )
            {
                TOOLS_INFO_EXCEPTION("package.xstor""No Encryption");
            }

            if (bHasCommonEncryptionData && ::package::PackageEncryptionDataLessOrEqual(pElement->m_xStream->GetCachedEncryptionData(), aCommonEncryptionData))
            {
                // If the stream can be opened with the common storage password
                // it must be stored with the common storage password as well
                uno::Reference< io::XStream > xDestStream =
                                            xDest->openStreamElement( aName,
                                                embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );

                pElement->m_xStream->CopyInternallyTo_Impl( xDestStream );

                uno::Reference< beans::XPropertySet > xProps( xDestStream, uno::UNO_QUERY_THROW );
                xProps->setPropertyValue(
                    u"UseCommonStoragePasswordEncryption"_ustr,
                    uno::Any( true ) );
            }
            else
            {
                // the stream is already opened for writing or was changed
                uno::Reference< embed::XStorage2 > xDest2( xDest, uno::UNO_QUERY_THROW );
                uno::Reference< io::XStream > xSubStr =
                                            xDest2->openEncryptedStream( aName,
                                                embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE,
                                                pElement->m_xStream->GetCachedEncryptionData().getAsConstNamedValueList() );
                SAL_WARN_IF( !xSubStr.is(), "package.xstor""No destination substream!" );

                pElement->m_xStream->CopyInternallyTo_Impl(xSubStr, pElement->m_xStream->GetCachedEncryptionData());
            }
        }
        else
        {
            // the stream is not opened at all, so it can be just opened for reading
            try
            {
                // If the stream can be opened with the common storage password
                // it must be stored with the common storage password as well

                uno::Reference< io::XStream > xOwnStream = pElement->m_xStream->GetStream(embed::ElementModes::READ,
                                                                                          false);
                uno::Reference< io::XStream > xDestStream =
                                            xDest->openStreamElement( aName,
                                                embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
                SAL_WARN_IF( !xDestStream.is(), "package.xstor""No destination substream!" );
                completeStorageStreamCopy_Impl( xOwnStream, xDestStream, m_nStorageType, GetAllRelationshipsIfAny() );

                uno::Reference< beans::XPropertySet > xProps( xDestStream, uno::UNO_QUERY_THROW );
                xProps->setPropertyValue(
                    u"UseCommonStoragePasswordEncryption"_ustr,
                    uno::Any( true ) );
            }
            catchconst packages::WrongPasswordException& )
            {
                TOOLS_INFO_EXCEPTION("package.xstor""Handled exception");

                // If the common storage password does not allow to open the stream
                // it could be copied in raw way, the problem is that the StartKey should be the same
                // in the ODF1.2 package, so an invalid package could be produced if the stream
                // is copied from ODF1.1 package, where it is allowed to have different StartKeys
                uno::Reference< embed::XStorageRawAccess > xRawDest( xDest, uno::UNO_QUERY_THROW );
                uno::Reference< io::XInputStream > xRawInStream = pElement->m_xStream->GetRawInStream();
                xRawDest->insertRawEncrStreamElement( aName, xRawInStream );
            }
        }
    }
}

uno::Sequence< uno::Sequence< beans::StringPair > > OStorage_Impl::GetAllRelationshipsIfAny()
{
    if ( m_nStorageType != embed::StorageFormats::OFOPXML )
        return uno::Sequence< uno::Sequence< beans::StringPair > >();

    ReadRelInfoIfNecessary();

    if ( m_nRelInfoStatus != RELINFO_READ
         && m_nRelInfoStatus != RELINFO_CHANGED_STREAM_READ
         && m_nRelInfoStatus != RELINFO_CHANGED )
            throw io::IOException( THROW_WHERE "Wrong relinfo stream!" );
    // m_nRelInfoStatus == RELINFO_CHANGED_BROKEN || m_nRelInfoStatus == RELINFO_BROKEN
    return m_aRelInfo;
}

void OStorage_Impl::CopyLastCommitTo( const uno::Reference< embed::XStorage >& xNewStor )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor""A committed storage is incomplete!" );
    if ( !m_xPackageFolder.is() )
        throw uno::RuntimeException( THROW_WHERE );

    OStorage_Impl aTempRepresent( nullptr,
                                embed::ElementModes::READ,
                                m_xPackageFolder,
                                m_xPackage,
                                m_xContext,
                                m_nStorageType);

    // TODO/LATER: could use direct copying
    aTempRepresent.CopyToStorage( xNewStor, false );
}

void OStorage_Impl::InsertIntoPackageFolder( const OUString& aName,
                                             const uno::Reference< container::XNameContainer >& xParentPackageFolder )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor""An inserted storage is incomplete!" );
    uno::Reference< uno::XInterface > xTmp( m_xPackageFolder, uno::UNO_QUERY_THROW );
    xParentPackageFolder->insertByName( aName, uno::Any( xTmp ) );

    m_bCommited = false;
}

void OStorage_Impl::Commit()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    if ( !m_bIsModified )
        return;

    // in case of a new empty storage it is possible that the contents are still not read
    // ( the storage of course has no contents, but the initialization is postponed till the first use,
    //   thus if a new storage was created and committed immediately it must be initialized here )
    ReadContents();

    // if storage is committed it should have a valid Package representation
    SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor""The package representation should exist!" );
    if ( !m_xPackageFolder.is() )
        throw embed::InvalidStorageException( THROW_WHERE );

    OSL_ENSURE( m_nStorageMode & embed::ElementModes::WRITE,
                "Commit of readonly storage, should be detected before!" );

    uno::Reference< container::XNameContainer > xNewPackageFolder;

    // here the storage will switch to the temporary package folder
    // if the storage was already committed and the parent was not committed after that
    // the switch should not be done since the package folder in use is a temporary one;
    // it can be detected by m_bCommited flag ( root storage doesn't need temporary representation )
    if ( !m_bCommited && !m_bIsRoot )
    {
        uno::Sequence< uno::Any > aSeq{ uno::Any(true) };
        xNewPackageFolder.set( m_xPackage->createInstanceWithArguments( aSeq ),
                               uno::UNO_QUERY );
    }
    else
        xNewPackageFolder = m_xPackageFolder;

    // remove replaced removed elements
    for ( auto& pDeleted : m_aDeletedVector )
    {

        if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pDeleted->m_bIsStorage )
            RemoveStreamRelInfo( pDeleted->m_aOriginalName );

        // the removed elements are not in new temporary storage
        if ( m_bCommited || m_bIsRoot )
            xNewPackageFolder->removeByName( pDeleted->m_aOriginalName );
        delete pDeleted;
        pDeleted = nullptr;
    }
    m_aDeletedVector.clear();

    // remove removed elements
    for (auto mapIt = m_aChildrenMap.begin(); mapIt != m_aChildrenMap.end(); )
    {
        for (auto it = mapIt->second.begin(); it != mapIt->second.end(); )
        {
            // renamed and inserted elements must be really inserted to package later
            // since they can conflict with removed elements
            auto & pElement = *it;
            if ( pElement->m_bIsRemoved )
            {
                if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pElement->m_bIsStorage )
                    RemoveStreamRelInfo( pElement->m_aOriginalName );

                // the removed elements are not in new temporary storage
                if ( m_bCommited || m_bIsRoot )
                    xNewPackageFolder->removeByName( pElement->m_aOriginalName );

                delete pElement;
                it = mapIt->second.erase(it);
            }
            else
                ++it;
        }
        if (mapIt->second.empty())
            mapIt = m_aChildrenMap.erase(mapIt);
        else
            ++mapIt;
    }


    // there should be no more deleted elements
    for ( const auto& pair : m_aChildrenMap )
        for (auto pElement : pair.second)
        {
            // if it is a 'duplicate commit' inserted elements must be really inserted to package later
            // since they can conflict with renamed elements
            if ( !pElement->m_bIsInserted )
            {
                // for now stream is opened in direct mode that means that in case
                // storage is committed all the streams from it are committed in current state.
                // following two steps are separated to allow easily implement transacted mode
                // for streams if we need it in future.
                // Only hierarchical access uses transacted streams currently
                if ( !pElement->m_bIsStorage && pElement->m_xStream
                  && !pElement->m_xStream->IsTransacted() )
                    pElement->m_xStream->Commit();

                // if the storage was not open, there is no need to commit it ???
                // the storage should be checked that it is committed
                if (pElement->m_bIsStorage && pElement->m_xStorage && pElement->m_xStorage->m_bCommited)
                {
                    // it's temporary PackageFolder should be inserted instead of current one
                    // also the new copy of PackageFolder should be used by the children storages

                    // the renamed elements are not in new temporary storage
                    if ( m_bCommited || m_bIsRoot )
                        xNewPackageFolder->removeByName( pElement->m_aOriginalName );

                    pElement->m_xStorage->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder);
                }
                else if (!pElement->m_bIsStorage && pElement->m_xStream && pElement->m_xStream->m_bFlushed)
                {
                    if ( m_nStorageType == embed::StorageFormats::OFOPXML )
                        CommitStreamRelInfo( /*aName*/pair.first, pElement );

                    // the renamed elements are not in new temporary storage
                    if ( m_bCommited || m_bIsRoot )
                        xNewPackageFolder->removeByName( pElement->m_aOriginalName );

                    pElement->m_xStream->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder);
                }
                else if ( !m_bCommited && !m_bIsRoot )
                {
                    // the element must be just copied to the new temporary package folder
                    // the connection with the original package should not be lost just because
                    // the element is still referred by the folder in the original hierarchy
                    uno::Any aPackageElement = m_xPackageFolder->getByName( pElement->m_aOriginalName );
                    xNewPackageFolder->insertByName( /*aName*/pair.first, aPackageElement );
                }
                else if ( pair.first != pElement->m_aOriginalName )
                {
                    // this is the case when xNewPackageFolder refers to m_xPackageFolder
                    // in case the name was changed and it is not a changed storage - rename the element
                    uno::Any aPackageElement = xNewPackageFolder->getByName( pElement->m_aOriginalName );
                    xNewPackageFolder->removeByName( pElement->m_aOriginalName );
                    xNewPackageFolder->insertByName( /*aName*/pair.first, aPackageElement );

                    if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pElement->m_bIsStorage )
                    {
                        if (!pElement->m_xStream)
                        {
                            OpenSubStream( pElement );
                            if (!pElement->m_xStream)
                                throw uno::RuntimeException( THROW_WHERE );
                        }

                        CommitStreamRelInfo( /*aName*/pair.first, pElement );
                    }
                }

                pElement->m_aOriginalName = pair.first;
            }
        }

    for ( const auto& pair : m_aChildrenMap )
        for (auto pElement : pair.second)
        {
            // now inserted elements can be inserted to the package
            if ( pElement->m_bIsInserted )
            {
                pElement->m_aOriginalName = pair.first;

                if ( pElement->m_bIsStorage )
                {
                    OSL_ENSURE(pElement->m_xStorage, "An inserted storage is incomplete!");
                    if (!pElement->m_xStorage)
                        throw uno::RuntimeException( THROW_WHERE );

                    if (pElement->m_xStorage->m_bCommited)
                    {
                        pElement->m_xStorage->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder);

                        pElement->m_bIsInserted = false;
                    }
                }
                else
                {
                    OSL_ENSURE(pElement->m_xStream, "An inserted stream is incomplete!");
                    if (!pElement->m_xStream)
                        throw uno::RuntimeException( THROW_WHERE );

                    if (!pElement->m_xStream->IsTransacted())
                        pElement->m_xStream->Commit();

                    if (pElement->m_xStream->m_bFlushed)
                    {
                        if ( m_nStorageType == embed::StorageFormats::OFOPXML )
                            CommitStreamRelInfo( /*aName*/pair.first, pElement );

                        pElement->m_xStream->InsertIntoPackageFolder( /*aName*/pair.first, xNewPackageFolder );

                        pElement->m_bIsInserted = false;
                    }
                }
            }
        }

    if ( m_nStorageType == embed::StorageFormats::PACKAGE )
    {
        // move properties to the destination package folder
        uno::Reference< beans::XPropertySet > xProps( xNewPackageFolder, uno::UNO_QUERY_THROW );
        xProps->setPropertyValue( u"MediaType"_ustr, uno::Any( m_aMediaType ) );
        xProps->setPropertyValue( u"Version"_ustr, uno::Any( m_aVersion ) );
    }

    if ( m_nStorageType == embed::StorageFormats::OFOPXML )
        CommitRelInfo( xNewPackageFolder ); // store own relations and commit complete relations storage

    if ( m_bIsRoot )
    {
        uno::Reference< util::XChangesBatch > xChangesBatch( m_xPackage, uno::UNO_QUERY_THROW );
        try
        {
            xChangesBatch->commitChanges();
        }
        catchconst lang::WrappedTargetException& r )
        {
            css::uno::Any ex( cppu::getCaughtException() );
            // the wrapped UseBackupException means that the target medium can be corrupted
            embed::UseBackupException aException;
            if ( r.TargetException >>= aException )
            {
                m_xStream.clear();
                m_xInputStream.clear();
                throw aException;
            }

            SAL_INFO("package.xstor""Rethrow: " << exceptionToString(ex));
            throw;
        }
    }
    else if ( !m_bCommited )
    {
        m_xPackageFolder = std::move(xNewPackageFolder);
        m_bCommited = true;
    }

    // after commit the mediatype treated as the correct one
    m_bMTFallbackUsed = false;
}

void OStorage_Impl::Revert()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    if ( !( m_nStorageMode & embed::ElementModes::WRITE ) )
        return// nothing to do

    // all the children must be removed
    // they will be created later on demand

    // rebuild the map - cannot do it in-place, because we're changing some of the key values
    std::unordered_map<OUString, std::vector<SotElement_Impl*>> oldMap;
    std::swap(oldMap, m_aChildrenMap);

    for (const auto & rPair : oldMap)
        for (auto pElement : rPair.second)
        {
            if ( pElement->m_bIsInserted )
                delete pElement;
            else
            {
                ClearElement( pElement );

                pElement->m_bIsRemoved = false;

                m_aChildrenMap[pElement->m_aOriginalName].push_back(pElement);
            }
        }

    // return replaced removed elements
    for ( auto& pDeleted : m_aDeletedVector )
    {
        m_aChildrenMap[pDeleted->m_aOriginalName].push_back(pDeleted);

        ClearElement( pDeleted );

        pDeleted->m_bIsRemoved = false;
    }
    m_aDeletedVector.clear();

    m_bControlMediaType = false;
    m_bControlVersion = false;

    GetStorageProperties();

    if ( m_nStorageType == embed::StorageFormats::OFOPXML )
    {
        // currently the relations storage is changed only on commit
        m_xNewRelInfoStream.clear();
        m_aRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
        m_nRelInfoStatus = RELINFO_NO_INIT;
    }
}

::comphelper::SequenceAsHashMap OStorage_Impl::GetCommonRootEncryptionData()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;

    if ( m_nStorageType != embed::StorageFormats::PACKAGE )
        throw packages::NoEncryptionException( THROW_WHERE );

    if ( m_bIsRoot )
    {
        if ( !m_bHasCommonEncryptionData )
            throw packages::NoEncryptionException( THROW_WHERE );

        return m_aCommonEncryptionData;
    }
    else
    {
        if ( !m_pParent )
            throw packages::NoEncryptionException( THROW_WHERE );

        return m_pParent->GetCommonRootEncryptionData();
    }
}

SotElement_Impl* OStorage_Impl::FindElement( const OUString& rName )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    SAL_WARN_IF( rName.isEmpty(), "package.xstor""Name is empty!" );

    ReadContents();

    auto mapIt = m_aChildrenMap.find(rName);
    if (mapIt == m_aChildrenMap.end() && m_bRepairPackage)
        mapIt = std::find_if(m_aChildrenMap.begin(), m_aChildrenMap.end(),
                             [&rName](const auto& pair)
                             { return rName.equalsIgnoreAsciiCase(pair.first); });
    if (mapIt == m_aChildrenMap.end())
        return nullptr;
    for (auto pElement : mapIt->second)
        if (!pElement->m_bIsRemoved)
            return pElement;

    return nullptr;
}

SotElement_Impl* OStorage_Impl::InsertStream( const OUString& aName, bool bEncr )
{
    SAL_WARN_IF( !m_xPackage.is(), "package.xstor""Not possible to refer to package as to factory!" );
    if ( !m_xPackage.is() )
        throw embed::InvalidStorageException( THROW_WHERE);

    uno::Sequence< uno::Any > aSeq{ uno::Any(false) };
    uno::Reference< uno::XInterface > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ) );

    SAL_WARN_IF( !xNewElement.is(), "package.xstor""Not possible to create a new stream!" );
    if ( !xNewElement.is() )
        throw io::IOException( THROW_WHERE );

    uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xNewElement, uno::UNO_QUERY_THROW );

    OSL_ENSURE( m_nStorageType == embed::StorageFormats::PACKAGE || !bEncr, "Only package storage supports encryption!" );
    if ( m_nStorageType != embed::StorageFormats::PACKAGE && bEncr )
        throw packages::NoEncryptionException( THROW_WHERE );

    // the mode is not needed for storage stream internal implementation
    SotElement_Impl* pNewElement = InsertElement( aName, false );
    pNewElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, bEncr, m_nStorageType, true));

    m_aChildrenMap[aName].push_back( pNewElement );
    m_bIsModified = true;
    m_bBroadcastModified = true;

    return pNewElement;
}

void OStorage_Impl::InsertRawStream( const OUString& aName, const uno::Reference< io::XInputStream >& xInStream )
{
    // insert of raw stream means insert and commit
    SAL_WARN_IF( !m_xPackage.is(), "package.xstor""Not possible to refer to package as to factory!" );
    if ( !m_xPackage.is() )
        throw embed::InvalidStorageException( THROW_WHERE );

    if ( m_nStorageType != embed::StorageFormats::PACKAGE )
        throw packages::NoEncryptionException( THROW_WHERE );

    uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
    uno::Reference< io::XInputStream > xInStrToInsert = xSeek.is() ? xInStream :
                                                                     GetSeekableTempCopy( xInStream );

    uno::Sequence< uno::Any > aSeq{ uno::Any(false) };
    uno::Reference< uno::XInterface > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ) );

    SAL_WARN_IF( !xNewElement.is(), "package.xstor""Not possible to create a new stream!" );
    if ( !xNewElement.is() )
        throw io::IOException( THROW_WHERE );

    uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xNewElement, uno::UNO_QUERY_THROW );
    xPackageSubStream->setRawStream( xInStrToInsert );

    // the mode is not needed for storage stream internal implementation
    SotElement_Impl* pNewElement = InsertElement( aName, false );
    pNewElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, true, m_nStorageType, false));
    // the stream is inserted and must be treated as a committed one
    pNewElement->m_xStream->SetToBeCommited();

    m_aChildrenMap[aName].push_back( pNewElement );
    m_bIsModified = true;
    m_bBroadcastModified = true;
}

std::unique_ptr<OStorage_Impl> OStorage_Impl::CreateNewStorageImpl( sal_Int32 nStorageMode )
{
    SAL_WARN_IF( !m_xPackage.is(), "package.xstor""Not possible to refer to package as to factory!" );
    if ( !m_xPackage.is() )
        throw embed::InvalidStorageException( THROW_WHERE );

    uno::Sequence< uno::Any > aSeq{ uno::Any(true) };
    uno::Reference< uno::XInterface > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ) );

    SAL_WARN_IF( !xNewElement.is(), "package.xstor""Not possible to create a new storage!" );
    if ( !xNewElement.is() )
        throw io::IOException( THROW_WHERE );

    uno::Reference< container::XNameContainer > xPackageSubFolder( xNewElement, uno::UNO_QUERY_THROW );
    std::unique_ptr<OStorage_Impl> pResult(
            new OStorage_Impl( this, nStorageMode, xPackageSubFolder, m_xPackage, m_xContext, m_nStorageType ));
    pResult->m_bIsModified = true;

    return pResult;
}

SotElement_Impl* OStorage_Impl::InsertStorage( const OUString& aName, sal_Int32 nStorageMode )
{
    SotElement_Impl* pNewElement = InsertElement( aName, true );

    pNewElement->m_xStorage = CreateNewStorageImpl(nStorageMode);

    m_aChildrenMap[aName].push_back( pNewElement );

    return pNewElement;
}

SotElement_Impl* OStorage_Impl::InsertElement( const OUString& aName, bool bIsStorage )
{
    assert( FindElement(aName) == nullptr && "Should not try to insert existing element");

    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    SotElement_Impl* pDeletedElm = nullptr;

    auto it = m_aChildrenMap.find(aName);
    if (it != m_aChildrenMap.end())
        for (auto pElement : it->second)
        {
            SAL_WARN_IF( !pElement->m_bIsRemoved, "package.xstor""Try to insert an element instead of existing one!" );
            if ( pElement->m_bIsRemoved )
            {
                SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor""Inserted elements must be deleted immediately!" );
                pDeletedElm = pElement;
            }
        }

    if ( pDeletedElm )
    {
        if ( pDeletedElm->m_bIsStorage )
            OpenSubStorage( pDeletedElm, embed::ElementModes::READWRITE );
        else
            OpenSubStream( pDeletedElm );

        auto & rVec = m_aChildrenMap[aName];
        std::erase(rVec, pDeletedElm);
        if (rVec.empty())
            m_aChildrenMap.erase(aName);
        m_aDeletedVector.push_back( pDeletedElm );
    }

    // create new element
    return new SotElement_Impl( aName, bIsStorage, true );
}

void OStorage_Impl::OpenSubStorage( SotElement_Impl* pElement, sal_Int32 nStorageMode )
{
    assert(pElement && "pElement is not set!");
    SAL_WARN_IF( !pElement->m_bIsStorage, "package.xstor""Storage flag is not set!" );

    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    if (!pElement->m_xStorage)
    {
        SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor""Inserted element must be created already!" );

        uno::Reference< uno::XInterface > xTmp;
        m_xPackageFolder->getByName( pElement->m_aOriginalName ) >>= xTmp;
        if ( !xTmp.is() )
            throw container::NoSuchElementException( THROW_WHERE );

        uno::Reference< container::XNameContainer > xPackageSubFolder( xTmp, uno::UNO_QUERY_THROW );
        pElement->m_xStorage.reset(new OStorage_Impl(this, nStorageMode, xPackageSubFolder, m_xPackage, m_xContext, m_nStorageType));
    }
}

void OStorage_Impl::OpenSubStream( SotElement_Impl* pElement )
{
    assert(pElement && "pElement is not set!");
    SAL_WARN_IF( pElement->m_bIsStorage, "package.xstor""Storage flag is set!" );

    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    if (pElement->m_xStream)
        return;

    SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor""Inserted element must be created already!" );

    uno::Reference< uno::XInterface > xTmp;
    m_xPackageFolder->getByName( pElement->m_aOriginalName ) >>= xTmp;
    if ( !xTmp.is() )
        throw container::NoSuchElementException( THROW_WHERE );

    uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xTmp, uno::UNO_QUERY_THROW );

    // the stream can never be inserted here, because inserted stream element holds the stream till commit or destruction
    pElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, false, m_nStorageType, false, GetRelInfoStreamForName(pElement->m_aOriginalName)));
}

uno::Sequence< OUString > OStorage_Impl::GetElementNames()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    ReadContents();

    sal_Int32 nCnt = 0;
    for ( const auto& pair : m_aChildrenMap )
        for (auto pElement : pair.second)
        {
            if ( !pElement->m_bIsRemoved )
                nCnt++;
        }

    uno::Sequence<OUString> aElementNames(nCnt);
    OUString* pArray = aElementNames.getArray();
    for ( const auto& pair : m_aChildrenMap )
        for (auto pElement : pair.second)
        {
            if ( !pElement->m_bIsRemoved )
                *pArray++ = pair.first;
        }

    return aElementNames;
}

void OStorage_Impl::RemoveElement( OUString const & rName, SotElement_Impl* pElement )
{
    assert(pElement);

    if ( (pElement->m_xStorage && ( pElement->m_xStorage->m_pAntiImpl || !pElement->m_xStorage->m_aReadOnlyWrapVector.empty() ))
      || (pElement->m_xStream && ( pElement->m_xStream->m_pAntiImpl || !pElement->m_xStream->m_aInputStreamsVector.empty() )) )
        throw io::IOException( THROW_WHERE ); // TODO: Access denied

    auto mapIt = m_aChildrenMap.find(rName);
    for (auto it = mapIt->second.begin(); it != mapIt->second.end(); ++it)
        if (pElement == *it)
        {
            if ( pElement->m_bIsInserted )
            {
                delete pElement;
                std::erase(mapIt->second, pElement);
                if (mapIt->second.empty())
                    m_aChildrenMap.erase(mapIt);
            }
            else
            {
                pElement->m_bIsRemoved = true;
                ClearElement( pElement );
            }
            return;
        }
    assert(false && "not found");

    // TODO/OFOPXML: the rel stream should be removed as well
}

void OStorage_Impl::ClearElement( SotElement_Impl* pElement )
{
    pElement->m_xStorage.reset();
    pElement->m_xStream.reset();
}

void OStorage_Impl::CloneStreamElement( const OUString& aStreamName,
                                        bool bEncryptionDataProvided,
                                        const ::comphelper::SequenceAsHashMap& aEncryptionData,
                                        uno::Reference< io::XStream >& xTargetStream )
{
    SotElement_Impl *pElement = FindElement( aStreamName );
    if ( !pElement )
    {
        // element does not exist, throw exception
        throw io::IOException( THROW_WHERE ); // TODO: access_denied
    }
    else if ( pElement->m_bIsStorage )
        throw io::IOException( THROW_WHERE );

    if (!pElement->m_xStream)
        OpenSubStream( pElement );

    if (!pElement->m_xStream || !pElement->m_xStream->m_xPackageStream.is())
        throw io::IOException( THROW_WHERE ); // TODO: general_error

    // the existence of m_pAntiImpl of the child is not interesting,
    // the copy will be created internally

    // usual copying is not applicable here, only last flushed version of the
    // child stream should be used for copying. Probably the children m_xPackageStream
    // can be used as a base of a new stream, that would be copied to result
    // storage. The only problem is that some package streams can be accessed from outside
    // at the same time (now solved by wrappers that remember own position).

    if (bEncryptionDataProvided)
        pElement->m_xStream->GetCopyOfLastCommit(xTargetStream, aEncryptionData);
    else
        pElement->m_xStream->GetCopyOfLastCommit(xTargetStream);
}

void OStorage_Impl::RemoveStreamRelInfo( std::u16string_view aOriginalName )
{
    // this method should be used only in OStorage_Impl::Commit() method
    // the aOriginalName can be empty, in this case the storage relation info should be removed

    if ( m_nStorageType == embed::StorageFormats::OFOPXML && m_xRelStorage.is() )
    {
        OUString aRelStreamName = OUString::Concat(aOriginalName) + ".rels";

        if ( m_xRelStorage->hasByName( aRelStreamName ) )
            m_xRelStorage->removeElement( aRelStreamName );
    }
}

void OStorage_Impl::CreateRelStorage()
{
    if ( m_nStorageType != embed::StorageFormats::OFOPXML )
        return;

    if ( m_xRelStorage.is() )
        return;

    if ( !m_pRelStorElement )
    {
        m_pRelStorElement = new SotElement_Impl( u"_rels"_ustr, truetrue );
        m_pRelStorElement->m_xStorage = CreateNewStorageImpl(embed::ElementModes::WRITE);
        m_pRelStorElement->m_xStorage->m_pParent = nullptr; // the relation storage is completely controlled by parent
    }

    if (!m_pRelStorElement->m_xStorage)
        OpenSubStorage( m_pRelStorElement, embed::ElementModes::WRITE );

    if (!m_pRelStorElement->m_xStorage)
        throw uno::RuntimeException( THROW_WHERE );

    m_xRelStorage = new OStorage(m_pRelStorElement->m_xStorage.get(), false);
}

void OStorage_Impl::CommitStreamRelInfo( std::u16string_view rName, SotElement_Impl const * pStreamElement )
{
    // this method should be used only in OStorage_Impl::Commit() method

    // the stream element must be provided
    if ( !pStreamElement )
        throw uno::RuntimeException( THROW_WHERE );

    if (m_nStorageType == embed::StorageFormats::OFOPXML && pStreamElement->m_xStream)
    {
        SAL_WARN_IF( rName.empty(), "package.xstor""The name must not be empty!" );

        if ( !m_xRelStorage.is() )
        {
            // Create new rels storage, this is commit scenario so it must be possible
            CreateRelStorage();
        }

        pStreamElement->m_xStream->CommitStreamRelInfo(m_xRelStorage, pStreamElement->m_aOriginalName, rName);
    }
}

uno::Reference< io::XInputStream > OStorage_Impl::GetRelInfoStreamForName(
    std::u16string_view aName )
{
    if ( m_nStorageType == embed::StorageFormats::OFOPXML )
    {
        ReadContents();
        if ( m_xRelStorage.is() )
        {
            OUString aRelStreamName = OUString::Concat(aName) + ".rels";
            if ( m_xRelStorage->hasByName( aRelStreamName ) )
            {
                uno::Reference< io::XStream > xStream = m_xRelStorage->openStreamElement( aRelStreamName, embed::ElementModes::READ );
                if ( xStream.is() )
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=96 G=94

¤ Dauer der Verarbeitung: 0.17 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.