Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  owriteablestream.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 <sal/config.h>

#include <cassert>
#include <memory>
#include <sal/log.hxx>

#include <com/sun/star/packages/NoEncryptionException.hpp>
#include <com/sun/star/packages/WrongPasswordException.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/XTypeProvider.hpp>
#include <com/sun/star/io/NotConnectedException.hpp>
#include <com/sun/star/io/TempFile.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/IOException.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/StorageFormats.hpp>
#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <cppuhelper/typeprovider.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <osl/diagnose.h>

#include <comphelper/processfactory.hxx>
#include <comphelper/servicehelper.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/ofopxmlhelper.hxx>
#include <comphelper/refcountedmutex.hxx>
#include <comphelper/sequence.hxx>

#include <comphelper/diagnose_ex.hxx>

#include <PackageConstants.hxx>
#include <utility>

#include "selfterminatefilestream.hxx"
#include "owriteablestream.hxx"
#include "oseekinstream.hxx"
#include "xstorage.hxx"

// since the copying uses 32000 blocks usually, it makes sense to have a smaller size
#define MAX_STORCACHE_SIZE 30000

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

namespace package
{

static void CopyInputToOutput(
    const css::uno::Reference< css::io::XInputStream >& xInput,
    SvStream& rOutput )
{
    static const sal_Int32 nConstBufferSize = 32000;

    if (auto pByteReader = dynamic_cast< comphelper::ByteReader* >( xInput.get() ))
    {
        sal_Int32 nRead;
        sal_Int8 aTempBuf[ nConstBufferSize ];
        do
        {
            nRead = pByteReader->readSomeBytes ( aTempBuf, nConstBufferSize );
            rOutput.WriteBytes ( aTempBuf, nRead );
        }
        while ( nRead == nConstBufferSize );
    }
    else
    {
        sal_Int32 nRead;
        uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize );

        do
        {
            nRead = xInput->readBytes ( aSequence, nConstBufferSize );
            rOutput.WriteBytes ( aSequence.getConstArray(), nRead );
        }
        while ( nRead == nConstBufferSize );
    }
}

bool PackageEncryptionDataLessOrEqual( const ::comphelper::SequenceAsHashMap& aHash1, const ::comphelper::SequenceAsHashMap& aHash2 )
{
    // tdf#93389: aHash2 may contain more than in aHash1, if it contains also data for other package
    // formats (as in case of autorecovery)
    bool bResult = !aHash1.empty() && aHash1.size() <= aHash2.size();
    for ( ::comphelper::SequenceAsHashMap::const_iterator aIter = aHash1.begin();
          bResult && aIter != aHash1.end();
          ++aIter )
    {
        uno::Sequence< sal_Int8 > aKey1;
        bResult = ( ( aIter->second >>= aKey1 ) && aKey1.hasElements() );
        if ( bResult )
        {
            const uno::Sequence< sal_Int8 > aKey2 = aHash2.getUnpackedValueOrDefault( aIter->first.maString, uno::Sequence< sal_Int8 >() );
            bResult = aKey1.getLength() == aKey2.getLength()
                && std::equal(std::cbegin(aKey1), std::cend(aKey1), aKey2.begin(), aKey2.end());
        }
    }

    return bResult;
}

// namespace package

namespace
{

void SetEncryptionKeyProperty_Impl( const uno::Reference< beans::XPropertySet >& xPropertySet,
                                    const uno::Sequence< beans::NamedValue >& aKey )
{
    SAL_WARN_IF( !xPropertySet.is(), "package.xstor""No property set is provided!" );
    if ( !xPropertySet.is() )
        throw uno::RuntimeException();

    try {
        xPropertySet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY, uno::Any( aKey ) );
    }
    catch ( const uno::Exception& ex )
    {
        TOOLS_WARN_EXCEPTION( "package.xstor""Can't write encryption related properties");
        throw io::IOException(ex.Message); // TODO
    }
}

uno::Any GetEncryptionKeyProperty_Impl( const uno::Reference< beans::XPropertySet >& ;xPropertySet )
{
    SAL_WARN_IF( !xPropertySet.is(), "package.xstor""No property set is provided!" );
    if ( !xPropertySet.is() )
        throw uno::RuntimeException();

    try {
        return xPropertySet->getPropertyValue(STORAGE_ENCRYPTION_KEYS_PROPERTY);
    }
    catch ( const uno::Exception& ex )
    {
        TOOLS_WARN_EXCEPTION( "package.xstor""Can't get encryption related properties");
        throw io::IOException(ex.Message); // TODO
    }
}

bool SequencesEqual( const uno::Sequence< sal_Int8 >& aSequence1, const uno::Sequence< sal_Int8 >& aSequence2 )
{
    return aSequence1.getLength() == aSequence2.getLength()
        && std::equal(aSequence1.begin(), aSequence1.end(), aSequence2.begin(), aSequence2.end());
}

bool SequencesEqual( const uno::Sequence< beans::NamedValue >& aSequence1, const uno::Sequence< beans::NamedValue >& aSequence2 )
{
    if ( aSequence1.getLength() != aSequence2.getLength() )
        return false;

    for ( const auto& rProp1 : aSequence1 )
    {
        bool bHasMember = false;
        uno::Sequence< sal_Int8 > aMember1;
        sal_Int32 nMember1 = 0;
        if ( rProp1.Value >>= aMember1 )
        {
            for ( const auto& rProp2 : aSequence2 )
            {
                if ( rProp1.Name == rProp2.Name )
                {
                    bHasMember = true;

                    uno::Sequence< sal_Int8 > aMember2;
                    if ( !( rProp2.Value >>= aMember2 ) || !SequencesEqual( aMember1, aMember2 ) )
                        return false;
                }
            }
        }
        else if ( rProp1.Value >>= nMember1 )
        {
            for ( const auto& rProp2 : aSequence2 )
            {
                if ( rProp1.Name == rProp2.Name )
                {
                    bHasMember = true;

                    sal_Int32 nMember2 = 0;
                    if ( !( rProp2.Value >>= nMember2 ) || nMember1 != nMember2 )
                        return false;
                }
            }
        }
        else
            return false;

        if ( !bHasMember )
            return false;
    }

    return true;
}

uno::Reference< io::XStream > CreateMemoryStream( const uno::Reference< uno::XComponentContext >& rContext )
{
    static constexpr OUStringLiteral sName(u"com.sun.star.comp.MemoryStream");
    return uno::Reference< io::XStream >(
        rContext->getServiceManager()->createInstanceWithContext(sName, rContext),
        uno::UNO_QUERY_THROW);
}

const beans::StringPair* lcl_findPairByName(const uno::Sequence<beans::StringPair>&&nbsp;rSeq, const OUString& rName)
{
    return std::find_if(rSeq.begin(), rSeq.end(),
        [&rName](const beans::StringPair& rPair) { return rPair.First == rName; });
}

// anonymous namespace

OWriteStream_Impl::OWriteStream_Impl( OStorage_Impl* pParent,
                                      const uno::Reference< packages::XDataSinkEncrSupport >& xPackageStream,
                                      const uno::Reference< lang::XSingleServiceFactory >& xPackage,
                                      uno::Reference< uno::XComponentContext > xContext,
                                      bool bForceEncrypted,
                                      sal_Int32 nStorageType,
                                      bool bDefaultCompress,
                                      uno::Reference< io::XInputStream > xRelInfoStream )
: m_xMutex( new comphelper::RefCountedMutex )
, m_pAntiImpl( nullptr )
, m_bHasDataToFlush( false )
, m_bFlushed( false )
, m_xPackageStream( xPackageStream )
, m_xContext(std::move( xContext ))
, m_pParent( pParent )
, m_bForceEncrypted( bForceEncrypted )
, m_bUseCommonEncryption( !bForceEncrypted && nStorageType == embed::StorageFormats::PACKAGE )
, m_bHasCachedEncryptionData( false )
, m_bCompressedSetExplicit( !bDefaultCompress )
, m_xPackage( xPackage )
, m_bHasInsertedStreamOptimization( false )
, m_nStorageType( nStorageType )
, m_xOrigRelInfoStream(std::move( xRelInfoStream ))
, m_bOrigRelInfoBroken( false )
, m_nRelInfoStatus( RELINFO_NO_INIT )
, m_nRelId( 1 )
{
    SAL_WARN_IF( !xPackageStream.is(), "package.xstor""No package stream is provided!" );
    SAL_WARN_IF( !xPackage.is(), "package.xstor""No package component is provided!" );
    SAL_WARN_IF( !m_xContext.is(), "package.xstor""No package stream is provided!" );
    OSL_ENSURE( pParent, "No parent storage is provided!" );
    OSL_ENSURE( m_nStorageType == embed::StorageFormats::OFOPXML || !m_xOrigRelInfoStream.is(), "The Relations info makes sense only for OFOPXML format!" );
}

OWriteStream_Impl::~OWriteStream_Impl()
{
    DisposeWrappers();

    m_oTempFile.reset();

    CleanCacheStream();
}

void OWriteStream_Impl::CleanCacheStream()
{
    if ( !m_xCacheStream.is() )
        return;

    try
    {
        uno::Reference< io::XInputStream > xInputCache = m_xCacheStream->getInputStream();
        if ( xInputCache.is() )
            xInputCache->closeInput();
    }
    catchconst uno::Exception& )
    {}

    try
    {
        uno::Reference< io::XOutputStream > xOutputCache = m_xCacheStream->getOutputStream();
        if ( xOutputCache.is() )
            xOutputCache->closeOutput();
    }
    catchconst uno::Exception& )
    {}

    m_xCacheStream.clear();
    m_xCacheSeek.clear();
}

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

    SAL_WARN_IF( !m_bFlushed, "package.xstor""This method must not be called for nonflushed streams!" );
    if ( m_bFlushed )
    {
        SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""An inserted stream is incomplete!" );
        uno::Reference< uno::XInterface > xTmp( m_xPackageStream, uno::UNO_QUERY_THROW );
        xParentPackageFolder->insertByName( aName, uno::Any( xTmp ) );

        m_bFlushed = false;
        m_bHasInsertedStreamOptimization = false;
    }
}
bool OWriteStream_Impl::IsEncrypted()
{
    if ( m_nStorageType != embed::StorageFormats::PACKAGE )
        return false;

    if ( m_bForceEncrypted || m_bHasCachedEncryptionData )
        return true;

    if ( m_oTempFile.has_value() || m_xCacheStream.is() )
        return false;

    GetStreamProperties();

    // the following value can not be cached since it can change after root commit
    bool bWasEncr = false;
    uno::Reference< beans::XPropertySet > xPropSet( m_xPackageStream, uno::UNO_QUERY );
    if ( xPropSet.is() )
    {
        uno::Any aValue = xPropSet->getPropertyValue(u"WasEncrypted"_ustr);
        if ( !( aValue >>= bWasEncr ) )
        {
            SAL_WARN( "package.xstor""The property WasEncrypted has wrong type!" );
        }
    }

    bool bToBeEncr = false;
    for (const auto& rProp : m_aProps)
    {
        if ( rProp.Name == "Encrypted" )
        {
            if ( !( rProp.Value >>= bToBeEncr ) )
            {
                SAL_WARN( "package.xstor""The property has wrong type!" );
            }
        }
    }

    // since a new key set to the package stream it should not be removed except the case when
    // the stream becomes nonencrypted
    uno::Sequence< beans::NamedValue > aKey;
    if ( bToBeEncr )
        GetEncryptionKeyProperty_Impl( xPropSet ) >>= aKey;

    // If the properties must be investigated the stream is either
    // was never changed or was changed, the parent was committed
    // and the stream was closed.
    // That means that if it is intended to use common storage key
    // it is already has no encryption but is marked to be stored
    // encrypted and the key is empty.
    if ( !bWasEncr && bToBeEncr && !aKey.hasElements() )
    {
        // the stream is intended to use common storage password
        m_bUseCommonEncryption = true;
        return false;
    }
    else
        return bToBeEncr;
}

void OWriteStream_Impl::SetDecrypted()
{
    SAL_WARN_IF( m_nStorageType != embed::StorageFormats::PACKAGE, "package.xstor""The encryption is supported only for package storages!" );
    if ( m_nStorageType != embed::StorageFormats::PACKAGE )
        throw uno::RuntimeException();

    GetStreamProperties();

    // let the stream be modified
    FillTempGetFileName();
    m_bHasDataToFlush = true;

    // remove encryption
    m_bForceEncrypted = false;
    m_bHasCachedEncryptionData = false;
    m_aEncryptionData.clear();

    for ( auto& rProp : asNonConstRange(m_aProps) )
    {
        if ( rProp.Name == "Encrypted" )
            rProp.Value <<= false;
    }
}

void OWriteStream_Impl::SetEncrypted( const ::comphelper::SequenceAsHashMap& aEncryptionData )
{
    SAL_WARN_IF( m_nStorageType != embed::StorageFormats::PACKAGE, "package.xstor""The encryption is supported only for package storages!" );
    if ( m_nStorageType != embed::StorageFormats::PACKAGE )
        throw uno::RuntimeException();

    if ( aEncryptionData.empty() )
        throw uno::RuntimeException();

    GetStreamProperties();

    // let the stream be modified
    FillTempGetFileName();
    m_bHasDataToFlush = true;

    // introduce encryption info
    for ( auto& rProp : asNonConstRange(m_aProps) )
    {
        if ( rProp.Name == "Encrypted" )
            rProp.Value <<= true;
    }

    m_bUseCommonEncryption = false// very important to set it to false

    m_bHasCachedEncryptionData = true;
    m_aEncryptionData = aEncryptionData;
}

void OWriteStream_Impl::DisposeWrappers()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
    if ( m_pAntiImpl )
    {
        try {
            m_pAntiImpl->dispose();
        }
        catch ( const uno::RuntimeException& )
        {
            TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");
        }

        m_pAntiImpl = nullptr;
    }
    m_pParent = nullptr;

    if ( m_aInputStreamsVector.empty() )
        return;

    for ( auto& pStream : m_aInputStreamsVector )
    {
        if ( pStream )
        {
            pStream->InternalDispose();
            pStream = nullptr;
        }
    }

    m_aInputStreamsVector.clear();
}

void OWriteStream_Impl::GetFilledTempFileIfNo( const uno::Reference< io::XInputStream >&&nbsp;xStream )
{
    if ( !m_oTempFile.has_value() )
    {
        m_oTempFile.emplace();

        try {
            if ( xStream.is() )
            {
                // the current position of the original stream should be still OK, copy further
                package::CopyInputToOutput( xStream, *m_oTempFile->GetStream(StreamMode::READWRITE) );
            }
        }
        catchconst packages::WrongPasswordException& )
        {
            TOOLS_INFO_EXCEPTION("package.xstor""Rethrow");
            m_oTempFile.reset();
            throw;
        }
        catchconst uno::Exception& )
        {
            TOOLS_INFO_EXCEPTION("package.xstor""Rethrow");
            m_oTempFile.reset();
            throw;
        }

        if ( m_oTempFile.has_value() )
            CleanCacheStream();
    }
}

void OWriteStream_Impl::FillTempGetFileName()
{
    // should try to create cache first, if the amount of contents is too big, the temp file should be taken
    if ( !m_xCacheStream.is() && !m_oTempFile.has_value() )
    {
        uno::Reference< io::XInputStream > xOrigStream = m_xPackageStream->getDataStream();
        if ( !xOrigStream.is() )
        {
            // in case of new inserted package stream it is possible that input stream still was not set
            uno::Reference< io::XStream > xCacheStream = CreateMemoryStream( m_xContext );
            SAL_WARN_IF( !xCacheStream.is(), "package.xstor""If the stream can not be created an exception must be thrown!" );
            m_xCacheSeek.set( xCacheStream, uno::UNO_QUERY_THROW );
            m_xCacheStream = std::move(xCacheStream);
        }
        else
        {
            sal_Int32 nRead = 0;
            uno::Sequence< sal_Int8 > aData( MAX_STORCACHE_SIZE + 1 );
            nRead = xOrigStream->readBytes( aData, MAX_STORCACHE_SIZE + 1 );
            if ( aData.getLength() > nRead )
                aData.realloc( nRead );

            if ( nRead <= MAX_STORCACHE_SIZE )
            {
                uno::Reference< io::XStream > xCacheStream = CreateMemoryStream( m_xContext );
                SAL_WARN_IF( !xCacheStream.is(), "package.xstor""If the stream can not be created an exception must be thrown!" );

                if ( nRead )
                {
                    uno::Reference< io::XOutputStream > xOutStream( xCacheStream->getOutputStream(), uno::UNO_SET_THROW );
                    xOutStream->writeBytes( aData );
                }
                m_xCacheSeek.set( xCacheStream, uno::UNO_QUERY_THROW );
                m_xCacheStream = std::move(xCacheStream);
                m_xCacheSeek->seek( 0 );
            }
            else if ( !m_oTempFile.has_value() )
            {
                m_oTempFile.emplace();

                try {
                    // copy stream contents to the file
                    SvStream* pStream = m_oTempFile->GetStream(StreamMode::READWRITE);
                    pStream->WriteBytes( aData.getConstArray(), aData.getLength() );

                    // the current position of the original stream should be still OK, copy further
                    package::CopyInputToOutput( xOrigStream, *pStream );
                }
                catchconst packages::WrongPasswordException& )
                {
                    m_oTempFile.reset();
                    throw;
                }
                catchconst uno::Exception& )
                {
                    m_oTempFile.reset();
                }
            }
        }
    }
}

uno::Reference< io::XStream > OWriteStream_Impl::GetTempFileAsStream()
{
    uno::Reference< io::XStream > xTempStream;

    if ( !m_xCacheStream.is() )
    {
        if ( !m_oTempFile.has_value() )
            FillTempGetFileName();

        if ( m_oTempFile.has_value() )
        {
            // the temporary file is not used if the cache is used
            try
            {
                SvStream* pStream = m_oTempFile->GetStream(StreamMode::READWRITE);
                pStream->Seek(0);
                xTempStream = new utl::OStreamWrapper(pStream, /*bOwner*/false);
            }
            catchconst uno::Exception& )
            {
                TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");
            }
        }
    }

    if ( m_xCacheStream.is() )
        xTempStream = m_xCacheStream;

    // the method must always return a stream
    // in case the stream can not be open
    // an exception should be thrown
    if ( !xTempStream.is() )
        throw io::IOException(u"no temp stream"_ustr); //TODO:

    return xTempStream;
}

uno::Reference< io::XInputStream > OWriteStream_Impl::GetTempFileAsInputStream()
{
    uno::Reference< io::XInputStream > xInputStream;

    if ( !m_xCacheStream.is() )
    {
        if ( !m_oTempFile.has_value() )
            FillTempGetFileName();

        if ( m_oTempFile.has_value() )
        {
            // the temporary file is not used if the cache is used
            try
            {
                SvStream* pStream = m_oTempFile->GetStream(StreamMode::READWRITE);
                pStream->Seek(0);
                xInputStream = new utl::OStreamWrapper(pStream, /*bOwner*/false);
            }
            catchconst uno::Exception& )
            {
                TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");
            }
        }
    }

    if ( m_xCacheStream.is() )
        xInputStream = m_xCacheStream->getInputStream();

    // the method must always return a stream
    // in case the stream can not be open
    // an exception should be thrown
    if ( !xInputStream.is() )
        throw io::IOException(); // TODO:

    return xInputStream;
}

void OWriteStream_Impl::InsertStreamDirectly( const uno::Reference< io::XInputStream >&&nbsp;xInStream,
                                              const uno::Sequence< beans::PropertyValue >& aProps )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;

    // this call can be made only during parent storage commit
    // the  parent storage is responsible for the correct handling
    // of deleted and renamed contents

    SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""No package stream is set!" );

    if ( m_bHasDataToFlush )
        throw io::IOException(u"m_bHasDataToFlush==true"_ustr);

    OSL_ENSURE( !m_oTempFile.has_value() && !m_xCacheStream.is(), "The temporary must not exist!" );

    // use new file as current persistent representation
    // the new file will be removed after it's stream is closed
    m_xPackageStream->setDataStream( xInStream );

    // copy properties to the package stream
    uno::Reference< beans::XPropertySet > xPropertySet( m_xPackageStream, uno::UNO_QUERY_THROW );

    // The storage-package communication has a problem
    // the storage caches properties, thus if the package changes one of them itself
    // the storage does not know about it

    // Depending from MediaType value the package can change the compressed property itself
    // Thus if Compressed property is provided it must be set as the latest one
    bool bCompressedIsSet = false;
    bool bCompressed = false;
    OUString aComprPropName( u"Compressed"_ustr );
    OUString aMedTypePropName( u"MediaType"_ustr );
    for ( const auto& rProp : aProps )
    {
        if ( rProp.Name == aComprPropName )
        {
            bCompressedIsSet = true;
            rProp.Value >>= bCompressed;
        }
        else if ( ( m_nStorageType == embed::StorageFormats::OFOPXML || m_nStorageType == embed::StorageFormats::PACKAGE )
               && rProp.Name == aMedTypePropName )
        {
            xPropertySet->setPropertyValue( rProp.Name, rProp.Value );
        }
        else if ( m_nStorageType == embed::StorageFormats::PACKAGE && rProp.Name == "UseCommonStoragePasswordEncryption" )
            rProp.Value >>= m_bUseCommonEncryption;
        else
            throw lang::IllegalArgumentException();

        // if there are cached properties update them
        if ( rProp.Name == aMedTypePropName || rProp.Name == aComprPropName )
            for ( auto& rMemProp : asNonConstRange(m_aProps) )
            {
                if ( rProp.Name == rMemProp.Name )
                    rMemProp.Value = rProp.Value;
            }
    }

    if ( bCompressedIsSet )
    {
        xPropertySet->setPropertyValue( aComprPropName, uno::Any( bCompressed ) );
        m_bCompressedSetExplicit = true;
    }

    if ( m_bUseCommonEncryption )
    {
        if ( m_nStorageType != embed::StorageFormats::PACKAGE )
            throw uno::RuntimeException();

        // set to be encrypted but do not use encryption key
        xPropertySet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY,
                                        uno::Any( uno::Sequence< beans::NamedValue >() ) );
        xPropertySet->setPropertyValue( u"Encrypted"_ustr, uno::Any( true ) );
    }

    // the stream should be free soon, after package is stored
    m_bHasDataToFlush = false;
    m_bFlushed = true// will allow to use transaction on stream level if will need it
    m_bHasInsertedStreamOptimization = true;
}

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

    SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""No package stream is set!" );

    if ( !m_bHasDataToFlush )
        return;

    uno::Reference< packages::XDataSinkEncrSupport > xNewPackageStream;
    uno::Sequence< uno::Any > aSeq{ uno::Any(false) };

    if ( m_xCacheStream.is() )
    {
        if ( m_pAntiImpl )
            m_pAntiImpl->DeInit();

        uno::Reference< io::XInputStream > xInStream( m_xCacheStream->getInputStream(), uno::UNO_SET_THROW );

        xNewPackageStream.set( m_xPackage->createInstanceWithArguments( aSeq ), uno::UNO_QUERY_THROW );

        xNewPackageStream->setDataStream( xInStream );

        m_xCacheStream.clear();
        m_xCacheSeek.clear();

    }
    else if ( m_oTempFile.has_value() )
    {
        if ( m_pAntiImpl )
            m_pAntiImpl->DeInit();

        rtl::Reference< OSelfTerminateFileStream > xInStream;
        try
        {
            xInStream = new OSelfTerminateFileStream(m_xContext, std::move(*m_oTempFile));
        }
        catchconst uno::Exception& )
        {
            TOOLS_WARN_EXCEPTION("package""");
        }

        if ( !xInStream.is() )
            throw io::IOException();

        xNewPackageStream.set( m_xPackage->createInstanceWithArguments( aSeq ), uno::UNO_QUERY_THROW );

        // TODO/NEW: Let the temporary file be removed after commit
        xNewPackageStream->setDataStream( xInStream );
        m_oTempFile.reset();
    }
    else // if ( m_bHasInsertedStreamOptimization )
    {
        // if the optimization is used the stream can be accessed directly
        xNewPackageStream = m_xPackageStream;
    }

    // copy properties to the package stream
    uno::Reference< beans::XPropertySet > xPropertySet( xNewPackageStream, uno::UNO_QUERY_THROW );

    for ( auto& rProp : asNonConstRange(m_aProps) )
    {
        if ( rProp.Name == "Size" )
        {
            if ( m_pAntiImpl && !m_bHasInsertedStreamOptimization && m_pAntiImpl->m_xSeekable.is() )
            {
                rProp.Value <<= m_pAntiImpl->m_xSeekable->getLength();
                xPropertySet->setPropertyValue( rProp.Name, rProp.Value );
            }
        }
        else
            xPropertySet->setPropertyValue( rProp.Name, rProp.Value );
    }

    if ( m_bUseCommonEncryption )
    {
        if ( m_nStorageType != embed::StorageFormats::PACKAGE )
            throw uno::RuntimeException();

        // set to be encrypted but do not use encryption key
        xPropertySet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY,
                                        uno::Any( uno::Sequence< beans::NamedValue >() ) );
        xPropertySet->setPropertyValue( u"Encrypted"_ustr,
                                        uno::Any( true ) );
    }
    else if ( m_bHasCachedEncryptionData )
    {
        if ( m_nStorageType != embed::StorageFormats::PACKAGE )
            throw uno::RuntimeException();

        xPropertySet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY,
                                        uno::Any( m_aEncryptionData.getAsConstNamedValueList() ) );
    }

    // the stream should be free soon, after package is stored
    m_xPackageStream = std::move(xNewPackageStream);
    m_bHasDataToFlush = false;
    m_bFlushed = true// will allow to use transaction on stream level if will need it
}

void OWriteStream_Impl::Revert()
{
    // can be called only from parent storage
    // means complete reload of the stream

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

    if ( !m_bHasDataToFlush )
        return// nothing to do

    OSL_ENSURE( m_oTempFile.has_value() || m_xCacheStream.is(), "The temporary must exist!" );

    if ( m_xCacheStream.is() )
    {
        m_xCacheStream.clear();
        m_xCacheSeek.clear();
    }

    m_oTempFile.reset();

    m_aProps.realloc( 0 );

    m_bHasDataToFlush = false;

    m_bUseCommonEncryption = true;
    m_bHasCachedEncryptionData = false;
    m_aEncryptionData.clear();

    if ( m_nStorageType != embed::StorageFormats::OFOPXML )
        return;

    // currently the relations storage is changed only on commit
    m_xNewRelInfoStream.clear();
    m_aNewRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
    if ( m_xOrigRelInfoStream.is() )
    {
        // the original stream is still here, that means that it was not parsed
        m_aOrigRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
        m_nRelInfoStatus = RELINFO_NO_INIT;
    }
    else
    {
        // the original stream was already parsed
        if ( !m_bOrigRelInfoBroken )
            m_nRelInfoStatus = RELINFO_READ;
        else
            m_nRelInfoStatus = RELINFO_BROKEN;
    }
}

uno::Sequence< beans::PropertyValue > const & OWriteStream_Impl::GetStreamProperties()
{
    if ( !m_aProps.hasElements() )
        m_aProps = ReadPackageStreamProperties();

    return m_aProps;
}

uno::Sequence< beans::PropertyValue > OWriteStream_Impl::InsertOwnProps(
                                                                    const uno::Sequence< beans::PropertyValue >& aProps,
                                                                    bool bUseCommonEncryption )
{
    uno::Sequence< beans::PropertyValue > aResult( aProps );
    beans::PropertyValue aPropVal;

    if ( m_nStorageType == embed::StorageFormats::PACKAGE )
    {
        aPropVal.Name = "UseCommonStoragePasswordEncryption";
        aPropVal.Value <<= bUseCommonEncryption;
    }
    else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
    {
        ReadRelInfoIfNecessary();

        aPropVal.Name = "RelationsInfo";
        if ( m_nRelInfoStatus == RELINFO_READ )
            aPropVal.Value <<= m_aOrigRelInfo;
        else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ || m_nRelInfoStatus == RELINFO_CHANGED )
            aPropVal.Value <<= m_aNewRelInfo;
        else // m_nRelInfoStatus == RELINFO_CHANGED_BROKEN || m_nRelInfoStatus == RELINFO_BROKEN
            throw io::IOException( u"Wrong relinfo stream!"_ustr );
    }
    if (!aPropVal.Name.isEmpty())
    {
        sal_Int32 i = 0;
        for (auto p = aResult.getConstArray(); i < aResult.getLength(); ++i)
            if (p[i].Name == aPropVal.Name)
                break;
        if (i == aResult.getLength())
            aResult.realloc(i + 1);
        aResult.getArray()[i] = std::move(aPropVal);
    }

    return aResult;
}

bool OWriteStream_Impl::IsTransacted()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;
    return ( m_pAntiImpl && m_pAntiImpl->m_bTransacted );
}

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

    if ( m_nRelInfoStatus == RELINFO_NO_INIT )
    {
        try
        {
            // Init from original stream
            if ( m_xOrigRelInfoStream.is() )
                m_aOrigRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(
                                        m_xOrigRelInfoStream,
                                        u"_rels/*.rels",
                                        m_xContext );

            // in case of success the stream must be thrown away, that means that the OrigRelInfo is initialized
            // the reason for this is that the original stream might not be seekable ( at the same time the new
            // provided stream must be seekable ), so it must be read only once
            m_xOrigRelInfoStream.clear();
            m_nRelInfoStatus = RELINFO_READ;
        }
        catchconst uno::Exception& )
        {
            TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");

            m_nRelInfoStatus = RELINFO_BROKEN;
            m_bOrigRelInfoBroken = true;
        }
    }
    else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
    {
        // Init from the new stream
        try
        {
            if ( m_xNewRelInfoStream.is() )
                m_aNewRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence(
                                        m_xNewRelInfoStream,
                                        u"_rels/*.rels",
                                        m_xContext );

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

uno::Sequence< beans::PropertyValue > OWriteStream_Impl::ReadPackageStreamProperties()
{
    sal_Int32 nPropNum = 0;
    if ( m_nStorageType == embed::StorageFormats::ZIP )
        nPropNum = 2;
    else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
        nPropNum = 3;
    else if ( m_nStorageType == embed::StorageFormats::PACKAGE )
        nPropNum = 4;
    assert(nPropNum >= 2);
    uno::Sequence< beans::PropertyValue > aResult( nPropNum );
    auto aResultRange = asNonConstRange(aResult);

    // The "Compressed" property must be set after "MediaType" property,
    // since the setting of the last one can change the value of the first one
    static constexpr OUStringLiteral sMediaType = u"MediaType";
    static constexpr OUString sCompressed = u"Compressed"_ustr;
    static constexpr OUString sSize = u"Size"_ustr;
    static constexpr OUStringLiteral sEncrypted = u"Encrypted";
    if ( m_nStorageType == embed::StorageFormats::OFOPXML || m_nStorageType == embed::StorageFormats::PACKAGE )
    {
        aResultRange[0].Name = sMediaType;
        aResultRange[1].Name = sCompressed;
        aResultRange[2].Name = sSize;

        if ( m_nStorageType == embed::StorageFormats::PACKAGE )
            aResultRange[3].Name = sEncrypted;
    }
    else
    {
        aResultRange[0].Name = sCompressed;
        aResultRange[1].Name = sSize;
    }

    // TODO: may be also raw stream should be marked

    uno::Reference< beans::XPropertySet > xPropSet( m_xPackageStream, uno::UNO_QUERY_THROW );
    for ( auto& rProp : aResultRange )
    {
        try {
            rProp.Value = xPropSet->getPropertyValue( rProp.Name );
        }
        catchconst uno::Exception& )
        {
            TOOLS_WARN_EXCEPTION( "package.xstor""A property can't be retrieved" );
        }
    }

    return aResult;
}

void OWriteStream_Impl::CopyInternallyTo_Impl( const uno::Reference< io::XStream >& xDestStream,
                                                const ::comphelper::SequenceAsHashMap& aEncryptionData )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;

    SAL_WARN_IF( m_bUseCommonEncryption, "package.xstor""The stream can not be encrypted!" );

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

    if ( m_pAntiImpl )
    {
        m_pAntiImpl->CopyToStreamInternally_Impl( xDestStream );
    }
    else
    {
        uno::Reference< io::XStream > xOwnStream = GetStream( embed::ElementModes::READ, aEncryptionData, false );
        if ( !xOwnStream.is() )
            throw io::IOException(); // TODO

        OStorage_Impl::completeStorageStreamCopy_Impl( xOwnStream, xDestStream, m_nStorageType, GetAllRelationshipsIfAny() );
    }

    uno::Reference< embed::XEncryptionProtectedSource2 > xEncr( xDestStream, uno::UNO_QUERY );
    if ( xEncr.is() )
        xEncr->setEncryptionData( aEncryptionData.getAsConstNamedValueList() );
}

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

    ReadRelInfoIfNecessary();

    if ( m_nRelInfoStatus == RELINFO_READ )
        return m_aOrigRelInfo;
    else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ || m_nRelInfoStatus == RELINFO_CHANGED )
        return m_aNewRelInfo;
    else // m_nRelInfoStatus == RELINFO_CHANGED_BROKEN || m_nRelInfoStatus == RELINFO_BROKEN
            throw io::IOException( u"Wrong relinfo stream!"_ustr );
}

void OWriteStream_Impl::CopyInternallyTo_Impl( const uno::Reference< io::XStream >& xDestStream )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;

    if ( m_pAntiImpl )
    {
        m_pAntiImpl->CopyToStreamInternally_Impl( xDestStream );
    }
    else
    {
        uno::Reference< io::XStream > xOwnStream = GetStream( embed::ElementModes::READ, false );
        if ( !xOwnStream.is() )
            throw io::IOException(); // TODO

        OStorage_Impl::completeStorageStreamCopy_Impl( xOwnStream, xDestStream, m_nStorageType, GetAllRelationshipsIfAny() );
    }
}

uno::Reference< io::XStream > OWriteStream_Impl::GetStream( sal_Int32 nStreamMode, const ::comphelper::SequenceAsHashMap& aEncryptionData, bool bHierarchyAccess )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;

    SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""No package stream is set!" );

    if ( m_pAntiImpl )
        throw io::IOException(); // TODO:

    if ( !IsEncrypted() )
        throw packages::NoEncryptionException();

    uno::Reference< io::XStream > xResultStream;

    uno::Reference< beans::XPropertySet > xPropertySet( m_xPackageStream, uno::UNO_QUERY_THROW );

    if ( m_bHasCachedEncryptionData )
    {
        if ( !::package::PackageEncryptionDataLessOrEqual( m_aEncryptionData, aEncryptionData ) )
            throw packages::WrongPasswordException();

        // the correct key must be set already
        xResultStream = GetStream_Impl( nStreamMode, bHierarchyAccess );
    }
    else
    {
        SetEncryptionKeyProperty_Impl( xPropertySet, aEncryptionData.getAsConstNamedValueList() );

        try {
            xResultStream = GetStream_Impl( nStreamMode, bHierarchyAccess );

            m_bUseCommonEncryption = false// very important to set it to false
            m_bHasCachedEncryptionData = true;
            m_aEncryptionData = aEncryptionData;
        }
        catchconst packages::WrongPasswordException& )
        {
            TOOLS_INFO_EXCEPTION("package.xstor""Rethrow");
            SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
            throw;
        }
        catch ( const uno::Exception& ex )
        {
            TOOLS_WARN_EXCEPTION("package.xstor""GetStream: decrypting stream failed");
            SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
            throw io::IOException(ex.Message); // TODO:
        }
    }

    SAL_WARN_IF( !xResultStream.is(), "package.xstor""In case stream can not be retrieved an exception must be thrown!" );

    return xResultStream;
}

uno::Reference< io::XStream > OWriteStream_Impl::GetStream( sal_Int32 nStreamMode, bool bHierarchyAccess )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;

    SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""No package stream is set!" );

    if ( m_pAntiImpl )
        throw io::IOException(); // TODO:

    uno::Reference< io::XStream > xResultStream;

    if ( IsEncrypted() )
    {
        ::comphelper::SequenceAsHashMap aGlobalEncryptionData;
        try
        {
            aGlobalEncryptionData = GetCommonRootEncryptionData();
        }
        catchconst packages::NoEncryptionException& )
        {
            TOOLS_INFO_EXCEPTION("package.xstor""Rethrow");
            throw packages::WrongPasswordException();
        }

        xResultStream = GetStream( nStreamMode, aGlobalEncryptionData, bHierarchyAccess );
    }
    else
        xResultStream = GetStream_Impl( nStreamMode, bHierarchyAccess );

    return xResultStream;
}

uno::Reference< io::XStream > OWriteStream_Impl::GetStream_Impl( sal_Int32 nStreamMode, bool bHierarchyAccess )
{
    // private method, no mutex is used
    GetStreamProperties();

    // TODO/LATER: this info might be read later, on demand in future
    ReadRelInfoIfNecessary();

    if ( ( nStreamMode & embed::ElementModes::READWRITE ) == embed::ElementModes::READ )
    {
        uno::Reference< io::XInputStream > xInStream;
        if ( m_xCacheStream.is() || m_oTempFile.has_value() )
            xInStream = GetTempFileAsInputStream(); //TODO:
        else
            xInStream = m_xPackageStream->getDataStream();

        // The stream does not exist in the storage
        if ( !xInStream.is() )
            throw io::IOException();

        rtl::Reference<OInputCompStream> pStream = new OInputCompStream( *this, xInStream, InsertOwnProps( m_aProps, m_bUseCommonEncryption ), m_nStorageType );
        m_aInputStreamsVector.push_back( pStream.get() );
        return pStream;
    }
    else if ( ( nStreamMode & embed::ElementModes::READWRITE ) == embed::ElementModes::SEEKABLEREAD )
    {
        if ( !m_xCacheStream.is() && !m_oTempFile.has_value() && !( m_xPackageStream->getDataStream().is() ) )
        {
            // The stream does not exist in the storage
            throw io::IOException();
        }

        uno::Reference< io::XInputStream > xInStream = GetTempFileAsInputStream(); //TODO:

        if ( !xInStream.is() )
            throw io::IOException();

        rtl::Reference<OInputSeekStream> pStream = new OInputSeekStream( *this, xInStream, InsertOwnProps( m_aProps, m_bUseCommonEncryption ), m_nStorageType );
        m_aInputStreamsVector.push_back( pStream.get() );
        return pStream;
    }
    else if ( ( nStreamMode & embed::ElementModes::WRITE ) == embed::ElementModes::WRITE )
    {
        if ( !m_aInputStreamsVector.empty() )
            throw io::IOException(); // TODO:

        uno::Reference< io::XStream > xStream;
        if ( ( nStreamMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE )
        {
            m_oTempFile.reset();
            if ( m_xCacheStream.is() )
                CleanCacheStream();

            m_bHasDataToFlush = true;

            // this call is triggered by the parent and it will recognize the change of the state
            if ( m_pParent )
                m_pParent->m_bIsModified = true;

            xStream = CreateMemoryStream( m_xContext );
            m_xCacheSeek.set( xStream, uno::UNO_QUERY_THROW );
            m_xCacheStream = xStream;
        }
        else if ( !m_bHasInsertedStreamOptimization )
        {
            if ( !m_oTempFile.has_value() && !m_xCacheStream.is() && !( m_xPackageStream->getDataStream().is() ) )
            {
                // The stream does not exist in the storage
                m_bHasDataToFlush = true;

                // this call is triggered by the parent and it will recognize the change of the state
                if ( m_pParent )
                    m_pParent->m_bIsModified = true;
                xStream = GetTempFileAsStream();
            }

            // if the stream exists the temporary file is created on demand
            // xStream = GetTempFileAsStream();
        }

        rtl::Reference<OWriteStream> tmp;
        assert(m_xMutex.is() && "No mutex!");
        if ( !xStream.is() )
            tmp = new OWriteStream( *this, bHierarchyAccess );
        else
            tmp = new OWriteStream( *this, xStream, bHierarchyAccess );

        m_pAntiImpl = tmp.get();
        return tmp;
    }

    throw lang::IllegalArgumentException(); // TODO
}

uno::Reference< io::XInputStream > OWriteStream_Impl::GetPlainRawInStream()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;

    SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""No package stream is set!" );

    // this method is used only internally, this stream object should not go outside of this implementation
    // if ( m_pAntiImpl )
    //  throw io::IOException(); // TODO:

    return m_xPackageStream->getPlainRawStream();
}

uno::Reference< io::XInputStream > OWriteStream_Impl::GetRawInStream()
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ;

    SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""No package stream is set!" );

    if ( m_pAntiImpl )
        throw io::IOException(); // TODO:

    SAL_WARN_IF( !IsEncrypted(), "package.xstor""Impossible to get raw representation for nonencrypted stream!" );
    if ( !IsEncrypted() )
        throw packages::NoEncryptionException();

    return m_xPackageStream->getRawStream();
}

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

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

    return m_pParent->GetCommonRootEncryptionData();
}

void OWriteStream_Impl::InputStreamDisposed( OInputCompStream* pStream )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );
    std::erase(m_aInputStreamsVector, pStream);
}

void OWriteStream_Impl::CreateReadonlyCopyBasedOnData( const uno::Reference< io::XInputStream >& xDataToCopy, const uno::Sequence< beans::PropertyValue >& aProps, uno::Reference< io::XStream >& xTargetStream )
{
    uno::Reference < io::XStream > xTempFile;
    if ( !xTargetStream.is() )
        xTempFile = new utl::TempFileFastService;
    else
        xTempFile = xTargetStream;

    uno::Reference < io::XSeekable > xTempSeek( xTempFile, uno::UNO_QUERY_THROW );

    uno::Reference < io::XOutputStream > xTempOut(xTempFile->getOutputStream(), uno::UNO_SET_THROW);

    if ( xDataToCopy.is() )
        ::comphelper::OStorageHelper::CopyInputToOutput( xDataToCopy, xTempOut );

    xTempOut->closeOutput();
    xTempSeek->seek( 0 );

    uno::Reference< io::XInputStream > xInStream = xTempFile->getInputStream();
    if ( !xInStream.is() )
        throw io::IOException();

    // TODO: remember last state of m_bUseCommonEncryption
    if ( !xTargetStream.is() )
        xTargetStream.set(
            new OInputSeekStream( xInStream, InsertOwnProps( aProps, m_bUseCommonEncryption ), m_nStorageType ) );
}

void OWriteStream_Impl::GetCopyOfLastCommit( uno::Reference< io::XStream >& xTargetStream )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""The source stream for copying is incomplete!" );
    if ( !m_xPackageStream.is() )
        throw uno::RuntimeException();

    uno::Reference< io::XInputStream > xDataToCopy;
    if ( IsEncrypted() )
    {
        // an encrypted stream must contain input stream
        ::comphelper::SequenceAsHashMap aGlobalEncryptionData;
        try
        {
            aGlobalEncryptionData = GetCommonRootEncryptionData();
        }
        catchconst packages::NoEncryptionException& )
        {
            TOOLS_INFO_EXCEPTION("package.xstor""No Element");
            throw packages::WrongPasswordException();
        }

        GetCopyOfLastCommit( xTargetStream, aGlobalEncryptionData );
    }
    else
    {
        xDataToCopy = m_xPackageStream->getDataStream();

        // in case of new inserted package stream it is possible that input stream still was not set
        GetStreamProperties();

        CreateReadonlyCopyBasedOnData( xDataToCopy, m_aProps, xTargetStream );
    }
}

void OWriteStream_Impl::GetCopyOfLastCommit( uno::Reference< io::XStream >& xTargetStream, const ::comphelper::SequenceAsHashMap& aEncryptionData )
{
    ::osl::MutexGuard aGuard( m_xMutex->GetMutex() );

    SAL_WARN_IF( !m_xPackageStream.is(), "package.xstor""The source stream for copying is incomplete!" );
    if ( !m_xPackageStream.is() )
        throw uno::RuntimeException();

    if ( !IsEncrypted() )
        throw packages::NoEncryptionException();

    uno::Reference< io::XInputStream > xDataToCopy;

    if ( m_bHasCachedEncryptionData )
    {
        // TODO: introduce last committed cashed password information and use it here
        // that means "use common pass" also should be remembered on flash
        uno::Sequence< beans::NamedValue > aKey = aEncryptionData.getAsConstNamedValueList();

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

        bool bEncr = false;
        xProps->getPropertyValue( u"Encrypted"_ustr ) >>= bEncr;
        if ( !bEncr )
            throw packages::NoEncryptionException();

        uno::Sequence< beans::NamedValue > aPackKey;
        xProps->getPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY ) >>= aPackKey;
        if ( !SequencesEqual( aKey, aPackKey ) )
            throw packages::WrongPasswordException();

        // the correct key must be set already
        xDataToCopy = m_xPackageStream->getDataStream();
    }
    else
    {
        uno::Reference< beans::XPropertySet > xPropertySet( m_xPackageStream, uno::UNO_QUERY );
        SetEncryptionKeyProperty_Impl( xPropertySet, aEncryptionData.getAsConstNamedValueList() );

        try {
            xDataToCopy = m_xPackageStream->getDataStream();

            if ( !xDataToCopy.is() )
            {
                SAL_WARN( "package.xstor""Encrypted ZipStream must already have input stream inside!" );
                SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
            }
        }
        catchconst uno::Exception& )
        {
            TOOLS_WARN_EXCEPTION( "package.xstor""Can't open encrypted stream");
            SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
            throw;
        }

        SetEncryptionKeyProperty_Impl( xPropertySet, uno::Sequence< beans::NamedValue >() );
    }

    // in case of new inserted package stream it is possible that input stream still was not set
    GetStreamProperties();

    CreateReadonlyCopyBasedOnData( xDataToCopy, m_aProps, xTargetStream );
}

void OWriteStream_Impl::CommitStreamRelInfo( const uno::Reference< embed::XStorage >&&nbsp;xRelStorage, std::u16string_view aOrigStreamName, std::u16string_view aNewStreamName )
{
    // at this point of time the old stream must be already cleaned
    OSL_ENSURE( m_nStorageType == embed::StorageFormats::OFOPXML, "The method should be used only with OFOPXML format!" );

    if ( m_nStorageType != embed::StorageFormats::OFOPXML )
        return;

    OSL_ENSURE( !aOrigStreamName.empty() && !aNewStreamName.empty() && xRelStorage.is(),
                "Wrong relation persistence information is provided!" );

    if ( !xRelStorage.is() || aOrigStreamName.empty() || aNewStreamName.empty() )
        throw uno::RuntimeException();

    if ( m_nRelInfoStatus == RELINFO_BROKEN || m_nRelInfoStatus == RELINFO_CHANGED_BROKEN )
        throw io::IOException(); // TODO:

    OUString aOrigRelStreamName = OUString::Concat(aOrigStreamName) + ".rels";
    OUString aNewRelStreamName = OUString::Concat(aNewStreamName) + ".rels";

    bool bRenamed = aOrigRelStreamName != aNewRelStreamName;
    if ( m_nRelInfoStatus == RELINFO_CHANGED
      || m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ
      || m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
    {
        if ( bRenamed && xRelStorage->hasByName( aOrigRelStreamName ) )
            xRelStorage->removeElement( aOrigRelStreamName );

        if ( m_nRelInfoStatus == RELINFO_CHANGED )
        {
            if ( m_aNewRelInfo.hasElements() )
            {
                uno::Reference< io::XStream > xRelsStream =
                    xRelStorage->openStreamElement( aNewRelStreamName,
                                                      embed::ElementModes::TRUNCATE | embed::ElementModes::READWRITE );

                uno::Reference< io::XOutputStream > xOutStream = xRelsStream->getOutputStream();
                if ( !xOutStream.is() )
                    throw uno::RuntimeException();

                ::comphelper::OFOPXMLHelper::WriteRelationsInfoSequence( xOutStream, m_aNewRelInfo, m_xContext );

                // set the mediatype
                uno::Reference< beans::XPropertySet > xPropSet( xRelsStream, uno::UNO_QUERY_THROW );
                xPropSet->setPropertyValue(
                    u"MediaType"_ustr,
                    uno::Any( u"application/vnd.openxmlformats-package.relationships+xml"_ustr ) );

                m_nRelInfoStatus = RELINFO_READ;
            }
        }
        else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ
                  || m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
        {
            uno::Reference< io::XStream > xRelsStream =
                xRelStorage->openStreamElement( aNewRelStreamName,
                                                    embed::ElementModes::TRUNCATE | embed::ElementModes::READWRITE );

            uno::Reference< io::XOutputStream > xOutputStream = xRelsStream->getOutputStream();
            if ( !xOutputStream.is() )
                throw uno::RuntimeException();

            uno::Reference< io::XSeekable > xSeek( m_xNewRelInfoStream, uno::UNO_QUERY_THROW );
            xSeek->seek( 0 );
            ::comphelper::OStorageHelper::CopyInputToOutput( m_xNewRelInfoStream, xOutputStream );
            xSeek->seek( 0 );

            // set the mediatype
            uno::Reference< beans::XPropertySet > xPropSet( xRelsStream, uno::UNO_QUERY_THROW );
            xPropSet->setPropertyValue(u"MediaType"_ustr,
                uno::Any( u"application/vnd.openxmlformats-package.relationships+xml"_ustr ) );

            if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM )
                m_nRelInfoStatus = RELINFO_NO_INIT;
            else
            {
                // the information is already parsed and the stream is stored, no need in temporary stream any more
                m_xNewRelInfoStream.clear();
                m_nRelInfoStatus = RELINFO_READ;
            }
        }

        // the original stream makes no sense after this step
        m_xOrigRelInfoStream = m_xNewRelInfoStream;
        m_aOrigRelInfo = m_aNewRelInfo;
        m_bOrigRelInfoBroken = false;
        m_aNewRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >();
        m_xNewRelInfoStream.clear();
    }
    else
    {
        // the stream is not changed but it might be renamed
        if ( bRenamed && xRelStorage->hasByName( aOrigRelStreamName ) )
            xRelStorage->renameElement( aOrigRelStreamName, aNewRelStreamName );
    }
}

// OWriteStream implementation

OWriteStream::OWriteStream( OWriteStream_Impl& rImpl, bool bTransacted )
: m_pImpl( &rImpl )
, m_xSharedMutex( rImpl.m_xMutex )
, m_aListenersContainer( rImpl.m_xMutex->GetMutex() )
, m_nStorageType( m_pImpl->m_nStorageType )
, m_bInStreamDisconnected( false )
, m_bInitOnDemand( true )
, m_nInitPosition( 0 )
, m_bTransacted( bTransacted )
{
}

OWriteStream::OWriteStream( OWriteStream_Impl& rImpl, uno::Reference< io::XStream > const & xStream, bool bTransacted )
: m_pImpl( &rImpl )
, m_xSharedMutex( rImpl.m_xMutex )
, m_aListenersContainer( rImpl.m_xMutex->GetMutex() )
, m_nStorageType( m_pImpl->m_nStorageType )
, m_bInStreamDisconnected( false )
, m_bInitOnDemand( false )
, m_nInitPosition( 0 )
, m_bTransacted( bTransacted )
{
    if ( xStream.is() )
    {
        m_xInStream = xStream->getInputStream();
        m_xOutStream = xStream->getOutputStream();
        m_xSeekable.set( xStream, uno::UNO_QUERY );
        OSL_ENSURE( m_xInStream.is() && m_xOutStream.is() && m_xSeekable.is(), "Stream implementation is incomplete!" );
    }
}

OWriteStream::~OWriteStream()
{
    ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );
    if ( m_pImpl )
    {
        osl_atomic_increment(&m_refCount);
        try {
            dispose();
        }
        catchconst uno::RuntimeException& )
        {
            TOOLS_INFO_EXCEPTION("package.xstor""Quiet exception");
        }
    }
}

void OWriteStream::DeInit()
{
    if ( !m_pImpl )
        return// do nothing

    if ( m_xSeekable.is() )
        m_nInitPosition = m_xSeekable->getPosition();

    m_xInStream.clear();
    m_xOutStream.clear();
    m_xSeekable.clear();
    m_bInitOnDemand = true;
}

void OWriteStream::CheckInitOnDemand()
{
    if ( !m_pImpl )
    {
        SAL_INFO("package.xstor""Disposed!");
        throw lang::DisposedException();
    }

    if ( !m_bInitOnDemand )
        return;

    SAL_INFO( "package.xstor""package (mv76033) OWriteStream::CheckInitOnDemand, initializing" );
    uno::Reference< io::XStream > xStream = m_pImpl->GetTempFileAsStream();
    if ( xStream.is() )
    {
        m_xInStream.set( xStream->getInputStream(), uno::UNO_SET_THROW );
        m_xOutStream.set( xStream->getOutputStream(), uno::UNO_SET_THROW );
        m_xSeekable.set( xStream, uno::UNO_QUERY_THROW );
        m_xSeekable->seek( m_nInitPosition );

        m_nInitPosition = 0;
        m_bInitOnDemand = false;
    }
}

void OWriteStream::CopyToStreamInternally_Impl( const uno::Reference< io::XStream >& ;xDest )
{
    ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );

    CheckInitOnDemand();

    if ( !m_xInStream.is() )
        throw uno::RuntimeException();

    if ( !m_xSeekable.is() )
        throw uno::RuntimeException();

    uno::Reference< beans::XPropertySet > xDestProps( xDest, uno::UNO_QUERY_THROW );

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

    sal_Int64 nCurPos = m_xSeekable->getPosition();
    m_xSeekable->seek( 0 );

    uno::Exception eThrown;
    bool bThrown = false;
    try {
        ::comphelper::OStorageHelper::CopyInputToOutput( m_xInStream, xDestOutStream );
    }
    catch ( const uno::Exception& e )
    {
        eThrown = e;
        bThrown = true;
    }

    // position-related section below is critical
    // if it fails the stream will become invalid
    try {
        m_xSeekable->seek( nCurPos );
    }
    catch ( const uno::Exception& )
    {
        // TODO: set the stream in invalid state or dispose
        TOOLS_WARN_EXCEPTION( "package.xstor""The stream become invalid during copying" );
        throw uno::RuntimeException();
    }

    if ( bThrown )
        throw eThrown;

    // now the properties can be copied
    // the order of the properties setting is not important for StorageStream API
    OUString aPropName (u"Compressed"_ustr);
    xDestProps->setPropertyValue( aPropName, getPropertyValue( aPropName ) );
    if ( m_nStorageType == embed::StorageFormats::PACKAGE || m_nStorageType == embed::StorageFormats::OFOPXML )
    {
        aPropName = "MediaType";
        xDestProps->setPropertyValue( aPropName, getPropertyValue( aPropName ) );

        if ( m_nStorageType == embed::StorageFormats::PACKAGE )
        {
            aPropName = "UseCommonStoragePasswordEncryption";
            xDestProps->setPropertyValue( aPropName, getPropertyValue( aPropName ) );
        }
    }
}

void OWriteStream::ModifyParentUnlockMutex_Impl(osl::ClearableMutexGuard& aGuard)
{
    if ( m_pImpl->m_pParent )
    {
        if ( m_pImpl->m_pParent->HasModifiedListener() )
        {
            uno::Reference< util::XModifiable > xParentModif( static_cast<util::XModifiable*>(m_pImpl->m_pParent->m_pAntiImpl) );
            aGuard.clear();
            xParentModif->setModified( true );
        }
        else
            m_pImpl->m_pParent->m_bIsModified = true;
    }
}

uno::Any SAL_CALL OWriteStream::queryInterface( const uno::Type& rType )
{
    // common interfaces
    uno::Any aReturn = ::cppu::queryInterface
                (   rType
                    ,   static_cast<lang::XTypeProvider*> ( this )
                    ,   static_cast<io::XInputStream*> ( this )
                    ,   static_cast<io::XOutputStream*> ( this )
                    ,   static_cast<io::XStream*> ( this )
                    ,   static_cast<embed::XExtendedStorageStream*> ( this )
                    ,   static_cast<io::XSeekable*> ( this )
                    ,   static_cast<io::XTruncate*> ( this )
                    ,   static_cast<lang::XComponent*> ( this )
                    ,   static_cast<beans::XPropertySet*> ( this ) );

    if ( aReturn.hasValue() )
        return aReturn ;

    if ( m_nStorageType == embed::StorageFormats::PACKAGE )
    {
        aReturn = ::cppu::queryInterface
                    (   rType
                        ,   static_cast<embed::XEncryptionProtectedSource2*> ( this )
                        ,   static_cast<embed::XEncryptionProtectedSource*> ( this ) );
    }
    else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
    {
        aReturn = ::cppu::queryInterface
                    (   rType
                        ,   static_cast<embed::XRelationshipAccess*> ( this ) );
    }

    if ( aReturn.hasValue() )
        return aReturn ;

    if ( m_bTransacted )
    {
        aReturn = ::cppu::queryInterface
                    (   rType
                        ,   static_cast<embed::XTransactedObject*> ( this )
                        ,   static_cast<embed::XTransactionBroadcaster*> ( this ) );

        if ( aReturn.hasValue() )
            return aReturn ;
    }

    return OWeakObject::queryInterface( rType );
}

void SAL_CALL OWriteStream::acquire() noexcept
{
    OWeakObject::acquire();
}

void SAL_CALL OWriteStream::release() noexcept
{
    OWeakObject::release();
}

uno::Sequence< uno::Type > SAL_CALL OWriteStream::getTypes()
{
    if (! m_oTypeCollection)
    {
        ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() );

        if (! m_oTypeCollection)
        {
            if ( m_bTransacted )
            {
                if ( m_nStorageType == embed::StorageFormats::PACKAGE )
                {
                    ::cppu::OTypeCollection aTmpCollection
                                    (   cppu::UnoType<lang::XTypeProvider>::get()
                                    ,   cppu::UnoType<io::XInputStream>::get()
                                    ,   cppu::UnoType<io::XOutputStream>::get()
                                    ,   cppu::UnoType<io::XStream>::get()
                                    ,   cppu::UnoType<io::XSeekable>::get()
                                    ,   cppu::UnoType<io::XTruncate>::get()
                                    ,   cppu::UnoType<lang::XComponent>::get()
                                    ,   cppu::UnoType<embed::XEncryptionProtectedSource2>::get()
                                    ,   cppu::UnoType<embed::XEncryptionProtectedSource>::get()
                                    ,   cppu::UnoType<embed::XExtendedStorageStream>::get()
                                    ,   cppu::UnoType<embed::XTransactedObject>::get()
                                    ,   cppu::UnoType<embed::XTransactionBroadcaster>::get());

                    m_oTypeCollection.emplace(
                                        cppu::UnoType<beans::XPropertySet>::get()
                                    ,   aTmpCollection.getTypes());
                }
                else if ( m_nStorageType == embed::StorageFormats::OFOPXML )
                {
                    m_oTypeCollection.emplace(
                                        cppu::UnoType<lang::XTypeProvider>::get()
                                    ,   cppu::UnoType<io::XInputStream>::get()
                                    ,   cppu::UnoType<io::XOutputStream>::get()
                                    ,   cppu::UnoType<io::XStream>::get()
                                    ,   cppu::UnoType<io::XSeekable>::get()
                                    ,   cppu::UnoType<io::XTruncate>::get()
                                    ,   cppu::UnoType<lang::XComponent>::get()
                                    ,   cppu::UnoType<embed::XRelationshipAccess>::get()
                                    ,   cppu::UnoType<embed::XExtendedStorageStream>::get()
                                    ,   cppu::UnoType<embed::XTransactedObject>::get()
                                    ,   cppu::UnoType<embed::XTransactionBroadcaster>::get()
                                    ,   cppu::UnoType<beans::XPropertySet>::get());
                }
                else // if ( m_pData->m_nStorageType == embed::StorageFormats::ZIP )
                {
                    m_oTypeCollection.emplace(
                                        cppu::UnoType<lang::XTypeProvider>::get()
                                    ,   cppu::UnoType<io::XInputStream>::get()
                                    ,   cppu::UnoType<io::XOutputStream>::get()
                                    ,   cppu::UnoType<io::XStream>::get()
                                    ,   cppu::UnoType<io::XSeekable>::get()
                                    ,   cppu::UnoType<io::XTruncate>::get()
                                    ,   cppu::UnoType<lang::XComponent>::get()
                                    ,   cppu::UnoType<embed::XExtendedStorageStream>::get()
                                    ,   cppu::UnoType<embed::XTransactedObject>::get()
                                    ,   cppu::UnoType<embed::XTransactionBroadcaster>::get()
                                    ,   cppu::UnoType<beans::XPropertySet>::get());
                }
            }
            else
            {
                if ( m_nStorageType == embed::StorageFormats::PACKAGE )
                {
                    m_oTypeCollection.emplace(
--> --------------------

--> maximum size reached

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

Messung V0.5
C=92 H=95 G=93

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge