/* -*- 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 ( 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 );
// 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()
{
{
::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" );
OUString aPropertyName = u"URL"_ustr; for (constauto& 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();
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::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 (constauto& 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;
} elseif ( 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;
} elseif ( 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;
}
uno::Reference< container::XHierarchicalNameAccess > xHNameAccess( m_xPackage, uno::UNO_QUERY );
SAL_WARN_IF( !xHNameAccess.is(), "package.xstor", "The package could not be created!" );
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 );
}
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());
}
} catch( const 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;
}
if ( !m_xPackageFolder.is() ) throw embed::InvalidStorageException( THROW_WHERE );
for ( constauto& 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() );
// 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
// 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();
}
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
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 );
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
// 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 );
}
}
}
}
// 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!" );
// 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 )
{
// 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 ( constauto& 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 );
// 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);
} elseif ( !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 );
} elseif ( 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 );
}
for ( constauto& 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 );
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();
} catch( const 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;
}
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 (constauto & rPair : oldMap) for (auto pElement : rPair.second)
{ if ( pElement->m_bIsInserted ) delete pElement; else
{
ClearElement( pElement );
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;
}
}
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](constauto& 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);
SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new stream!" ); if ( !xNewElement.is() ) throw io::IOException( THROW_WHERE );
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 );
SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new stream!" ); if ( !xNewElement.is() ) throw io::IOException( 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, true, m_nStorageType, false)); // the stream is inserted and must be treated as a committed one
pNewElement->m_xStream->SetToBeCommited();
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 );
SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new storage!" ); if ( !xNewElement.is() ) throw io::IOException( THROW_WHERE );
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 returnnew 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!" );
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!" );
// 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)));
}
// 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
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, true, true );
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();
}
void OStorage_Impl::CommitRelInfo( const uno::Reference< container::XNameContainer >& xNewPackageFolder )
{ // this method should be used only in OStorage_Impl::Commit() method
OUString aRelsStorName(u"_rels"_ustr);
if ( !xNewPackageFolder.is() ) throw uno::RuntimeException( THROW_WHERE );
if ( m_nStorageType != embed::StorageFormats::OFOPXML ) return;
// 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));
// 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));
OStorage::OStorage( OStorage_Impl* pImpl, bool bReadOnlyWrap )
: m_pImpl( pImpl )
, m_xSharedMutex( m_pImpl->m_xMutex )
, m_aListenersContainer( m_pImpl->m_xMutex->GetMutex() )
, m_bReadOnlyWrap( bReadOnlyWrap )
{ // this call can be done only from OStorage_Impl implementation to create child storage
assert( m_pImpl && m_pImpl->m_xMutex.is() && "The provided pointer & mutex MUST NOT be empty!");
OSL_ENSURE( ( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE ) == embed::ElementModes::WRITE ||
m_bReadOnlyWrap, "The wrapper can not allow writing in case implementation does not!" );
if ( !bReadOnlyWrap )
m_pImpl->m_pAntiImpl = this;
}
// the source object is also a kind of locker for the current object // since the listeners could dispose the object while being notified
lang::EventObject aSource( getXWeak() );
m_aListenersContainer.disposeAndClear( aSource );
if ( !m_pImpl ) return;
m_pImpl->m_nModifiedListenerCount = 0;
if ( m_bReadOnlyWrap )
{
OSL_ENSURE( m_aOpenSubComponentsVector.empty() || m_pSubElDispListener, "If any subelements are open the listener must exist!" );
if (m_pSubElDispListener)
{
m_pSubElDispListener->OwnerIsDisposed();
// iterate through m_pData->m_aOpenSubComponentsVector // deregister m_pData->m_pSubElDispListener and dispose all of them if ( !m_aOpenSubComponentsVector.empty() )
{ for ( constauto& pComp : m_aOpenSubComponentsVector )
{
uno::Reference< lang::XComponent > xTmp = pComp; if ( xTmp.is() )
{
xTmp->removeEventListener( uno::Reference< lang::XEventListener >( static_cast< lang::XEventListener* >( m_pSubElDispListener.get())));
if ( bNotifyImpl )
{ if ( m_pImpl->m_bIsRoot ) delete m_pImpl; else
{ // the non-committed changes for the storage must be removed
m_pImpl->Revert();
}
}
}
m_pImpl = nullptr;
}
void OStorage::ChildIsDisposed( const uno::Reference< uno::XInterface >& xChild )
{ // this method can only be called by child disposing listener
// this method must not contain any locking // the locking is done in the listener
void OStorage::BroadcastModifiedIfNecessary()
{ // no need to lock mutex here for the checking of m_pImpl, and m_pData is alive until the object is destructed if ( !m_pImpl )
{
SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); throw lang::DisposedException( THROW_WHERE );
}
if ( !m_pImpl->m_bBroadcastModified ) return;
m_pImpl->m_bBroadcastModified = false;
SAL_WARN_IF( m_bReadOnlyWrap, "package.xstor", "The storage can not be modified at all!" );
void OStorage::BroadcastTransaction( sal_Int8 nMessage ) /* 1 - preCommit 2 - committed 3 - preRevert 4 - reverted
*/
{ // no need to lock mutex here for the checking of m_pImpl, and m_pData is alive until the object is destructed if ( !m_pImpl )
{
SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); throw lang::DisposedException( THROW_WHERE );
}
SAL_WARN_IF( m_bReadOnlyWrap, "package.xstor", "The storage can not be modified at all!" );
lang::EventObject aSource( getXWeak() );
comphelper::OInterfaceContainerHelper2* pContainer =
m_aListenersContainer.getContainer(
cppu::UnoType<embed::XTransactionListener>::get()); if ( !pContainer ) return;
OSL_ENSURE( !m_bReadOnlyWrap || ( nOpenMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE, "An element can not be opened for writing in readonly storage!" );
SotElement_Impl *pElement = m_pImpl->FindElement( aStreamName ); if ( !pElement )
{ // element does not exist, check if creation is allowed if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE )
|| (( nOpenMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE )
|| ( nOpenMode & embed::ElementModes::NOCREATE ) == embed::ElementModes::NOCREATE )
{ throw io::IOException("Element does not exist and cannot be " "created: \"" + aStreamName + "\""); // TODO: access_denied
}
// create a new StreamElement and insert it into the list
pElement = m_pImpl->InsertStream( aStreamName, bEncr );
} elseif ( pElement->m_bIsStorage )
{ throw io::IOException( THROW_WHERE );
}
assert(pElement && "In case element can not be created an exception must be thrown!");
if (!pElement->m_xStream)
m_pImpl->OpenSubStream( pElement );
if (!pElement->m_xStream) throw io::IOException( THROW_WHERE );
uno::Reference< io::XStream > xResult; try
{
SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamName, nOpenMode, false );
assert(pElement && pElement->m_xStream && "In case element can not be created an exception must be thrown!");
xResult = pElement->m_xStream->GetStream(nOpenMode, false);
SAL_WARN_IF( !xResult.is(), "package.xstor", "The method must throw exception instead of removing empty result!" );
// it's always possible to read written storage in this implementation
nStorageMode |= embed::ElementModes::READ;
rtl::Reference< OStorage > xResult; try
{
SotElement_Impl *pElement = m_pImpl->FindElement( aStorName ); if ( !pElement )
{ // element does not exist, check if creation is allowed if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE )
|| (( nStorageMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE )
|| ( nStorageMode & embed::ElementModes::NOCREATE ) == embed::ElementModes::NOCREATE ) throw io::IOException( THROW_WHERE ); // TODO: access_denied
// create a new StorageElement and insert it into the list
pElement = m_pImpl->InsertStorage( aStorName, nStorageMode );
} elseif ( !pElement->m_bIsStorage )
{ throw io::IOException( THROW_WHERE );
} elseif (pElement->m_xStorage)
{ // storage has already been opened; it may be opened another time, if it the mode allows to do so if (pElement->m_xStorage->m_pAntiImpl)
{ throw io::IOException( THROW_WHERE ); // TODO: access_denied
} elseif ( !pElement->m_xStorage->m_aReadOnlyWrapVector.empty()
&& ( nStorageMode & embed::ElementModes::WRITE ) )
{ throw io::IOException( THROW_WHERE ); // TODO: access_denied
} else
{ // in case parent storage allows writing the readonly mode of the child storage is // virtual, that means that it is just enough to change the flag to let it be writable // and since there is no AntiImpl nobody should be notified about it
pElement->m_xStorage->m_nStorageMode = nStorageMode | embed::ElementModes::READ;
if ( nStorageMode & embed::ElementModes::TRUNCATE )
{ for (constauto & rPair : pElement->m_xStorage->m_aChildrenMap) for (auto pElementToDel : rPair.second)
m_pImpl->RemoveElement( /*aName*/rPair.first, pElementToDel );
}
}
}
if (!pElement->m_xStorage)
m_pImpl->OpenSubStorage(pElement, nStorageMode);
if (!pElement->m_xStorage) throw io::IOException( THROW_WHERE ); // TODO: general_error
uno::Reference< io::XStream > xResult; try
{
SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamName, nOpenMode, true );
assert(pElement && pElement->m_xStream && "In case element can not be created an exception must be thrown!");
xResult = pElement->m_xStream->GetStream(nOpenMode, aEncryptionData, false);
SAL_WARN_IF( !xResult.is(), "package.xstor", "The method must throw exception instead of removing empty result!" );
if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML ) throw uno::RuntimeException( THROW_WHERE ); // the interface is not supported and must not be accessible
if ( !xTempIn ) throw io::IOException( THROW_WHERE );
// Copy temporary file to a new one
::comphelper::OStorageHelper::CopyInputToOutput( xRawInStream, xTempIn );
xTempIn->closeOutput();
xTempIn->seek( 0 );
// XModifiable // TODO: if there will be no demand on this interface it will be removed from implementation, // I do not want to remove it now since it is still possible that it will be inserted // to the service back.
if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "removeEncryption() method is not available for nonroot storages!" ); if ( !m_pImpl->m_bIsRoot ) return;
m_pImpl->m_bHasCommonEncryptionData = false;
m_pImpl->m_aCommonEncryptionData.clear();
} catch( const uno::RuntimeException& )
{
TOOLS_WARN_EXCEPTION( "package.xstor", "The call must not fail, it is pretty simple!" ); throw;
} catch( const uno::Exception& )
{
TOOLS_WARN_EXCEPTION( "package.xstor", "The call must not fail, it is pretty simple!" ); throw io::IOException( THROW_WHERE );
}
}
if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setEncryptionData() method is not available for nonroot storages!" ); if ( !m_pImpl->m_bIsRoot ) return;
if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setEncryptionAlgorithms() method is not available for nonroot storages!" ); if ( !m_pImpl->m_bIsRoot ) return;
if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setGpgProperties() method is not available for nonroot storages!" ); if ( !m_pImpl->m_bIsRoot ) return;
if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage
uno::Sequence< beans::NamedValue > aResult;
SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "getEncryptionAlgorithms() method is not available for nonroot storages!" ); if ( m_pImpl->m_bIsRoot )
{ try {
m_pImpl->ReadContents();
} catch ( const uno::RuntimeException& )
{
TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); throw;
} catch ( const uno::Exception& )
{
uno::Any aCaught( ::cppu::getCaughtException() );
SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught));
throw lang::WrappedTargetRuntimeException( THROW_WHERE "Can not open package!",
getXWeak(),
aCaught );
}
// WORKAROUND: // The old document might have no version in the manifest.xml, so we have to allow to set the version // even for readonly storages, so that the version from content.xml can be used. if ( m_bReadOnlyWrap && aPropertyName != "Version" ) throw uno::RuntimeException( THROW_WHERE ); // TODO: Access denied
uno::Reference< io::XSeekable > xSeek( xInRelStream, uno::UNO_QUERY ); if ( !xSeek.is() )
{ // currently this is an internal property that is used for optimization // and the stream must support XSeekable interface // TODO/LATER: in future it can be changed if property is used from outside throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 );
}
if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) throw uno::RuntimeException( THROW_WHERE );
// TODO/LATER: in future the unification of the ID could be checked const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); const beans::StringPair aIDRel(u"Id"_ustr, sID);
if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) throw uno::RuntimeException( THROW_WHERE );
// TODO/LATER: in future the unification of the ID could be checked const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships();
std::vector< uno::Sequence< beans::StringPair > > aResult;
aResult.reserve(aSeq.getLength());
std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResult),
[&sType](const uno::Sequence<beans::StringPair>& rRel) { auto pRel = lcl_findPairByName(rRel, u"Type"_ustr); return pRel != rRel.end() // the type is usually a URL, so the check should be case insensitive
&& pRel->Second.equalsIgnoreAsciiCase( sType );
});
// XOptimizedStorage void SAL_CALL OStorage::insertRawNonEncrStreamElementDirect( const OUString& /*sStreamName*/, const uno::Reference< io::XInputStream >& /*xInStream*/ )
{ // not implemented currently because there is still no demand // might need to be implemented if direct copying of compressed streams is used throw io::IOException( THROW_WHERE );
}
if ( pElement ) throw container::ElementExistException( THROW_WHERE );
pElement = OpenStreamElement_Impl( aStreamName, embed::ElementModes::READWRITE, false);
assert(pElement && pElement->m_xStream && "In case element can not be created an exception must be thrown!");
// TODO/LATER: Currently it is only implemented for MediaType property of substorages, might be changed in future if ( !pElement->m_bIsStorage || m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE || aPropertyName != "MediaType" ) throw beans::PropertyVetoException( THROW_WHERE );
if (!pElement->m_xStorage)
m_pImpl->OpenSubStorage( pElement, embed::ElementModes::READ );
if (!pElement->m_xStorage) throw io::IOException( THROW_WHERE ); // TODO: general_error
std::vector<OUString> aListPath = OHierarchyHolder_Impl::GetListPathFromString( aStreamPath );
OSL_ENSURE( aListPath.size(), "The result list must not be empty!" );
uno::Reference< embed::XExtendedStorageStream > xResult; if ( aListPath.size() == 1 )
{ try
{ // that must be a direct request for a stream // the transacted version of the stream should be opened
SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamPath, nOpenMode, false );
assert(pElement && pElement->m_xStream && "In case element can not be created an exception must be thrown!");
xResult.set(pElement->m_xStream->GetStream(nOpenMode, true),
uno::UNO_QUERY_THROW);
} catch ( const container::NoSuchElementException & )
{ throw io::IOException( THROW_WHERE ); // file not found
}
} else
{ // there are still storages in between if (!m_pHierarchyHolder)
m_pHierarchyHolder.reset(new OHierarchyHolder_Impl(this));
std::vector<OUString> aListPath = OHierarchyHolder_Impl::GetListPathFromString( aStreamPath );
OSL_ENSURE( aListPath.size(), "The result list must not be empty!" );
if (!m_pHierarchyHolder)
m_pHierarchyHolder.reset(new OHierarchyHolder_Impl(this));
std::vector<OUString> aListPath = OHierarchyHolder_Impl::GetListPathFromString( aStreamPath );
OSL_ENSURE( aListPath.size(), "The result list must not be empty!" );
uno::Reference< embed::XExtendedStorageStream > xResult; if ( aListPath.size() == 1 )
{ // that must be a direct request for a stream // the transacted version of the stream should be opened
SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamPath, nOpenMode, true );
assert(pElement && pElement->m_xStream && "In case element can not be created an exception must be thrown!");
xResult.set(pElement->m_xStream->GetStream(nOpenMode, aEncryptionData, true),
uno::UNO_QUERY_THROW);
} else
{ // there are still storages in between if (!m_pHierarchyHolder)
m_pHierarchyHolder.reset(new OHierarchyHolder_Impl(this));
¤ 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.0.118Bemerkung:
(vorverarbeitet am 2026-05-04)
¤
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.