diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /package/source/xstor | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'package/source/xstor')
19 files changed, 12183 insertions, 0 deletions
diff --git a/package/source/xstor/disposelistener.cxx b/package/source/xstor/disposelistener.cxx new file mode 100644 index 000000000..b7ee40957 --- /dev/null +++ b/package/source/xstor/disposelistener.cxx @@ -0,0 +1,46 @@ +/* -*- 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 "disposelistener.hxx" +#include "xstorage.hxx" + +using namespace ::com::sun::star; + +OChildDispListener_Impl::OChildDispListener_Impl( OStorage& aStorage ) +: m_pStorage( &aStorage ) +{} + +OChildDispListener_Impl::~OChildDispListener_Impl() +{} + +void OChildDispListener_Impl::OwnerIsDisposed() +{ + std::scoped_lock aGuard( m_aMutex ); + m_pStorage = nullptr; +} + +void SAL_CALL OChildDispListener_Impl::disposing( const lang::EventObject& Source ) +{ + std::scoped_lock aGuard( m_aMutex ); + // ObjectIsDisposed must not contain any locking! + if ( m_pStorage && Source.Source.is() ) + m_pStorage->ChildIsDisposed( Source.Source ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/disposelistener.hxx b/package/source/xstor/disposelistener.hxx new file mode 100644 index 000000000..b635b58ce --- /dev/null +++ b/package/source/xstor/disposelistener.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_DISPOSELISTENER_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_DISPOSELISTENER_HXX + +#include <com/sun/star/lang/XEventListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <mutex> + +class OStorage; +class OChildDispListener_Impl : public ::cppu::WeakImplHelper<css::lang::XEventListener> +{ + std::mutex m_aMutex; + OStorage* m_pStorage; + +public: + explicit OChildDispListener_Impl(OStorage& aStorage); + virtual ~OChildDispListener_Impl() override; + + void OwnerIsDisposed(); + + virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/ocompinstream.cxx b/package/source/xstor/ocompinstream.cxx new file mode 100644 index 000000000..14bfd3cab --- /dev/null +++ b/package/source/xstor/ocompinstream.cxx @@ -0,0 +1,602 @@ +/* -*- 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 "ocompinstream.hxx" +#include <com/sun/star/embed/StorageFormats.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <comphelper/sequence.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include "owriteablestream.hxx" + +using namespace ::com::sun::star; + +OInputCompStream::OInputCompStream( OWriteStream_Impl& aImpl, + uno::Reference < io::XInputStream > const & xStream, + const uno::Sequence< beans::PropertyValue >& aProps, + sal_Int32 nStorageType ) +: m_pImpl( &aImpl ) +, m_xMutex( m_pImpl->m_xMutex ) +, m_xStream( xStream ) +, m_aProperties( aProps ) +, m_bDisposed( false ) +, m_nStorageType( nStorageType ) +{ + OSL_ENSURE( m_pImpl->m_xMutex.is(), "No mutex is provided!" ); + if ( !m_pImpl->m_xMutex.is() ) + throw uno::RuntimeException(); // just a disaster + + assert(m_xStream.is()); +} + +OInputCompStream::OInputCompStream( uno::Reference < io::XInputStream > const & xStream, + const uno::Sequence< beans::PropertyValue >& aProps, + sal_Int32 nStorageType ) +: m_pImpl( nullptr ) +, m_xMutex( new comphelper::RefCountedMutex ) +, m_xStream( xStream ) +, m_aProperties( aProps ) +, m_bDisposed( false ) +, m_nStorageType( nStorageType ) +{ + assert(m_xStream.is()); +} + +OInputCompStream::~OInputCompStream() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( !m_bDisposed ) + { + osl_atomic_increment(&m_refCount); + dispose(); + } +} + +uno::Any SAL_CALL OInputCompStream::queryInterface( const uno::Type& rType ) +{ + // common interfaces + uno::Any aReturn = ::cppu::queryInterface + ( rType + , static_cast<io::XInputStream*> ( this ) + , static_cast<io::XStream*> ( this ) + , static_cast<lang::XComponent*> ( this ) + , static_cast<beans::XPropertySet*> ( this ) + , static_cast<embed::XExtendedStorageStream*> ( this ) ); + + if ( aReturn.hasValue() ) + return aReturn ; + + if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + { + aReturn = ::cppu::queryInterface + ( rType + , static_cast<embed::XRelationshipAccess*> ( this ) ); + + if ( aReturn.hasValue() ) + return aReturn ; + } + + return OWeakObject::queryInterface( rType ); +} + +sal_Int32 SAL_CALL OInputCompStream::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + return m_xStream->readBytes( aData, nBytesToRead ); +} + +sal_Int32 SAL_CALL OInputCompStream::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + return m_xStream->readSomeBytes( aData, nMaxBytesToRead ); + +} + +void SAL_CALL OInputCompStream::skipBytes( sal_Int32 nBytesToSkip ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + m_xStream->skipBytes( nBytesToSkip ); + +} + +sal_Int32 SAL_CALL OInputCompStream::available( ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + return m_xStream->available(); + +} + +void SAL_CALL OInputCompStream::closeInput( ) +{ + dispose(); +} + +uno::Reference< io::XInputStream > SAL_CALL OInputCompStream::getInputStream() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + return this; +} + +uno::Reference< io::XOutputStream > SAL_CALL OInputCompStream::getOutputStream() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + return uno::Reference< io::XOutputStream >(); +} + +void OInputCompStream::InternalDispose() +{ + // can be called only by OWriteStream_Impl + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + // 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( static_cast< ::cppu::OWeakObject*>( this ) ); + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->disposeAndClear( aSource ); + + try + { + m_xStream->closeInput(); + } + catch( uno::Exception& ) + {} + + m_pImpl = nullptr; + m_bDisposed = true; +} + +void SAL_CALL OInputCompStream::dispose( ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pInterfaceContainer ) + { + lang::EventObject aSource( static_cast< ::cppu::OWeakObject*>( this ) ); + m_pInterfaceContainer->disposeAndClear( aSource ); + } + + m_xStream->closeInput(); + + if ( m_pImpl ) + { + m_pImpl->InputStreamDisposed( this ); + m_pImpl = nullptr; + } + + m_bDisposed = true; +} + +void SAL_CALL OInputCompStream::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_pInterfaceContainer ) + m_pInterfaceContainer.reset( new ::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>( m_xMutex->GetMutex() ) ); + + m_pInterfaceContainer->addInterface( xListener ); +} + +void SAL_CALL OInputCompStream::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pInterfaceContainer ) + m_pInterfaceContainer->removeInterface( xListener ); +} + +sal_Bool SAL_CALL OInputCompStream::hasByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + try + { + getRelationshipByID( sID ); + return true; + } + catch( container::NoSuchElementException& ) + {} + + return false; +} + +namespace +{ + +const beans::StringPair* lcl_findPairByName(const uno::Sequence<beans::StringPair>& rSeq, const OUString& rName) +{ + return std::find_if(rSeq.begin(), rSeq.end(), + [&rName](const beans::StringPair& rPair) { return rPair.First == rName; }); +} + +} + +OUString SAL_CALL OInputCompStream::getTargetByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID ); + auto pRel = lcl_findPairByName(aSeq, "Target"); + if (pRel != aSeq.end()) + return pRel->Second; + + return OUString(); +} + +OUString SAL_CALL OInputCompStream::getTypeByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID ); + auto pRel = lcl_findPairByName(aSeq, "Type"); + if (pRel != aSeq.end()) + return pRel->Second; + + return OUString(); +} + +uno::Sequence< beans::StringPair > SAL_CALL OInputCompStream::getRelationshipByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + // 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("Id", sID); + auto pRel = std::find_if(aSeq.begin(), aSeq.end(), + [&aIDRel](const uno::Sequence<beans::StringPair>& rRel){ + return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); }); + if (pRel != aSeq.end()) + return *pRel; + + throw container::NoSuchElementException(); +} + +uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OInputCompStream::getRelationshipsByType( const OUString& sType ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + // TODO/LATER: in future the unification of the ID could be checked + const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); + const beans::StringPair aTypeRel("Type", sType); + std::vector< uno::Sequence<beans::StringPair> > aResult; + aResult.reserve(aSeq.getLength()); + + std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResult), + [&aTypeRel](const uno::Sequence<beans::StringPair>& rRel) { + return std::find(rRel.begin(), rRel.end(), aTypeRel) != rRel.end(); }); + + return comphelper::containerToSequence(aResult); +} + +uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OInputCompStream::getAllRelationships() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + // TODO/LATER: in future the information could be taken directly from m_pImpl when possible + auto pProp = std::find_if(std::cbegin(m_aProperties), std::cend(m_aProperties), + [](const beans::PropertyValue& rProp) { return rProp.Name == "RelationsInfo"; }); + if (pProp != std::cend(m_aProperties)) + { + uno::Sequence< uno::Sequence< beans::StringPair > > aResult; + if (pProp->Value >>= aResult) + return aResult; + } + + throw io::IOException("relations info could not be read"); // the relations info could not be read +} + +void SAL_CALL OInputCompStream::insertRelationshipByID( const OUString& /*sID*/, const uno::Sequence< beans::StringPair >& /*aEntry*/, sal_Bool /*bReplace*/ ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + throw io::IOException(); // TODO: Access denied +} + +void SAL_CALL OInputCompStream::removeRelationshipByID( const OUString& /*sID*/ ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + throw io::IOException(); // TODO: Access denied +} + +void SAL_CALL OInputCompStream::insertRelationships( const uno::Sequence< uno::Sequence< beans::StringPair > >& /*aEntries*/, sal_Bool /*bReplace*/ ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + throw io::IOException(); // TODO: Access denied +} + +void SAL_CALL OInputCompStream::clearRelationships() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + throw io::IOException(); // TODO: Access denied +} + +uno::Reference< beans::XPropertySetInfo > SAL_CALL OInputCompStream::getPropertySetInfo() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: + return uno::Reference< beans::XPropertySetInfo >(); +} + +void SAL_CALL OInputCompStream::setPropertyValue( const OUString& aPropertyName, const uno::Any& /*aValue*/ ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + // all the provided properties are accessible + if (std::any_of(std::cbegin(m_aProperties), std::cend(m_aProperties), + [&aPropertyName](const beans::PropertyValue& rProp) { return rProp.Name == aPropertyName; })) + throw beans::PropertyVetoException(); // TODO + + throw beans::UnknownPropertyException(aPropertyName); // TODO +} + +uno::Any SAL_CALL OInputCompStream::getPropertyValue( const OUString& aProp ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + OUString aPropertyName; + if ( aProp == "IsEncrypted" ) + aPropertyName = "Encrypted"; + else + aPropertyName = aProp; + + if ( aPropertyName == "RelationsInfo" ) + throw beans::UnknownPropertyException(aPropertyName); // TODO + + // all the provided properties are accessible + auto pProp = std::find_if(std::cbegin(m_aProperties), std::cend(m_aProperties), + [&aPropertyName](const beans::PropertyValue& rProp) { return rProp.Name == aPropertyName; }); + if (pProp != std::cend(m_aProperties)) + return pProp->Value; + + throw beans::UnknownPropertyException(aPropertyName); // TODO +} + +void SAL_CALL OInputCompStream::addPropertyChangeListener( + const OUString& /*aPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: +} + +void SAL_CALL OInputCompStream::removePropertyChangeListener( + const OUString& /*aPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: +} + +void SAL_CALL OInputCompStream::addVetoableChangeListener( + const OUString& /*PropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: +} + +void SAL_CALL OInputCompStream::removeVetoableChangeListener( + const OUString& /*PropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/ocompinstream.hxx b/package/source/xstor/ocompinstream.hxx new file mode 100644 index 000000000..4001cf118 --- /dev/null +++ b/package/source/xstor/ocompinstream.hxx @@ -0,0 +1,109 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_OCOMPINSTREAM_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_OCOMPINSTREAM_HXX + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/embed/XExtendedStorageStream.hpp> +#include <com/sun/star/embed/XRelationshipAccess.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/refcountedmutex.hxx> +#include <rtl/ref.hxx> +#include <memory> + + +struct OWriteStream_Impl; + +class OInputCompStream : public cppu::WeakImplHelper < css::io::XInputStream + ,css::embed::XExtendedStorageStream + ,css::embed::XRelationshipAccess + ,css::beans::XPropertySet > +{ +protected: + OWriteStream_Impl* m_pImpl; + rtl::Reference<comphelper::RefCountedMutex> m_xMutex; + css::uno::Reference < css::io::XInputStream > m_xStream; + std::unique_ptr<::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>> m_pInterfaceContainer; + css::uno::Sequence < css::beans::PropertyValue > m_aProperties; + bool m_bDisposed; + sal_Int32 m_nStorageType; + +public: + OInputCompStream( OWriteStream_Impl& pImpl, + css::uno::Reference< css::io::XInputStream > const & xStream, + const css::uno::Sequence< css::beans::PropertyValue >& aProps, + sal_Int32 nStorageType ); + + OInputCompStream( css::uno::Reference< css::io::XInputStream > const & xStream, + const css::uno::Sequence< css::beans::PropertyValue >& aProps, + sal_Int32 nStorageType ); + + virtual ~OInputCompStream() override; + + void InternalDispose(); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override; + + // XInputStream + virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override; + virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override; + virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override; + virtual sal_Int32 SAL_CALL available( ) override; + virtual void SAL_CALL closeInput( ) override; + + //XStream + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override; + virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream( ) override; + + //XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + //XRelationshipAccess + virtual sal_Bool SAL_CALL hasByID( const OUString& sID ) override; + virtual OUString SAL_CALL getTargetByID( const OUString& sID ) override; + virtual OUString SAL_CALL getTypeByID( const OUString& sID ) override; + virtual css::uno::Sequence< css::beans::StringPair > SAL_CALL getRelationshipByID( const OUString& sID ) override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getRelationshipsByType( const OUString& sType ) override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getAllRelationships( ) override; + virtual void SAL_CALL insertRelationshipByID( const OUString& sID, const css::uno::Sequence< css::beans::StringPair >& aEntry, sal_Bool bReplace ) override; + virtual void SAL_CALL removeRelationshipByID( const OUString& sID ) override; + virtual void SAL_CALL insertRelationships( const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > >& aEntries, sal_Bool bReplace ) override; + virtual void SAL_CALL clearRelationships( ) override; + + //XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/ohierarchyholder.cxx b/package/source/xstor/ohierarchyholder.cxx new file mode 100644 index 000000000..209136bcd --- /dev/null +++ b/package/source/xstor/ohierarchyholder.cxx @@ -0,0 +1,327 @@ +/* -*- 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 <com/sun/star/io/IOException.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XHierarchicalStorageAccess2.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/XTransactionBroadcaster.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <o3tl/string_view.hxx> + +#include "ohierarchyholder.hxx" + +using namespace ::com::sun::star; + +// OHierarchyHolder_Impl + +uno::Reference< embed::XExtendedStorageStream > OHierarchyHolder_Impl::GetStreamHierarchically( sal_Int32 nStorageMode, std::vector<OUString>& aListPath, sal_Int32 nStreamMode, const ::comphelper::SequenceAsHashMap& aEncryptionData ) +{ + uno::Reference< embed::XStorage > xOwnStor( m_xWeakOwnStorage.get(), uno::UNO_QUERY_THROW ); + + if ( !( nStorageMode & embed::ElementModes::WRITE ) && ( nStreamMode & embed::ElementModes::WRITE ) ) + throw io::IOException("invalid storage/stream mode combo"); + + uno::Reference< embed::XExtendedStorageStream > xResult = + m_xChild->GetStreamHierarchically( nStorageMode, aListPath, nStreamMode, aEncryptionData ); + if ( !xResult.is() ) + throw uno::RuntimeException(); + + return xResult; +} + +void OHierarchyHolder_Impl::RemoveStreamHierarchically( std::vector<OUString>& aListPath ) +{ + uno::Reference< embed::XStorage > xOwnStor( m_xWeakOwnStorage.get(), uno::UNO_QUERY_THROW ); + + m_xChild->RemoveStreamHierarchically( aListPath ); +} + +// static +std::vector<OUString> OHierarchyHolder_Impl::GetListPathFromString( std::u16string_view aPath ) +{ + std::vector<OUString> aResult; + sal_Int32 nIndex = 0; + do + { + OUString aName( o3tl::getToken(aPath, 0, '/', nIndex ) ); + if ( aName.isEmpty() ) + throw lang::IllegalArgumentException(); + + aResult.push_back( aName ); + } + while ( nIndex >= 0 ); + + return aResult; +} + +// OHierarchyElement_Impl + +uno::Reference< embed::XExtendedStorageStream > OHierarchyElement_Impl::GetStreamHierarchically( sal_Int32 nStorageMode, std::vector<OUString>& aListPath, sal_Int32 nStreamMode, const ::comphelper::SequenceAsHashMap& aEncryptionData ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( !( nStorageMode & embed::ElementModes::WRITE ) && ( nStreamMode & embed::ElementModes::WRITE ) ) + throw io::IOException("invalid storage/stream mode combo"); + + if ( aListPath.empty() ) + throw uno::RuntimeException(); + + OUString aNextName = *(aListPath.begin()); + aListPath.erase( aListPath.begin() ); + + uno::Reference< embed::XExtendedStorageStream > xResult; + + uno::Reference< embed::XStorage > xOwnStor = m_xOwnStorage.is() ? m_xOwnStorage + : uno::Reference< embed::XStorage >( m_xWeakOwnStorage.get(), uno::UNO_QUERY_THROW ); + + if ( aListPath.empty() ) + { + if ( aEncryptionData.empty() ) + { + uno::Reference< embed::XHierarchicalStorageAccess > xHStorage( xOwnStor, uno::UNO_QUERY_THROW ); + xResult = xHStorage->openStreamElementByHierarchicalName( aNextName, nStreamMode ); + } + else + { + uno::Reference< embed::XHierarchicalStorageAccess2 > xHStorage( xOwnStor, uno::UNO_QUERY_THROW ); + xResult = xHStorage->openEncryptedStreamByHierarchicalName( aNextName, nStreamMode, aEncryptionData.getAsConstNamedValueList() ); + } + + uno::Reference< embed::XTransactedObject > xTransact( xResult, uno::UNO_QUERY ); + if ( xTransact.is() ) + { + // the existence of the transacted object means that the stream is opened for writing also + // so the whole chain must be committed + uno::Reference< embed::XTransactionBroadcaster > xTrBroadcast( xTransact, uno::UNO_QUERY_THROW ); + xTrBroadcast->addTransactionListener( static_cast< embed::XTransactionListener* >( this ) ); + } + else + { + uno::Reference< lang::XComponent > xStreamComp( xResult, uno::UNO_QUERY_THROW ); + xStreamComp->addEventListener( static_cast< lang::XEventListener* >( this ) ); + } + + m_aOpenStreams.emplace_back( xResult ); + } + else + { + bool bNewElement = false; + ::rtl::Reference< OHierarchyElement_Impl > aElement; + OHierarchyElementList_Impl::iterator aIter = m_aChildren.find( aNextName ); + if ( aIter != m_aChildren.end() ) + aElement = aIter->second; + + if ( !aElement.is() ) + { + bNewElement = true; + uno::Reference< embed::XStorage > xChildStorage = xOwnStor->openStorageElement( aNextName, nStorageMode ); + if ( !xChildStorage.is() ) + throw uno::RuntimeException(); + + aElement = new OHierarchyElement_Impl( xChildStorage ); + } + + xResult = aElement->GetStreamHierarchically( nStorageMode, aListPath, nStreamMode, aEncryptionData ); + if ( !xResult.is() ) + throw uno::RuntimeException(); + + if ( bNewElement ) + { + m_aChildren[aNextName] = aElement; + aElement->SetParent( this ); + } + } + + // the subelement was opened successfully, remember the storage to let it be locked + m_xOwnStorage = xOwnStor; + + return xResult; +} + +void OHierarchyElement_Impl::RemoveStreamHierarchically( std::vector<OUString>& aListPath ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( aListPath.empty() ) + throw uno::RuntimeException(); + + OUString aNextName = *(aListPath.begin()); + aListPath.erase( aListPath.begin() ); + + uno::Reference< embed::XStorage > xOwnStor = m_xOwnStorage.is() ? m_xOwnStorage + : uno::Reference< embed::XStorage >( m_xWeakOwnStorage.get(), uno::UNO_QUERY_THROW ); + + if ( aListPath.empty() ) + { + xOwnStor->removeElement( aNextName ); + } + else + { + ::rtl::Reference< OHierarchyElement_Impl > aElement; + OHierarchyElementList_Impl::iterator aIter = m_aChildren.find( aNextName ); + if ( aIter != m_aChildren.end() ) + aElement = aIter->second; + + if ( !aElement.is() ) + { + uno::Reference< embed::XStorage > xChildStorage = xOwnStor->openStorageElement( aNextName, + embed::ElementModes::READWRITE ); + if ( !xChildStorage.is() ) + throw uno::RuntimeException(); + + aElement = new OHierarchyElement_Impl( xChildStorage ); + } + + aElement->RemoveStreamHierarchically( aListPath ); + } + + uno::Reference< embed::XTransactedObject > xTransact( xOwnStor, uno::UNO_QUERY ); + if ( xTransact.is() ) + xTransact->commit(); + + TestForClosing(); +} + +void OHierarchyElement_Impl::Commit() +{ + ::rtl::Reference< OHierarchyElement_Impl > xKeepAlive( this ); + ::rtl::Reference< OHierarchyElement_Impl > aParent; + uno::Reference< embed::XStorage > xOwnStor; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + aParent = m_rParent; + xOwnStor = m_xOwnStorage; + } + + if ( xOwnStor.is() ) + { + uno::Reference< embed::XTransactedObject > xTransact( xOwnStor, uno::UNO_QUERY_THROW ); + xTransact->commit(); + if ( aParent.is() ) + aParent->Commit(); + } +} + +void OHierarchyElement_Impl::TestForClosing() +{ + ::rtl::Reference< OHierarchyElement_Impl > xKeepAlive( this ); + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( m_aOpenStreams.empty() && m_aChildren.empty() ) + { + if ( m_rParent.is() ) + { + // only the root storage should not be disposed, other storages can be disposed + if ( m_xOwnStorage.is() ) + { + try + { + m_xOwnStorage->dispose(); + } + catch( uno::Exception& ) + {} + } + + m_rParent->RemoveElement( this ); + } + + m_xOwnStorage.clear(); + } + } +} + +void SAL_CALL OHierarchyElement_Impl::disposing( const lang::EventObject& Source ) +{ + try + { + { + osl::MutexGuard aGuard(m_aMutex); + uno::Reference< embed::XExtendedStorageStream > xStream(Source.Source, uno::UNO_QUERY); + + m_aOpenStreams.erase(std::remove_if(m_aOpenStreams.begin(), m_aOpenStreams.end(), + [&xStream](const OWeakStorRefVector_Impl::value_type& rxStorage) { + return !rxStorage.get().is() || rxStorage.get() == xStream; }), + m_aOpenStreams.end()); + } + + TestForClosing(); + } + catch( uno::Exception& ex ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); // no exception must happen here, usually an exception means disaster + } +} + +void OHierarchyElement_Impl::RemoveElement( const ::rtl::Reference< OHierarchyElement_Impl >& aRef ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + OHierarchyElementList_Impl::iterator aIter = m_aChildren.begin(); + while (aIter != m_aChildren.end()) + { + if (aIter->second == aRef ) + aIter = m_aChildren.erase(aIter); + else + ++aIter; + } + } + + TestForClosing(); +} + +// XTransactionListener +void SAL_CALL OHierarchyElement_Impl::preCommit( const css::lang::EventObject& /*aEvent*/ ) +{ +} + +void SAL_CALL OHierarchyElement_Impl::commited( const css::lang::EventObject& /*aEvent*/ ) +{ + try + { + Commit(); + } + catch( const uno::Exception& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "Can not commit storage sequence!", + uno::Reference< uno::XInterface >(), + anyEx ); + } +} + +void SAL_CALL OHierarchyElement_Impl::preRevert( const css::lang::EventObject& /*aEvent*/ ) +{ +} + +void SAL_CALL OHierarchyElement_Impl::reverted( const css::lang::EventObject& /*aEvent*/ ) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/ohierarchyholder.hxx b/package/source/xstor/ohierarchyholder.hxx new file mode 100644 index 000000000..6f32b3f40 --- /dev/null +++ b/package/source/xstor/ohierarchyholder.hxx @@ -0,0 +1,115 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_OHIERARCHYHOLDER_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_OHIERARCHYHOLDER_HXX + +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XTransactionListener.hpp> +#include <com/sun/star/embed/XExtendedStorageStream.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <comphelper/sequenceashashmap.hxx> + +#include <rtl/ref.hxx> + +#include <unordered_map> +#include <vector> + +class OHierarchyElement_Impl; + +typedef std::unordered_map< OUString, + ::rtl::Reference< OHierarchyElement_Impl > > OHierarchyElementList_Impl; + +typedef ::std::vector< css::uno::WeakReference< css::embed::XExtendedStorageStream > > + OWeakStorRefVector_Impl; + +class OHierarchyElement_Impl : public cppu::WeakImplHelper< css::embed::XTransactionListener > +{ + ::osl::Mutex m_aMutex; + + ::rtl::Reference< OHierarchyElement_Impl > m_rParent; + css::uno::Reference< css::embed::XStorage > m_xOwnStorage; + css::uno::WeakReference< css::embed::XStorage > m_xWeakOwnStorage; + + OHierarchyElementList_Impl m_aChildren; + + OWeakStorRefVector_Impl m_aOpenStreams; + +public: + explicit OHierarchyElement_Impl( const css::uno::Reference< css::embed::XStorage >& xStorage ) + : m_xOwnStorage( xStorage ) + {} + + explicit OHierarchyElement_Impl( const css::uno::WeakReference< css::embed::XStorage >& xWeakStorage ) + : m_xWeakOwnStorage( xWeakStorage ) + {} + + void Commit(); + + void SetParent( const ::rtl::Reference< OHierarchyElement_Impl >& rParent ) { m_rParent = rParent; } + + void TestForClosing(); + + void RemoveElement( const ::rtl::Reference< OHierarchyElement_Impl >& aRef ); + + css::uno::Reference< css::embed::XExtendedStorageStream > + GetStreamHierarchically( sal_Int32 nStorageMode, + std::vector<OUString>& aPath, + sal_Int32 nStreamMode, + const ::comphelper::SequenceAsHashMap& aEncryptionData ); + + void RemoveStreamHierarchically( std::vector<OUString>& aListPath ); + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XTransactionListener + virtual void SAL_CALL preCommit( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL commited( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL preRevert( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL reverted( const css::lang::EventObject& aEvent ) override; + +}; + +class OHierarchyHolder_Impl : public ::cppu::OWeakObject +{ + css::uno::WeakReference< css::embed::XStorage > m_xWeakOwnStorage; + ::rtl::Reference< OHierarchyElement_Impl > m_xChild; +public: + explicit OHierarchyHolder_Impl( const css::uno::Reference< css::embed::XStorage >& xOwnStorage ) + : m_xWeakOwnStorage( xOwnStorage ) + , m_xChild( new OHierarchyElement_Impl( css::uno::WeakReference< css::embed::XStorage >( xOwnStorage ) ) ) + {} + + static std::vector<OUString> GetListPathFromString( std::u16string_view aPath ); + + css::uno::Reference< css::embed::XExtendedStorageStream > + GetStreamHierarchically( sal_Int32 nStorageMode, + std::vector<OUString>& aListPath, + sal_Int32 nStreamMode, + const ::comphelper::SequenceAsHashMap& aEncryptionData = ::comphelper::SequenceAsHashMap() ); + + void RemoveStreamHierarchically( std::vector<OUString>& aListPath ); +}; + +#endif // _OHIERARCHYHOLDER + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/oseekinstream.cxx b/package/source/xstor/oseekinstream.cxx new file mode 100644 index 000000000..f5e3d644d --- /dev/null +++ b/package/source/xstor/oseekinstream.cxx @@ -0,0 +1,144 @@ +/* -*- 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 <com/sun/star/lang/DisposedException.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include "oseekinstream.hxx" +#include "owriteablestream.hxx" + +using namespace ::com::sun::star; + +OInputSeekStream::OInputSeekStream( OWriteStream_Impl& pImpl, + uno::Reference < io::XInputStream > const & xStream, + const uno::Sequence< beans::PropertyValue >& aProps, + sal_Int32 nStorageType ) +: OInputCompStream( pImpl, xStream, aProps, nStorageType ) +{ + m_xSeekable.set( m_xStream, uno::UNO_QUERY ); + OSL_ENSURE( m_xSeekable.is(), "No seeking support!" ); +} + +OInputSeekStream::OInputSeekStream( uno::Reference < io::XInputStream > const & xStream, + const uno::Sequence< beans::PropertyValue >& aProps, + sal_Int32 nStorageType ) +: OInputCompStream( xStream, aProps, nStorageType ) +{ + m_xSeekable.set( m_xStream, uno::UNO_QUERY ); + OSL_ENSURE( m_xSeekable.is(), "No seeking support!" ); +} + +OInputSeekStream::~OInputSeekStream() +{ +} + +uno::Sequence< uno::Type > SAL_CALL OInputSeekStream::getTypes() +{ + static cppu::OTypeCollection aTypeCollection(cppu::UnoType<io::XSeekable>::get(), + OInputCompStream::getTypes()); + + return aTypeCollection.getTypes(); +} + +uno::Any SAL_CALL OInputSeekStream::queryInterface( const uno::Type& rType ) +{ + // Attention: + // Don't use mutex or guard in this method!!! Is a method of XInterface. + + uno::Any aReturn( ::cppu::queryInterface( rType, + static_cast< io::XSeekable* >( this ) ) ); + + if ( aReturn.hasValue() ) + { + return aReturn ; + } + + return OInputCompStream::queryInterface( rType ) ; +} + +void SAL_CALL OInputSeekStream::acquire() + noexcept +{ + OInputCompStream::acquire(); +} + +void SAL_CALL OInputSeekStream::release() + noexcept +{ + OInputCompStream::release(); +} + +void SAL_CALL OInputSeekStream::seek( sal_Int64 location ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xSeekable.is() ) + { + SAL_INFO("package.xstor", "No seekable!"); + throw uno::RuntimeException(); + } + + m_xSeekable->seek( location ); +} + +sal_Int64 SAL_CALL OInputSeekStream::getPosition() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xSeekable.is() ) + { + SAL_INFO("package.xstor", "No seekable!"); + throw uno::RuntimeException(); + } + + return m_xSeekable->getPosition(); +} + +sal_Int64 SAL_CALL OInputSeekStream::getLength() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_bDisposed ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xSeekable.is() ) + { + SAL_INFO("package.xstor", "No seekable!"); + throw uno::RuntimeException(); + } + + return m_xSeekable->getLength(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/oseekinstream.hxx b/package/source/xstor/oseekinstream.hxx new file mode 100644 index 000000000..a77289e9a --- /dev/null +++ b/package/source/xstor/oseekinstream.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_OSEEKINSTREAM_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_OSEEKINSTREAM_HXX + +#include <com/sun/star/io/XSeekable.hpp> + +#include "ocompinstream.hxx" + +class OInputSeekStream final : public OInputCompStream + , public css::io::XSeekable +{ + css::uno::Reference < css::io::XSeekable > m_xSeekable; + +public: + OInputSeekStream( OWriteStream_Impl& pImpl, + css::uno::Reference < css::io::XInputStream > const & xStream, + const css::uno::Sequence< css::beans::PropertyValue >& aProps, + sal_Int32 nStorageType ); + + OInputSeekStream( css::uno::Reference < css::io::XInputStream > const & xStream, + const css::uno::Sequence< css::beans::PropertyValue >& aProps, + sal_Int32 nStorageType ); + + virtual ~OInputSeekStream() override; + + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + //XSeekable + virtual void SAL_CALL seek( sal_Int64 location ) override; + virtual sal_Int64 SAL_CALL getPosition() override; + virtual sal_Int64 SAL_CALL getLength() override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/owriteablestream.cxx b/package/source/xstor/owriteablestream.cxx new file mode 100644 index 000000000..c9d012a61 --- /dev/null +++ b/package/source/xstor/owriteablestream.cxx @@ -0,0 +1,3276 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/config.h> +#include <sal/log.hxx> + +#include <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/XUnoTunnel.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/multicontainer2.hxx> +#include <comphelper/refcountedmutex.hxx> +#include <comphelper/sequence.hxx> + +#include <tools/diagnose_ex.h> + +#include <PackageConstants.hxx> + +#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; + +struct WSInternalData_Impl +{ + rtl::Reference<comphelper::RefCountedMutex> m_xSharedMutex; + ::std::unique_ptr< ::cppu::OTypeCollection> m_pTypeCollection; + comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenersContainer; // list of listeners + sal_Int32 m_nStorageType; + + // the mutex reference MUST NOT be empty + WSInternalData_Impl( const rtl::Reference<comphelper::RefCountedMutex>& rMutexRef, sal_Int32 nStorageType ) + : m_xSharedMutex( rMutexRef ) + , m_aListenersContainer( rMutexRef->GetMutex() ) + , m_nStorageType( nStorageType ) + {} +}; + +namespace package +{ + +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; +} + +bool KillFile( const OUString& aURL, const uno::Reference< uno::XComponentContext >& xContext ) +{ + if ( !xContext.is() ) + return false; + + bool bRet = false; + + try + { + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create( xContext ) ); + + xAccess->kill( aURL ); + bRet = true; + } + catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception"); + } + + return bRet; +} + +OUString GetNewTempFileURL( const uno::Reference< uno::XComponentContext >& rContext ) +{ + OUString aTempURL; + + uno::Reference < io::XTempFile > xTempFile( + io::TempFile::create(rContext), + uno::UNO_SET_THROW ); + + try { + xTempFile->setRemoveFile( false ); + aTempURL = xTempFile->getUri(); + } + catch ( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception"); + } + + if ( aTempURL.isEmpty() ) + throw uno::RuntimeException("Cannot create tempfile."); + + return aTempURL; +} + +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>& 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, + const uno::Reference< uno::XComponentContext >& rContext, + bool bForceEncrypted, + sal_Int32 nStorageType, + bool bDefaultCompress, + const uno::Reference< io::XInputStream >& xRelInfoStream ) +: m_xMutex( new comphelper::RefCountedMutex ) +, m_pAntiImpl( nullptr ) +, m_bHasDataToFlush( false ) +, m_bFlushed( false ) +, m_xPackageStream( xPackageStream ) +, m_xContext( rContext ) +, 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( 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(); + + if ( !m_aTempURL.isEmpty() ) + { + KillFile( m_aTempURL, comphelper::getProcessComponentContext() ); + m_aTempURL.clear(); + } + + CleanCacheStream(); +} + +void OWriteStream_Impl::CleanCacheStream() +{ + if ( !m_xCacheStream.is() ) + return; + + try + { + uno::Reference< io::XInputStream > xInputCache = m_xCacheStream->getInputStream(); + if ( xInputCache.is() ) + xInputCache->closeInput(); + } + catch( const uno::Exception& ) + {} + + try + { + uno::Reference< io::XOutputStream > xOutputCache = m_xCacheStream->getOutputStream(); + if ( xOutputCache.is() ) + xOutputCache->closeOutput(); + } + catch( const 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< lang::XUnoTunnel > xTunnel( m_xPackageStream, uno::UNO_QUERY_THROW ); + xParentPackageFolder->insertByName( aName, uno::Any( xTunnel ) ); + + 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_aTempURL.isEmpty() || 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("WasEncrypted"); + if ( !( aValue >>= bWasEncr ) ) + { + SAL_WARN( "package.xstor", "The property WasEncrypted has wrong type!" ); + } + } + + bool bToBeEncr = false; + for ( const auto& rProp : std::as_const(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(); +} + +OUString const & OWriteStream_Impl::GetFilledTempFileIfNo( const uno::Reference< io::XInputStream >& xStream ) +{ + if ( !m_aTempURL.getLength() ) + { + OUString aTempURL = GetNewTempFileURL( m_xContext ); + + try { + if ( !aTempURL.isEmpty() && xStream.is() ) + { + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( ucb::SimpleFileAccess::create( ::comphelper::getProcessComponentContext() ) ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempAccess->openFileWrite( aTempURL ); + if ( !xTempOutStream.is() ) + throw io::IOException("no temp stream"); // TODO: + // the current position of the original stream should be still OK, copy further + ::comphelper::OStorageHelper::CopyInputToOutput( xStream, xTempOutStream ); + xTempOutStream->closeOutput(); + xTempOutStream.clear(); + } + } + catch( const packages::WrongPasswordException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + KillFile( aTempURL, comphelper::getProcessComponentContext() ); + throw; + } + catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + KillFile( aTempURL, comphelper::getProcessComponentContext() ); + throw; + } + + if ( !aTempURL.isEmpty() ) + CleanCacheStream(); + + m_aTempURL = aTempURL; + } + + return m_aTempURL; +} + +OUString const & 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_aTempURL.isEmpty() ) + { + 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 = 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 = xCacheStream; + m_xCacheSeek->seek( 0 ); + } + else if ( m_aTempURL.isEmpty() ) + { + m_aTempURL = GetNewTempFileURL( m_xContext ); + + try { + if ( !m_aTempURL.isEmpty() ) + { + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( ucb::SimpleFileAccess::create( ::comphelper::getProcessComponentContext() ) ); + + uno::Reference< io::XOutputStream > xTempOutStream = xTempAccess->openFileWrite( m_aTempURL ); + if ( !xTempOutStream.is() ) + throw io::IOException("no temp stream"); // TODO: + + // copy stream contents to the file + xTempOutStream->writeBytes( aData ); + + // the current position of the original stream should be still OK, copy further + ::comphelper::OStorageHelper::CopyInputToOutput( xOrigStream, xTempOutStream ); + xTempOutStream->closeOutput(); + xTempOutStream.clear(); + } + } + catch( const packages::WrongPasswordException& ) + { + KillFile( m_aTempURL, comphelper::getProcessComponentContext() ); + m_aTempURL.clear(); + + throw; + } + catch( const uno::Exception& ) + { + KillFile( m_aTempURL, comphelper::getProcessComponentContext() ); + m_aTempURL.clear(); + } + } + } + } + + return m_aTempURL; +} + +uno::Reference< io::XStream > OWriteStream_Impl::GetTempFileAsStream() +{ + uno::Reference< io::XStream > xTempStream; + + if ( !m_xCacheStream.is() ) + { + if ( m_aTempURL.isEmpty() ) + m_aTempURL = FillTempGetFileName(); + + if ( !m_aTempURL.isEmpty() ) + { + // the temporary file is not used if the cache is used + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( ucb::SimpleFileAccess::create( ::comphelper::getProcessComponentContext() ) ); + + try + { + xTempStream = xTempAccess->openFileReadWrite( m_aTempURL ); + } + 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("no temp stream"); //TODO: + + return xTempStream; +} + +uno::Reference< io::XInputStream > OWriteStream_Impl::GetTempFileAsInputStream() +{ + uno::Reference< io::XInputStream > xInputStream; + + if ( !m_xCacheStream.is() ) + { + if ( m_aTempURL.isEmpty() ) + m_aTempURL = FillTempGetFileName(); + + if ( !m_aTempURL.isEmpty() ) + { + // the temporary file is not used if the cache is used + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( ucb::SimpleFileAccess::create( ::comphelper::getProcessComponentContext() ) ); + + try + { + xInputStream = xTempAccess->openFileRead( m_aTempURL ); + } + 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: + + return xInputStream; +} + +void OWriteStream_Impl::InsertStreamDirectly( const uno::Reference< io::XInputStream >& 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("m_bHasDataToFlush==true"); + + OSL_ENSURE( m_aTempURL.isEmpty() && !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( "Compressed" ); + OUString aMedTypePropName( "MediaType" ); + 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( "Encrypted", 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_aTempURL.isEmpty() ) + { + if ( m_pAntiImpl ) + m_pAntiImpl->DeInit(); + + uno::Reference< io::XInputStream > xInStream; + try + { + xInStream = new OSelfTerminateFileStream(m_xContext, m_aTempURL); + } + catch( const uno::Exception& ) + { + } + + 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_aTempURL.clear(); + } + 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( "Encrypted", + 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 = 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_aTempURL.isEmpty() || m_xCacheStream.is(), "The temporary must exist!" ); + + if ( m_xCacheStream.is() ) + { + m_xCacheStream.clear(); + m_xCacheSeek.clear(); + } + + if ( !m_aTempURL.isEmpty() ) + { + KillFile( m_aTempURL, comphelper::getProcessComponentContext() ); + m_aTempURL.clear(); + } + + 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( "Wrong relinfo stream!" ); + } + 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] = 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; + } + catch( const 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; + } + catch( const 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 OUStringLiteral sCompressed = u"Compressed"; + static constexpr OUStringLiteral sSize = u"Size"; + 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 ); + } + catch( const 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( "Wrong relinfo stream!" ); +} + +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; + } + catch( const 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", "Can't write encryption related properties"); + 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(); + } + catch( const 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_aTempURL.isEmpty() ) + 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_aTempURL.isEmpty() && !( 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 ) + { + if ( !m_aTempURL.isEmpty() ) + { + KillFile( m_aTempURL, comphelper::getProcessComponentContext() ); + m_aTempURL.clear(); + } + 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_aTempURL.isEmpty() && !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; + 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() ); + m_aInputStreamsVector.erase(std::remove(m_aInputStreamsVector.begin(), m_aInputStreamsVector.end(), 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 = io::TempFile::create(m_xContext); + else + xTempFile = xTargetStream; + + uno::Reference < io::XSeekable > xTempSeek( xTempFile, uno::UNO_QUERY_THROW ); + + uno::Reference < io::XOutputStream > xTempOut = xTempFile->getOutputStream(); + if ( !xTempOut.is() ) + throw uno::RuntimeException(); + + 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( + static_cast< ::cppu::OWeakObject* >( + new OInputSeekStream( xInStream, InsertOwnProps( aProps, m_bUseCommonEncryption ), m_nStorageType ) ), + uno::UNO_QUERY_THROW ); +} + +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(); + } + catch( const 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( "Encrypted" ) >>= 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 >() ); + } + } + catch( const 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 >& 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( + "MediaType", + uno::Any( OUString("application/vnd.openxmlformats-package.relationships+xml" ) ) ); + + 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("MediaType", + uno::Any( OUString("application/vnd.openxmlformats-package.relationships+xml" ) ) ); + + 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* pImpl, bool bTransacted ) +: m_pImpl( pImpl ) +, m_bInStreamDisconnected( false ) +, m_bInitOnDemand( true ) +, m_nInitPosition( 0 ) +, m_bTransacted( bTransacted ) +{ + OSL_ENSURE( pImpl, "No base implementation!" ); + OSL_ENSURE( m_pImpl->m_xMutex.is(), "No mutex!" ); + + if ( !m_pImpl || !m_pImpl->m_xMutex.is() ) + throw uno::RuntimeException(); // just a disaster + + m_pData.reset(new WSInternalData_Impl(pImpl->m_xMutex, m_pImpl->m_nStorageType)); +} + +OWriteStream::OWriteStream( OWriteStream_Impl* pImpl, uno::Reference< io::XStream > const & xStream, bool bTransacted ) +: m_pImpl( pImpl ) +, m_bInStreamDisconnected( false ) +, m_bInitOnDemand( false ) +, m_nInitPosition( 0 ) +, m_bTransacted( bTransacted ) +{ + OSL_ENSURE( pImpl && xStream.is(), "No base implementation!" ); + OSL_ENSURE( m_pImpl->m_xMutex.is(), "No mutex!" ); + + if ( !m_pImpl || !m_pImpl->m_xMutex.is() ) + throw uno::RuntimeException(); // just a disaster + + m_pData.reset(new WSInternalData_Impl(pImpl->m_xMutex, m_pImpl->m_nStorageType)); + + 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_pData->m_xSharedMutex->GetMutex() ); + if ( m_pImpl ) + { + osl_atomic_increment(&m_refCount); + try { + dispose(); + } + catch( const 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_pData->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 ("Compressed"); + xDestProps->setPropertyValue( aPropName, getPropertyValue( aPropName ) ); + if ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE || m_pData->m_nStorageType == embed::StorageFormats::OFOPXML ) + { + aPropName = "MediaType"; + xDestProps->setPropertyValue( aPropName, getPropertyValue( aPropName ) ); + + if ( m_pData->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 ) + , static_cast<lang::XUnoTunnel*> ( this ) ); + + if ( aReturn.hasValue() ) + return aReturn ; + + if ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE ) + { + aReturn = ::cppu::queryInterface + ( rType + , static_cast<embed::XEncryptionProtectedSource2*> ( this ) + , static_cast<embed::XEncryptionProtectedSource*> ( this ) ); + } + else if ( m_pData->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_pData->m_pTypeCollection) + { + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if (! m_pData->m_pTypeCollection) + { + if ( m_bTransacted ) + { + if ( m_pData->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_pData->m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( cppu::UnoType<beans::XPropertySet>::get() + , aTmpCollection.getTypes())); + } + else if ( m_pData->m_nStorageType == embed::StorageFormats::OFOPXML ) + { + m_pData->m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( 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_pData->m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( 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_pData->m_nStorageType == embed::StorageFormats::PACKAGE ) + { + m_pData->m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( 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<beans::XPropertySet>::get())); + } + else if ( m_pData->m_nStorageType == embed::StorageFormats::OFOPXML ) + { + m_pData->m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( 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<beans::XPropertySet>::get())); + } + else // if ( m_pData->m_nStorageType == embed::StorageFormats::ZIP ) + { + m_pData->m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( 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<beans::XPropertySet>::get())); + } + } + } + } + + return m_pData->m_pTypeCollection->getTypes() ; +} + +uno::Sequence< sal_Int8 > SAL_CALL OWriteStream::getImplementationId() +{ + static const comphelper::UnoIdInit lcl_ImplId; + return lcl_ImplId.getSeq(); +} + +sal_Int32 SAL_CALL OWriteStream::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xInStream.is() ) + throw io::NotConnectedException(); + + return m_xInStream->readBytes( aData, nBytesToRead ); +} + +sal_Int32 SAL_CALL OWriteStream::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xInStream.is() ) + throw io::NotConnectedException(); + + return m_xInStream->readSomeBytes( aData, nMaxBytesToRead ); +} + +void SAL_CALL OWriteStream::skipBytes( sal_Int32 nBytesToSkip ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xInStream.is() ) + throw io::NotConnectedException(); + + m_xInStream->skipBytes( nBytesToSkip ); +} + +sal_Int32 SAL_CALL OWriteStream::available( ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xInStream.is() ) + throw io::NotConnectedException(); + + return m_xInStream->available(); + +} + +void SAL_CALL OWriteStream::closeInput( ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bInitOnDemand && ( m_bInStreamDisconnected || !m_xInStream.is() ) ) + throw io::NotConnectedException(); + + // the input part of the stream stays open for internal purposes (to allow reading during copying) + // since it can not be reopened until output part is closed, it will be closed with output part. + m_bInStreamDisconnected = true; + // m_xInStream->closeInput(); + // m_xInStream.clear(); + + if ( !m_xOutStream.is() ) + dispose(); +} + +uno::Reference< io::XInputStream > SAL_CALL OWriteStream::getInputStream() +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bInitOnDemand && ( m_bInStreamDisconnected || !m_xInStream.is() ) ) + return uno::Reference< io::XInputStream >(); + + return this; +} + +uno::Reference< io::XOutputStream > SAL_CALL OWriteStream::getOutputStream() +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + try + { + CheckInitOnDemand(); + } + catch( const io::IOException& r ) + { + throw lang::WrappedTargetRuntimeException("OWriteStream::getOutputStream: Could not create backing temp file", + static_cast < OWeakObject * > ( this ), css::uno::Any ( r ) ); + } + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xOutStream.is() ) + return uno::Reference< io::XOutputStream >(); + + return this; +} + +void SAL_CALL OWriteStream::writeBytes( const uno::Sequence< sal_Int8 >& aData ) +{ + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + // the write method makes initialization itself, since it depends from the aData length + // NO CheckInitOnDemand()! + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bInitOnDemand ) + { + if ( !m_xOutStream.is() || !m_xSeekable.is()) + throw io::NotConnectedException(); + + if ( m_pImpl->m_xCacheStream.is() ) + { + // check whether the cache should be turned off + sal_Int64 nPos = m_xSeekable->getPosition(); + if ( nPos + aData.getLength() > MAX_STORCACHE_SIZE ) + { + // disconnect the cache and copy the data to the temporary file + m_xSeekable->seek( 0 ); + + // it is enough to copy the cached stream, the cache should already contain everything + if ( !m_pImpl->GetFilledTempFileIfNo( m_xInStream ).isEmpty() ) + { + DeInit(); + // the last position is known and it is differs from the current stream position + m_nInitPosition = nPos; + } + } + } + } + + if ( m_bInitOnDemand ) + { + 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; + } + } + + if ( !m_xOutStream.is() ) + throw io::NotConnectedException(); + + m_xOutStream->writeBytes( aData ); + m_pImpl->m_bHasDataToFlush = true; + + ModifyParentUnlockMutex_Impl( aGuard ); +} + +sal_Int32 OWriteStream::writeSomeBytes( const sal_Int8* pData, sal_Int32 nBytesToWrite ) +{ + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + // the write method makes initialization itself, since it depends from the aData length + // NO CheckInitOnDemand()! + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bInitOnDemand ) + { + if ( !m_xOutStream.is() || !m_xSeekable.is()) + throw io::NotConnectedException(); + + if ( m_pImpl->m_xCacheStream.is() ) + { + // check whether the cache should be turned off + sal_Int64 nPos = m_xSeekable->getPosition(); + if ( nPos + nBytesToWrite > MAX_STORCACHE_SIZE ) + { + // disconnect the cache and copy the data to the temporary file + m_xSeekable->seek( 0 ); + + // it is enough to copy the cached stream, the cache should already contain everything + if ( !m_pImpl->GetFilledTempFileIfNo( m_xInStream ).isEmpty() ) + { + DeInit(); + // the last position is known and it is differs from the current stream position + m_nInitPosition = nPos; + } + } + } + } + + if ( m_bInitOnDemand ) + { + 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; + } + } + + if ( !m_xOutStream.is() ) + throw io::NotConnectedException(); + + uno::Reference< css::lang::XUnoTunnel > xOutputTunnel( m_xOutStream, uno::UNO_QUERY ); + comphelper::ByteWriter* pByteWriter = nullptr; + if (xOutputTunnel) + pByteWriter = reinterpret_cast< comphelper::ByteWriter* >( xOutputTunnel->getSomething( comphelper::ByteWriter::getUnoTunnelId() ) ); + if (pByteWriter) + nBytesToWrite = pByteWriter->writeSomeBytes(pData, nBytesToWrite); + else + { + uno::Sequence<sal_Int8> aData(pData, nBytesToWrite); + m_xOutStream->writeBytes( aData ); + } + m_pImpl->m_bHasDataToFlush = true; + + ModifyParentUnlockMutex_Impl( aGuard ); + + return nBytesToWrite; +} + +sal_Int64 SAL_CALL OWriteStream::getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier ) +{ + if (rIdentifier == comphelper::ByteWriter::getUnoTunnelId()) + return reinterpret_cast<sal_Int64>(static_cast<comphelper::ByteWriter*>(this)); + return 0; +} + +void SAL_CALL OWriteStream::flush() +{ + // In case stream is flushed its current version becomes visible + // to the parent storage. Usually parent storage flushes the stream + // during own commit but a user can explicitly flush the stream + // so the changes will be available through cloning functionality. + + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bInitOnDemand ) + { + if ( !m_xOutStream.is() ) + throw io::NotConnectedException(); + + m_xOutStream->flush(); + m_pImpl->Commit(); + } +} + +void OWriteStream::CloseOutput_Impl() +{ + // all the checks must be done in calling method + + m_xOutStream->closeOutput(); + m_xOutStream.clear(); + + if ( m_bInitOnDemand ) + return; + + // after the stream is disposed it can be committed + // so transport correct size property + if ( !m_xSeekable.is() ) + throw uno::RuntimeException(); + + for ( auto& rProp : asNonConstRange(m_pImpl->m_aProps) ) + { + if ( rProp.Name == "Size" ) + rProp.Value <<= m_xSeekable->getLength(); + } +} + +void SAL_CALL OWriteStream::closeOutput() +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xOutStream.is() ) + throw io::NotConnectedException(); + + CloseOutput_Impl(); + + if ( m_bInStreamDisconnected || !m_xInStream.is() ) + dispose(); +} + +void SAL_CALL OWriteStream::seek( sal_Int64 location ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xSeekable.is() ) + throw uno::RuntimeException(); + + m_xSeekable->seek( location ); +} + +sal_Int64 SAL_CALL OWriteStream::getPosition() +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xSeekable.is() ) + throw uno::RuntimeException(); + + return m_xSeekable->getPosition(); +} + +sal_Int64 SAL_CALL OWriteStream::getLength() +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xSeekable.is() ) + throw uno::RuntimeException(); + + return m_xSeekable->getLength(); +} + +void SAL_CALL OWriteStream::truncate() +{ + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_xOutStream.is() ) + throw uno::RuntimeException(); + + uno::Reference< io::XTruncate > xTruncate( m_xOutStream, uno::UNO_QUERY_THROW ); + xTruncate->truncate(); + + m_pImpl->m_bHasDataToFlush = true; + + ModifyParentUnlockMutex_Impl( aGuard ); +} + +void SAL_CALL OWriteStream::dispose() +{ + // should be an internal method since it can be called only from parent storage + { + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_xOutStream.is() ) + CloseOutput_Impl(); + + if ( m_xInStream.is() ) + { + m_xInStream->closeInput(); + m_xInStream.clear(); + } + + m_xSeekable.clear(); + + m_pImpl->m_pAntiImpl = nullptr; + + if ( !m_bInitOnDemand ) + { + try + { + if ( !m_bTransacted ) + { + m_pImpl->Commit(); + } + else + { + // throw away all the changes + m_pImpl->Revert(); + } + } + catch( const uno::Exception& ) + { + uno::Any aCaught( ::cppu::getCaughtException() ); + SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(aCaught)); + throw lang::WrappedTargetRuntimeException("Can not commit/revert the storage!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + } + + m_pImpl = nullptr; + } + + // the listener might try to get rid of parent storage, and the storage would delete this object; + // for now the listener is just notified at the end of the method to workaround the problem + // in future a more elegant way should be found + + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) ); + m_pData->m_aListenersContainer.disposeAndClear( aSource ); +} + +void SAL_CALL OWriteStream::addEventListener( + const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + m_pData->m_aListenersContainer.addInterface( cppu::UnoType<lang::XEventListener>::get(), + xListener ); +} + +void SAL_CALL OWriteStream::removeEventListener( + const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + m_pData->m_aListenersContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), + xListener ); +} + +void SAL_CALL OWriteStream::setEncryptionPassword( const OUString& aPass ) +{ + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + OSL_ENSURE( m_pImpl->m_xPackageStream.is(), "No package stream is set!" ); + + m_pImpl->SetEncrypted( ::comphelper::OStorageHelper::CreatePackageEncryptionData( aPass ) ); + + ModifyParentUnlockMutex_Impl( aGuard ); +} + +void SAL_CALL OWriteStream::removeEncryption() +{ + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + OSL_ENSURE( m_pImpl->m_xPackageStream.is(), "No package stream is set!" ); + + m_pImpl->SetDecrypted(); + + ModifyParentUnlockMutex_Impl( aGuard ); +} + +void SAL_CALL OWriteStream::setEncryptionData( const uno::Sequence< beans::NamedValue >& aEncryptionData ) +{ + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + CheckInitOnDemand(); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + OSL_ENSURE( m_pImpl->m_xPackageStream.is(), "No package stream is set!" ); + + m_pImpl->SetEncrypted( aEncryptionData ); + + ModifyParentUnlockMutex_Impl( aGuard ); +} + +sal_Bool SAL_CALL OWriteStream::hasEncryptionData() +{ + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + if (!m_pImpl) + return false; + + bool bRet = false; + + try + { + bRet = m_pImpl->IsEncrypted(); + + if (!bRet && m_pImpl->m_bUseCommonEncryption && m_pImpl->m_pParent) + bRet = m_pImpl->m_pParent->m_bHasCommonEncryptionData; + } + 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( "Problems on hasEncryptionData!", + static_cast< ::cppu::OWeakObject* >( this ), + aCaught ); + } + + return bRet; +} + +sal_Bool SAL_CALL OWriteStream::hasByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + try + { + getRelationshipByID( sID ); + return true; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "No Element"); + } + + return false; +} + +OUString SAL_CALL OWriteStream::getTargetByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID ); + auto pRel = lcl_findPairByName(aSeq, "Target"); + if (pRel != aSeq.end()) + return pRel->Second; + + return OUString(); +} + +OUString SAL_CALL OWriteStream::getTypeByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID ); + auto pRel = lcl_findPairByName(aSeq, "Type"); + if (pRel != aSeq.end()) + return pRel->Second; + + return OUString(); +} + +uno::Sequence< beans::StringPair > SAL_CALL OWriteStream::getRelationshipByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + // 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("Id", sID); + auto pRel = std::find_if(aSeq.begin(), aSeq.end(), + [&aIDRel](const uno::Sequence<beans::StringPair>& rRel) { + return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); }); + if (pRel != aSeq.end()) + return *pRel; + + throw container::NoSuchElementException(); +} + +uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OWriteStream::getRelationshipsByType( const OUString& sType ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + // TODO/LATER: in future the unification of the ID could be checked + const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); + const beans::StringPair aTypeRel("Type", sType); + std::vector< uno::Sequence<beans::StringPair> > aResult; + aResult.reserve(aSeq.getLength()); + + std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResult), + [&aTypeRel](const uno::Sequence<beans::StringPair>& rRel) { + return std::find(rRel.begin(), rRel.end(), aTypeRel) != rRel.end(); }); + + return comphelper::containerToSequence(aResult); +} + +uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OWriteStream::getAllRelationships() +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + return m_pImpl->GetAllRelationshipsIfAny(); +} + +void SAL_CALL OWriteStream::insertRelationshipByID( const OUString& sID, const uno::Sequence< beans::StringPair >& aEntry, sal_Bool bReplace ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + const beans::StringPair aIDRel("Id", sID); + + uno::Sequence<beans::StringPair>* pPair = nullptr; + + // TODO/LATER: in future the unification of the ID could be checked + uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); + for ( sal_Int32 nInd = 0; nInd < aSeq.getLength(); nInd++ ) + { + const auto& rRel = aSeq[nInd]; + if (std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end()) + pPair = &aSeq.getArray()[nInd]; + } + + if ( pPair && !bReplace ) + throw container::ElementExistException(); // TODO + + if ( !pPair ) + { + sal_Int32 nIDInd = aSeq.getLength(); + aSeq.realloc( nIDInd + 1 ); + pPair = &aSeq.getArray()[nIDInd]; + } + + std::vector<beans::StringPair> aResult; + aResult.reserve(aEntry.getLength() + 1); + + aResult.push_back(aIDRel); + std::copy_if(aEntry.begin(), aEntry.end(), std::back_inserter(aResult), + [](const beans::StringPair& rRel) { return rRel.First != "Id"; }); + + *pPair = comphelper::containerToSequence(aResult); + + m_pImpl->m_aNewRelInfo = aSeq; + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; +} + +void SAL_CALL OWriteStream::removeRelationshipByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); + const beans::StringPair aIDRel("Id", sID); + auto pRel = std::find_if(std::cbegin(aSeq), std::cend(aSeq), + [&aIDRel](const uno::Sequence< beans::StringPair >& rRel) { + return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); }); + if (pRel != std::cend(aSeq)) + { + auto nInd = static_cast<sal_Int32>(std::distance(std::cbegin(aSeq), pRel)); + comphelper::removeElementAt(aSeq, nInd); + + m_pImpl->m_aNewRelInfo = aSeq; + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; + + // TODO/LATER: in future the unification of the ID could be checked + return; + } + + throw container::NoSuchElementException(); +} + +void SAL_CALL OWriteStream::insertRelationships( const uno::Sequence< uno::Sequence< beans::StringPair > >& aEntries, sal_Bool bReplace ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + OUString aIDTag( "Id" ); + const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); + std::vector< uno::Sequence<beans::StringPair> > aResultVec; + aResultVec.reserve(aSeq.getLength() + aEntries.getLength()); + + std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResultVec), + [&aIDTag, &aEntries, bReplace](const uno::Sequence<beans::StringPair>& rTargetRel) { + auto pTargetPair = lcl_findPairByName(rTargetRel, aIDTag); + if (pTargetPair == rTargetRel.end()) + return false; + + bool bIsSourceSame = std::any_of(aEntries.begin(), aEntries.end(), + [&pTargetPair](const uno::Sequence<beans::StringPair>& rSourceEntry) { + return std::find(rSourceEntry.begin(), rSourceEntry.end(), *pTargetPair) != rSourceEntry.end(); }); + + if ( bIsSourceSame && !bReplace ) + throw container::ElementExistException(); + + // if no such element in the provided sequence + return !bIsSourceSame; + }); + + std::transform(aEntries.begin(), aEntries.end(), std::back_inserter(aResultVec), + [&aIDTag](const uno::Sequence<beans::StringPair>& rEntry) -> uno::Sequence<beans::StringPair> { + auto pPair = lcl_findPairByName(rEntry, aIDTag); + if (pPair == rEntry.end()) + throw io::IOException(); // TODO: illegal relation ( no ID ) + + auto aResult = comphelper::sequenceToContainer<std::vector<beans::StringPair>>(rEntry); + auto nIDInd = std::distance(rEntry.begin(), pPair); + std::rotate(aResult.begin(), std::next(aResult.begin(), nIDInd), std::next(aResult.begin(), nIDInd + 1)); + + return comphelper::containerToSequence(aResult); + }); + + m_pImpl->m_aNewRelInfo = comphelper::containerToSequence(aResultVec); + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; +} + +void SAL_CALL OWriteStream::clearRelationships() +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( m_pData->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException(); + + m_pImpl->m_aNewRelInfo.realloc( 0 ); + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; +} + +uno::Reference< beans::XPropertySetInfo > SAL_CALL OWriteStream::getPropertySetInfo() +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + //TODO: + return uno::Reference< beans::XPropertySetInfo >(); +} + +void SAL_CALL OWriteStream::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + m_pImpl->GetStreamProperties(); + static constexpr OUStringLiteral aCompressedString( u"Compressed" ); + static constexpr OUStringLiteral aMediaTypeString( u"MediaType" ); + if ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE && aPropertyName == aMediaTypeString ) + { + // if the "Compressed" property is not set explicitly, the MediaType can change the default value + bool bCompressedValueFromType = true; + OUString aType; + aValue >>= aType; + + if ( !m_pImpl->m_bCompressedSetExplicit ) + { + if ( aType == "image/jpeg" || aType == "image/png" || aType == "image/gif" ) + bCompressedValueFromType = false; + } + + for ( auto& rProp : asNonConstRange(m_pImpl->m_aProps) ) + { + if ( aPropertyName == rProp.Name ) + rProp.Value = aValue; + else if ( !m_pImpl->m_bCompressedSetExplicit && aCompressedString == rProp.Name ) + rProp.Value <<= bCompressedValueFromType; + } + } + else if ( aPropertyName == aCompressedString ) + { + // if the "Compressed" property is not set explicitly, the MediaType can change the default value + m_pImpl->m_bCompressedSetExplicit = true; + for ( auto& rProp : asNonConstRange(m_pImpl->m_aProps) ) + { + if ( aPropertyName == rProp.Name ) + rProp.Value = aValue; + } + } + else if ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE + && aPropertyName == "UseCommonStoragePasswordEncryption" ) + { + bool bUseCommonEncryption = false; + if ( !(aValue >>= bUseCommonEncryption) ) + throw lang::IllegalArgumentException(); //TODO + + if ( m_bInitOnDemand && m_pImpl->m_bHasInsertedStreamOptimization ) + { + // the data stream is provided to the packagestream directly + m_pImpl->m_bUseCommonEncryption = bUseCommonEncryption; + } + else if ( bUseCommonEncryption ) + { + if ( !m_pImpl->m_bUseCommonEncryption ) + { + m_pImpl->SetDecrypted(); + m_pImpl->m_bUseCommonEncryption = true; + } + } + else + m_pImpl->m_bUseCommonEncryption = false; + } + else if ( m_pData->m_nStorageType == embed::StorageFormats::OFOPXML && aPropertyName == aMediaTypeString ) + { + for ( auto& rProp : asNonConstRange(m_pImpl->m_aProps) ) + { + if ( aPropertyName == rProp.Name ) + rProp.Value = aValue; + } + } + else if ( m_pData->m_nStorageType == embed::StorageFormats::OFOPXML && aPropertyName == "RelationsInfoStream" ) + { + uno::Reference< io::XInputStream > xInRelStream; + if ( !( aValue >>= xInRelStream ) || !xInRelStream.is() ) + throw lang::IllegalArgumentException(); // TODO + + 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(); // TODO + } + + m_pImpl->m_xNewRelInfoStream = xInRelStream; + m_pImpl->m_aNewRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED_STREAM; + } + else if ( m_pData->m_nStorageType == embed::StorageFormats::OFOPXML && aPropertyName == "RelationsInfo" ) + { + if ( !(aValue >>= m_pImpl->m_aNewRelInfo) ) + throw lang::IllegalArgumentException(); // TODO + } + else if ( aPropertyName == "Size" ) + throw beans::PropertyVetoException(); // TODO + else if ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE + && ( aPropertyName == "IsEncrypted" || aPropertyName == "Encrypted" ) ) + throw beans::PropertyVetoException(); // TODO + else if ( aPropertyName == "RelId" ) + { + aValue >>= m_pImpl->m_nRelId; + } + else + throw beans::UnknownPropertyException(aPropertyName); // TODO + + m_pImpl->m_bHasDataToFlush = true; + ModifyParentUnlockMutex_Impl( aGuard ); +} + +uno::Any SAL_CALL OWriteStream::getPropertyValue( const OUString& aProp ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( aProp == "RelId" ) + { + return uno::Any( m_pImpl->GetNewRelId() ); + } + + OUString aPropertyName; + if ( aProp == "IsEncrypted" ) + aPropertyName = "Encrypted"; + else + aPropertyName = aProp; + + if ( ( ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE || m_pData->m_nStorageType == embed::StorageFormats::OFOPXML ) + && aPropertyName == "MediaType" ) + || ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE && aPropertyName == "Encrypted" ) + || aPropertyName == "Compressed" ) + { + m_pImpl->GetStreamProperties(); + + auto pProp = std::find_if(std::cbegin(m_pImpl->m_aProps), std::cend(m_pImpl->m_aProps), + [&aPropertyName](const css::beans::PropertyValue& rProp){ return aPropertyName == rProp.Name; }); + if (pProp != std::cend(m_pImpl->m_aProps)) + return pProp->Value; + } + else if ( m_pData->m_nStorageType == embed::StorageFormats::PACKAGE + && aPropertyName == "UseCommonStoragePasswordEncryption" ) + return uno::Any( m_pImpl->m_bUseCommonEncryption ); + else if ( aPropertyName == "Size" ) + { + bool bThrow = false; + try + { + CheckInitOnDemand(); + } + catch (const io::IOException&) + { + bThrow = true; + } + if (bThrow || !m_xSeekable.is()) + throw uno::RuntimeException(); + + return uno::Any( m_xSeekable->getLength() ); + } + + throw beans::UnknownPropertyException(aPropertyName); // TODO +} + +void SAL_CALL OWriteStream::addPropertyChangeListener( + const OUString& /*aPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: +} + +void SAL_CALL OWriteStream::removePropertyChangeListener( + const OUString& /*aPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: +} + +void SAL_CALL OWriteStream::addVetoableChangeListener( + const OUString& /*PropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: +} + +void SAL_CALL OWriteStream::removeVetoableChangeListener( + const OUString& /*PropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + //TODO: +} + +// XTransactedObject + +void OWriteStream::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", "Disposed!"); + throw lang::DisposedException(); + } + + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) ); + + comphelper::OInterfaceContainerHelper2* pContainer = + m_pData->m_aListenersContainer.getContainer( + cppu::UnoType<embed::XTransactionListener>::get()); + if ( !pContainer ) + return; + + comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer ); + while ( pIterator.hasMoreElements( ) ) + { + OSL_ENSURE( nMessage >= 1 && nMessage <= 4, "Wrong internal notification code is used!" ); + + switch( nMessage ) + { + case STOR_MESS_PRECOMMIT: + static_cast<embed::XTransactionListener*>( pIterator.next( ) )->preCommit( aSource ); + break; + case STOR_MESS_COMMITTED: + static_cast<embed::XTransactionListener*>( pIterator.next( ) )->commited( aSource ); + break; + case STOR_MESS_PREREVERT: + static_cast<embed::XTransactionListener*>( pIterator.next( ) )->preRevert( aSource ); + break; + case STOR_MESS_REVERTED: + static_cast< embed::XTransactionListener*>( pIterator.next( ) )->reverted( aSource ); + break; + } + } +} +void SAL_CALL OWriteStream::commit() +{ + SAL_INFO( "package.xstor", "package (mv76033) OWriteStream::commit" ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bTransacted ) + throw uno::RuntimeException(); + + try { + BroadcastTransaction( STOR_MESS_PRECOMMIT ); + + osl::ClearableMutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + m_pImpl->Commit(); + + // when the storage is committed the parent is modified + ModifyParentUnlockMutex_Impl( aGuard ); + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( "Problems on commit!", + static_cast< ::cppu::OWeakObject* >( this ), + aCaught ); + } + + BroadcastTransaction( STOR_MESS_COMMITTED ); +} + +void SAL_CALL OWriteStream::revert() +{ + SAL_INFO( "package.xstor", "package (mv76033) OWriteStream::revert" ); + + // the method removes all the changes done after last commit + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bTransacted ) + throw uno::RuntimeException(); + + BroadcastTransaction( STOR_MESS_PREREVERT ); + + { + osl::MutexGuard aGuard(m_pData->m_xSharedMutex->GetMutex()); + + if (!m_pImpl) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + try { + m_pImpl->Revert(); + } + catch (const io::IOException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const embed::StorageWrappedTargetException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException("Problems on revert!", + static_cast<::cppu::OWeakObject*>(this), + aCaught); + } + } + + BroadcastTransaction( STOR_MESS_REVERTED ); +} + +// XTransactionBroadcaster + +void SAL_CALL OWriteStream::addTransactionListener( const uno::Reference< embed::XTransactionListener >& aListener ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bTransacted ) + throw uno::RuntimeException(); + + m_pData->m_aListenersContainer.addInterface( cppu::UnoType<embed::XTransactionListener>::get(), + aListener ); +} + +void SAL_CALL OWriteStream::removeTransactionListener( const uno::Reference< embed::XTransactionListener >& aListener ) +{ + ::osl::MutexGuard aGuard( m_pData->m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", "Disposed!"); + throw lang::DisposedException(); + } + + if ( !m_bTransacted ) + throw uno::RuntimeException(); + + m_pData->m_aListenersContainer.removeInterface( cppu::UnoType<embed::XTransactionListener>::get(), + aListener ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/owriteablestream.hxx b/package/source/xstor/owriteablestream.hxx new file mode 100644 index 000000000..6c2b657e3 --- /dev/null +++ b/package/source/xstor/owriteablestream.hxx @@ -0,0 +1,359 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_OWRITEABLESTREAM_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_OWRITEABLESTREAM_HXX + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/packages/XDataSinkEncrSupport.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/embed/XEncryptionProtectedSource2.hpp> +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/XRelationshipAccess.hpp> +#include <com/sun/star/embed/XExtendedStorageStream.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/XTransactionBroadcaster.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/StringPair.hpp> + +#include <cppuhelper/weak.hxx> + +#include <comphelper/bytereader.hxx> +#include <comphelper/refcountedmutex.hxx> +#include <comphelper/sequenceashashmap.hxx> + +#include <vector> +#include <memory> +#include <string_view> + +#include "ocompinstream.hxx" + +namespace com::sun::star::uno { + class XComponentContext; +} + +namespace package { + // all data in aHash1 is contained in aHash2 + bool PackageEncryptionDataLessOrEqual( const ::comphelper::SequenceAsHashMap& aHash1, const ::comphelper::SequenceAsHashMap& aHash2 ); +} + +struct WSInternalData_Impl; + +struct OStorage_Impl; +class OWriteStream; + +struct OWriteStream_Impl +{ + rtl::Reference<comphelper::RefCountedMutex> m_xMutex; + + friend struct OStorage_Impl; + friend class OWriteStream; + friend class OInputCompStream; + + OWriteStream* m_pAntiImpl; + OUString m_aTempURL; + + css::uno::Reference< css::io::XStream > m_xCacheStream; + css::uno::Reference< css::io::XSeekable > m_xCacheSeek; + + std::vector< OInputCompStream* > m_aInputStreamsVector; + + bool m_bHasDataToFlush; // only modified elements will be sent to the original content + bool m_bFlushed; // sending the streams is coordinated by the root storage of the package + + css::uno::Reference< css::packages::XDataSinkEncrSupport > m_xPackageStream; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + OStorage_Impl* m_pParent; + + css::uno::Sequence< css::beans::PropertyValue > m_aProps; + + bool m_bForceEncrypted; + + bool m_bUseCommonEncryption; + bool m_bHasCachedEncryptionData; + ::comphelper::SequenceAsHashMap m_aEncryptionData; + + bool m_bCompressedSetExplicit; + + css::uno::Reference< css::lang::XSingleServiceFactory > m_xPackage; + + bool m_bHasInsertedStreamOptimization; + + sal_Int32 m_nStorageType; + + // Relations info related data, stored in *.rels file in OFOPXML format + css::uno::Reference< css::io::XInputStream > m_xOrigRelInfoStream; + css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aOrigRelInfo; + bool m_bOrigRelInfoBroken; + + css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aNewRelInfo; + css::uno::Reference< css::io::XInputStream > m_xNewRelInfoStream; + sal_Int16 m_nRelInfoStatus; + sal_Int32 m_nRelId; + +private: + OUString const & GetFilledTempFileIfNo( const css::uno::Reference< css::io::XInputStream >& xStream ); + OUString const & FillTempGetFileName(); + css::uno::Reference< css::io::XStream > GetTempFileAsStream(); + css::uno::Reference< css::io::XInputStream > GetTempFileAsInputStream(); + + css::uno::Reference< css::io::XStream > GetStream_Impl( sal_Int32 nStreamMode, + bool bHierarchyAccess ); + + /// @throws css::packages::NoEncryptionException + ::comphelper::SequenceAsHashMap GetCommonRootEncryptionData(); + + css::uno::Sequence< css::beans::PropertyValue > ReadPackageStreamProperties(); + css::uno::Sequence< css::beans::PropertyValue > InsertOwnProps( + const css::uno::Sequence< css::beans::PropertyValue >& aProps, + bool bUseCommonEncryption ); + +public: + OWriteStream_Impl( + OStorage_Impl* pParent, + const css::uno::Reference< css::packages::XDataSinkEncrSupport >& xPackageStream, + const css::uno::Reference< css::lang::XSingleServiceFactory >& xPackage, + const css::uno::Reference< css::uno::XComponentContext >& xContext, + bool bForceEncrypted, + sal_Int32 nStorageType, + bool bDefaultCompress, + const css::uno::Reference< css::io::XInputStream >& xRelInfoStream = + css::uno::Reference< css::io::XInputStream >() ); + + ~OWriteStream_Impl(); + + void CleanCacheStream(); + + bool UsesCommonEncryption_Impl() const { return m_bUseCommonEncryption; } + bool HasTempFile_Impl() const { return ( m_aTempURL.getLength() != 0 ); } + bool IsTransacted(); + + bool HasWriteOwner_Impl() const { return ( m_pAntiImpl != nullptr ); } + + void InsertIntoPackageFolder( + const OUString& aName, + const css::uno::Reference< css::container::XNameContainer >& xParentPackageFolder ); + + void SetToBeCommited() { m_bFlushed = true; } + + bool HasCachedEncryptionData() const { return m_bHasCachedEncryptionData; } + ::comphelper::SequenceAsHashMap& GetCachedEncryptionData() { return m_aEncryptionData; } + + bool IsModified() const { return m_bHasDataToFlush || m_bFlushed; } + + bool IsEncrypted(); + void SetDecrypted(); + void SetEncrypted( const ::comphelper::SequenceAsHashMap& aEncryptionData ); + + void DisposeWrappers(); + + void InsertStreamDirectly( + const css::uno::Reference< css::io::XInputStream >& xInStream, + const css::uno::Sequence< css::beans::PropertyValue >& aProps ); + + void Commit(); + void Revert(); + + css::uno::Sequence< css::beans::PropertyValue > const & GetStreamProperties(); + + css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > GetAllRelationshipsIfAny(); + + void CopyInternallyTo_Impl( const css::uno::Reference< css::io::XStream >& xDestStream, + const ::comphelper::SequenceAsHashMap& aEncryptionData ); + void CopyInternallyTo_Impl( const css::uno::Reference< css::io::XStream >& xDestStream ); + + css::uno::Reference< css::io::XStream > GetStream( + sal_Int32 nStreamMode, + const ::comphelper::SequenceAsHashMap& aEncryptionData, + bool bHierarchyAccess ); + + css::uno::Reference< css::io::XStream > GetStream( + sal_Int32 nStreamMode, + bool bHierarchyAccess ); + + css::uno::Reference< css::io::XInputStream > GetRawInStream(); + css::uno::Reference< css::io::XInputStream > GetPlainRawInStream(); + + void InputStreamDisposed( OInputCompStream* pStream ); + + void CreateReadonlyCopyBasedOnData( + const css::uno::Reference< css::io::XInputStream >& xDataToCopy, + const css::uno::Sequence< css::beans::PropertyValue >& aProps, + css::uno::Reference< css::io::XStream >& xTargetStream ); + + void GetCopyOfLastCommit( css::uno::Reference< css::io::XStream >& xTargetStream ); + void GetCopyOfLastCommit( + css::uno::Reference< css::io::XStream >& xTargetStream, + const ::comphelper::SequenceAsHashMap& aEncryptionData ); + + void CommitStreamRelInfo( + const css::uno::Reference< css::embed::XStorage >& xRelStorage, + std::u16string_view aOrigStreamName, + std::u16string_view aNewStreamName ); + + void ReadRelInfoIfNecessary(); + + sal_Int32 GetNewRelId() { return m_nRelId ++; } +}; + +class OWriteStream : public css::lang::XTypeProvider + , public css::io::XInputStream + , public css::io::XOutputStream + , public css::embed::XExtendedStorageStream + , public css::io::XSeekable + , public css::io::XTruncate + , public css::embed::XEncryptionProtectedSource2 + , public css::embed::XRelationshipAccess + , public css::embed::XTransactedObject + , public css::embed::XTransactionBroadcaster + , public css::beans::XPropertySet + , public css::lang::XUnoTunnel + , public ::cppu::OWeakObject + , public comphelper::ByteWriter +{ + friend struct OWriteStream_Impl; + +protected: + css::uno::Reference < css::io::XInputStream > m_xInStream; + css::uno::Reference < css::io::XOutputStream > m_xOutStream; + css::uno::Reference < css::io::XSeekable > m_xSeekable; + + OWriteStream_Impl* m_pImpl; + std::unique_ptr<WSInternalData_Impl> m_pData; + + bool m_bInStreamDisconnected; + bool m_bInitOnDemand; + sal_Int64 m_nInitPosition; + + bool m_bTransacted; + + OWriteStream( OWriteStream_Impl* pImpl, bool bTransacted ); + OWriteStream( OWriteStream_Impl* pImpl, css::uno::Reference< css::io::XStream > const & xStream, bool bTransacted ); + + void CloseOutput_Impl(); + + void CopyToStreamInternally_Impl( const css::uno::Reference< css::io::XStream >& xStream ); + + void ModifyParentUnlockMutex_Impl(osl::ClearableMutexGuard& aGuard); + + void BroadcastTransaction( sal_Int8 nMessage ); + +public: + + virtual ~OWriteStream() override; + + void CheckInitOnDemand(); + void DeInit(); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XInputStream + virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override; + virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override; + virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override; + virtual sal_Int32 SAL_CALL available( ) override; + virtual void SAL_CALL closeInput( ) override; + + // XOutputStream + virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override; + virtual void SAL_CALL flush( ) override; + virtual void SAL_CALL closeOutput( ) override; + + //XSeekable + virtual void SAL_CALL seek( sal_Int64 location ) override; + virtual sal_Int64 SAL_CALL getPosition() override; + virtual sal_Int64 SAL_CALL getLength() override; + + //XStream + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override; + virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream( ) override; + + // XTruncate + virtual void SAL_CALL truncate() override; + + //XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + //XEncryptionProtectedSource + virtual void SAL_CALL setEncryptionPassword( const OUString& aPass ) override; + virtual void SAL_CALL removeEncryption() override; + + //XEncryptionProtectedSource2 + virtual void SAL_CALL setEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override; + virtual sal_Bool SAL_CALL hasEncryptionData() override; + + //XRelationshipAccess + virtual sal_Bool SAL_CALL hasByID( const OUString& sID ) override; + virtual OUString SAL_CALL getTargetByID( const OUString& sID ) override; + virtual OUString SAL_CALL getTypeByID( const OUString& sID ) override; + virtual css::uno::Sequence< css::beans::StringPair > SAL_CALL getRelationshipByID( const OUString& sID ) override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getRelationshipsByType( const OUString& sType ) override; + virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getAllRelationships( ) override; + virtual void SAL_CALL insertRelationshipByID( const OUString& sID, const css::uno::Sequence< css::beans::StringPair >& aEntry, sal_Bool bReplace ) override; + virtual void SAL_CALL removeRelationshipByID( const OUString& sID ) override; + virtual void SAL_CALL insertRelationships( const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > >& aEntries, sal_Bool bReplace ) override; + virtual void SAL_CALL clearRelationships( ) override; + + //XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XTransactedObject + virtual void SAL_CALL commit() override; + virtual void SAL_CALL revert() override; + + // XTransactionBroadcaster + virtual void SAL_CALL addTransactionListener( + const css::uno::Reference< css::embed::XTransactionListener >& aListener ) override; + virtual void SAL_CALL removeTransactionListener( + const css::uno::Reference< css::embed::XTransactionListener >& aListener ) override; + + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + // comphelper::ByteWriter + virtual sal_Int32 writeSomeBytes(const sal_Int8* aData, sal_Int32 nBytesToWrite) override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/selfterminatefilestream.cxx b/package/source/xstor/selfterminatefilestream.cxx new file mode 100644 index 000000000..9ee3673ff --- /dev/null +++ b/package/source/xstor/selfterminatefilestream.cxx @@ -0,0 +1,105 @@ +/* -*- 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 <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include "selfterminatefilestream.hxx" +#include <comphelper/processfactory.hxx> + +using namespace ::com::sun::star; + +OSelfTerminateFileStream::OSelfTerminateFileStream( const uno::Reference< uno::XComponentContext >& xContext, const OUString& aURL ) +: m_aURL( aURL ) +{ + uno::Reference< uno::XComponentContext > xOwnContext = xContext; + if ( !xOwnContext.is() ) + xOwnContext.set( ::comphelper::getProcessComponentContext(), uno::UNO_SET_THROW ); + + // IMPORTANT: The implementation is based on idea that m_xFileAccess, m_xInputStream and m_xSeekable are always set + // otherwise an exception is thrown in constructor + + m_xFileAccess.set( ucb::SimpleFileAccess::create(xOwnContext) ); + + m_xInputStream.set( m_xFileAccess->openFileRead( aURL ), uno::UNO_SET_THROW ); + m_xSeekable.set( m_xInputStream, uno::UNO_QUERY_THROW ); +} + +OSelfTerminateFileStream::~OSelfTerminateFileStream() +{ + CloseStreamDeleteFile(); +} + +void OSelfTerminateFileStream::CloseStreamDeleteFile() +{ + try + { + m_xInputStream->closeInput(); + } + catch( uno::Exception& ) + {} + + try + { + m_xFileAccess->kill( m_aURL ); + } + catch( uno::Exception& ) + {} +} + +sal_Int32 SAL_CALL OSelfTerminateFileStream::readBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) +{ + return m_xInputStream->readBytes( aData, nBytesToRead ); +} + +sal_Int32 SAL_CALL OSelfTerminateFileStream::readSomeBytes( uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) +{ + return m_xInputStream->readSomeBytes( aData, nMaxBytesToRead ); +} + +void SAL_CALL OSelfTerminateFileStream::skipBytes( sal_Int32 nBytesToSkip ) +{ + return m_xInputStream->skipBytes( nBytesToSkip ); +} + +sal_Int32 SAL_CALL OSelfTerminateFileStream::available( ) +{ + return m_xInputStream->available(); +} + +void SAL_CALL OSelfTerminateFileStream::closeInput( ) +{ + CloseStreamDeleteFile(); +} + +void SAL_CALL OSelfTerminateFileStream::seek( sal_Int64 location ) +{ + m_xSeekable->seek( location ); +} + +sal_Int64 SAL_CALL OSelfTerminateFileStream::getPosition() +{ + return m_xSeekable->getPosition(); +} + +sal_Int64 SAL_CALL OSelfTerminateFileStream::getLength() +{ + return m_xSeekable->getLength(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/selfterminatefilestream.hxx b/package/source/xstor/selfterminatefilestream.hxx new file mode 100644 index 000000000..14bf3a049 --- /dev/null +++ b/package/source/xstor/selfterminatefilestream.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_SELFTERMINATEFILESTREAM_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_SELFTERMINATEFILESTREAM_HXX + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess3.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/implbase.hxx> + +struct OWriteStream_Impl; + +class OSelfTerminateFileStream final : public cppu::WeakImplHelper< css::io::XInputStream, + css::io::XSeekable > +{ + css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xFileAccess; + + OUString m_aURL; + + css::uno::Reference< css::io::XInputStream > m_xInputStream; + css::uno::Reference< css::io::XSeekable > m_xSeekable; + +public: + OSelfTerminateFileStream( const css::uno::Reference< css::uno::XComponentContext >& xContext, const OUString& aURL ); + + virtual ~OSelfTerminateFileStream() override; + + void CloseStreamDeleteFile(); + + // XInputStream + virtual sal_Int32 SAL_CALL readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead ) override; + virtual sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead ) override; + virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip ) override; + virtual sal_Int32 SAL_CALL available() override; + virtual void SAL_CALL closeInput() override; + + //XSeekable + virtual void SAL_CALL seek( sal_Int64 location ) override; + virtual sal_Int64 SAL_CALL getPosition() override; + virtual sal_Int64 SAL_CALL getLength() override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/switchpersistencestream.cxx b/package/source/xstor/switchpersistencestream.cxx new file mode 100644 index 000000000..ff9828a72 --- /dev/null +++ b/package/source/xstor/switchpersistencestream.cxx @@ -0,0 +1,409 @@ +/* -*- 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 <osl/diagnose.h> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/NotConnectedException.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <comphelper/storagehelper.hxx> +#include "switchpersistencestream.hxx" + +using namespace ::com::sun::star; + +struct SPStreamData_Impl +{ + bool m_bInStreamBased; + + // the streams below are not visible from outside so there is no need to remember position + + // original stream related members + uno::Reference< io::XTruncate > m_xOrigTruncate; + uno::Reference< io::XSeekable > m_xOrigSeekable; + uno::Reference< io::XInputStream > m_xOrigInStream; + uno::Reference< io::XOutputStream > m_xOrigOutStream; + + bool m_bInOpen; + bool m_bOutOpen; + + SPStreamData_Impl( + bool bInStreamBased, + const uno::Reference< io::XTruncate >& xOrigTruncate, + const uno::Reference< io::XSeekable >& xOrigSeekable, + const uno::Reference< io::XInputStream >& xOrigInStream, + const uno::Reference< io::XOutputStream >& xOrigOutStream, + bool bInOpen, + bool bOutOpen ) + : m_bInStreamBased( bInStreamBased ) + , m_xOrigTruncate( xOrigTruncate ) + , m_xOrigSeekable( xOrigSeekable ) + , m_xOrigInStream( xOrigInStream ) + , m_xOrigOutStream( xOrigOutStream ) + , m_bInOpen( bInOpen ) + , m_bOutOpen( bOutOpen ) + { + } +}; + +SwitchablePersistenceStream::SwitchablePersistenceStream( + const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< io::XStream >& xStream ) +: m_xContext( xContext ) +{ + SwitchPersistenceTo( xStream ); +} + +SwitchablePersistenceStream::SwitchablePersistenceStream( + const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< io::XInputStream >& xInputStream ) +: m_xContext( xContext ) +{ + SwitchPersistenceTo( xInputStream ); +} + +SwitchablePersistenceStream::~SwitchablePersistenceStream() +{ + CloseAll_Impl(); +} + +void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XStream >& xStream ) +{ + uno::Reference< io::XTruncate > xNewTruncate( xStream, uno::UNO_QUERY_THROW ); + uno::Reference< io::XSeekable > xNewSeekable( xStream, uno::UNO_QUERY_THROW ); + uno::Reference< io::XInputStream > xNewInStream = xStream->getInputStream(); + uno::Reference< io::XOutputStream > xNewOutStream = xStream->getOutputStream(); + if ( !xNewInStream.is() || !xNewOutStream.is() ) + throw uno::RuntimeException(); + + sal_Int64 nPos = 0; + bool bInOpen = false; + bool bOutOpen = false; + + if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() ) + { + // check that the length is the same + if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() ) + throw uno::RuntimeException(); + + // get the current position + nPos = m_pStreamData->m_xOrigSeekable->getPosition(); + bInOpen = m_pStreamData->m_bInOpen; + bOutOpen = m_pStreamData->m_bOutOpen; + } + + xNewSeekable->seek( nPos ); + + CloseAll_Impl(); + + m_pStreamData.reset( new SPStreamData_Impl( false, + xNewTruncate, xNewSeekable, xNewInStream, xNewOutStream, + bInOpen, bOutOpen ) ); +} + +void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XInputStream >& xInputStream ) +{ + uno::Reference< io::XTruncate > xNewTruncate; + uno::Reference< io::XSeekable > xNewSeekable( xInputStream, uno::UNO_QUERY_THROW ); + uno::Reference< io::XOutputStream > xNewOutStream; + if ( !xInputStream.is() ) + throw uno::RuntimeException(); + + sal_Int64 nPos = 0; + bool bInOpen = false; + bool bOutOpen = false; + + if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() ) + { + // check that the length is the same + if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() ) + throw uno::RuntimeException(); + + // get the current position + nPos = m_pStreamData->m_xOrigSeekable->getPosition(); + bInOpen = m_pStreamData->m_bInOpen; + bOutOpen = m_pStreamData->m_bOutOpen; + } + + xNewSeekable->seek( nPos ); + + CloseAll_Impl(); + + m_pStreamData.reset( new SPStreamData_Impl( true, + xNewTruncate, xNewSeekable, xInputStream, xNewOutStream, + bInOpen, bOutOpen ) ); + +} + +void SwitchablePersistenceStream::CopyAndSwitchPersistenceTo( const uno::Reference< io::XStream >& xStream ) +{ + uno::Reference< io::XStream > xTargetStream = xStream; + uno::Reference< io::XSeekable > xTargetSeek; + + if ( !xTargetStream.is() ) + { + xTargetStream.set( io::TempFile::create(m_xContext), uno::UNO_QUERY_THROW ); + xTargetSeek.set( xTargetStream, uno::UNO_QUERY_THROW ); + } + else + { + // the provided stream must be empty + xTargetSeek.set( xTargetStream, uno::UNO_QUERY_THROW ); + if ( xTargetSeek->getLength() ) + throw io::IOException("provided stream not empty"); + } + + uno::Reference< io::XTruncate > xTargetTruncate( xTargetStream, uno::UNO_QUERY_THROW ); + uno::Reference< io::XInputStream > xTargetInStream = xTargetStream->getInputStream(); + uno::Reference< io::XOutputStream > xTargetOutStream = xTargetStream->getOutputStream(); + if ( !xTargetInStream.is() || !xTargetOutStream.is() ) + throw uno::RuntimeException(); + + if ( !m_pStreamData->m_xOrigInStream.is() || !m_pStreamData->m_xOrigSeekable.is() ) + throw uno::RuntimeException(); + + sal_Int64 nPos = m_pStreamData->m_xOrigSeekable->getPosition(); + m_pStreamData->m_xOrigSeekable->seek( 0 ); + ::comphelper::OStorageHelper::CopyInputToOutput( m_pStreamData->m_xOrigInStream, xTargetOutStream ); + xTargetOutStream->flush(); + xTargetSeek->seek( nPos ); + + bool bInOpen = m_pStreamData->m_bInOpen; + bool bOutOpen = m_pStreamData->m_bOutOpen; + + CloseAll_Impl(); + + m_pStreamData.reset( new SPStreamData_Impl( false, + xTargetTruncate, xTargetSeek, xTargetInStream, xTargetOutStream, + bInOpen, bOutOpen ) ); +} + +void SwitchablePersistenceStream::CloseAll_Impl() +{ + m_pStreamData.reset(); +} + +// css::io::XStream +uno::Reference< io::XInputStream > SAL_CALL SwitchablePersistenceStream::getInputStream( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( m_pStreamData ) + m_pStreamData->m_bInOpen = true; + return static_cast< io::XInputStream* >( this ); +} + +uno::Reference< io::XOutputStream > SAL_CALL SwitchablePersistenceStream::getOutputStream( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( m_pStreamData ) + m_pStreamData->m_bOutOpen = true; + return static_cast< io::XOutputStream* >( this ); +} + +// css::io::XInputStream +::sal_Int32 SAL_CALL SwitchablePersistenceStream::readBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigInStream.is() ) + throw uno::RuntimeException(); + + return m_pStreamData->m_xOrigInStream->readBytes( aData, nBytesToRead ); +} + +::sal_Int32 SAL_CALL SwitchablePersistenceStream::readSomeBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigInStream.is() ) + throw uno::RuntimeException(); + + return m_pStreamData->m_xOrigInStream->readBytes( aData, nMaxBytesToRead ); +} + +void SAL_CALL SwitchablePersistenceStream::skipBytes( ::sal_Int32 nBytesToSkip ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigInStream.is() ) + throw uno::RuntimeException(); + + m_pStreamData->m_xOrigInStream->skipBytes( nBytesToSkip ); +} + +::sal_Int32 SAL_CALL SwitchablePersistenceStream::available( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigInStream.is() ) + throw uno::RuntimeException(); + + return m_pStreamData->m_xOrigInStream->available(); +} + +void SAL_CALL SwitchablePersistenceStream::closeInput() +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + m_pStreamData->m_bInOpen = false; + if ( !m_pStreamData->m_bOutOpen ) + CloseAll_Impl(); +} + +// css::io::XOutputStream +void SAL_CALL SwitchablePersistenceStream::writeBytes( const uno::Sequence< ::sal_Int8 >& aData ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + if ( m_pStreamData->m_bInStreamBased ) + throw io::IOException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigOutStream.is() ) + throw uno::RuntimeException(); + + m_pStreamData->m_xOrigOutStream->writeBytes( aData ); +} + +void SAL_CALL SwitchablePersistenceStream::flush( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData || m_pStreamData->m_bInStreamBased ) + { + OSL_FAIL( "flush() is not acceptable!" ); + return; + // in future throw exception, for now some code might call flush() on closed stream + // since file ucp implementation allows it + // throw io::NotConnectedException(); + } + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigOutStream.is() ) + throw uno::RuntimeException(); + + m_pStreamData->m_xOrigOutStream->flush(); +} + +void SAL_CALL SwitchablePersistenceStream::closeOutput( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + m_pStreamData->m_bOutOpen = false; + if ( !m_pStreamData->m_bInOpen ) + CloseAll_Impl(); +} + +// css::io::XTruncate +void SAL_CALL SwitchablePersistenceStream::truncate( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + if ( m_pStreamData->m_bInStreamBased ) + throw io::IOException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigTruncate.is() ) + throw uno::RuntimeException(); + + m_pStreamData->m_xOrigTruncate->truncate(); +} + +// css::io::XSeekable +void SAL_CALL SwitchablePersistenceStream::seek( ::sal_Int64 location ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigSeekable.is() ) + throw uno::RuntimeException(); + + m_pStreamData->m_xOrigSeekable->seek( location ); +} + +::sal_Int64 SAL_CALL SwitchablePersistenceStream::getPosition( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigSeekable.is() ) + throw uno::RuntimeException(); + + return m_pStreamData->m_xOrigSeekable->getPosition(); +} + +::sal_Int64 SAL_CALL SwitchablePersistenceStream::getLength( ) +{ + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + // the original stream data should be provided + if ( !m_pStreamData->m_xOrigSeekable.is() ) + throw uno::RuntimeException(); + + return m_pStreamData->m_xOrigSeekable->getLength(); +} + +void SAL_CALL SwitchablePersistenceStream::waitForCompletion() +{ + if ( !m_pStreamData ) + throw io::NotConnectedException(); + + uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( m_pStreamData->m_xOrigOutStream, uno::UNO_QUERY ); + if ( asyncOutputMonitor.is() ) + asyncOutputMonitor->waitForCompletion(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/switchpersistencestream.hxx b/package/source/xstor/switchpersistencestream.hxx new file mode 100644 index 000000000..f1e9ddcee --- /dev/null +++ b/package/source/xstor/switchpersistencestream.hxx @@ -0,0 +1,107 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_SWITCHPERSISTENCESTREAM_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_SWITCHPERSISTENCESTREAM_HXX + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/XAsyncOutputMonitor.hpp> +#include <mutex> +#include <cppuhelper/implbase.hxx> + +// SwitchablePersistenceStream + +// Allows to switch the stream persistence on the fly. The target +// stream ( if not filled by the implementation ) MUST have the same +// size as the original one! + +struct SPStreamData_Impl; +class SwitchablePersistenceStream + : public ::cppu::WeakImplHelper < + css::io::XStream, + css::io::XInputStream, + css::io::XOutputStream, + css::io::XTruncate, + css::io::XSeekable, + css::io::XAsyncOutputMonitor > +{ + std::mutex m_aMutex; + + const css::uno::Reference< css::uno::XComponentContext > m_xContext; + + std::unique_ptr<SPStreamData_Impl> m_pStreamData; + + void CloseAll_Impl(); + +public: + + SwitchablePersistenceStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XStream >& xStream ); + + SwitchablePersistenceStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Reference< css::io::XInputStream >& xInStream ); + + virtual ~SwitchablePersistenceStream() override; + + void SwitchPersistenceTo( const css::uno::Reference< css::io::XStream >& xStream ); + + void SwitchPersistenceTo( const css::uno::Reference< css::io::XInputStream >& xInputStream ); + + void CopyAndSwitchPersistenceTo( const css::uno::Reference< css::io::XStream >& xStream ); + +// css::io::XStream + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override; + virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream( ) override; + +// css::io::XInputStream + virtual ::sal_Int32 SAL_CALL readBytes( css::uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead ) override; + virtual ::sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead ) override; + virtual void SAL_CALL skipBytes( ::sal_Int32 nBytesToSkip ) override; + virtual ::sal_Int32 SAL_CALL available( ) override; + virtual void SAL_CALL closeInput( ) override; + +// css::io::XOutputStream + virtual void SAL_CALL writeBytes( const css::uno::Sequence< ::sal_Int8 >& aData ) override; + virtual void SAL_CALL flush( ) override; + virtual void SAL_CALL closeOutput( ) override; + +// css::io::XTruncate + virtual void SAL_CALL truncate( ) override; + +// css::io::XSeekable + virtual void SAL_CALL seek( ::sal_Int64 location ) override; + virtual ::sal_Int64 SAL_CALL getPosition( ) override; + virtual ::sal_Int64 SAL_CALL getLength( ) override; + +// css::io::XAsyncOutputMonitor + virtual void SAL_CALL waitForCompletion( ) override; + +}; + +#endif // INCLUDED_PACKAGE_SOURCE_XSTOR_SWITCHPERSISTENCESTREAM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/xfactory.cxx b/package/source/xstor/xfactory.cxx new file mode 100644 index 000000000..38c8d4df2 --- /dev/null +++ b/package/source/xstor/xfactory.cxx @@ -0,0 +1,293 @@ +/* -*- 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 <sal/log.hxx> + +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/StorageFormats.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XSeekable.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <comphelper/storagehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <osl/diagnose.h> + +#include "xfactory.hxx" +#include "xstorage.hxx" + +using namespace ::com::sun::star; + +#if OSL_DEBUG_LEVEL > 0 +#define THROW_WHERE SAL_WHERE +#else +#define THROW_WHERE "" +#endif + +static bool CheckPackageSignature_Impl( const uno::Reference< io::XInputStream >& xInputStream, + const uno::Reference< io::XSeekable >& xSeekable ) +{ + if ( !xInputStream.is() || !xSeekable.is() ) + throw uno::RuntimeException(); + + if ( xSeekable->getLength() ) + { + uno::Sequence< sal_Int8 > aData( 4 ); + xSeekable->seek( 0 ); + sal_Int32 nRead = xInputStream->readBytes( aData, 4 ); + xSeekable->seek( 0 ); + + // TODO/LATER: should the disk spanned files be supported? + // 0x50, 0x4b, 0x07, 0x08 + return ( nRead == 4 && aData[0] == 0x50 && aData[1] == 0x4b && aData[2] == 0x03 && aData[3] == 0x04 ); + } + else + return true; // allow to create a storage based on empty stream +} + + + +uno::Reference< uno::XInterface > SAL_CALL OStorageFactory::createInstance() +{ + // TODO: reimplement TempStream service to support XStream interface + uno::Reference < io::XStream > xTempStream( + io::TempFile::create(m_xContext), + uno::UNO_QUERY_THROW ); + + return static_cast<OWeakObject*>(new OStorage(xTempStream, embed::ElementModes::READWRITE, + uno::Sequence<beans::PropertyValue>(), m_xContext, + embed::StorageFormats::PACKAGE)); +} + +uno::Reference< uno::XInterface > SAL_CALL OStorageFactory::createInstanceWithArguments( + const uno::Sequence< uno::Any >& aArguments ) +{ + // The request for storage can be done with up to three arguments + + // The first argument specifies a source for the storage + // it can be URL, XStream, XInputStream. + // The second value is a mode the storage should be open in. + // And the third value is a media descriptor. + + sal_Int32 nArgNum = aArguments.getLength(); + OSL_ENSURE( nArgNum < 4, "Wrong parameter number" ); + + if ( !nArgNum ) + return createInstance(); + + // first try to retrieve storage open mode if any + // by default the storage will be open in readonly mode + sal_Int32 nStorageMode = embed::ElementModes::READ; + if ( nArgNum >= 2 ) + { + if( !( aArguments[1] >>= nStorageMode ) ) + { + OSL_FAIL( "Wrong second argument!" ); + throw lang::IllegalArgumentException(); // TODO: + } + // it's always possible to read written storage in this implementation + nStorageMode |= embed::ElementModes::READ; + } + + if ( ( nStorageMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE + && ( nStorageMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE ) + throw lang::IllegalArgumentException(); // TODO: + + // retrieve storage source stream + OUString aURL; + uno::Reference< io::XStream > xStream; + uno::Reference< io::XInputStream > xInputStream; + + if ( aArguments[0] >>= aURL ) + { + if ( aURL.isEmpty() ) + { + OSL_FAIL( "Empty URL is provided!" ); + throw lang::IllegalArgumentException(); // TODO: + } + + if ( aURL.startsWithIgnoreAsciiCase("vnd.sun.star.pkg:") ) + { + OSL_FAIL( "Packages URL's are not valid for storages!" ); // ??? + throw lang::IllegalArgumentException(); // TODO: + } + + uno::Reference < ucb::XSimpleFileAccess3 > xTempAccess( + ucb::SimpleFileAccess::create( + m_xContext ) ); + + if ( nStorageMode & embed::ElementModes::WRITE ) + xStream = xTempAccess->openFileReadWrite( aURL ); + else + xInputStream = xTempAccess->openFileRead( aURL ); + } + else if ( !( aArguments[0] >>= xStream ) && !( aArguments[0] >>= xInputStream ) ) + { + OSL_FAIL( "Wrong first argument!" ); + throw uno::Exception("wrong first arg", nullptr); // TODO: Illegal argument + } + + // retrieve mediadescriptor and set storage properties + uno::Sequence< beans::PropertyValue > aDescr; + uno::Sequence< beans::PropertyValue > aPropsToSet; + + sal_Int32 nStorageType = embed::StorageFormats::PACKAGE; + + if ( nArgNum >= 3 ) + { + if( aArguments[2] >>= aDescr ) + { + if ( !aURL.isEmpty() ) + { + aPropsToSet = { comphelper::makePropertyValue("URL", aURL) }; + } + + sal_Int32 nNumArgs = 1; + for ( const auto& rProp : std::as_const(aDescr) ) + { + if ( rProp.Name == "InteractionHandler" + || rProp.Name == "Password" + || rProp.Name == "RepairPackage" + || rProp.Name == "StatusIndicator" ) + { + aPropsToSet.realloc( ++nNumArgs ); + auto pPropsToSet = aPropsToSet.getArray(); + pPropsToSet[nNumArgs-1].Name = rProp.Name; + pPropsToSet[nNumArgs-1].Value = rProp.Value; + } + else if ( rProp.Name == "StorageFormat" ) + { + OUString aFormatName; + sal_Int32 nFormatID = 0; + if ( rProp.Value >>= aFormatName ) + { + if ( aFormatName == PACKAGE_STORAGE_FORMAT_STRING ) + nStorageType = embed::StorageFormats::PACKAGE; + else if ( aFormatName == ZIP_STORAGE_FORMAT_STRING ) + nStorageType = embed::StorageFormats::ZIP; + else if ( aFormatName == OFOPXML_STORAGE_FORMAT_STRING ) + nStorageType = embed::StorageFormats::OFOPXML; + else + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); + } + else if ( rProp.Value >>= nFormatID ) + { + if ( nFormatID != embed::StorageFormats::PACKAGE + && nFormatID != embed::StorageFormats::ZIP + && nFormatID != embed::StorageFormats::OFOPXML ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); + + nStorageType = nFormatID; + } + else + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); + } + else if (rProp.Name == "NoFileSync") + { + // Forward NoFileSync to the storage. + aPropsToSet.realloc(++nNumArgs); + auto pPropsToSet = aPropsToSet.getArray(); + pPropsToSet[nNumArgs - 1].Name = rProp.Name; + pPropsToSet[nNumArgs - 1].Value = rProp.Value; + } + else + OSL_FAIL( "Unacceptable property, will be ignored!" ); + } + } + else + { + OSL_FAIL( "Wrong third argument!" ); + throw uno::Exception("wrong 3rd arg", nullptr); // TODO: Illegal argument + } + + } + + // create storage based on source + if ( xInputStream.is() ) + { + // if xInputStream is set the storage should be open from it + if ( nStorageMode & embed::ElementModes::WRITE ) + throw uno::Exception("storagemode==write", nullptr); // TODO: access denied + + uno::Reference< io::XSeekable > xSeekable( xInputStream, uno::UNO_QUERY ); + if ( !xSeekable.is() ) + { + // TODO: wrap stream to let it be seekable + OSL_FAIL( "Nonseekable streams are not supported for now!" ); + } + + if ( !CheckPackageSignature_Impl( xInputStream, xSeekable ) ) + throw io::IOException("package signature check failed, probably not a package file", nullptr); // TODO: this is not a package file + + return static_cast<OWeakObject*>( + new OStorage(xInputStream, nStorageMode, aPropsToSet, m_xContext, nStorageType)); + } + else if ( xStream.is() ) + { + if ( ( ( nStorageMode & embed::ElementModes::WRITE ) && !xStream->getOutputStream().is() ) + || !xStream->getInputStream().is() ) + throw uno::Exception("access denied", nullptr); // TODO: access denied + + uno::Reference< io::XSeekable > xSeekable( xStream, uno::UNO_QUERY ); + if ( !xSeekable.is() ) + { + // TODO: wrap stream to let it be seekable + OSL_FAIL( "Nonseekable streams are not supported for now!" ); + } + + if ( !CheckPackageSignature_Impl( xStream->getInputStream(), xSeekable ) ) + throw io::IOException(); // TODO: this is not a package file + + return static_cast<OWeakObject*>( + new OStorage(xStream, nStorageMode, aPropsToSet, m_xContext, nStorageType)); + } + + throw uno::Exception("no input stream or regular stream", nullptr); // general error during creation +} + +OUString SAL_CALL OStorageFactory::getImplementationName() +{ + return "com.sun.star.comp.embed.StorageFactory"; +} + +sal_Bool SAL_CALL OStorageFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL OStorageFactory::getSupportedServiceNames() +{ + return { "com.sun.star.embed.StorageFactory", + "com.sun.star.comp.embed.StorageFactory" }; +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +package_OStorageFactory_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OStorageFactory(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/xfactory.hxx b/package/source/xstor/xfactory.hxx new file mode 100644 index 000000000..366911849 --- /dev/null +++ b/package/source/xstor/xfactory.hxx @@ -0,0 +1,55 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_XFACTORY_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_XFACTORY_HXX + +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <cppuhelper/implbase.hxx> +#include <osl/diagnose.h> + +class OStorageFactory : public ::cppu::WeakImplHelper< css::lang::XSingleServiceFactory, + css::lang::XServiceInfo > +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit OStorageFactory( const css::uno::Reference< css::uno::XComponentContext >& xContext ) + : m_xContext( xContext ) + { + OSL_ENSURE( xContext.is(), "No service manager is provided!" ); + } + + // XSingleServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance() override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/xstor.component b/package/source/xstor/xstor.component new file mode 100644 index 000000000..ff6de82b6 --- /dev/null +++ b/package/source/xstor/xstor.component @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.embed.StorageFactory" + constructor="package_OStorageFactory_get_implementation" single-instance="true"> + <service name="com.sun.star.comp.embed.StorageFactory"/> + <service name="com.sun.star.embed.StorageFactory"/> + </implementation> +</component> diff --git a/package/source/xstor/xstorage.cxx b/package/source/xstor/xstorage.cxx new file mode 100644 index 000000000..bdcd699a7 --- /dev/null +++ b/package/source/xstor/xstorage.cxx @@ -0,0 +1,5503 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/config.h> +#include <sal/log.hxx> + +#include <cassert> +#include <string_view> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/InvalidStorageException.hpp> +#include <com/sun/star/embed/UseBackupException.hpp> +#include <com/sun/star/embed/StorageFormats.hpp> +#include <com/sun/star/embed/StorageWrappedTargetException.hpp> +#include <com/sun/star/packages/NoEncryptionException.hpp> +#include <com/sun/star/packages/NoRawFormatException.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> + +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/beans/NamedValue.hpp> + +#include <PackageConstants.hxx> + +#include <comphelper/sequence.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include <comphelper/servicehelper.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/ofopxmlhelper.hxx> +#include <tools/diagnose_ex.h> + +#include "xstorage.hxx" +#include "owriteablestream.hxx" +#include "switchpersistencestream.hxx" + +using namespace ::com::sun::star; + +#if OSL_DEBUG_LEVEL > 0 +#define THROW_WHERE SAL_WHERE +#else +#define THROW_WHERE "" +#endif + +// static +void OStorage_Impl::completeStorageStreamCopy_Impl( + const uno::Reference< io::XStream >& xSource, + const uno::Reference< io::XStream >& xDest, + sal_Int32 nStorageType, + const uno::Sequence< uno::Sequence< beans::StringPair > >& aRelInfo ) +{ + uno::Reference< beans::XPropertySet > xSourceProps( xSource, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xDestProps( xDest, uno::UNO_QUERY_THROW ); + + uno::Reference< io::XOutputStream > xDestOutStream = xDest->getOutputStream(); + if ( !xDestOutStream.is() ) + throw io::IOException( THROW_WHERE ); + + uno::Reference< io::XInputStream > xSourceInStream = xSource->getInputStream(); + if ( !xSourceInStream.is() ) + throw io::IOException( THROW_WHERE ); + + // TODO: headers of encrypted streams should be copied also + ::comphelper::OStorageHelper::CopyInputToOutput( xSourceInStream, xDestOutStream ); + + uno::Sequence<OUString> aPropNames { "Compressed", "MediaType", + "UseCommonStoragePasswordEncryption" }; + + if ( nStorageType == embed::StorageFormats::OFOPXML ) + { + // TODO/LATER: in future it might make sense to provide the stream if there is one + uno::Reference< embed::XRelationshipAccess > xRelAccess( xDest, uno::UNO_QUERY_THROW ); + xRelAccess->clearRelationships(); + xRelAccess->insertRelationships( aRelInfo, false ); + + aPropNames.realloc( 2 ); + } + else if ( nStorageType != embed::StorageFormats::PACKAGE ) + { + aPropNames.realloc( 1 ); + } + + for ( const auto& rPropName : std::as_const(aPropNames) ) + xDestProps->setPropertyValue( rPropName, xSourceProps->getPropertyValue( rPropName ) ); +} + +static uno::Reference< io::XInputStream > GetSeekableTempCopy( const uno::Reference< io::XInputStream >& xInStream, + const uno::Reference< uno::XComponentContext >& xContext ) +{ + uno::Reference < io::XTempFile > xTempFile = io::TempFile::create(xContext); + uno::Reference < io::XOutputStream > xTempOut = xTempFile->getOutputStream(); + uno::Reference < io::XInputStream > xTempIn = xTempFile->getInputStream(); + + if ( !xTempOut.is() || !xTempIn.is() ) + throw io::IOException( THROW_WHERE ); + + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOut ); + xTempOut->closeOutput(); + + return xTempIn; +} + +SotElement_Impl::SotElement_Impl(const OUString& rName, bool bStor, bool bNew) + : m_aOriginalName(rName) + , m_bIsRemoved(false) + , m_bIsInserted(bNew) + , m_bIsStorage(bStor) +{ +} + +// most of properties are holt by the storage but are not used +OStorage_Impl::OStorage_Impl( uno::Reference< io::XInputStream > const & xInputStream, + sal_Int32 nMode, + const uno::Sequence< beans::PropertyValue >& xProperties, + uno::Reference< uno::XComponentContext > const & xContext, + sal_Int32 nStorageType ) +: m_xMutex( new comphelper::RefCountedMutex ) +, m_pAntiImpl( nullptr ) +, m_nStorageMode( nMode & ~embed::ElementModes::SEEKABLE ) +, m_bIsModified( ( nMode & ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) == ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) +, m_bBroadcastModified( false ) +, m_bCommited( false ) +, m_bIsRoot( true ) +, m_bListCreated( false ) +, m_nModifiedListenerCount( 0 ) +, m_xContext( xContext ) +, m_xProperties( xProperties ) +, m_bHasCommonEncryptionData( false ) +, m_pParent( nullptr ) +, m_bControlMediaType( false ) +, m_bMTFallbackUsed( false ) +, m_bControlVersion( false ) +, m_nStorageType( nStorageType ) +, m_pRelStorElement( nullptr ) +, m_nRelInfoStatus( RELINFO_NO_INIT ) +{ + // all the checks done below by assertion statements must be done by factory + SAL_WARN_IF( !xInputStream.is(), "package.xstor", "No input stream is provided!" ); + assert(xContext.is()); + + m_pSwitchStream = new SwitchablePersistenceStream(xContext, 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(xContext, xStream); + m_xStream = static_cast< io::XStream* >( m_pSwitchStream.get() ); + } + else + { + m_pSwitchStream = new SwitchablePersistenceStream(xContext, xStream->getInputStream()); + m_xInputStream = m_pSwitchStream->getInputStream(); + } +} + +OStorage_Impl::OStorage_Impl( OStorage_Impl* pParent, + sal_Int32 nMode, + uno::Reference< container::XNameContainer > const & xPackageFolder, + uno::Reference< lang::XSingleServiceFactory > const & xPackage, + uno::Reference< uno::XComponentContext > const & xContext, + sal_Int32 nStorageType ) +: m_xMutex( new comphelper::RefCountedMutex ) +, m_pAntiImpl( nullptr ) +, m_nStorageMode( nMode & ~embed::ElementModes::SEEKABLE ) +, m_bIsModified( ( nMode & ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) == ( embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ) ) +, m_bBroadcastModified( false ) +, m_bCommited( false ) +, m_bIsRoot( false ) +, m_bListCreated( false ) +, m_nModifiedListenerCount( 0 ) +, m_xPackageFolder( xPackageFolder ) +, m_xPackage( xPackage ) +, m_xContext( xContext ) +, m_bHasCommonEncryptionData( false ) +, m_pParent( pParent ) // can be empty in case of temporary readonly substorages and relation storage +, m_bControlMediaType( false ) +, m_bMTFallbackUsed( false ) +, m_bControlVersion( false ) +, m_nStorageType( nStorageType ) +, m_pRelStorElement( nullptr ) +, m_nRelInfoStatus( RELINFO_NO_INIT ) +{ + SAL_WARN_IF( !xPackageFolder.is(), "package.xstor", "No package folder!" ); + assert(xContext.is()); +} + +OStorage_Impl::~OStorage_Impl() +{ + { + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + if ( m_pAntiImpl ) // root storage wrapper must set this member to NULL before destruction of object + { + SAL_WARN_IF( m_bIsRoot, "package.xstor", "The root storage wrapper must be disposed already" ); + + try { + m_pAntiImpl->InternalDispose( false ); + } + catch ( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception"); + } + m_pAntiImpl = nullptr; + } + else if ( !m_aReadOnlyWrapVector.empty() ) + { + for ( auto& rStorage : m_aReadOnlyWrapVector ) + { + uno::Reference< embed::XStorage > xTmp = rStorage.m_xWeakRef; + if ( xTmp.is() ) + try { + rStorage.m_pPointer->InternalDispose( false ); + } catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception"); + } + } + + m_aReadOnlyWrapVector.clear(); + } + + m_pParent = nullptr; + } + + for (const auto & pair : m_aChildrenMap) + for (auto pElement : pair.second) + delete pElement; + m_aChildrenMap.clear(); + + std::for_each(m_aDeletedVector.begin(), m_aDeletedVector.end(), std::default_delete<SotElement_Impl>()); + m_aDeletedVector.clear(); + + if ( m_nStorageType == embed::StorageFormats::OFOPXML && m_pRelStorElement ) + { + delete m_pRelStorElement; + m_pRelStorElement = nullptr; + } + + m_xPackageFolder.clear(); + m_xPackage.clear(); + + OUString aPropertyName = "URL"; + for ( const auto& rProp : std::as_const(m_xProperties) ) + { + if ( rProp.Name == aPropertyName ) + { + // the storage is URL based so all the streams are opened by factory and should be closed + try + { + if ( m_xInputStream.is() ) + { + m_xInputStream->closeInput(); + m_xInputStream.clear(); + } + + if ( m_xStream.is() ) + { + uno::Reference< io::XInputStream > xInStr = m_xStream->getInputStream(); + if ( xInStr.is() ) + xInStr->closeInput(); + + uno::Reference< io::XOutputStream > xOutStr = m_xStream->getOutputStream(); + if ( xOutStr.is() ) + xOutStr->closeOutput(); + + m_xStream.clear(); + } + } + catch (const uno::Exception&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception"); + } + } + } +} + +void OStorage_Impl::SetReadOnlyWrap( OStorage& aStorage ) +{ + // Weak reference is used inside the holder so the refcount must not be zero at this point + OSL_ENSURE( aStorage.GetRefCount_Impl(), "There must be a reference alive to use this method!" ); + m_aReadOnlyWrapVector.emplace_back( &aStorage ); +} + +void OStorage_Impl::RemoveReadOnlyWrap( const OStorage& aStorage ) +{ + for ( StorageHoldersType::iterator pStorageIter = m_aReadOnlyWrapVector.begin(); + pStorageIter != m_aReadOnlyWrapVector.end();) + { + uno::Reference< embed::XStorage > xTmp = pStorageIter->m_xWeakRef; + if ( !xTmp.is() || pStorageIter->m_pPointer == &aStorage ) + { + try { + pStorageIter->m_pPointer->InternalDispose( false ); + } catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception"); + } + + pStorageIter = m_aReadOnlyWrapVector.erase(pStorageIter); + } + else + ++pStorageIter; + } +} + +void OStorage_Impl::OpenOwnPackage() +{ + SAL_WARN_IF( !m_bIsRoot, "package.xstor", "Opening of the package has no sense!" ); + + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( !m_xPackageFolder.is() ) + { + if ( !m_xPackage.is() ) + { + uno::Sequence< uno::Any > aArguments( 2 ); + auto pArguments = aArguments.getArray(); + if ( m_nStorageMode & embed::ElementModes::WRITE ) + pArguments[ 0 ] <<= 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( "AllowRemoveOnInsert", + uno::Any( false ) ); + + sal_Int32 nArgNum = 2; + for ( const auto& rProp : std::as_const(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; + } + else if ( rProp.Name == "Password" ) + { + // TODO: implement password setting for documents + // the password entry must be removed after setting + } + } + + if ( m_nStorageType == embed::StorageFormats::ZIP ) + { + // let the package support only plain zip format + beans::NamedValue aNamedValue; + aNamedValue.Name = "StorageFormat"; + aNamedValue.Value <<= OUString( "ZipFormat" ); + aArguments.realloc( ++nArgNum ); + pArguments = aArguments.getArray(); + pArguments[nArgNum-1] <<= aNamedValue; + } + else if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + { + // let the package support OFOPXML media type handling + beans::NamedValue aNamedValue; + aNamedValue.Name = "StorageFormat"; + aNamedValue.Value <<= OUString( "OFOPXMLFormat" ); + aArguments.realloc( ++nArgNum ); + pArguments = aArguments.getArray(); + pArguments[nArgNum-1] <<= aNamedValue; + } + + m_xPackage.set( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.packages.comp.ZipPackage", aArguments, m_xContext), + uno::UNO_QUERY ); + } + + uno::Reference< container::XHierarchicalNameAccess > xHNameAccess( m_xPackage, uno::UNO_QUERY ); + SAL_WARN_IF( !xHNameAccess.is(), "package.xstor", "The package could not be created!" ); + + if ( xHNameAccess.is() ) + { + uno::Any aFolder = xHNameAccess->getByHierarchicalName("/"); + aFolder >>= m_xPackageFolder; + } + } + + SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor", "The package root folder can not be opened!" ); + if ( !m_xPackageFolder.is() ) + throw embed::InvalidStorageException( THROW_WHERE ); +} + +bool OStorage_Impl::HasChildren() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + ReadContents(); + return !m_aChildrenMap.empty(); +} + +void OStorage_Impl::GetStorageProperties() +{ + if ( m_nStorageType != embed::StorageFormats::PACKAGE ) + return; + + uno::Reference< beans::XPropertySet > xProps( m_xPackageFolder, uno::UNO_QUERY_THROW ); + + if ( !m_bControlMediaType ) + { + uno::Reference< beans::XPropertySet > xPackageProps( m_xPackage, uno::UNO_QUERY_THROW ); + xPackageProps->getPropertyValue( MEDIATYPE_FALLBACK_USED_PROPERTY ) >>= m_bMTFallbackUsed; + + static constexpr OUStringLiteral sMediaType(u"MediaType"); + xProps->getPropertyValue( sMediaType ) >>= m_aMediaType; + m_bControlMediaType = true; + } + + if ( !m_bControlVersion ) + { + xProps->getPropertyValue( "Version" ) >>= m_aVersion; + m_bControlVersion = true; + } + + // the properties of OFOPXML will be handled directly +} + +void OStorage_Impl::ReadRelInfoIfNecessary() +{ + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + return; + + if ( m_nRelInfoStatus == RELINFO_NO_INIT ) + { + // Init from original stream + uno::Reference< io::XInputStream > xRelInfoStream + = GetRelInfoStreamForName( std::u16string_view() ); + try + { + if ( xRelInfoStream.is() ) + m_aRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence( + xRelInfoStream, + u"_rels/.rels", + m_xContext ); + m_nRelInfoStatus = RELINFO_READ; + } + catch (css::uno::Exception &) + { + TOOLS_INFO_EXCEPTION("package.xstor", ""); + } + } + else if ( m_nRelInfoStatus == RELINFO_CHANGED_STREAM ) + { + // Init from the new stream + try + { + if ( m_xNewRelInfoStream.is() ) + m_aRelInfo = ::comphelper::OFOPXMLHelper::ReadRelationsInfoSequence( + m_xNewRelInfoStream, + u"_rels/.rels", + m_xContext ); + + m_nRelInfoStatus = RELINFO_CHANGED_STREAM_READ; + } + catch( const uno::Exception& ) + { + m_nRelInfoStatus = RELINFO_CHANGED_BROKEN; + } + } +} + +void OStorage_Impl::ReadContents() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( m_bListCreated ) + return; + + if ( m_bIsRoot ) + OpenOwnPackage(); + + uno::Reference< container::XEnumerationAccess > xEnumAccess( m_xPackageFolder, uno::UNO_QUERY_THROW ); + uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration(); + if ( !xEnum.is() ) + throw uno::RuntimeException( THROW_WHERE ); + + m_bListCreated = true; + + while( xEnum->hasMoreElements() ) + { + try { + uno::Reference< container::XNamed > xNamed; + xEnum->nextElement() >>= xNamed; + + if ( !xNamed.is() ) + { + SAL_WARN( "package.xstor", "XNamed is not supported!" ); + throw uno::RuntimeException( THROW_WHERE ); + } + + OUString aName = xNamed->getName(); + SAL_WARN_IF( aName.isEmpty(), "package.xstor", "Empty name!" ); + + uno::Reference< container::XNameContainer > xNameContainer( xNamed, uno::UNO_QUERY ); + + std::unique_ptr<SotElement_Impl> xNewElement(new SotElement_Impl(aName, xNameContainer.is(), false)); + if ( m_nStorageType == embed::StorageFormats::OFOPXML && aName == "_rels" ) + { + if (!xNewElement->m_bIsStorage) + throw io::IOException( THROW_WHERE ); // TODO: Unexpected format + + m_pRelStorElement = xNewElement.release(); + CreateRelStorage(); + } + else + { + if ( ( m_nStorageMode & embed::ElementModes::TRUNCATE ) == embed::ElementModes::TRUNCATE ) + { + // if a storage is truncated all of it elements are marked as deleted + xNewElement->m_bIsRemoved = true; + } + + m_aChildrenMap[aName].push_back(xNewElement.release()); + } + } + 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; + } + + // cache changeable folder properties + GetStorageProperties(); +} + +void OStorage_Impl::CopyToStorage( const uno::Reference< embed::XStorage >& xDest, bool bDirect ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + uno::Reference< beans::XPropertySet > xPropSet( xDest, uno::UNO_QUERY ); + if ( !xPropSet.is() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); + + sal_Int32 nDestMode = embed::ElementModes::READ; + xPropSet->getPropertyValue( "OpenMode" ) >>= nDestMode; + + if ( !( nDestMode & embed::ElementModes::WRITE ) ) + throw io::IOException( THROW_WHERE ); // TODO: access_denied + + ReadContents(); + + if ( !m_xPackageFolder.is() ) + throw embed::InvalidStorageException( THROW_WHERE ); + + for ( const auto& pair : m_aChildrenMap ) + for (auto pElement : pair.second) + { + if ( !pElement->m_bIsRemoved ) + CopyStorageElement( pElement, xDest, /*aName*/pair.first, bDirect ); + } + + // move storage properties to the destination one ( means changeable properties ) + if ( m_nStorageType == embed::StorageFormats::PACKAGE ) + { + xPropSet->setPropertyValue( "MediaType", uno::Any( m_aMediaType ) ); + xPropSet->setPropertyValue( "Version", 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( "IsRoot" ) >>= bIsRoot ) && bIsRoot ) + { + try + { + uno::Reference< embed::XEncryptionProtectedStorage > xEncr( xDest, uno::UNO_QUERY ); + if ( xEncr.is() ) + { + xEncr->setEncryptionData( GetCommonRootEncryptionData().getAsConstNamedValueList() ); + + uno::Sequence< beans::NamedValue > aAlgorithms; + uno::Reference< beans::XPropertySet > xPackPropSet( m_xPackage, uno::UNO_QUERY_THROW ); + xPackPropSet->getPropertyValue( ENCRYPTION_ALGORITHMS_PROPERTY ) + >>= aAlgorithms; + xEncr->setEncryptionAlgorithms( aAlgorithms ); + } + } + catch( const packages::NoEncryptionException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "No Encryption"); + } + } + } + else if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + { + + // TODO/LATER: currently the optimization is not active + // uno::Reference< io::XInputStream > xRelInfoStream = GetRelInfoStreamForName( OUString() ); // own stream + // if ( xRelInfoStream.is() ) + // { + // // Relations info stream is a writeonly property, introduced only to optimize copying + // // Should be used carefully since no check for stream consistency is done, and the stream must not stay locked + + // OUString aRelInfoString = "RelationsInfoStream"; + // xPropSet->setPropertyValue( aRelInfoString, uno::makeAny( GetSeekableTempCopy( xRelInfoStream, m_xFactory ) ) ); + // } + + uno::Reference< embed::XRelationshipAccess > xRels( xDest, uno::UNO_QUERY ); + if ( !xRels.is() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); + + xRels->insertRelationships( GetAllRelationshipsIfAny(), false ); + } + + // if possible the destination storage should be committed after successful copying + uno::Reference< embed::XTransactedObject > xObjToCommit( xDest, uno::UNO_QUERY ); + if ( xObjToCommit.is() ) + xObjToCommit->commit(); +} + +void OStorage_Impl::CopyStorageElement( SotElement_Impl* pElement, + const uno::Reference< embed::XStorage >& xDest, + const OUString& aName, + bool bDirect ) +{ + SAL_WARN_IF( !xDest.is(), "package.xstor", "No destination storage!" ); + SAL_WARN_IF( aName.isEmpty(), "package.xstor", "Empty element name!" ); + + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + uno::Reference< container::XNameAccess > xDestAccess( xDest, uno::UNO_QUERY_THROW ); + if ( xDestAccess->hasByName( aName ) + && !( pElement->m_bIsStorage && xDest->isStorageElement( aName ) ) ) + xDest->removeElement( aName ); + + if ( pElement->m_bIsStorage ) + { + uno::Reference< embed::XStorage > xSubDest = + xDest->openStorageElement( aName, + embed::ElementModes::WRITE ); + + SAL_WARN_IF( !xSubDest.is(), "package.xstor", "No destination substorage!" ); + + if (!pElement->m_xStorage) + { + OpenSubStorage( pElement, embed::ElementModes::READ ); + if (!pElement->m_xStorage) + throw io::IOException( THROW_WHERE ); + } + + pElement->m_xStorage->CopyToStorage(xSubDest, bDirect); + } + else + { + if (!pElement->m_xStream) + { + OpenSubStream( pElement ); + if (!pElement->m_xStream) + throw io::IOException( THROW_WHERE ); + } + + if (!pElement->m_xStream->IsEncrypted()) + { + if ( bDirect ) + { + // fill in the properties for the stream + uno::Sequence< beans::PropertyValue > aStrProps(0); + const uno::Sequence< beans::PropertyValue > aSrcPkgProps = pElement->m_xStream->GetStreamProperties(); + sal_Int32 nNum = 0; + for ( const auto& rSrcPkgProp : aSrcPkgProps ) + { + if ( rSrcPkgProp.Name == "MediaType" || rSrcPkgProp.Name == "Compressed" ) + { + aStrProps.realloc( ++nNum ); + auto pStrProps = aStrProps.getArray(); + pStrProps[nNum-1].Name = rSrcPkgProp.Name; + pStrProps[nNum-1].Value = rSrcPkgProp.Value; + } + } + + if ( m_nStorageType == embed::StorageFormats::PACKAGE ) + { + aStrProps.realloc( ++nNum ); + auto pStrProps = aStrProps.getArray(); + pStrProps[nNum-1].Name = "UseCommonStoragePasswordEncryption"; + pStrProps[nNum-1].Value <<= pElement->m_xStream->UsesCommonEncryption_Impl(); + } + else if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + { + // TODO/LATER: currently the optimization is not active + // uno::Reference< io::XInputStream > xInStream = GetRelInfoStreamForName( OUString() ); // own rels stream + // if ( xInStream.is() ) + // { + // aStrProps.realloc( ++nNum ); + // aStrProps[nNum-1].Name = "RelationsInfoStream"; + // aStrProps[nNum-1].Value <<= GetSeekableTempCopy( xInStream, m_xFactory ); + // } + + uno::Reference< embed::XRelationshipAccess > xRels( xDest, uno::UNO_QUERY ); + if ( !xRels.is() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); + + xRels->insertRelationships( GetAllRelationshipsIfAny(), false ); + } + + uno::Reference< embed::XOptimizedStorage > xOptDest( xDest, uno::UNO_QUERY_THROW ); + uno::Reference < io::XInputStream > xInputToInsert; + + if (pElement->m_xStream->HasTempFile_Impl() || !pElement->m_xStream->m_xPackageStream.is()) + { + SAL_WARN_IF(!pElement->m_xStream->m_xPackageStream.is(), "package.xstor", "No package stream!"); + + // if the stream is modified - the temporary file must be used for insertion + xInputToInsert = pElement->m_xStream->GetTempFileAsInputStream(); + } + else + { + // for now get just nonseekable access to the stream + // TODO/LATER: the raw stream can be used + + xInputToInsert = pElement->m_xStream->m_xPackageStream->getDataStream(); + } + + if ( !xInputToInsert.is() ) + throw io::IOException( THROW_WHERE ); + + xOptDest->insertStreamElementDirect( aName, xInputToInsert, aStrProps ); + } + else + { + uno::Reference< io::XStream > xSubStr = + xDest->openStreamElement( aName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + SAL_WARN_IF( !xSubStr.is(), "package.xstor", "No destination substream!" ); + + pElement->m_xStream->CopyInternallyTo_Impl(xSubStr); + } + } + else if ( m_nStorageType != embed::StorageFormats::PACKAGE ) + { + SAL_WARN( "package.xstor", "Encryption is only supported in package storage!" ); + throw io::IOException( THROW_WHERE ); + } + else if ( pElement->m_xStream->HasCachedEncryptionData() + && ( pElement->m_xStream->IsModified() || pElement->m_xStream->HasWriteOwner_Impl() ) ) + { + ::comphelper::SequenceAsHashMap aCommonEncryptionData; + bool bHasCommonEncryptionData = false; + try + { + aCommonEncryptionData = GetCommonRootEncryptionData(); + bHasCommonEncryptionData = true; + } + catch( const packages::NoEncryptionException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "No Encryption"); + } + + if (bHasCommonEncryptionData && ::package::PackageEncryptionDataLessOrEqual(pElement->m_xStream->GetCachedEncryptionData(), aCommonEncryptionData)) + { + // If the stream can be opened with the common storage password + // it must be stored with the common storage password as well + uno::Reference< io::XStream > xDestStream = + xDest->openStreamElement( aName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + + pElement->m_xStream->CopyInternallyTo_Impl( xDestStream ); + + uno::Reference< beans::XPropertySet > xProps( xDestStream, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue( + "UseCommonStoragePasswordEncryption", + uno::Any( true ) ); + } + else + { + // the stream is already opened for writing or was changed + uno::Reference< embed::XStorage2 > xDest2( xDest, uno::UNO_QUERY_THROW ); + uno::Reference< io::XStream > xSubStr = + xDest2->openEncryptedStream( aName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE, + pElement->m_xStream->GetCachedEncryptionData().getAsConstNamedValueList() ); + SAL_WARN_IF( !xSubStr.is(), "package.xstor", "No destination substream!" ); + + pElement->m_xStream->CopyInternallyTo_Impl(xSubStr, pElement->m_xStream->GetCachedEncryptionData()); + } + } + else + { + // the stream is not opened at all, so it can be just opened for reading + try + { + // If the stream can be opened with the common storage password + // it must be stored with the common storage password as well + + uno::Reference< io::XStream > xOwnStream = pElement->m_xStream->GetStream(embed::ElementModes::READ, + false); + uno::Reference< io::XStream > xDestStream = + xDest->openStreamElement( aName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + SAL_WARN_IF( !xDestStream.is(), "package.xstor", "No destination substream!" ); + completeStorageStreamCopy_Impl( xOwnStream, xDestStream, m_nStorageType, GetAllRelationshipsIfAny() ); + + uno::Reference< beans::XPropertySet > xProps( xDestStream, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue( + "UseCommonStoragePasswordEncryption", + uno::Any( true ) ); + } + catch( const packages::WrongPasswordException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Handled exception"); + + // If the common storage password does not allow to open the stream + // it could be copied in raw way, the problem is that the StartKey should be the same + // in the ODF1.2 package, so an invalid package could be produced if the stream + // is copied from ODF1.1 package, where it is allowed to have different StartKeys + uno::Reference< embed::XStorageRawAccess > xRawDest( xDest, uno::UNO_QUERY_THROW ); + uno::Reference< io::XInputStream > xRawInStream = pElement->m_xStream->GetRawInStream(); + xRawDest->insertRawEncrStreamElement( aName, xRawInStream ); + } + } + } +} + +uno::Sequence< uno::Sequence< beans::StringPair > > OStorage_Impl::GetAllRelationshipsIfAny() +{ + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + return uno::Sequence< uno::Sequence< beans::StringPair > >(); + + ReadRelInfoIfNecessary(); + + if ( m_nRelInfoStatus != RELINFO_READ + && m_nRelInfoStatus != RELINFO_CHANGED_STREAM_READ + && m_nRelInfoStatus != RELINFO_CHANGED ) + throw io::IOException( THROW_WHERE "Wrong relinfo stream!" ); + // m_nRelInfoStatus == RELINFO_CHANGED_BROKEN || m_nRelInfoStatus == RELINFO_BROKEN + return m_aRelInfo; +} + +void OStorage_Impl::CopyLastCommitTo( const uno::Reference< embed::XStorage >& xNewStor ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor", "A committed storage is incomplete!" ); + if ( !m_xPackageFolder.is() ) + throw uno::RuntimeException( THROW_WHERE ); + + OStorage_Impl aTempRepresent( nullptr, + embed::ElementModes::READ, + m_xPackageFolder, + m_xPackage, + m_xContext, + m_nStorageType); + + // TODO/LATER: could use direct copying + aTempRepresent.CopyToStorage( xNewStor, false ); +} + +void OStorage_Impl::InsertIntoPackageFolder( const OUString& aName, + const uno::Reference< container::XNameContainer >& xParentPackageFolder ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor", "An inserted storage is incomplete!" ); + uno::Reference< lang::XUnoTunnel > xTunnel( m_xPackageFolder, uno::UNO_QUERY_THROW ); + xParentPackageFolder->insertByName( aName, uno::Any( xTunnel ) ); + + m_bCommited = false; +} + +void OStorage_Impl::Commit() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( !m_bIsModified ) + return; + + // in case of a new empty storage it is possible that the contents are still not read + // ( the storage of course has no contents, but the initialization is postponed till the first use, + // thus if a new storage was created and committed immediately it must be initialized here ) + ReadContents(); + + // if storage is committed it should have a valid Package representation + SAL_WARN_IF( !m_xPackageFolder.is(), "package.xstor", "The package representation should exist!" ); + if ( !m_xPackageFolder.is() ) + throw embed::InvalidStorageException( THROW_WHERE ); + + OSL_ENSURE( m_nStorageMode & embed::ElementModes::WRITE, + "Commit of readonly storage, should be detected before!" ); + + uno::Reference< container::XNameContainer > xNewPackageFolder; + + // here the storage will switch to the temporary package folder + // if the storage was already committed and the parent was not committed after that + // the switch should not be done since the package folder in use is a temporary one; + // it can be detected by m_bCommited flag ( root storage doesn't need temporary representation ) + if ( !m_bCommited && !m_bIsRoot ) + { + uno::Sequence< uno::Any > aSeq{ uno::Any(true) }; + xNewPackageFolder.set( m_xPackage->createInstanceWithArguments( aSeq ), + uno::UNO_QUERY ); + } + else + xNewPackageFolder = m_xPackageFolder; + + // remove replaced removed elements + for ( auto& pDeleted : m_aDeletedVector ) + { + + if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pDeleted->m_bIsStorage ) + RemoveStreamRelInfo( pDeleted->m_aOriginalName ); + + // the removed elements are not in new temporary storage + if ( m_bCommited || m_bIsRoot ) + xNewPackageFolder->removeByName( pDeleted->m_aOriginalName ); + delete pDeleted; + pDeleted = nullptr; + } + m_aDeletedVector.clear(); + + // remove removed elements + for (auto mapIt = m_aChildrenMap.begin(); mapIt != m_aChildrenMap.end(); ) + { + for (auto it = mapIt->second.begin(); it != mapIt->second.end(); ) + { + // renamed and inserted elements must be really inserted to package later + // since they can conflict with removed elements + auto & pElement = *it; + if ( pElement->m_bIsRemoved ) + { + if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pElement->m_bIsStorage ) + RemoveStreamRelInfo( pElement->m_aOriginalName ); + + // the removed elements are not in new temporary storage + if ( m_bCommited || m_bIsRoot ) + xNewPackageFolder->removeByName( pElement->m_aOriginalName ); + + delete pElement; + it = mapIt->second.erase(it); + } + else + ++it; + } + if (mapIt->second.empty()) + mapIt = m_aChildrenMap.erase(mapIt); + else + ++mapIt; + } + + + // there should be no more deleted elements + for ( const auto& pair : m_aChildrenMap ) + for (auto pElement : pair.second) + { + // if it is a 'duplicate commit' inserted elements must be really inserted to package later + // since they can conflict with renamed elements + if ( !pElement->m_bIsInserted ) + { + // for now stream is opened in direct mode that means that in case + // storage is committed all the streams from it are committed in current state. + // following two steps are separated to allow easily implement transacted mode + // for streams if we need it in future. + // Only hierarchical access uses transacted streams currently + if ( !pElement->m_bIsStorage && pElement->m_xStream + && !pElement->m_xStream->IsTransacted() ) + pElement->m_xStream->Commit(); + + // if the storage was not open, there is no need to commit it ??? + // the storage should be checked that it is committed + if (pElement->m_bIsStorage && pElement->m_xStorage && pElement->m_xStorage->m_bCommited) + { + // it's temporary PackageFolder should be inserted instead of current one + // also the new copy of PackageFolder should be used by the children storages + + // the renamed elements are not in new temporary storage + if ( m_bCommited || m_bIsRoot ) + xNewPackageFolder->removeByName( pElement->m_aOriginalName ); + + pElement->m_xStorage->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder); + } + else if (!pElement->m_bIsStorage && pElement->m_xStream && pElement->m_xStream->m_bFlushed) + { + if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + CommitStreamRelInfo( /*aName*/pair.first, pElement ); + + // the renamed elements are not in new temporary storage + if ( m_bCommited || m_bIsRoot ) + xNewPackageFolder->removeByName( pElement->m_aOriginalName ); + + pElement->m_xStream->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder); + } + else if ( !m_bCommited && !m_bIsRoot ) + { + // the element must be just copied to the new temporary package folder + // the connection with the original package should not be lost just because + // the element is still referred by the folder in the original hierarchy + uno::Any aPackageElement = m_xPackageFolder->getByName( pElement->m_aOriginalName ); + xNewPackageFolder->insertByName( /*aName*/pair.first, aPackageElement ); + } + else if ( pair.first != pElement->m_aOriginalName ) + { + // this is the case when xNewPackageFolder refers to m_xPackageFolder + // in case the name was changed and it is not a changed storage - rename the element + uno::Any aPackageElement = xNewPackageFolder->getByName( pElement->m_aOriginalName ); + xNewPackageFolder->removeByName( pElement->m_aOriginalName ); + xNewPackageFolder->insertByName( /*aName*/pair.first, aPackageElement ); + + if ( m_nStorageType == embed::StorageFormats::OFOPXML && !pElement->m_bIsStorage ) + { + if (!pElement->m_xStream) + { + OpenSubStream( pElement ); + if (!pElement->m_xStream) + throw uno::RuntimeException( THROW_WHERE ); + } + + CommitStreamRelInfo( /*aName*/pair.first, pElement ); + } + } + + pElement->m_aOriginalName = pair.first; + } + } + + for ( const auto& pair : m_aChildrenMap ) + for (auto pElement : pair.second) + { + // now inserted elements can be inserted to the package + if ( pElement->m_bIsInserted ) + { + pElement->m_aOriginalName = pair.first; + + if ( pElement->m_bIsStorage ) + { + OSL_ENSURE(pElement->m_xStorage, "An inserted storage is incomplete!"); + if (!pElement->m_xStorage) + throw uno::RuntimeException( THROW_WHERE ); + + if (pElement->m_xStorage->m_bCommited) + { + pElement->m_xStorage->InsertIntoPackageFolder(/*aName*/pair.first, xNewPackageFolder); + + pElement->m_bIsInserted = false; + } + } + else + { + OSL_ENSURE(pElement->m_xStream, "An inserted stream is incomplete!"); + if (!pElement->m_xStream) + throw uno::RuntimeException( THROW_WHERE ); + + if (!pElement->m_xStream->IsTransacted()) + pElement->m_xStream->Commit(); + + if (pElement->m_xStream->m_bFlushed) + { + if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + CommitStreamRelInfo( /*aName*/pair.first, pElement ); + + pElement->m_xStream->InsertIntoPackageFolder( /*aName*/pair.first, xNewPackageFolder ); + + pElement->m_bIsInserted = false; + } + } + } + } + + if ( m_nStorageType == embed::StorageFormats::PACKAGE ) + { + // move properties to the destination package folder + uno::Reference< beans::XPropertySet > xProps( xNewPackageFolder, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue( "MediaType", uno::Any( m_aMediaType ) ); + xProps->setPropertyValue( "Version", uno::Any( m_aVersion ) ); + } + + if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + CommitRelInfo( xNewPackageFolder ); // store own relations and commit complete relations storage + + if ( m_bIsRoot ) + { + uno::Reference< util::XChangesBatch > xChangesBatch( m_xPackage, uno::UNO_QUERY_THROW ); + try + { + xChangesBatch->commitChanges(); + } + 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; + } + + SAL_INFO("package.xstor", "Rethrow: " << exceptionToString(ex)); + throw; + } + } + else if ( !m_bCommited ) + { + m_xPackageFolder = xNewPackageFolder; + m_bCommited = true; + } + + // after commit the mediatype treated as the correct one + m_bMTFallbackUsed = false; +} + +void OStorage_Impl::Revert() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if ( !( m_nStorageMode & embed::ElementModes::WRITE ) ) + return; // nothing to do + + // all the children must be removed + // they will be created later on demand + + // rebuild the map - cannot do it in-place, because we're changing some of the key values + std::unordered_map<OUString, std::vector<SotElement_Impl*>> oldMap; + std::swap(oldMap, m_aChildrenMap); + + for (const auto & rPair : oldMap) + for (auto pElement : rPair.second) + { + if ( pElement->m_bIsInserted ) + delete pElement; + else + { + ClearElement( pElement ); + + pElement->m_bIsRemoved = false; + + m_aChildrenMap[pElement->m_aOriginalName].push_back(pElement); + } + } + + // return replaced removed elements + for ( auto& pDeleted : m_aDeletedVector ) + { + m_aChildrenMap[pDeleted->m_aOriginalName].push_back(pDeleted); + + ClearElement( pDeleted ); + + pDeleted->m_bIsRemoved = false; + } + m_aDeletedVector.clear(); + + m_bControlMediaType = false; + m_bControlVersion = false; + + GetStorageProperties(); + + if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + { + // currently the relations storage is changed only on commit + m_xNewRelInfoStream.clear(); + m_aRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >(); + m_nRelInfoStatus = RELINFO_NO_INIT; + } +} + +::comphelper::SequenceAsHashMap OStorage_Impl::GetCommonRootEncryptionData() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ) ; + + if ( m_nStorageType != embed::StorageFormats::PACKAGE ) + throw packages::NoEncryptionException( THROW_WHERE ); + + if ( m_bIsRoot ) + { + if ( !m_bHasCommonEncryptionData ) + throw packages::NoEncryptionException( THROW_WHERE ); + + return m_aCommonEncryptionData; + } + else + { + if ( !m_pParent ) + throw packages::NoEncryptionException( THROW_WHERE ); + + return m_pParent->GetCommonRootEncryptionData(); + } +} + +SotElement_Impl* OStorage_Impl::FindElement( const OUString& rName ) +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + SAL_WARN_IF( rName.isEmpty(), "package.xstor", "Name is empty!" ); + + ReadContents(); + + auto mapIt = m_aChildrenMap.find(rName); + if (mapIt == m_aChildrenMap.end()) + return nullptr; + for (auto pElement : mapIt->second) + if (!pElement->m_bIsRemoved) + return pElement; + + return nullptr; +} + +SotElement_Impl* OStorage_Impl::InsertStream( const OUString& aName, bool bEncr ) +{ + SAL_WARN_IF( !m_xPackage.is(), "package.xstor", "Not possible to refer to package as to factory!" ); + if ( !m_xPackage.is() ) + throw embed::InvalidStorageException( THROW_WHERE); + + uno::Sequence< uno::Any > aSeq{ uno::Any(false) }; + uno::Reference< lang::XUnoTunnel > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ), + uno::UNO_QUERY ); + + SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new stream!" ); + if ( !xNewElement.is() ) + throw io::IOException( THROW_WHERE ); + + uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xNewElement, uno::UNO_QUERY_THROW ); + + OSL_ENSURE( m_nStorageType == embed::StorageFormats::PACKAGE || !bEncr, "Only package storage supports encryption!" ); + if ( m_nStorageType != embed::StorageFormats::PACKAGE && bEncr ) + throw packages::NoEncryptionException( THROW_WHERE ); + + // the mode is not needed for storage stream internal implementation + SotElement_Impl* pNewElement = InsertElement( aName, false ); + pNewElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, bEncr, m_nStorageType, true)); + + m_aChildrenMap[aName].push_back( pNewElement ); + m_bIsModified = true; + m_bBroadcastModified = true; + + return pNewElement; +} + +void OStorage_Impl::InsertRawStream( const OUString& aName, const uno::Reference< io::XInputStream >& xInStream ) +{ + // insert of raw stream means insert and commit + SAL_WARN_IF( !m_xPackage.is(), "package.xstor", "Not possible to refer to package as to factory!" ); + if ( !m_xPackage.is() ) + throw embed::InvalidStorageException( THROW_WHERE ); + + if ( m_nStorageType != embed::StorageFormats::PACKAGE ) + throw packages::NoEncryptionException( THROW_WHERE ); + + uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY ); + uno::Reference< io::XInputStream > xInStrToInsert = xSeek.is() ? xInStream : + GetSeekableTempCopy( xInStream, m_xContext ); + + uno::Sequence< uno::Any > aSeq{ uno::Any(false) }; + uno::Reference< lang::XUnoTunnel > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ), + uno::UNO_QUERY ); + + SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new stream!" ); + if ( !xNewElement.is() ) + throw io::IOException( THROW_WHERE ); + + uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xNewElement, uno::UNO_QUERY_THROW ); + xPackageSubStream->setRawStream( xInStrToInsert ); + + // the mode is not needed for storage stream internal implementation + SotElement_Impl* pNewElement = InsertElement( aName, false ); + pNewElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, true, m_nStorageType, false)); + // the stream is inserted and must be treated as a committed one + pNewElement->m_xStream->SetToBeCommited(); + + m_aChildrenMap[aName].push_back( pNewElement ); + m_bIsModified = true; + m_bBroadcastModified = true; +} + +std::unique_ptr<OStorage_Impl> OStorage_Impl::CreateNewStorageImpl( sal_Int32 nStorageMode ) +{ + SAL_WARN_IF( !m_xPackage.is(), "package.xstor", "Not possible to refer to package as to factory!" ); + if ( !m_xPackage.is() ) + throw embed::InvalidStorageException( THROW_WHERE ); + + uno::Sequence< uno::Any > aSeq{ uno::Any(true) }; + uno::Reference< lang::XUnoTunnel > xNewElement( m_xPackage->createInstanceWithArguments( aSeq ), + uno::UNO_QUERY ); + + SAL_WARN_IF( !xNewElement.is(), "package.xstor", "Not possible to create a new storage!" ); + if ( !xNewElement.is() ) + throw io::IOException( THROW_WHERE ); + + uno::Reference< container::XNameContainer > xPackageSubFolder( xNewElement, uno::UNO_QUERY_THROW ); + std::unique_ptr<OStorage_Impl> pResult( + new OStorage_Impl( this, nStorageMode, xPackageSubFolder, m_xPackage, m_xContext, m_nStorageType )); + pResult->m_bIsModified = true; + + return pResult; +} + +SotElement_Impl* OStorage_Impl::InsertStorage( const OUString& aName, sal_Int32 nStorageMode ) +{ + SotElement_Impl* pNewElement = InsertElement( aName, true ); + + pNewElement->m_xStorage = CreateNewStorageImpl(nStorageMode); + + m_aChildrenMap[aName].push_back( pNewElement ); + + return pNewElement; +} + +SotElement_Impl* OStorage_Impl::InsertElement( const OUString& aName, bool bIsStorage ) +{ + assert( FindElement(aName) == nullptr && "Should not try to insert existing element"); + + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + SotElement_Impl* pDeletedElm = nullptr; + + auto it = m_aChildrenMap.find(aName); + if (it != m_aChildrenMap.end()) + for (auto pElement : it->second) + { + SAL_WARN_IF( !pElement->m_bIsRemoved, "package.xstor", "Try to insert an element instead of existing one!" ); + if ( pElement->m_bIsRemoved ) + { + SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor", "Inserted elements must be deleted immediately!" ); + pDeletedElm = pElement; + } + } + + if ( pDeletedElm ) + { + if ( pDeletedElm->m_bIsStorage ) + OpenSubStorage( pDeletedElm, embed::ElementModes::READWRITE ); + else + OpenSubStream( pDeletedElm ); + + auto & rVec = m_aChildrenMap[aName]; + rVec.erase(std::remove(rVec.begin(), rVec.end(), pDeletedElm), rVec.end()); + if (rVec.empty()) + m_aChildrenMap.erase(aName); + m_aDeletedVector.push_back( pDeletedElm ); + } + + // create new element + return new SotElement_Impl( aName, bIsStorage, true ); +} + +void OStorage_Impl::OpenSubStorage( SotElement_Impl* pElement, sal_Int32 nStorageMode ) +{ + SAL_WARN_IF( !pElement, "package.xstor", "pElement is not set!" ); + SAL_WARN_IF( !pElement->m_bIsStorage, "package.xstor", "Storage flag is not set!" ); + + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if (!pElement->m_xStorage) + { + SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor", "Inserted element must be created already!" ); + + uno::Reference< lang::XUnoTunnel > xTunnel; + m_xPackageFolder->getByName( pElement->m_aOriginalName ) >>= xTunnel; + if ( !xTunnel.is() ) + throw container::NoSuchElementException( THROW_WHERE ); + + uno::Reference< container::XNameContainer > xPackageSubFolder( xTunnel, uno::UNO_QUERY_THROW ); + pElement->m_xStorage.reset(new OStorage_Impl(this, nStorageMode, xPackageSubFolder, m_xPackage, m_xContext, m_nStorageType)); + } +} + +void OStorage_Impl::OpenSubStream( SotElement_Impl* pElement ) +{ + SAL_WARN_IF( !pElement, "package.xstor", "pElement is not set!" ); + SAL_WARN_IF( pElement->m_bIsStorage, "package.xstor", "Storage flag is set!" ); + + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + if (pElement->m_xStream) + return; + + SAL_WARN_IF( pElement->m_bIsInserted, "package.xstor", "Inserted element must be created already!" ); + + uno::Reference< lang::XUnoTunnel > xTunnel; + m_xPackageFolder->getByName( pElement->m_aOriginalName ) >>= xTunnel; + if ( !xTunnel.is() ) + throw container::NoSuchElementException( THROW_WHERE ); + + uno::Reference< packages::XDataSinkEncrSupport > xPackageSubStream( xTunnel, uno::UNO_QUERY_THROW ); + + // the stream can never be inserted here, because inserted stream element holds the stream till commit or destruction + pElement->m_xStream.reset(new OWriteStream_Impl(this, xPackageSubStream, m_xPackage, m_xContext, false, m_nStorageType, false, GetRelInfoStreamForName(pElement->m_aOriginalName))); +} + +uno::Sequence< OUString > OStorage_Impl::GetElementNames() +{ + ::osl::MutexGuard aGuard( m_xMutex->GetMutex() ); + + ReadContents(); + + sal_Int32 nCnt = 0; + for ( const auto& pair : m_aChildrenMap ) + for (auto pElement : pair.second) + { + if ( !pElement->m_bIsRemoved ) + nCnt++; + } + + uno::Sequence<OUString> aElementNames(nCnt); + OUString* pArray = aElementNames.getArray(); + for ( const auto& pair : m_aChildrenMap ) + for (auto pElement : pair.second) + { + if ( !pElement->m_bIsRemoved ) + *pArray++ = pair.first; + } + + return aElementNames; +} + +void OStorage_Impl::RemoveElement( OUString const & rName, SotElement_Impl* pElement ) +{ + assert(pElement); + + if ( (pElement->m_xStorage && ( pElement->m_xStorage->m_pAntiImpl || !pElement->m_xStorage->m_aReadOnlyWrapVector.empty() )) + || (pElement->m_xStream && ( pElement->m_xStream->m_pAntiImpl || !pElement->m_xStream->m_aInputStreamsVector.empty() )) ) + throw io::IOException( THROW_WHERE ); // TODO: Access denied + + auto mapIt = m_aChildrenMap.find(rName); + for (auto it = mapIt->second.begin(); it != mapIt->second.end(); ++it) + if (pElement == *it) + { + if ( pElement->m_bIsInserted ) + { + delete pElement; + mapIt->second.erase(std::remove(mapIt->second.begin(), mapIt->second.end(), pElement), mapIt->second.end()); + if (mapIt->second.empty()) + m_aChildrenMap.erase(mapIt); + } + else + { + pElement->m_bIsRemoved = true; + ClearElement( pElement ); + } + return; + } + assert(false && "not found"); + + // TODO/OFOPXML: the rel stream should be removed as well +} + +void OStorage_Impl::ClearElement( SotElement_Impl* pElement ) +{ + pElement->m_xStorage.reset(); + pElement->m_xStream.reset(); +} + +void OStorage_Impl::CloneStreamElement( const OUString& aStreamName, + bool bEncryptionDataProvided, + const ::comphelper::SequenceAsHashMap& aEncryptionData, + uno::Reference< io::XStream >& xTargetStream ) +{ + SotElement_Impl *pElement = FindElement( aStreamName ); + if ( !pElement ) + { + // element does not exist, throw exception + throw io::IOException( THROW_WHERE ); // TODO: access_denied + } + else if ( pElement->m_bIsStorage ) + throw io::IOException( THROW_WHERE ); + + if (!pElement->m_xStream) + OpenSubStream( pElement ); + + if (!pElement->m_xStream || !pElement->m_xStream->m_xPackageStream.is()) + throw io::IOException( THROW_WHERE ); // TODO: general_error + + // the existence of m_pAntiImpl of the child is not interesting, + // the copy will be created internally + + // usual copying is not applicable here, only last flushed version of the + // child stream should be used for copying. Probably the children m_xPackageStream + // can be used as a base of a new stream, that would be copied to result + // storage. The only problem is that some package streams can be accessed from outside + // at the same time (now solved by wrappers that remember own position). + + if (bEncryptionDataProvided) + pElement->m_xStream->GetCopyOfLastCommit(xTargetStream, aEncryptionData); + else + pElement->m_xStream->GetCopyOfLastCommit(xTargetStream); +} + +void OStorage_Impl::RemoveStreamRelInfo( std::u16string_view aOriginalName ) +{ + // this method should be used only in OStorage_Impl::Commit() method + // the aOriginalName can be empty, in this case the storage relation info should be removed + + if ( m_nStorageType == embed::StorageFormats::OFOPXML && m_xRelStorage.is() ) + { + OUString aRelStreamName = OUString::Concat(aOriginalName) + ".rels"; + + if ( m_xRelStorage->hasByName( aRelStreamName ) ) + m_xRelStorage->removeElement( aRelStreamName ); + } +} + +void OStorage_Impl::CreateRelStorage() +{ + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + return; + + if ( m_xRelStorage.is() ) + return; + + if ( !m_pRelStorElement ) + { + m_pRelStorElement = new SotElement_Impl( "_rels", true, true ); + m_pRelStorElement->m_xStorage = CreateNewStorageImpl(embed::ElementModes::WRITE); + if (m_pRelStorElement->m_xStorage) + m_pRelStorElement->m_xStorage->m_pParent = nullptr; // the relation storage is completely controlled by parent + } + + if (!m_pRelStorElement->m_xStorage) + OpenSubStorage( m_pRelStorElement, embed::ElementModes::WRITE ); + + if (!m_pRelStorElement->m_xStorage) + throw uno::RuntimeException( THROW_WHERE ); + + m_xRelStorage = new OStorage(m_pRelStorElement->m_xStorage.get(), false); +} + +void OStorage_Impl::CommitStreamRelInfo( std::u16string_view rName, SotElement_Impl const * pStreamElement ) +{ + // this method should be used only in OStorage_Impl::Commit() method + + // the stream element must be provided + if ( !pStreamElement ) + throw uno::RuntimeException( THROW_WHERE ); + + if (m_nStorageType == embed::StorageFormats::OFOPXML && pStreamElement->m_xStream) + { + SAL_WARN_IF( rName.empty(), "package.xstor", "The name must not be empty!" ); + + if ( !m_xRelStorage.is() ) + { + // Create new rels storage, this is commit scenario so it must be possible + CreateRelStorage(); + } + + pStreamElement->m_xStream->CommitStreamRelInfo(m_xRelStorage, pStreamElement->m_aOriginalName, rName); + } +} + +uno::Reference< io::XInputStream > OStorage_Impl::GetRelInfoStreamForName( + std::u16string_view aName ) +{ + if ( m_nStorageType == embed::StorageFormats::OFOPXML ) + { + ReadContents(); + if ( m_xRelStorage.is() ) + { + OUString aRelStreamName = OUString::Concat(aName) + ".rels"; + if ( m_xRelStorage->hasByName( aRelStreamName ) ) + { + uno::Reference< io::XStream > xStream = m_xRelStorage->openStreamElement( aRelStreamName, embed::ElementModes::READ ); + if ( xStream.is() ) + return xStream->getInputStream(); + } + } + } + + return uno::Reference< io::XInputStream >(); +} + +void OStorage_Impl::CommitRelInfo( const uno::Reference< container::XNameContainer >& xNewPackageFolder ) +{ + // this method should be used only in OStorage_Impl::Commit() method + OUString aRelsStorName("_rels"); + + if ( !xNewPackageFolder.is() ) + throw uno::RuntimeException( THROW_WHERE ); + + if ( m_nStorageType != embed::StorageFormats::OFOPXML ) + return; + + if ( m_nRelInfoStatus == RELINFO_BROKEN || m_nRelInfoStatus == RELINFO_CHANGED_BROKEN ) + throw io::IOException( THROW_WHERE ); + + if (m_nRelInfoStatus == RELINFO_CHANGED) + { + if (m_aRelInfo.hasElements()) + { + CreateRelStorage(); + + uno::Reference<io::XStream> xRelsStream = m_xRelStorage->openStreamElement( + ".rels", embed::ElementModes::TRUNCATE | embed::ElementModes::READWRITE); + + uno::Reference<io::XOutputStream> xOutStream = xRelsStream->getOutputStream(); + if (!xOutStream.is()) + throw uno::RuntimeException(THROW_WHERE); + + ::comphelper::OFOPXMLHelper::WriteRelationsInfoSequence(xOutStream, m_aRelInfo, + m_xContext); + + // set the mediatype + uno::Reference<beans::XPropertySet> xPropSet(xRelsStream, uno::UNO_QUERY_THROW); + xPropSet->setPropertyValue( + "MediaType", uno::Any(OUString( + "application/vnd.openxmlformats-package.relationships+xml"))); + + m_nRelInfoStatus = RELINFO_READ; + } + else if (m_xRelStorage.is()) + RemoveStreamRelInfo(std::u16string_view()); // remove own rel info + } + else if (m_nRelInfoStatus == RELINFO_CHANGED_STREAM_READ + || m_nRelInfoStatus == RELINFO_CHANGED_STREAM) + { + CreateRelStorage(); + + uno::Reference<io::XStream> xRelsStream = m_xRelStorage->openStreamElement( + ".rels", embed::ElementModes::TRUNCATE | embed::ElementModes::READWRITE); + + uno::Reference<io::XOutputStream> xOutputStream = xRelsStream->getOutputStream(); + if (!xOutputStream.is()) + throw uno::RuntimeException(THROW_WHERE); + + uno::Reference<io::XSeekable> xSeek(m_xNewRelInfoStream, uno::UNO_QUERY_THROW); + xSeek->seek(0); + ::comphelper::OStorageHelper::CopyInputToOutput(m_xNewRelInfoStream, xOutputStream); + + // set the mediatype + uno::Reference<beans::XPropertySet> xPropSet(xRelsStream, uno::UNO_QUERY_THROW); + xPropSet->setPropertyValue( + "MediaType", + uno::Any(OUString("application/vnd.openxmlformats-package.relationships+xml"))); + + m_xNewRelInfoStream.clear(); + if (m_nRelInfoStatus == RELINFO_CHANGED_STREAM) + { + m_aRelInfo = uno::Sequence<uno::Sequence<beans::StringPair>>(); + m_nRelInfoStatus = RELINFO_NO_INIT; + } + else + m_nRelInfoStatus = RELINFO_READ; + } + + if ( !m_xRelStorage.is() ) + return; + + if ( m_xRelStorage->hasElements() ) + { + uno::Reference< embed::XTransactedObject > xTrans( m_xRelStorage, uno::UNO_QUERY_THROW ); + xTrans->commit(); + } + + if ( xNewPackageFolder.is() && xNewPackageFolder->hasByName( aRelsStorName ) ) + xNewPackageFolder->removeByName( aRelsStorName ); + + if ( !m_xRelStorage->hasElements() ) + { + // the empty relations storage should not be created + delete m_pRelStorElement; + m_pRelStorElement = nullptr; + m_xRelStorage.clear(); + } + else if ( m_pRelStorElement && m_pRelStorElement->m_xStorage && xNewPackageFolder.is() ) + m_pRelStorElement->m_xStorage->InsertIntoPackageFolder( aRelsStorName, xNewPackageFolder ); +} + +// OStorage implementation + +OStorage::OStorage( 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_pImpl( new OStorage_Impl( xInputStream, nMode, xProperties, xContext, nStorageType ) ) +, m_xSharedMutex( m_pImpl->m_xMutex ) +, m_aListenersContainer( m_pImpl->m_xMutex->GetMutex() ) +, m_bReadOnlyWrap( false ) +{ + m_pImpl->m_pAntiImpl = this; +} + +OStorage::OStorage( 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_pImpl( new OStorage_Impl( xStream, nMode, xProperties, xContext, nStorageType ) ) +, m_xSharedMutex( m_pImpl->m_xMutex ) +, m_aListenersContainer( m_pImpl->m_xMutex->GetMutex() ) +, m_bReadOnlyWrap( false ) +{ + m_pImpl->m_pAntiImpl = this; +} + +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; +} + +OStorage::~OStorage() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + if ( m_pImpl ) + { + osl_atomic_increment(&m_refCount); // to call dispose + try { + dispose(); + } + catch( const uno::RuntimeException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Handled exception"); + } + } +} + +void OStorage::InternalDispose( bool bNotifyImpl ) +{ + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + // 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( static_cast< ::cppu::OWeakObject* >(this) ); + m_aListenersContainer.disposeAndClear( aSource ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + 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 ( const auto& 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()))); + + try { + xTmp->dispose(); + } catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Quiet exception"); + } + } + } + + m_aOpenSubComponentsVector.clear(); + } + } + + if ( bNotifyImpl ) + m_pImpl->RemoveReadOnlyWrap( *this ); + } + else + { + m_pImpl->m_pAntiImpl = nullptr; + + 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 + + auto& rVec = m_aOpenSubComponentsVector; + rVec.erase(std::remove_if(rVec.begin(), rVec.end(), + [&xChild](const uno::Reference<lang::XComponent>& xTmp) { + return !xTmp.is() || xTmp == xChild; + }), + rVec.end()); +} + +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!" ); + + lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) ); + + comphelper::OInterfaceContainerHelper2* pContainer = + m_aListenersContainer.getContainer( + cppu::UnoType<util::XModifyListener>::get()); + if ( pContainer ) + { + comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer ); + while ( pIterator.hasMoreElements( ) ) + { + static_cast<util::XModifyListener*>( pIterator.next( ) )->modified( aSource ); + } + } +} + +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( static_cast< ::cppu::OWeakObject* >(this) ); + + comphelper::OInterfaceContainerHelper2* pContainer = + m_aListenersContainer.getContainer( + cppu::UnoType<embed::XTransactionListener>::get()); + if ( !pContainer ) + return; + + comphelper::OInterfaceIteratorHelper2 pIterator( *pContainer ); + while ( pIterator.hasMoreElements( ) ) + { + OSL_ENSURE( nMessage >= 1 && nMessage <= 4, "Wrong internal notification code is used!" ); + + switch( nMessage ) + { + case STOR_MESS_PRECOMMIT: + static_cast<embed::XTransactionListener*>( pIterator.next( ) )->preCommit( aSource ); + break; + case STOR_MESS_COMMITTED: + static_cast<embed::XTransactionListener*>( pIterator.next( ) )->commited( aSource ); + break; + case STOR_MESS_PREREVERT: + static_cast<embed::XTransactionListener*>( pIterator.next( ) )->preRevert( aSource ); + break; + case STOR_MESS_REVERTED: + static_cast<embed::XTransactionListener*>( pIterator.next( ) )->reverted( aSource ); + break; + } + } +} + +SotElement_Impl* OStorage::OpenStreamElement_Impl( const OUString& aStreamName, sal_Int32 nOpenMode, bool bEncr ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + 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 ); + } + else if ( pElement->m_bIsStorage ) + { + throw io::IOException( THROW_WHERE ); + } + + SAL_WARN_IF( !pElement, "package.xstor", "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 ); + + return pElement; +} + +void OStorage::MakeLinkToSubComponent_Impl( const uno::Reference< lang::XComponent >& xComponent ) +{ + if ( !xComponent.is() ) + throw uno::RuntimeException( THROW_WHERE ); + + if (!m_pSubElDispListener) + { + m_pSubElDispListener = new OChildDispListener_Impl( *this ); + } + + xComponent->addEventListener( m_pSubElDispListener ); + + m_aOpenSubComponentsVector.emplace_back(xComponent ); +} + +// XInterface + +uno::Any SAL_CALL OStorage::queryInterface( const uno::Type& rType ) +{ + // common interfaces + uno::Any aReturn = ::cppu::queryInterface + ( rType + , static_cast<lang::XTypeProvider*> ( this ) + , static_cast<embed::XStorage*> ( this ) + , static_cast<embed::XStorage2*> ( this ) + , static_cast<embed::XTransactedObject*> ( this ) + , static_cast<embed::XTransactionBroadcaster*> ( this ) + , static_cast<util::XModifiable*> ( this ) + , static_cast<container::XNameAccess*> ( this ) + , static_cast<container::XElementAccess*> ( this ) + , static_cast<lang::XComponent*> ( this ) + , static_cast<beans::XPropertySet*> ( this ) + , static_cast<embed::XOptimizedStorage*> ( this ) ); + + if ( aReturn.hasValue() ) + return aReturn ; + + aReturn = ::cppu::queryInterface + ( rType + , static_cast<embed::XHierarchicalStorageAccess*> ( this ) + , static_cast<embed::XHierarchicalStorageAccess2*> ( this ) ); + + if ( aReturn.hasValue() ) + return aReturn ; + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE ) + { + if ( m_pImpl->m_bIsRoot ) + { + aReturn = ::cppu::queryInterface + ( rType + , static_cast<embed::XStorageRawAccess*> ( this ) + , static_cast<embed::XEncryptionProtectedSource*> ( this ) + , static_cast<embed::XEncryptionProtectedSource2*> ( this ) + , static_cast<embed::XEncryptionProtectedStorage*> ( this ) ); + } + else + { + aReturn = ::cppu::queryInterface + ( rType + , static_cast<embed::XStorageRawAccess*> ( this ) ); + } + } + else if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML ) + { + aReturn = ::cppu::queryInterface + ( rType + , static_cast<embed::XRelationshipAccess*> ( this ) ); + } + + if ( aReturn.hasValue() ) + return aReturn ; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL OStorage::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL OStorage::release() noexcept +{ + OWeakObject::release(); +} + +// XTypeProvider +uno::Sequence< uno::Type > SAL_CALL OStorage::getTypes() +{ + if (! m_pTypeCollection) + { + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if (! m_pTypeCollection) + { + if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE ) + { + if ( m_pImpl->m_bIsRoot ) + { + m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( cppu::UnoType<lang::XTypeProvider>::get() + , cppu::UnoType<embed::XStorage>::get() + , cppu::UnoType<embed::XStorage2>::get() + , cppu::UnoType<embed::XStorageRawAccess>::get() + , cppu::UnoType<embed::XTransactedObject>::get() + , cppu::UnoType<embed::XTransactionBroadcaster>::get() + , cppu::UnoType<util::XModifiable>::get() + , cppu::UnoType<embed::XEncryptionProtectedStorage>::get() + , cppu::UnoType<embed::XEncryptionProtectedSource2>::get() + , cppu::UnoType<embed::XEncryptionProtectedSource>::get() + , cppu::UnoType<beans::XPropertySet>::get())); + } + else + { + m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( cppu::UnoType<lang::XTypeProvider>::get() + , cppu::UnoType<embed::XStorage>::get() + , cppu::UnoType<embed::XStorage2>::get() + , cppu::UnoType<embed::XStorageRawAccess>::get() + , cppu::UnoType<embed::XTransactedObject>::get() + , cppu::UnoType<embed::XTransactionBroadcaster>::get() + , cppu::UnoType<util::XModifiable>::get() + , cppu::UnoType<beans::XPropertySet>::get())); + } + } + else if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML ) + { + m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( cppu::UnoType<lang::XTypeProvider>::get() + , cppu::UnoType<embed::XStorage>::get() + , cppu::UnoType<embed::XTransactedObject>::get() + , cppu::UnoType<embed::XTransactionBroadcaster>::get() + , cppu::UnoType<util::XModifiable>::get() + , cppu::UnoType<embed::XRelationshipAccess>::get() + , cppu::UnoType<beans::XPropertySet>::get())); + } + else + { + m_pTypeCollection.reset(new ::cppu::OTypeCollection + ( cppu::UnoType<lang::XTypeProvider>::get() + , cppu::UnoType<embed::XStorage>::get() + , cppu::UnoType<embed::XTransactedObject>::get() + , cppu::UnoType<embed::XTransactionBroadcaster>::get() + , cppu::UnoType<util::XModifiable>::get() + , cppu::UnoType<beans::XPropertySet>::get())); + } + } + } + + return m_pTypeCollection->getTypes() ; +} + +uno::Sequence< sal_Int8 > SAL_CALL OStorage::getImplementationId() +{ + static const comphelper::UnoIdInit lcl_ImplId; + return lcl_ImplId.getSeq(); +} + +// XStorage +void SAL_CALL OStorage::copyToStorage( const uno::Reference< embed::XStorage >& xDest ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( !xDest.is() || xDest == uno::Reference< uno::XInterface >( static_cast< OWeakObject*> ( this ), uno::UNO_QUERY ) ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); + + try { + m_pImpl->CopyToStorage( xDest, false ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't copy storage!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +uno::Reference< io::XStream > SAL_CALL OStorage::openStreamElement( + const OUString& aStreamName, sal_Int32 nOpenMode ) +{ + osl::ClearableMutexGuard aGuard(m_xSharedMutex->GetMutex()); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStreamName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable element name + + if ( ( nOpenMode & embed::ElementModes::WRITE ) && m_bReadOnlyWrap ) + throw io::IOException( THROW_WHERE ); // TODO: access denied + + uno::Reference< io::XStream > xResult; + try + { + SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamName, nOpenMode, false ); + OSL_ENSURE(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!" ); + + if ( m_bReadOnlyWrap ) + { + // before the storage disposes the stream it must deregister itself as listener + uno::Reference< lang::XComponent > xStreamComponent( xResult, uno::UNO_QUERY_THROW ); + MakeLinkToSubComponent_Impl( xStreamComponent ); + } + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const packages::WrongPasswordException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException(THROW_WHERE "Can't open stream element!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + + aGuard.clear(); + + BroadcastModifiedIfNecessary(); + + return xResult; +} + +uno::Reference< io::XStream > SAL_CALL OStorage::openEncryptedStreamElement( + const OUString& aStreamName, sal_Int32 nOpenMode, const OUString& aPass ) +{ + return openEncryptedStream( aStreamName, nOpenMode, ::comphelper::OStorageHelper::CreatePackageEncryptionData( aPass ) ); +} + +uno::Reference< embed::XStorage > SAL_CALL OStorage::openStorageElement( + const OUString& aStorName, sal_Int32 nStorageMode ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aStorName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStorName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStorName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable storage name + + if ( ( nStorageMode & embed::ElementModes::WRITE ) && m_bReadOnlyWrap ) + throw io::IOException( THROW_WHERE ); // TODO: access denied + + if ( ( nStorageMode & embed::ElementModes::TRUNCATE ) + && !( nStorageMode & embed::ElementModes::WRITE ) ) + throw io::IOException( THROW_WHERE ); // TODO: access denied + + // it's always possible to read written storage in this implementation + nStorageMode |= embed::ElementModes::READ; + + uno::Reference< embed::XStorage > 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 ); + } + else if ( !pElement->m_bIsStorage ) + { + throw io::IOException( THROW_WHERE ); + } + else if (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 + } + else if ( !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 (const auto & 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 + + bool bReadOnlyWrap = ( ( nStorageMode & embed::ElementModes::WRITE ) != embed::ElementModes::WRITE ); + rtl::Reference<OStorage> pResultStorage = new OStorage(pElement->m_xStorage.get(), bReadOnlyWrap); + xResult = pResultStorage; + + if ( bReadOnlyWrap ) + { + // Before this call is done the object must be refcounted already + pElement->m_xStorage->SetReadOnlyWrap(*pResultStorage); + + // before the storage disposes the stream it must deregister itself as listener + uno::Reference< lang::XComponent > xStorageComponent( xResult, uno::UNO_QUERY_THROW ); + MakeLinkToSubComponent_Impl( xStorageComponent ); + } + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't open storage!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + + return xResult; +} + +uno::Reference< io::XStream > SAL_CALL OStorage::cloneStreamElement( const OUString& aStreamName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStreamName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable storage name + + try + { + uno::Reference< io::XStream > xResult; + m_pImpl->CloneStreamElement( aStreamName, false, ::comphelper::SequenceAsHashMap(), xResult ); + if ( !xResult.is() ) + throw uno::RuntimeException( THROW_WHERE ); + return xResult; + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const packages::WrongPasswordException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't clone stream!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +uno::Reference< io::XStream > SAL_CALL OStorage::cloneEncryptedStreamElement( + const OUString& aStreamName, + const OUString& aPass ) +{ + return cloneEncryptedStream( aStreamName, ::comphelper::OStorageHelper::CreatePackageEncryptionData( aPass ) ); +} + +void SAL_CALL OStorage::copyLastCommitTo( + const uno::Reference< embed::XStorage >& xTargetStorage ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + try + { + m_pImpl->CopyLastCommitTo( xTargetStorage ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't copy last commit version!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + +} + +void SAL_CALL OStorage::copyStorageElementLastCommitTo( + const OUString& aStorName, + const uno::Reference< embed::XStorage >& xTargetStorage ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aStorName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStorName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStorName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable storage name + + try + { + SotElement_Impl *pElement = m_pImpl->FindElement( aStorName ); + if ( !pElement ) + { + // element does not exist, throw exception + throw io::IOException( THROW_WHERE ); // TODO: access_denied + } + else if ( !pElement->m_bIsStorage ) + { + throw io::IOException( THROW_WHERE ); + } + + if (!pElement->m_xStorage) + m_pImpl->OpenSubStorage( pElement, embed::ElementModes::READ ); + + if (!pElement->m_xStorage) + throw io::IOException( THROW_WHERE ); // TODO: general_error + + // the existence of m_pAntiImpl of the child is not interesting, + // the copy will be created internally + + pElement->m_xStorage->CopyLastCommitTo(xTargetStorage); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't copy last commit element version!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +sal_Bool SAL_CALL OStorage::isStreamElement( const OUString& aElementName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aElementName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable name + + SotElement_Impl* pElement = nullptr; + + try + { + pElement = m_pImpl->FindElement( aElementName ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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't detect whether it is a stream!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + + if ( !pElement ) + throw container::NoSuchElementException( THROW_WHERE ); //??? + + return !pElement->m_bIsStorage; +} + +sal_Bool SAL_CALL OStorage::isStorageElement( const OUString& aElementName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aElementName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); + + SotElement_Impl* pElement = nullptr; + + try + { + pElement = m_pImpl->FindElement( aElementName ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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't detect whether it is a storage", + uno::Reference< io::XInputStream >(), + aCaught ); + } + + if ( !pElement ) + throw container::NoSuchElementException( THROW_WHERE ); //??? + + return pElement->m_bIsStorage; +} + +void SAL_CALL OStorage::removeElement( const OUString& aElementName ) +{ + { + osl::MutexGuard aGuard(m_xSharedMutex->GetMutex()); + + if (!m_pImpl) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException(THROW_WHERE); + } + + if (aElementName.isEmpty() + || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aElementName, false)) + throw lang::IllegalArgumentException(THROW_WHERE "Unexpected entry name syntax.", + uno::Reference<uno::XInterface>(), 1); + + if (m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aElementName == "_rels") + throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference<uno::XInterface>(), + 1); // TODO: unacceptable name + + if (!(m_pImpl->m_nStorageMode & embed::ElementModes::WRITE)) + throw io::IOException(THROW_WHERE); // TODO: access denied + + try + { + auto pElement = m_pImpl->FindElement(aElementName); + if ( !pElement ) + throw container::NoSuchElementException(THROW_WHERE); //??? + + m_pImpl->RemoveElement(aElementName, pElement); + + m_pImpl->m_bIsModified = true; + m_pImpl->m_bBroadcastModified = true; + } + catch (const embed::InvalidStorageException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const lang::IllegalArgumentException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const container::NoSuchElementException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const io::IOException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const embed::StorageWrappedTargetException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException(THROW_WHERE "Can't remove element!", + uno::Reference<io::XInputStream>(), aCaught); + } + } + + BroadcastModifiedIfNecessary(); +} + +void SAL_CALL OStorage::renameElement( const OUString& aElementName, const OUString& aNewName ) +{ + { + osl::MutexGuard aGuard(m_xSharedMutex->GetMutex()); + + if (!m_pImpl) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException(THROW_WHERE); + } + + if (aElementName.isEmpty() + || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aElementName, false) + || aNewName.isEmpty() + || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aNewName, false)) + throw lang::IllegalArgumentException(THROW_WHERE "Unexpected entry name syntax.", + uno::Reference<uno::XInterface>(), 1); + + if (m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML + && (aElementName == "_rels" || aNewName == "_rels")) + throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference<uno::XInterface>(), + 0); // TODO: unacceptable element name + + if (!(m_pImpl->m_nStorageMode & embed::ElementModes::WRITE)) + throw io::IOException(THROW_WHERE); // TODO: access denied + + try + { + SotElement_Impl* pRefElement = m_pImpl->FindElement(aNewName); + if (pRefElement) + throw container::ElementExistException(THROW_WHERE); //??? + + auto pElement = m_pImpl->FindElement( aElementName ); + if ( !pElement ) + throw container::NoSuchElementException(THROW_WHERE); //??? + + auto mapIt = m_pImpl->m_aChildrenMap.find(aElementName); + auto rVec = mapIt->second; + for (auto it = rVec.begin(); it != rVec.end(); ++it) + if (pElement == *it) + { + rVec.erase(std::remove(rVec.begin(), rVec.end(), pElement), rVec.end()); + if (rVec.empty()) + m_pImpl->m_aChildrenMap.erase(mapIt); + break; + } + m_pImpl->m_aChildrenMap[aNewName].push_back(pElement); + m_pImpl->m_bIsModified = true; + m_pImpl->m_bBroadcastModified = true; + } + catch (const embed::InvalidStorageException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const lang::IllegalArgumentException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const container::NoSuchElementException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const container::ElementExistException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const io::IOException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const embed::StorageWrappedTargetException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException(THROW_WHERE "Can't rename element!", + uno::Reference<io::XInputStream>(), aCaught); + } + } + + BroadcastModifiedIfNecessary(); +} + +void SAL_CALL OStorage::copyElementTo( const OUString& aElementName, + const uno::Reference< embed::XStorage >& xDest, + const OUString& aNewName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false ) + || aNewName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aNewName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( !xDest.is() ) + // || xDest == uno::Reference< uno::XInterface >( static_cast< OWeakObject* >( this ), uno::UNO_QUERY ) ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && ( aElementName == "_rels" || aNewName == "_rels" ) ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); // unacceptable element name + + try + { + SotElement_Impl* pElement = m_pImpl->FindElement( aElementName ); + if ( !pElement ) + throw container::NoSuchElementException( THROW_WHERE ); + + uno::Reference< XNameAccess > xNameAccess( xDest, uno::UNO_QUERY_THROW ); + if ( xNameAccess->hasByName( aNewName ) ) + throw container::ElementExistException( THROW_WHERE ); + + m_pImpl->CopyStorageElement( pElement, xDest, aNewName, false ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const container::ElementExistException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't copy element!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +void SAL_CALL OStorage::moveElementTo( const OUString& aElementName, + const uno::Reference< embed::XStorage >& xDest, + const OUString& aNewName ) +{ + { + osl::MutexGuard aGuard(m_xSharedMutex->GetMutex()); + + if (!m_pImpl) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException(THROW_WHERE); + } + + if (aElementName.isEmpty() + || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aElementName, false) + || aNewName.isEmpty() + || !::comphelper::OStorageHelper::IsValidZipEntryFileName(aNewName, false)) + throw lang::IllegalArgumentException(THROW_WHERE "Unexpected entry name syntax.", + uno::Reference<uno::XInterface>(), 1); + + if (!xDest.is() + || xDest + == uno::Reference<uno::XInterface>(static_cast<OWeakObject*>(this), + uno::UNO_QUERY)) + throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference<uno::XInterface>(), 2); + + if (m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML + && (aElementName == "_rels" || aNewName == "_rels")) + throw lang::IllegalArgumentException(THROW_WHERE, uno::Reference<uno::XInterface>(), + 0); // unacceptable element name + + if (!(m_pImpl->m_nStorageMode & embed::ElementModes::WRITE)) + throw io::IOException(THROW_WHERE); // TODO: access denied + + try + { + auto pElement = m_pImpl->FindElement( aElementName ); + if ( !pElement ) + throw container::NoSuchElementException(THROW_WHERE); //??? + + uno::Reference<XNameAccess> xNameAccess(xDest, uno::UNO_QUERY_THROW); + if (xNameAccess->hasByName(aNewName)) + throw container::ElementExistException(THROW_WHERE); + + m_pImpl->CopyStorageElement(pElement, xDest, aNewName, false); + + m_pImpl->RemoveElement(aElementName, pElement); + + m_pImpl->m_bIsModified = true; + m_pImpl->m_bBroadcastModified = true; + } + catch (const embed::InvalidStorageException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const lang::IllegalArgumentException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const container::NoSuchElementException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const container::ElementExistException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const embed::StorageWrappedTargetException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch (const io::IOException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException(THROW_WHERE "Can't move element!", + uno::Reference<io::XInputStream>(), aCaught); + } + } + + BroadcastModifiedIfNecessary(); +} + +// XStorage2 +uno::Reference< io::XStream > SAL_CALL OStorage::openEncryptedStream( + const OUString& aStreamName, sal_Int32 nOpenMode, const uno::Sequence< beans::NamedValue >& aEncryptionData ) +{ + osl::ClearableMutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( ( nOpenMode & embed::ElementModes::WRITE ) && m_bReadOnlyWrap ) + throw io::IOException( THROW_WHERE ); // TODO: access denied + + if ( !aEncryptionData.hasElements() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 3 ); + + uno::Reference< io::XStream > xResult; + try + { + SotElement_Impl *pElement = OpenStreamElement_Impl( aStreamName, nOpenMode, true ); + OSL_ENSURE(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_bReadOnlyWrap ) + { + // before the storage disposes the stream it must deregister itself as listener + uno::Reference< lang::XComponent > xStreamComponent( xResult, uno::UNO_QUERY_THROW ); + MakeLinkToSubComponent_Impl( xStreamComponent ); + } + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const packages::NoEncryptionException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const packages::WrongPasswordException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't open encrypted stream!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + + aGuard.clear(); + + BroadcastModifiedIfNecessary(); + + return xResult; +} + +uno::Reference< io::XStream > SAL_CALL OStorage::cloneEncryptedStream( + const OUString& aStreamName, + const uno::Sequence< beans::NamedValue >& aEncryptionData ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( !aEncryptionData.hasElements() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 ); + + try + { + uno::Reference< io::XStream > xResult; + m_pImpl->CloneStreamElement( aStreamName, true, aEncryptionData, xResult ); + if ( !xResult.is() ) + throw uno::RuntimeException( THROW_WHERE ); + return xResult; + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const packages::NoEncryptionException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const packages::WrongPasswordException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't clone encrypted stream!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +// XStorageRawAccess +uno::Reference< io::XInputStream > SAL_CALL OStorage::getPlainRawStreamElement( + const OUString& sStreamName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); // the interface is not supported and must not be accessible + + if ( sStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( sStreamName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + uno::Reference < io::XInputStream > xTempIn; + try + { + SotElement_Impl* pElement = m_pImpl->FindElement( sStreamName ); + if ( !pElement ) + throw container::NoSuchElementException( THROW_WHERE ); + + if (!pElement->m_xStream) + { + m_pImpl->OpenSubStream( pElement ); + if (!pElement->m_xStream) + throw io::IOException( THROW_WHERE ); + } + + uno::Reference<io::XInputStream> xRawInStream = pElement->m_xStream->GetPlainRawInStream(); + if ( !xRawInStream.is() ) + throw io::IOException( THROW_WHERE ); + + uno::Reference < io::XTempFile > xTempFile = io::TempFile::create( m_pImpl->m_xContext ); + uno::Reference < io::XOutputStream > xTempOut = xTempFile->getOutputStream(); + xTempIn = xTempFile->getInputStream(); + uno::Reference < io::XSeekable > xSeek( xTempOut, uno::UNO_QUERY ); + + if ( !xTempOut.is() || !xTempIn.is() || !xSeek.is() ) + throw io::IOException( THROW_WHERE ); + + // Copy temporary file to a new one + ::comphelper::OStorageHelper::CopyInputToOutput( xRawInStream, xTempOut ); + xTempOut->closeOutput(); + xSeek->seek( 0 ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't get plain raw stream!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + + return xTempIn; +} + +uno::Reference< io::XInputStream > SAL_CALL OStorage::getRawEncrStreamElement( + const OUString& sStreamName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) + throw packages::NoEncryptionException( THROW_WHERE ); + + if ( sStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( sStreamName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + uno::Reference < io::XInputStream > xTempIn; + try + { + SotElement_Impl* pElement = m_pImpl->FindElement( sStreamName ); + if ( !pElement ) + throw container::NoSuchElementException( THROW_WHERE ); + + if (!pElement->m_xStream) + { + m_pImpl->OpenSubStream( pElement ); + if (!pElement->m_xStream) + throw io::IOException( THROW_WHERE ); + } + + if (!pElement->m_xStream->IsEncrypted()) + throw packages::NoEncryptionException( THROW_WHERE ); + + uno::Reference< io::XInputStream > xRawInStream = pElement->m_xStream->GetRawInStream(); + if ( !xRawInStream.is() ) + throw io::IOException( THROW_WHERE ); + + uno::Reference < io::XTempFile > xTempFile = io::TempFile::create(m_pImpl->m_xContext); + uno::Reference < io::XOutputStream > xTempOut = xTempFile->getOutputStream(); + xTempIn = xTempFile->getInputStream(); + uno::Reference < io::XSeekable > xSeek( xTempOut, uno::UNO_QUERY ); + + if ( !xTempOut.is() || !xTempIn.is() || !xSeek.is() ) + throw io::IOException( THROW_WHERE ); + + // Copy temporary file to a new one + ::comphelper::OStorageHelper::CopyInputToOutput( xRawInStream, xTempOut ); + xTempOut->closeOutput(); + xSeek->seek( 0 ); + + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const packages::NoEncryptionException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't get raw stream!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + + return xTempIn; +} + +void SAL_CALL OStorage::insertRawEncrStreamElement( const OUString& aStreamName, + const uno::Reference< io::XInputStream >& xInStream ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) + throw embed::InvalidStorageException( THROW_WHERE ); + + if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( !xInStream.is() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 ); + + if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE ) ) + throw io::IOException( THROW_WHERE ); // TODO: access denied + + try + { + SotElement_Impl* pElement = m_pImpl->FindElement( aStreamName ); + if ( pElement ) + throw container::ElementExistException( THROW_WHERE ); + + m_pImpl->InsertRawStream( aStreamName, xInStream ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const packages::NoRawFormatException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const container::ElementExistException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't insert raw stream!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +// XTransactedObject +void SAL_CALL OStorage::commit() +{ + uno::Reference< util::XModifiable > xParentModif; + + try { + BroadcastTransaction( STOR_MESS_PRECOMMIT ); + + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_bReadOnlyWrap ) + throw io::IOException( THROW_WHERE ); // TODO: access_denied + + m_pImpl->Commit(); // the root storage initiates the storing to source + + // when the storage is committed the parent is modified + if ( m_pImpl->m_pParent && m_pImpl->m_pParent->m_pAntiImpl ) + xParentModif = static_cast<util::XModifiable*>(m_pImpl->m_pParent->m_pAntiImpl); + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Problems on commit!", + static_cast< ::cppu::OWeakObject* >( this ), + aCaught ); + } + + setModified( false ); + if ( xParentModif.is() ) + xParentModif->setModified( true ); + + BroadcastTransaction( STOR_MESS_COMMITTED ); +} + +void SAL_CALL OStorage::revert() +{ + // the method removes all the changes done after last commit + + BroadcastTransaction( STOR_MESS_PREREVERT ); + + { + osl::MutexGuard aGuard(m_xSharedMutex->GetMutex()); + + if (!m_pImpl) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException(THROW_WHERE); + } + + for (const auto & rPair : m_pImpl->m_aChildrenMap) + for (auto pElement : rPair.second) + { + bool bThrow = (pElement->m_xStorage + && (pElement->m_xStorage->m_pAntiImpl + || !pElement->m_xStorage->m_aReadOnlyWrapVector.empty())) + || (pElement->m_xStream + && (pElement->m_xStream->m_pAntiImpl + || !pElement->m_xStream->m_aInputStreamsVector.empty())); + if (bThrow) + throw io::IOException(THROW_WHERE); // TODO: access denied + } + + if (m_bReadOnlyWrap || !m_pImpl->m_bListCreated) + return; // nothing to do + + try + { + m_pImpl->Revert(); + m_pImpl->m_bIsModified = false; + m_pImpl->m_bBroadcastModified = true; + } + catch (const io::IOException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch (const embed::StorageWrappedTargetException&) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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 embed::StorageWrappedTargetException(THROW_WHERE "Problems on revert!", + static_cast<::cppu::OWeakObject*>(this), + aCaught); + } + } + + setModified( false ); + BroadcastTransaction( STOR_MESS_REVERTED ); +} + +// XTransactionBroadcaster +void SAL_CALL OStorage::addTransactionListener( const uno::Reference< embed::XTransactionListener >& aListener ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + m_aListenersContainer.addInterface( cppu::UnoType<embed::XTransactionListener>::get(), + aListener ); +} + +void SAL_CALL OStorage::removeTransactionListener( const uno::Reference< embed::XTransactionListener >& aListener ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + m_aListenersContainer.removeInterface( cppu::UnoType<embed::XTransactionListener>::get(), + aListener ); +} + +// 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. + +sal_Bool SAL_CALL OStorage::isModified() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + return m_pImpl->m_bIsModified; +} + +void SAL_CALL OStorage::setModified( sal_Bool bModified ) +{ + { + osl::MutexGuard aGuard(m_xSharedMutex->GetMutex()); + + if (!m_pImpl) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException(THROW_WHERE); + } + + if (m_bReadOnlyWrap) + throw beans::PropertyVetoException(THROW_WHERE); // TODO: access denied + + if (m_pImpl->m_bIsModified != bool(bModified)) + m_pImpl->m_bIsModified = bModified; + } + + if ( bModified ) + { + m_pImpl->m_bBroadcastModified = true; + BroadcastModifiedIfNecessary(); + } +} + +void SAL_CALL OStorage::addModifyListener( + const uno::Reference< util::XModifyListener >& aListener ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + osl_atomic_increment( &m_pImpl->m_nModifiedListenerCount ); + m_aListenersContainer.addInterface( + cppu::UnoType<util::XModifyListener>::get(), aListener ); +} + +void SAL_CALL OStorage::removeModifyListener( + const uno::Reference< util::XModifyListener >& aListener ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + osl_atomic_decrement( &m_pImpl->m_nModifiedListenerCount ); + m_aListenersContainer.removeInterface( + cppu::UnoType<util::XModifyListener>::get(), aListener ); +} + +// XNameAccess + +uno::Any SAL_CALL OStorage::getByName( const OUString& aName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable element name + + uno::Any aResult; + try + { + SotElement_Impl* pElement = m_pImpl->FindElement( aName ); + if ( !pElement ) + throw container::NoSuchElementException( THROW_WHERE ); + + if ( pElement->m_bIsStorage ) + aResult <<= openStorageElement( aName, embed::ElementModes::READ ); + else + aResult <<= openStreamElement( aName, embed::ElementModes::READ ); + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const lang::WrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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::WrappedTargetException( THROW_WHERE "Can not open storage!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + + return aResult; +} + +uno::Sequence< OUString > SAL_CALL OStorage::getElementNames() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + try + { + return m_pImpl->GetElementNames(); + } + 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 storage!", + static_cast< OWeakObject* >( this ), + aCaught ); + } +} + +sal_Bool SAL_CALL OStorage::hasByName( const OUString& aName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aName.isEmpty() ) + return false; + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aName == "_rels" ) + return false; + + SotElement_Impl* pElement = nullptr; + try + { + pElement = m_pImpl->FindElement( aName ); + } + 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 storage!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + + return ( pElement != nullptr ); +} + +uno::Type SAL_CALL OStorage::getElementType() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + // it is a multitype container + return uno::Type(); +} + +sal_Bool SAL_CALL OStorage::hasElements() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + try + { + return m_pImpl->HasChildren(); + } + 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 storage!", + static_cast< OWeakObject* >( this ), + aCaught ); + } +} + +// XComponent +void SAL_CALL OStorage::dispose() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + try + { + InternalDispose( true ); + } + 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 storage!", + static_cast< OWeakObject* >( this ), + aCaught ); + } +} + +void SAL_CALL OStorage::addEventListener( + const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + m_aListenersContainer.addInterface( + cppu::UnoType<lang::XEventListener>::get(), xListener ); +} + +void SAL_CALL OStorage::removeEventListener( + const uno::Reference< lang::XEventListener >& xListener ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + m_aListenersContainer.removeInterface( + cppu::UnoType<lang::XEventListener>::get(), xListener ); +} + +// XEncryptionProtectedSource + +void SAL_CALL OStorage::setEncryptionPassword( const OUString& aPass ) +{ + setEncryptionData( ::comphelper::OStorageHelper::CreatePackageEncryptionData( aPass ) ); +} + +void SAL_CALL OStorage::removeEncryption() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( 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", "removeEncryption() method is not available for nonroot storages!" ); + if ( !m_pImpl->m_bIsRoot ) + return; + + 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!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + + // TODO: check if the password is valid + // update all streams that was encrypted with old password + + uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW ); + try + { + xPackPropSet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY, + uno::Any( uno::Sequence< beans::NamedValue >() ) ); + + 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 ); + } +} + +// XEncryptionProtectedSource2 + +void SAL_CALL OStorage::setEncryptionData( const uno::Sequence< beans::NamedValue >& aEncryptionData ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) + throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage + + if ( !aEncryptionData.hasElements() ) + throw uno::RuntimeException( THROW_WHERE "Unexpected empty encryption data!" ); + + SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setEncryptionData() method is not available for nonroot storages!" ); + if ( !m_pImpl->m_bIsRoot ) + return; + + 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!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + + uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW ); + try + { + ::comphelper::SequenceAsHashMap aEncryptionMap( aEncryptionData ); + xPackPropSet->setPropertyValue( STORAGE_ENCRYPTION_KEYS_PROPERTY, + uno::Any( aEncryptionMap.getAsConstNamedValueList() ) ); + + m_pImpl->m_bHasCommonEncryptionData = true; + m_pImpl->m_aCommonEncryptionData = aEncryptionMap; + } + catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:" ); + + throw io::IOException( THROW_WHERE ); + } +} + +sal_Bool SAL_CALL OStorage::hasEncryptionData() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + return m_pImpl && m_pImpl->m_bHasCommonEncryptionData; +} + +// XEncryptionProtectedStorage + +void SAL_CALL OStorage::setEncryptionAlgorithms( const uno::Sequence< beans::NamedValue >& aAlgorithms ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) + throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage + + if ( !aAlgorithms.hasElements() ) + throw uno::RuntimeException( THROW_WHERE "Unexpected empty encryption algorithms list!" ); + + SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setEncryptionAlgorithms() method is not available for nonroot storages!" ); + if ( !m_pImpl->m_bIsRoot ) + return; + + 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!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + + uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW ); + try + { + xPackPropSet->setPropertyValue( ENCRYPTION_ALGORITHMS_PROPERTY, + uno::Any( aAlgorithms ) ); + } + 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!", + static_cast< OWeakObject* >( this ), + aCaught ); + } +} + +void SAL_CALL OStorage::setGpgProperties( const uno::Sequence< uno::Sequence< beans::NamedValue > >& aProps ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) + throw uno::RuntimeException( THROW_WHERE ); // the interface must be visible only for package storage + + if ( !aProps.hasElements() ) + throw uno::RuntimeException( THROW_WHERE "Unexpected empty encryption algorithms list!" ); + + SAL_WARN_IF( !m_pImpl->m_bIsRoot, "package.xstor", "setGpgProperties() method is not available for nonroot storages!" ); + if ( !m_pImpl->m_bIsRoot ) + return; + + try { + m_pImpl->ReadContents(); + } + catch ( const uno::RuntimeException& aRuntimeException ) + { + SAL_INFO("package.xstor", "Rethrow: " << aRuntimeException.Message); + 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!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + + uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW ); + try + { + xPackPropSet->setPropertyValue( ENCRYPTION_GPG_PROPERTIES, + uno::Any( aProps ) ); + } + catch ( const uno::RuntimeException& aRuntimeException ) + { + SAL_INFO("package.xstor", "Rethrow: " << aRuntimeException.Message); + 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!", + static_cast< OWeakObject* >( this ), + aCaught ); + } +} + +uno::Sequence< beans::NamedValue > SAL_CALL OStorage::getEncryptionAlgorithms() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + 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!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + + uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW ); + try + { + xPackPropSet->getPropertyValue( ENCRYPTION_ALGORITHMS_PROPERTY ) >>= aResult; + } + 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!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + } + + return aResult; +} + +// XPropertySet + +uno::Reference< beans::XPropertySetInfo > SAL_CALL OStorage::getPropertySetInfo() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + //TODO: + return uno::Reference< beans::XPropertySetInfo >(); +} + +void SAL_CALL OStorage::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + //TODO: think about interaction handler + + // 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 + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::ZIP ) + throw beans::UnknownPropertyException( aPropertyName ); + else if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE ) + { + if ( aPropertyName == "MediaType" ) + { + aValue >>= m_pImpl->m_aMediaType; + m_pImpl->m_bControlMediaType = true; + + m_pImpl->m_bBroadcastModified = true; + m_pImpl->m_bIsModified = true; + } + else if ( aPropertyName == "Version" ) + { + aValue >>= m_pImpl->m_aVersion; + m_pImpl->m_bControlVersion = true; + + // this property can be set even for readonly storage + if ( !m_bReadOnlyWrap ) + { + m_pImpl->m_bBroadcastModified = true; + m_pImpl->m_bIsModified = true; + } + } + else if ( ( m_pImpl->m_bIsRoot && ( aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY + || aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY + || aPropertyName == IS_INCONSISTENT_PROPERTY + || aPropertyName == "URL" + || aPropertyName == "RepairPackage" + || aPropertyName == ENCRYPTION_GPG_PROPERTIES) ) + || aPropertyName == "IsRoot" + || aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY ) + throw beans::PropertyVetoException( THROW_WHERE ); + else + throw beans::UnknownPropertyException( aPropertyName ); + } + else if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML ) + { + if ( aPropertyName == "RelationsInfoStream" ) + { + uno::Reference< io::XInputStream > xInRelStream; + if ( !( aValue >>= xInRelStream ) || !xInRelStream.is() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); + + 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 ); + } + + m_pImpl->m_xNewRelInfoStream = xInRelStream; + m_pImpl->m_aRelInfo = uno::Sequence< uno::Sequence< beans::StringPair > >(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED_STREAM; + m_pImpl->m_bBroadcastModified = true; + m_pImpl->m_bIsModified = true; + } + else if ( aPropertyName == "RelationsInfo" ) + { + if ( !(aValue >>= m_pImpl->m_aRelInfo) ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); + + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; + m_pImpl->m_bBroadcastModified = true; + m_pImpl->m_bIsModified = true; + } + else if ( ( m_pImpl->m_bIsRoot && ( aPropertyName == "URL" || aPropertyName == "RepairPackage") ) + || aPropertyName == "IsRoot" ) + throw beans::PropertyVetoException( THROW_WHERE ); + else + throw beans::UnknownPropertyException( aPropertyName ); + } + else + throw beans::UnknownPropertyException( aPropertyName ); + + BroadcastModifiedIfNecessary(); +} + +uno::Any SAL_CALL OStorage::getPropertyValue( const OUString& aPropertyName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE + && ( aPropertyName == "MediaType" || aPropertyName == MEDIATYPE_FALLBACK_USED_PROPERTY || aPropertyName == "Version" ) ) + { + 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::WrappedTargetException( + "Can't read contents!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + + if ( aPropertyName == "MediaType" ) + return uno::Any( m_pImpl->m_aMediaType ); + else if ( aPropertyName == "Version" ) + return uno::Any( m_pImpl->m_aVersion ); + else + return uno::Any( m_pImpl->m_bMTFallbackUsed ); + } + else if ( aPropertyName == "IsRoot" ) + { + return uno::Any( m_pImpl->m_bIsRoot ); + } + else if ( aPropertyName == "OpenMode" ) + { + return uno::Any( m_pImpl->m_nStorageMode ); + } + else if ( m_pImpl->m_bIsRoot ) + { + if ( aPropertyName == "URL" + || aPropertyName == "RepairPackage" ) + { + auto pProp = std::find_if(std::cbegin(m_pImpl->m_xProperties), std::cend(m_pImpl->m_xProperties), + [&aPropertyName](const css::beans::PropertyValue& rProp) { return rProp.Name == aPropertyName; }); + if (pProp != std::cend(m_pImpl->m_xProperties)) + return pProp->Value; + + if ( aPropertyName == "URL" ) + return uno::Any( OUString() ); + + return uno::Any( false ); // RepairPackage + } + else if ( m_pImpl->m_nStorageType == embed::StorageFormats::PACKAGE + && ( aPropertyName == HAS_ENCRYPTED_ENTRIES_PROPERTY + || aPropertyName == HAS_NONENCRYPTED_ENTRIES_PROPERTY + || aPropertyName == ENCRYPTION_GPG_PROPERTIES + || aPropertyName == IS_INCONSISTENT_PROPERTY ) ) + { + try { + m_pImpl->ReadContents(); + uno::Reference< beans::XPropertySet > xPackPropSet( m_pImpl->m_xPackage, uno::UNO_QUERY_THROW ); + return xPackPropSet->getPropertyValue( aPropertyName ); + } + 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::WrappedTargetException( THROW_WHERE "Can not open package!", + static_cast< OWeakObject* >( this ), + aCaught ); + } + } + } + + throw beans::UnknownPropertyException(aPropertyName); +} + +void SAL_CALL OStorage::addPropertyChangeListener( + const OUString& /*aPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + //TODO: +} + +void SAL_CALL OStorage::removePropertyChangeListener( + const OUString& /*aPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + //TODO: +} + +void SAL_CALL OStorage::addVetoableChangeListener( + const OUString& /*PropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + //TODO: +} + +void SAL_CALL OStorage::removeVetoableChangeListener( + const OUString& /*PropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + //TODO: +} + +// XRelationshipAccess + +// TODO/LATER: the storage and stream implementations of this interface are very similar, they could use a helper class + +sal_Bool SAL_CALL OStorage::hasByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); + + try + { + getRelationshipByID( sID ); + return true; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + } + + return false; +} + +namespace +{ + +const beans::StringPair* lcl_findPairByName(const uno::Sequence<beans::StringPair>& rSeq, const OUString& rName) +{ + return std::find_if(rSeq.begin(), rSeq.end(), [&rName](const beans::StringPair& rPair) { return rPair.First == rName; }); +} + +} + +OUString SAL_CALL OStorage::getTargetByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); + + const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID ); + auto pRel = lcl_findPairByName(aSeq, "Target"); + if (pRel != aSeq.end()) + return pRel->Second; + + return OUString(); +} + +OUString SAL_CALL OStorage::getTypeByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); + + const uno::Sequence< beans::StringPair > aSeq = getRelationshipByID( sID ); + auto pRel = lcl_findPairByName(aSeq, "Type"); + if (pRel != aSeq.end()) + return pRel->Second; + + return OUString(); +} + +uno::Sequence< beans::StringPair > SAL_CALL OStorage::getRelationshipByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + 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("Id", sID); + + auto pRel = std::find_if(aSeq.begin(), aSeq.end(), + [&aIDRel](const uno::Sequence<beans::StringPair>& rRel) { + return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); }); + if (pRel != aSeq.end()) + return *pRel; + + throw container::NoSuchElementException( THROW_WHERE ); +} + +uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OStorage::getRelationshipsByType( const OUString& sType ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + 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, "Type"); + return pRel != rRel.end() + // the type is usually a URL, so the check should be case insensitive + && pRel->Second.equalsIgnoreAsciiCase( sType ); + }); + + return comphelper::containerToSequence(aResult); +} + +uno::Sequence< uno::Sequence< beans::StringPair > > SAL_CALL OStorage::getAllRelationships() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); + + uno::Sequence< uno::Sequence< beans::StringPair > > aRet; + try + { + aRet = m_pImpl->GetAllRelationshipsIfAny(); + } + catch (const io::IOException&) + { + throw; + } + catch (const uno::RuntimeException&) + { + throw; + } + catch (const uno::Exception &) + { + uno::Any aCaught( ::cppu::getCaughtException() ); + throw lang::WrappedTargetRuntimeException(THROW_WHERE "Can't getAllRelationships!", + uno::Reference< uno::XInterface >(), + aCaught); + } + + return aRet; +} + +void SAL_CALL OStorage::insertRelationshipByID( const OUString& sID, const uno::Sequence< beans::StringPair >& aEntry, sal_Bool bReplace ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); + + const beans::StringPair aIDRel("Id", sID); + + uno::Sequence<beans::StringPair>* pResult = nullptr; + + // TODO/LATER: in future the unification of the ID could be checked + uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); + for ( sal_Int32 nInd = 0; nInd < aSeq.getLength(); nInd++ ) + { + const auto& rRel = aSeq[nInd]; + if (std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end()) + pResult = &aSeq.getArray()[nInd]; + } + + if ( pResult && !bReplace ) + throw container::ElementExistException( THROW_WHERE ); + + if ( !pResult ) + { + const sal_Int32 nIDInd = aSeq.getLength(); + aSeq.realloc( nIDInd + 1 ); + pResult = &aSeq.getArray()[nIDInd]; + } + + std::vector<beans::StringPair> aResult; + aResult.reserve(aEntry.getLength() + 1); + + aResult.push_back(aIDRel); + std::copy_if(aEntry.begin(), aEntry.end(), std::back_inserter(aResult), + [](const beans::StringPair& rPair) { return rPair.First != "Id"; }); + + *pResult = comphelper::containerToSequence(aResult); + + m_pImpl->m_aRelInfo = aSeq; + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; +} + +void SAL_CALL OStorage::removeRelationshipByID( const OUString& sID ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); + + uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); + const beans::StringPair aIDRel("Id", sID); + auto pRel = std::find_if(std::cbegin(aSeq), std::cend(aSeq), + [&aIDRel](const uno::Sequence< beans::StringPair >& rRel) { + return std::find(rRel.begin(), rRel.end(), aIDRel) != rRel.end(); }); + if (pRel != std::cend(aSeq)) + { + auto nInd = static_cast<sal_Int32>(std::distance(std::cbegin(aSeq), pRel)); + comphelper::removeElementAt(aSeq, nInd); + + m_pImpl->m_aRelInfo = aSeq; + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; + + // TODO/LATER: in future the unification of the ID could be checked + return; + } + + throw container::NoSuchElementException( THROW_WHERE ); +} + +void SAL_CALL OStorage::insertRelationships( const uno::Sequence< uno::Sequence< beans::StringPair > >& aEntries, sal_Bool bReplace ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); + + OUString aIDTag( "Id" ); + const uno::Sequence< uno::Sequence< beans::StringPair > > aSeq = getAllRelationships(); + std::vector< uno::Sequence<beans::StringPair> > aResultVec; + aResultVec.reserve(aSeq.getLength() + aEntries.getLength()); + + std::copy_if(aSeq.begin(), aSeq.end(), std::back_inserter(aResultVec), + [&aIDTag, &aEntries, bReplace](const uno::Sequence<beans::StringPair>& rTargetRel) { + auto pTargetPair = lcl_findPairByName(rTargetRel, aIDTag); + if (pTargetPair == rTargetRel.end()) + return false; + + bool bIsSourceSame = std::any_of(aEntries.begin(), aEntries.end(), + [&pTargetPair](const uno::Sequence<beans::StringPair>& rSourceEntry) { + return std::find(rSourceEntry.begin(), rSourceEntry.end(), *pTargetPair) != rSourceEntry.end(); }); + + if ( bIsSourceSame && !bReplace ) + throw container::ElementExistException( THROW_WHERE ); + + // if no such element in the provided sequence + return !bIsSourceSame; + }); + + std::transform(aEntries.begin(), aEntries.end(), std::back_inserter(aResultVec), + [&aIDTag](const uno::Sequence<beans::StringPair>& rEntry) -> uno::Sequence<beans::StringPair> { + auto pPair = lcl_findPairByName(rEntry, aIDTag); + if (pPair == rEntry.end()) + throw io::IOException( THROW_WHERE ); // TODO: illegal relation ( no ID ) + + auto aResult = comphelper::sequenceToContainer<std::vector<beans::StringPair>>(rEntry); + auto nIDInd = std::distance(rEntry.begin(), pPair); + std::rotate(aResult.begin(), std::next(aResult.begin(), nIDInd), std::next(aResult.begin(), nIDInd + 1)); + + return comphelper::containerToSequence(aResult); + }); + + m_pImpl->m_aRelInfo = comphelper::containerToSequence(aResultVec); + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; +} + +void SAL_CALL OStorage::clearRelationships() +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::OFOPXML ) + throw uno::RuntimeException( THROW_WHERE ); + + m_pImpl->m_aRelInfo.realloc( 0 ); + m_pImpl->m_xNewRelInfoStream.clear(); + m_pImpl->m_nRelInfoStatus = RELINFO_CHANGED; +} + +// 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 ); +} + +void SAL_CALL OStorage::insertStreamElementDirect( + const OUString& aStreamName, + const uno::Reference< io::XInputStream >& xInStream, + const uno::Sequence< beans::PropertyValue >& aProps ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStreamName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable storage name + + if ( m_bReadOnlyWrap ) + throw io::IOException( THROW_WHERE ); // TODO: access denied + + try + { + SotElement_Impl* pElement = m_pImpl->FindElement( aStreamName ); + + if ( pElement ) + throw container::ElementExistException( THROW_WHERE ); + + pElement = OpenStreamElement_Impl( aStreamName, embed::ElementModes::READWRITE, false ); + OSL_ENSURE(pElement && pElement->m_xStream, "In case element can not be created an exception must be thrown!"); + + pElement->m_xStream->InsertStreamDirectly(xInStream, aProps); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const container::ElementExistException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't insert stream directly!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +void SAL_CALL OStorage::copyElementDirectlyTo( + const OUString& aElementName, + const uno::Reference< embed::XOptimizedStorage >& xDest, + const OUString& aNewName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false ) + || aNewName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aNewName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( !xDest.is() || xDest == uno::Reference< uno::XInterface >( static_cast< OWeakObject* >( this ), uno::UNO_QUERY ) ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && ( aElementName == "_rels" || aNewName == "_rels" ) ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); // unacceptable name + + try + { + SotElement_Impl* pElement = m_pImpl->FindElement( aElementName ); + if ( !pElement ) + throw container::NoSuchElementException( THROW_WHERE ); + + uno::Reference< XNameAccess > xNameAccess( xDest, uno::UNO_QUERY_THROW ); + if ( xNameAccess->hasByName( aNewName ) ) + throw container::ElementExistException( THROW_WHERE ); + + // let the element be copied directly + uno::Reference< embed::XStorage > xStorDest( xDest, uno::UNO_QUERY_THROW ); + m_pImpl->CopyStorageElement( pElement, xStorDest, aNewName, true ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const container::ElementExistException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't copy element directly!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +void SAL_CALL OStorage::writeAndAttachToStream( const uno::Reference< io::XStream >& xStream ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( !m_pImpl->m_bIsRoot ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); + + if ( !m_pImpl->m_pSwitchStream ) + throw uno::RuntimeException( THROW_WHERE ); + + try + { + m_pImpl->m_pSwitchStream->CopyAndSwitchPersistenceTo( xStream ); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:" ); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't write and attach to stream!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + +} + +void SAL_CALL OStorage::attachToURL( const OUString& sURL, + sal_Bool bReadOnly ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( !m_pImpl->m_bIsRoot ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 0 ); + + if ( !m_pImpl->m_pSwitchStream ) + throw uno::RuntimeException( THROW_WHERE ); + + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( + ucb::SimpleFileAccess::create( m_pImpl->m_xContext ) ); + + try + { + if ( bReadOnly ) + { + uno::Reference< io::XInputStream > xInputStream = xAccess->openFileRead( sURL ); + m_pImpl->m_pSwitchStream->SwitchPersistenceTo( xInputStream ); + } + else + { + uno::Reference< io::XStream > xStream = xAccess->openFileReadWrite( sURL ); + m_pImpl->m_pSwitchStream->SwitchPersistenceTo( xStream ); + } + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't attach to URL!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +uno::Any SAL_CALL OStorage::getElementPropertyValue( const OUString& aElementName, const OUString& aPropertyName ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aElementName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aElementName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aElementName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // TODO: unacceptable name + + try + { + SotElement_Impl *pElement = m_pImpl->FindElement( aElementName ); + if ( !pElement ) + throw container::NoSuchElementException( THROW_WHERE ); + + // 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 + + pElement->m_xStorage->ReadContents(); + return uno::Any(pElement->m_xStorage->m_aMediaType); + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const container::NoSuchElementException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const beans::UnknownPropertyException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const beans::PropertyVetoException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't get element property!", + uno::Reference< io::XInputStream >(), + aCaught ); + } +} + +void SAL_CALL OStorage::copyStreamElementData( const OUString& aStreamName, const uno::Reference< io::XStream >& xTargetStream ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aStreamName.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamName, false ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( m_pImpl->m_nStorageType == embed::StorageFormats::OFOPXML && aStreamName == "_rels" ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 1 ); // unacceptable name + + if ( !xTargetStream.is() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 2 ); + + try + { + uno::Reference< io::XStream > xNonconstRef = xTargetStream; + m_pImpl->CloneStreamElement( aStreamName, false, ::comphelper::SequenceAsHashMap(), xNonconstRef ); + + SAL_WARN_IF( xNonconstRef != xTargetStream, "package.xstor", "The provided stream reference seems not be filled in correctly!" ); + if ( xNonconstRef != xTargetStream ) + throw uno::RuntimeException( THROW_WHERE ); // if the stream reference is set it must not be changed! + } + catch( const embed::InvalidStorageException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const lang::IllegalArgumentException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const packages::WrongPasswordException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const io::IOException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + catch( const embed::StorageWrappedTargetException& ) + { + TOOLS_INFO_EXCEPTION("package.xstor", "Rethrow:"); + throw; + } + 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 embed::StorageWrappedTargetException( THROW_WHERE "Can't copy stream data!", + uno::Reference< io::XInputStream >(), + aCaught ); + } + +} + +// XHierarchicalStorageAccess +uno::Reference< embed::XExtendedStorageStream > SAL_CALL OStorage::openStreamElementByHierarchicalName( const OUString& aStreamPath, ::sal_Int32 nOpenMode ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aStreamPath.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamPath, true ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE ) + && ( nOpenMode & embed::ElementModes::WRITE ) ) + throw io::IOException( THROW_WHERE ); // Access denied + + 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_rHierarchyHolder.is() ) + m_rHierarchyHolder = new OHierarchyHolder_Impl( + uno::Reference< embed::XStorage >( static_cast< embed::XStorage* >( this ) ) ); + + xResult = m_rHierarchyHolder->GetStreamHierarchically( + ( m_pImpl->m_nStorageMode & embed::ElementModes::READWRITE ), + aListPath, + nOpenMode ); + } + + if ( !xResult.is() ) + throw uno::RuntimeException( THROW_WHERE ); + + return xResult; +} + +uno::Reference< embed::XExtendedStorageStream > SAL_CALL OStorage::openEncryptedStreamElementByHierarchicalName( const OUString& aStreamPath, ::sal_Int32 nOpenMode, const OUString& sPassword ) +{ + return openEncryptedStreamByHierarchicalName( aStreamPath, nOpenMode, ::comphelper::OStorageHelper::CreatePackageEncryptionData( sPassword ) ); +} + +void SAL_CALL OStorage::removeStreamElementByHierarchicalName( const OUString& aStreamPath ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( aStreamPath.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamPath, true ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE ) ) + throw io::IOException( THROW_WHERE ); // Access denied + + std::vector<OUString> aListPath = OHierarchyHolder_Impl::GetListPathFromString( aStreamPath ); + OSL_ENSURE( aListPath.size(), "The result list must not be empty!" ); + + if ( !m_rHierarchyHolder.is() ) + m_rHierarchyHolder = new OHierarchyHolder_Impl( + uno::Reference< embed::XStorage >( static_cast< embed::XStorage* >( this ) ) ); + + m_rHierarchyHolder->RemoveStreamHierarchically( aListPath ); +} + +// XHierarchicalStorageAccess2 +uno::Reference< embed::XExtendedStorageStream > SAL_CALL OStorage::openEncryptedStreamByHierarchicalName( const OUString& aStreamPath, ::sal_Int32 nOpenMode, const uno::Sequence< beans::NamedValue >& aEncryptionData ) +{ + ::osl::MutexGuard aGuard( m_xSharedMutex->GetMutex() ); + + if ( !m_pImpl ) + { + SAL_INFO("package.xstor", THROW_WHERE "Disposed!"); + throw lang::DisposedException( THROW_WHERE ); + } + + if ( m_pImpl->m_nStorageType != embed::StorageFormats::PACKAGE ) + throw packages::NoEncryptionException( THROW_WHERE ); + + if ( aStreamPath.isEmpty() || !::comphelper::OStorageHelper::IsValidZipEntryFileName( aStreamPath, true ) ) + throw lang::IllegalArgumentException( THROW_WHERE "Unexpected entry name syntax.", uno::Reference< uno::XInterface >(), 1 ); + + if ( !aEncryptionData.hasElements() ) + throw lang::IllegalArgumentException( THROW_WHERE, uno::Reference< uno::XInterface >(), 3 ); + + if ( !( m_pImpl->m_nStorageMode & embed::ElementModes::WRITE ) + && ( nOpenMode & embed::ElementModes::WRITE ) ) + throw io::IOException( THROW_WHERE ); // Access denied + + 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 ); + OSL_ENSURE(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_rHierarchyHolder.is() ) + m_rHierarchyHolder = new OHierarchyHolder_Impl( + uno::Reference< embed::XStorage >( static_cast< embed::XStorage* >( this ) ) ); + + xResult = m_rHierarchyHolder->GetStreamHierarchically( + ( m_pImpl->m_nStorageMode & embed::ElementModes::READWRITE ), + aListPath, + nOpenMode, + aEncryptionData ); + } + + if ( !xResult.is() ) + throw uno::RuntimeException( THROW_WHERE ); + + return xResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/package/source/xstor/xstorage.hxx b/package/source/xstor/xstorage.hxx new file mode 100644 index 000000000..35a80e717 --- /dev/null +++ b/package/source/xstor/xstorage.hxx @@ -0,0 +1,538 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_PACKAGE_SOURCE_XSTOR_XSTORAGE_HXX +#define INCLUDED_PACKAGE_SOURCE_XSTOR_XSTORAGE_HXX + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/embed/XStorage2.hpp> +#include <com/sun/star/embed/XOptimizedStorage.hpp> +#include <com/sun/star/embed/XHierarchicalStorageAccess2.hpp> +#include <com/sun/star/embed/XStorageRawAccess.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/embed/XTransactionBroadcaster.hpp> +#include <com/sun/star/embed/XEncryptionProtectedStorage.hpp> +#include <com/sun/star/embed/XRelationshipAccess.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/StringPair.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XTypeProvider.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/weakref.hxx> +#include <comphelper/multicontainer2.hxx> +#include <comphelper/refcountedmutex.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <o3tl/deleter.hxx> +#include <rtl/ref.hxx> + +#include "ohierarchyholder.hxx" +#include "disposelistener.hxx" + +#include <vector> +#include <memory> +#include <string_view> + +namespace com::sun::star::uno { + class XComponentContext; +} + +#define RELINFO_NO_INIT 1 +#define RELINFO_READ 2 +#define RELINFO_CHANGED 3 +#define RELINFO_CHANGED_STREAM 4 +#define RELINFO_CHANGED_STREAM_READ 5 +#define RELINFO_BROKEN 6 +#define RELINFO_CHANGED_BROKEN 7 + +#define STOR_MESS_PRECOMMIT 1 +#define STOR_MESS_COMMITTED 2 +#define STOR_MESS_PREREVERT 3 +#define STOR_MESS_REVERTED 4 + +// a common implementation for an entry + +struct OStorage_Impl; +struct OWriteStream_Impl; + +struct SotElement_Impl +{ + OUString m_aOriginalName; + bool m_bIsRemoved; + bool m_bIsInserted; + bool m_bIsStorage; + + std::unique_ptr<OStorage_Impl> m_xStorage; + std::unique_ptr<OWriteStream_Impl, o3tl::default_delete<OWriteStream_Impl>> m_xStream; + +public: + SotElement_Impl(const OUString& rName, bool bStor, bool bNew); +}; + +// Main storage implementation + +class OStorage; + +struct StorageHolder_Impl +{ + OStorage* m_pPointer; + css::uno::WeakReference< css::embed::XStorage > m_xWeakRef; + + explicit inline StorageHolder_Impl( OStorage* pStorage ); +}; + +class SwitchablePersistenceStream; +struct OStorage_Impl +{ + typedef std::vector<StorageHolder_Impl> StorageHoldersType; + + rtl::Reference<comphelper::RefCountedMutex> m_xMutex; + + OStorage* m_pAntiImpl; // only valid if external references exists + StorageHoldersType m_aReadOnlyWrapVector; // only valid if readonly external reference exists + + sal_Int32 m_nStorageMode; // open mode ( read/write/trunc/nocreate ) + bool m_bIsModified; // only modified elements will be sent to the original content + bool m_bBroadcastModified; // will be set if notification is required + + bool m_bCommited; // sending the streams is coordinated by the root storage of the package + + bool m_bIsRoot; // marks this storage as root storages that manages all commits and reverts + bool m_bListCreated; + + /// Count of registered modification listeners + oslInterlockedCount m_nModifiedListenerCount; + bool HasModifiedListener() const + { + return m_nModifiedListenerCount > 0 && m_pAntiImpl != nullptr; + } + + std::unordered_map<OUString, std::vector<SotElement_Impl*>> m_aChildrenMap; + std::vector< SotElement_Impl* > m_aDeletedVector; + + css::uno::Reference< css::container::XNameContainer > m_xPackageFolder; + + css::uno::Reference< css::lang::XSingleServiceFactory > m_xPackage; + css::uno::Reference< css::uno::XComponentContext > m_xContext; + + // valid only for root storage + css::uno::Reference< css::io::XInputStream > m_xInputStream; // ??? may be stored in properties + css::uno::Reference< css::io::XStream > m_xStream; // ??? may be stored in properties + css::uno::Sequence< css::beans::PropertyValue > m_xProperties; + bool m_bHasCommonEncryptionData; + ::comphelper::SequenceAsHashMap m_aCommonEncryptionData; + + // must be empty in case of root storage + OStorage_Impl* m_pParent; + + bool m_bControlMediaType; + OUString m_aMediaType; + bool m_bMTFallbackUsed; + + bool m_bControlVersion; + OUString m_aVersion; + + rtl::Reference<SwitchablePersistenceStream> m_pSwitchStream; + + sal_Int32 m_nStorageType; // the mode in which the storage is used + + // the _rels substorage that is handled in a special way in embed::StorageFormats::OFOPXML + SotElement_Impl* m_pRelStorElement; + css::uno::Reference< css::embed::XStorage > m_xRelStorage; + css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > m_aRelInfo; + css::uno::Reference< css::io::XInputStream > m_xNewRelInfoStream; + sal_Int16 m_nRelInfoStatus; + + // Constructors + OStorage_Impl( css::uno::Reference< css::io::XInputStream > const & xInputStream, + sal_Int32 nMode, + const css::uno::Sequence< css::beans::PropertyValue >& xProperties, + css::uno::Reference< css::uno::XComponentContext > const & xContext, + sal_Int32 nStorageType ); + + OStorage_Impl( css::uno::Reference< css::io::XStream > const & xStream, + sal_Int32 nMode, + const css::uno::Sequence< css::beans::PropertyValue >& xProperties, + css::uno::Reference< css::uno::XComponentContext > const & xContext, + sal_Int32 nStorageType ); + + // constructor for a substorage + OStorage_Impl( OStorage_Impl* pParent, + sal_Int32 nMode, + css::uno::Reference< css::container::XNameContainer > const & xPackageFolder, + css::uno::Reference< css::lang::XSingleServiceFactory > const & xPackage, + css::uno::Reference< css::uno::XComponentContext > const & xContext, + sal_Int32 nStorageType ); + + ~OStorage_Impl(); + + void SetReadOnlyWrap( OStorage& aStorage ); + void RemoveReadOnlyWrap( const OStorage& aStorage ); + + void OpenOwnPackage(); + void ReadContents(); + void ReadRelInfoIfNecessary(); + + bool HasChildren(); + void GetStorageProperties(); + + css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > GetAllRelationshipsIfAny(); + void CopyLastCommitTo( const css::uno::Reference< css::embed::XStorage >& xNewStor ); + + void InsertIntoPackageFolder( + const OUString& aName, + const css::uno::Reference< css::container::XNameContainer >& xParentPackageFolder ); + + void Commit(); + void Revert(); + + /// @throws css::packages::NoEncryptionException + ::comphelper::SequenceAsHashMap GetCommonRootEncryptionData(); + + void CopyToStorage( const css::uno::Reference< css::embed::XStorage >& xDest, + bool bDirect ); + void CopyStorageElement( SotElement_Impl* pElement, + const css::uno::Reference< css::embed::XStorage >& xDest, + const OUString& aName, + bool bDirect ); + + SotElement_Impl* FindElement( const OUString& rName ); + + SotElement_Impl* InsertStream( const OUString& aName, bool bEncr ); + void InsertRawStream( const OUString& aName, const css::uno::Reference< css::io::XInputStream >& xInStream ); + + std::unique_ptr<OStorage_Impl> CreateNewStorageImpl( sal_Int32 nStorageMode ); + SotElement_Impl* InsertStorage( const OUString& aName, sal_Int32 nStorageMode ); + SotElement_Impl* InsertElement( const OUString& aName, bool bIsStorage ); + + void OpenSubStorage( SotElement_Impl* pElement, sal_Int32 nStorageMode ); + void OpenSubStream( SotElement_Impl* pElement ); + + css::uno::Sequence< OUString > GetElementNames(); + + void RemoveElement( OUString const & rName, SotElement_Impl* pElement ); + static void ClearElement( SotElement_Impl* pElement ); + + /// @throws css::embed::InvalidStorageException + /// @throws css::lang::IllegalArgumentException + /// @throws css::packages::WrongPasswordException + /// @throws css::packages::NoEncryptionException + /// @throws css::container::NoSuchElementException + /// @throws css::io::IOException + /// @throws css::embed::StorageWrappedTargetException + /// @throws css::uno::RuntimeException + void CloneStreamElement( + const OUString& aStreamName, + bool bPassProvided, + const ::comphelper::SequenceAsHashMap& aEncryptionData, + css::uno::Reference< css::io::XStream >& xTargetStream ); + + void RemoveStreamRelInfo( std::u16string_view aOriginalName ); + void CreateRelStorage(); + void CommitStreamRelInfo( std::u16string_view rName, SotElement_Impl const * pStreamElement ); + css::uno::Reference< css::io::XInputStream > GetRelInfoStreamForName( + std::u16string_view aName ); + void CommitRelInfo( const css::uno::Reference< css::container::XNameContainer >& xNewPackageFolder ); + + static void completeStorageStreamCopy_Impl( + const css::uno::Reference< css::io::XStream >& xSource, + const css::uno::Reference< css::io::XStream >& xDest, + sal_Int32 nStorageType, + const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > >& aRelInfo ); + +}; + +class OStorage final : public css::lang::XTypeProvider + , public css::embed::XStorage2 + , public css::embed::XStorageRawAccess + , public css::embed::XTransactedObject + , public css::embed::XTransactionBroadcaster + , public css::util::XModifiable + , public css::embed::XEncryptionProtectedStorage + , public css::beans::XPropertySet + , public css::embed::XOptimizedStorage + , public css::embed::XRelationshipAccess + , public css::embed::XHierarchicalStorageAccess2 + , public ::cppu::OWeakObject +{ + OStorage_Impl* m_pImpl; + rtl::Reference<comphelper::RefCountedMutex> m_xSharedMutex; + comphelper::OMultiTypeInterfaceContainerHelper2 m_aListenersContainer; // list of listeners + ::std::unique_ptr< ::cppu::OTypeCollection> m_pTypeCollection; + bool m_bReadOnlyWrap; + ::rtl::Reference<OChildDispListener_Impl> m_pSubElDispListener; + ::std::vector< css::uno::WeakReference< css::lang::XComponent > > m_aOpenSubComponentsVector; + ::rtl::Reference< OHierarchyHolder_Impl > m_rHierarchyHolder; + + SotElement_Impl* OpenStreamElement_Impl( const OUString& aStreamName, sal_Int32 nOpenMode, bool bEncr ); + + void BroadcastModifiedIfNecessary(); + + void BroadcastTransaction( sal_Int8 nMessage ); + + void MakeLinkToSubComponent_Impl( + const css::uno::Reference< css::lang::XComponent >& xComponent ); + +public: + + OStorage( css::uno::Reference< css::io::XInputStream > const & xInputStream, + sal_Int32 nMode, + const css::uno::Sequence< css::beans::PropertyValue >& xProperties, + css::uno::Reference< css::uno::XComponentContext > const & xContext, + sal_Int32 nStorageType ); + + OStorage( css::uno::Reference< css::io::XStream > const & xStream, + sal_Int32 nMode, + const css::uno::Sequence< css::beans::PropertyValue >& xProperties, + css::uno::Reference< css::uno::XComponentContext > const & xContext, + sal_Int32 nStorageType ); + + OStorage( OStorage_Impl* pImpl, bool bReadOnlyWrap ); + + virtual ~OStorage() override; + + void InternalDispose( bool bNotifyImpl ); + + void ChildIsDisposed( const css::uno::Reference< css::uno::XInterface >& xChild ); + + sal_Int32 GetRefCount_Impl() const { return m_refCount; } + + // XInterface + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& rType ) override; + + virtual void SAL_CALL acquire() noexcept override; + + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XStorage + + virtual void SAL_CALL copyToStorage( const css::uno::Reference< css::embed::XStorage >& xDest ) override; + + virtual css::uno::Reference< css::io::XStream > SAL_CALL openStreamElement( + const OUString& aStreamName, sal_Int32 nOpenMode ) override; + + virtual css::uno::Reference< css::io::XStream > SAL_CALL openEncryptedStreamElement( + const OUString& aStreamName, sal_Int32 nOpenMode, const OUString& aPass ) override; + + virtual css::uno::Reference< css::embed::XStorage > SAL_CALL openStorageElement( + const OUString& aStorName, sal_Int32 nStorageMode ) override; + + virtual css::uno::Reference< css::io::XStream > SAL_CALL cloneStreamElement( + const OUString& aStreamName ) override; + + virtual css::uno::Reference< css::io::XStream > SAL_CALL cloneEncryptedStreamElement( + const OUString& aStreamName, const OUString& aPass ) override; + + virtual void SAL_CALL copyLastCommitTo( + const css::uno::Reference< css::embed::XStorage >& xTargetStorage ) override; + + virtual void SAL_CALL copyStorageElementLastCommitTo( + const OUString& aStorName, + const css::uno::Reference< css::embed::XStorage >& xTargetStorage ) override; + + virtual sal_Bool SAL_CALL isStreamElement( const OUString& aElementName ) override; + + virtual sal_Bool SAL_CALL isStorageElement( const OUString& aElementName ) override; + + virtual void SAL_CALL removeElement( const OUString& aElementName ) override; + + virtual void SAL_CALL renameElement( const OUString& rEleName, const OUString& rNewName ) override; + + virtual void SAL_CALL copyElementTo( const OUString& aElementName, + const css::uno::Reference< css::embed::XStorage >& xDest, + const OUString& aNewName ) override; + + virtual void SAL_CALL moveElementTo( const OUString& aElementName, + const css::uno::Reference< css::embed::XStorage >& xDest, + const OUString& rNewName ) override; + + // XStorage2 + + virtual css::uno::Reference< css::io::XStream > SAL_CALL openEncryptedStream( const OUString& sStreamName, ::sal_Int32 nOpenMode, const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override; + + virtual css::uno::Reference< css::io::XStream > SAL_CALL cloneEncryptedStream( const OUString& sStreamName, const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override; + + // XStorageRawAccess + + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getPlainRawStreamElement( + const OUString& sStreamName ) override; + + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getRawEncrStreamElement( + const OUString& sStreamName ) override; + + virtual void SAL_CALL insertRawEncrStreamElement( const OUString& aStreamName, + const css::uno::Reference< css::io::XInputStream >& xInStream ) override; + + // XTransactedObject + virtual void SAL_CALL commit() override; + + virtual void SAL_CALL revert() override; + + // XTransactionBroadcaster + virtual void SAL_CALL addTransactionListener( + const css::uno::Reference< css::embed::XTransactionListener >& aListener ) override; + + virtual void SAL_CALL removeTransactionListener( + const css::uno::Reference< css::embed::XTransactionListener >& aListener ) override; + + // XModifiable + + virtual sal_Bool SAL_CALL isModified() override; + + virtual void SAL_CALL setModified( sal_Bool bModified ) override; + + virtual void SAL_CALL addModifyListener( + const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + virtual void SAL_CALL removeModifyListener( + const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // XNameAccess + + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + virtual css::uno::Type SAL_CALL getElementType() override; + + virtual sal_Bool SAL_CALL hasElements() override; + + // XComponent + + virtual void SAL_CALL dispose() override; + + virtual void SAL_CALL addEventListener( + const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + + virtual void SAL_CALL removeEventListener( + const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + + // XEncryptionProtectedSource + + virtual void SAL_CALL setEncryptionPassword( const OUString& aPass ) override; + + virtual void SAL_CALL removeEncryption() override; + + // XEncryptionProtectedSource2 + + virtual void SAL_CALL setEncryptionData( + const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override; + + virtual sal_Bool SAL_CALL hasEncryptionData() override; + + // XEncryptionProtectedStorage + + virtual void SAL_CALL setEncryptionAlgorithms( const css::uno::Sequence< css::beans::NamedValue >& aAlgorithms ) override; + virtual void SAL_CALL setGpgProperties( const css::uno::Sequence< css::uno::Sequence< css::beans::NamedValue > >& aCryptProps ) override; + + virtual css::uno::Sequence< css::beans::NamedValue > SAL_CALL getEncryptionAlgorithms() override; + + // XPropertySet + + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + + virtual void SAL_CALL addPropertyChangeListener( + const OUString& aPropertyName, + const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + + virtual void SAL_CALL removePropertyChangeListener( + const OUString& aPropertyName, + const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + + virtual void SAL_CALL addVetoableChangeListener( + const OUString& PropertyName, + const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XOptimizedStorage + virtual void SAL_CALL insertRawNonEncrStreamElementDirect( const OUString& sStreamName, const css::uno::Reference< css::io::XInputStream >& xInStream ) override; + + virtual void SAL_CALL insertStreamElementDirect( const OUString& sStreamName, const css::uno::Reference< css::io::XInputStream >& xInStream, const css::uno::Sequence< css::beans::PropertyValue >& aProps ) override; + + virtual void SAL_CALL copyElementDirectlyTo( const OUString& sSourceName, const css::uno::Reference< css::embed::XOptimizedStorage >& xTargetStorage, const OUString& sTargetName ) override; + + virtual void SAL_CALL writeAndAttachToStream( const css::uno::Reference< css::io::XStream >& xStream ) override; + + virtual void SAL_CALL attachToURL( const OUString& sURL, sal_Bool bReadOnly ) override; + + virtual css::uno::Any SAL_CALL getElementPropertyValue( const OUString& sElementName, const OUString& sPropertyName ) override; + + virtual void SAL_CALL copyStreamElementData( const OUString& sStreamName, const css::uno::Reference< css::io::XStream >& xTargetStream ) override; + + // XRelationshipAccess + virtual sal_Bool SAL_CALL hasByID( const OUString& sID ) override; + + virtual OUString SAL_CALL getTargetByID( const OUString& sID ) override; + + virtual OUString SAL_CALL getTypeByID( const OUString& sID ) override; + + virtual css::uno::Sequence< css::beans::StringPair > SAL_CALL getRelationshipByID( const OUString& sID ) override; + + virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getRelationshipsByType( const OUString& sType ) override; + + virtual css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > > SAL_CALL getAllRelationships( ) override; + + virtual void SAL_CALL insertRelationshipByID( const OUString& sID, const css::uno::Sequence< css::beans::StringPair >& aEntry, sal_Bool bReplace ) override; + + virtual void SAL_CALL removeRelationshipByID( const OUString& sID ) override; + + virtual void SAL_CALL insertRelationships( const css::uno::Sequence< css::uno::Sequence< css::beans::StringPair > >& aEntries, sal_Bool bReplace ) override; + + virtual void SAL_CALL clearRelationships( ) override; + + // XHierarchicalStorageAccess + virtual css::uno::Reference< css::embed::XExtendedStorageStream > SAL_CALL openStreamElementByHierarchicalName( const OUString& sStreamPath, ::sal_Int32 nOpenMode ) override; + + virtual css::uno::Reference< css::embed::XExtendedStorageStream > SAL_CALL openEncryptedStreamElementByHierarchicalName( const OUString& sStreamName, ::sal_Int32 nOpenMode, const OUString& sPassword ) override; + + virtual void SAL_CALL removeStreamElementByHierarchicalName( const OUString& sElementPath ) override; + + // XHierarchicalStorageAccess2 + virtual css::uno::Reference< css::embed::XExtendedStorageStream > SAL_CALL openEncryptedStreamByHierarchicalName( const OUString& sStreamName, ::sal_Int32 nOpenMode, const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData ) override; +}; + +StorageHolder_Impl::StorageHolder_Impl( OStorage* pStorage ) +: m_pPointer( pStorage ) +, m_xWeakRef( css::uno::Reference< css::embed::XStorage >( pStorage ) ) +{ +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |