/* -*- 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 .
*/
if ( m_bForceEncrypted || m_bHasCachedEncryptionData ) returntrue;
if ( m_oTempFile.has_value() || m_xCacheStream.is() ) returnfalse;
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 (constauto& 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; returnfalse;
} 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;
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) );
}
} catch( const packages::WrongPasswordException& )
{
TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow");
m_oTempFile.reset(); throw;
} catch( const 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!" );
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 );
} catch( const packages::WrongPasswordException& )
{
m_oTempFile.reset(); throw;
} catch( const uno::Exception& )
{
m_oTempFile.reset();
}
}
}
}
}
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);
} catch( const 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:
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);
} catch( const 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:
// 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 ( constauto& rProp : aProps )
{ if ( rProp.Name == aComprPropName )
{
bCompressedIsSet = true;
rProp.Value >>= bCompressed;
} elseif ( ( m_nStorageType == embed::StorageFormats::OFOPXML || m_nStorageType == embed::StorageFormats::PACKAGE )
&& rProp.Name == aMedTypePropName )
{
xPropertySet->setPropertyValue( rProp.Name, rProp.Value );
} elseif ( 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 ( 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;
}
// 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 );
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 ) );
} elseif ( m_bHasCachedEncryptionData )
{ if ( m_nStorageType != embed::StorageFormats::PACKAGE ) throw uno::RuntimeException();
// 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
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;
}
}
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;
} catch( const uno::Exception& )
{
TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception");
m_nRelInfoStatus = RELINFO_BROKEN;
m_bOrigRelInfoBroken = true;
}
} elseif ( 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 );
// 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;
// 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;
} elseif ( !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 );
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:
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();
// TODO: remember last state of m_bUseCommonEncryption if ( !xTargetStream.is() )
xTargetStream.set( new OInputSeekStream( xInStream, InsertOwnProps( aProps, m_bUseCommonEncryption ), m_nStorageType ) );
}
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();
} catch( const packages::NoEncryptionException& )
{
TOOLS_INFO_EXCEPTION("package.xstor", "No Element"); throw packages::WrongPasswordException();
}
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();
void OWriteStream_Impl::CommitStreamRelInfo( const uno::Reference< embed::XStorage >& 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();
// 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 );
}
}
// 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 ) );
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.