| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_ucb.hxx" |
| |
| /************************************************************************** |
| TODO |
| ************************************************************************** |
| |
| - remove root storage access workaround |
| |
| *************************************************************************/ |
| |
| #define ROOTSTORAGE_ACCESS_WORKAROUND 1 |
| |
| #include <memory> |
| |
| #include "com/sun/star/beans/XPropertySet.hpp" |
| #include "com/sun/star/embed/ElementModes.hpp" |
| #include "com/sun/star/lang/XSingleServiceFactory.hpp" |
| |
| #include "tdoc_uri.hxx" |
| #include "tdoc_docmgr.hxx" |
| #include "tdoc_stgelems.hxx" |
| |
| #include "tdoc_storage.hxx" |
| |
| using namespace com::sun::star; |
| using namespace tdoc_ucp; |
| |
| |
| //========================================================================= |
| //========================================================================= |
| // |
| // StorageElementFactory Implementation. |
| // |
| //========================================================================= |
| //========================================================================= |
| |
| StorageElementFactory::StorageElementFactory( |
| const uno::Reference< lang::XMultiServiceFactory > & xSMgr, |
| const rtl::Reference< OfficeDocumentsManager > & xDocsMgr ) |
| : m_xDocsMgr( xDocsMgr ), |
| m_xSMgr( xSMgr ) |
| { |
| } |
| |
| //========================================================================= |
| StorageElementFactory::~StorageElementFactory() |
| { |
| OSL_ENSURE( m_aMap.size() == 0, |
| "StorageElementFactory::~StorageElementFactory - Dangling storages!" ); |
| } |
| |
| //========================================================================= |
| uno::Reference< embed::XStorage > |
| StorageElementFactory::createTemporaryStorage() |
| throw ( uno::Exception, |
| uno::RuntimeException ) |
| { |
| uno::Reference< embed::XStorage > xStorage; |
| uno::Reference< lang::XSingleServiceFactory > xStorageFac; |
| if ( m_xSMgr.is() ) |
| { |
| xStorageFac = uno::Reference< lang::XSingleServiceFactory >( |
| m_xSMgr->createInstance( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "com.sun.star.embed.StorageFactory" ) ) ), |
| uno::UNO_QUERY ); |
| } |
| |
| OSL_ENSURE( xStorageFac.is(), "Can't create storage factory!" ); |
| if ( xStorageFac.is() ) |
| xStorage = uno::Reference< embed::XStorage >( |
| xStorageFac->createInstance(), |
| uno::UNO_QUERY ); |
| |
| if ( !xStorage.is() ) |
| throw uno::RuntimeException(); |
| |
| return xStorage; |
| } |
| |
| //========================================================================= |
| uno::Reference< embed::XStorage > |
| StorageElementFactory::createStorage( const rtl::OUString & rUri, |
| StorageAccessMode eMode ) |
| throw ( embed::InvalidStorageException, |
| lang::IllegalArgumentException, |
| io::IOException, |
| embed::StorageWrappedTargetException, |
| uno::RuntimeException ) |
| { |
| osl::MutexGuard aGuard( m_aMutex ); |
| |
| if ( ( eMode != READ ) && |
| ( eMode != READ_WRITE_NOCREATE ) && |
| ( eMode != READ_WRITE_CREATE ) ) |
| throw lang::IllegalArgumentException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Invalid open mode!" ) ), |
| uno::Reference< uno::XInterface >(), |
| sal_Int16( 2 ) ); |
| |
| Uri aUri( rUri ); |
| if ( aUri.isRoot() ) |
| { |
| throw lang::IllegalArgumentException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Root never has a storage!" ) ), |
| uno::Reference< uno::XInterface >(), |
| sal_Int16( 1 ) ); |
| } |
| |
| rtl::OUString aUriKey |
| ( ( rUri.getStr()[ rUri.getLength() - 1 ] == sal_Unicode( '/' ) ) |
| ? rUri.copy( 0, rUri.getLength() - 1 ) |
| : rUri ); |
| |
| StorageMap::iterator aIt ( m_aMap.begin() ); |
| StorageMap::iterator aEnd( m_aMap.end() ); |
| |
| while ( aIt != aEnd ) |
| { |
| if ( (*aIt).first.first == aUriKey ) |
| { |
| // URI matches. Now, check open mode. |
| bool bMatch = true; |
| switch ( eMode ) |
| { |
| case READ: |
| // No need to check; storage is at least readable. |
| bMatch = true; |
| break; |
| |
| case READ_WRITE_NOCREATE: |
| case READ_WRITE_CREATE: |
| // If found storage is writable, it can be used. |
| // If not, a new one must be created. |
| bMatch = (*aIt).first.second; |
| break; |
| } |
| |
| if ( bMatch ) |
| break; |
| } |
| ++aIt; |
| } |
| |
| if ( aIt == aEnd ) |
| { |
| uno::Reference< embed::XStorage > xParentStorage; |
| |
| // documents never have a parent storage. |
| if ( !aUri.isDocument() ) |
| { |
| xParentStorage = queryParentStorage( aUriKey, eMode ); |
| |
| if ( !xParentStorage.is() ) |
| { |
| // requested to create new storage, but failed? |
| OSL_ENSURE( eMode != READ_WRITE_CREATE, |
| "Unable to create parent storage!" ); |
| return xParentStorage; |
| } |
| } |
| |
| uno::Reference< embed::XStorage > xStorage |
| = queryStorage( xParentStorage, aUriKey, eMode ); |
| |
| if ( !xStorage.is() ) |
| { |
| // requested to create new storage, but failed? |
| OSL_ENSURE( eMode != READ_WRITE_CREATE, |
| "Unable to create storage!" ); |
| return xStorage; |
| } |
| |
| bool bWritable = ( ( eMode == READ_WRITE_NOCREATE ) |
| || ( eMode == READ_WRITE_CREATE ) ); |
| |
| std::auto_ptr< Storage > xElement( |
| new Storage( m_xSMgr, this, aUriKey, xParentStorage, xStorage ) ); |
| |
| aIt = m_aMap.insert( |
| StorageMap::value_type( |
| std::pair< rtl::OUString, bool >( aUriKey, bWritable ), |
| xElement.get() ) ).first; |
| |
| aIt->second->m_aContainerIt = aIt; |
| xElement.release(); |
| return aIt->second; |
| } |
| else if ( osl_incrementInterlockedCount( &aIt->second->m_refCount ) > 1 ) |
| { |
| rtl::Reference< Storage > xElement( aIt->second ); |
| osl_decrementInterlockedCount( &aIt->second->m_refCount ); |
| return aIt->second; |
| } |
| else |
| { |
| osl_decrementInterlockedCount( &aIt->second->m_refCount ); |
| aIt->second->m_aContainerIt = m_aMap.end(); |
| |
| uno::Reference< embed::XStorage > xParentStorage; |
| |
| // documents never have a parent storage. |
| if ( !aUri.isDocument() ) |
| { |
| xParentStorage = queryParentStorage( aUriKey, eMode ); |
| |
| if ( !xParentStorage.is() ) |
| { |
| // requested to create new storage, but failed? |
| OSL_ENSURE( eMode != READ_WRITE_CREATE, |
| "Unable to create parent storage!" ); |
| return xParentStorage; |
| } |
| } |
| |
| uno::Reference< embed::XStorage > xStorage |
| = queryStorage( xParentStorage, aUriKey, eMode ); |
| |
| if ( !xStorage.is() ) |
| { |
| // requested to create new storage, but failed? |
| OSL_ENSURE( eMode != READ_WRITE_CREATE, |
| "Unable to create storage!" ); |
| return xStorage; |
| } |
| |
| aIt->second |
| = new Storage( m_xSMgr, this, aUriKey, xParentStorage, xStorage ); |
| aIt->second->m_aContainerIt = aIt; |
| return aIt->second; |
| } |
| } |
| |
| //========================================================================= |
| uno::Reference< io::XInputStream > |
| StorageElementFactory::createInputStream( const rtl::OUString & rUri, |
| const rtl::OUString & rPassword ) |
| throw ( embed::InvalidStorageException, |
| lang::IllegalArgumentException, |
| io::IOException, |
| embed::StorageWrappedTargetException, |
| packages::WrongPasswordException, |
| uno::RuntimeException ) |
| { |
| osl::MutexGuard aGuard( m_aMutex ); |
| |
| uno::Reference< embed::XStorage > xParentStorage |
| = queryParentStorage( rUri, READ ); |
| |
| // Each stream must have a parent storage. |
| if ( !xParentStorage.is() ) |
| return uno::Reference< io::XInputStream >(); |
| |
| uno::Reference< io::XStream > xStream |
| = queryStream( xParentStorage, rUri, rPassword, READ, false ); |
| |
| if ( !xStream.is() ) |
| return uno::Reference< io::XInputStream >(); |
| |
| return xStream->getInputStream(); |
| } |
| |
| //========================================================================= |
| uno::Reference< io::XOutputStream > |
| StorageElementFactory::createOutputStream( const rtl::OUString & rUri, |
| const rtl::OUString & rPassword, |
| bool bTruncate ) |
| throw ( embed::InvalidStorageException, |
| lang::IllegalArgumentException, |
| io::IOException, |
| embed::StorageWrappedTargetException, |
| packages::WrongPasswordException, |
| uno::RuntimeException ) |
| { |
| osl::MutexGuard aGuard( m_aMutex ); |
| |
| uno::Reference< embed::XStorage > xParentStorage |
| = queryParentStorage( rUri, READ_WRITE_CREATE ); |
| |
| // Each stream must have a parent storage. |
| if ( !xParentStorage.is() ) |
| { |
| OSL_ENSURE( false, |
| "StorageElementFactory::createOutputStream - " |
| "Unable to create parent storage!" ); |
| return uno::Reference< io::XOutputStream >(); |
| } |
| |
| uno::Reference< io::XStream > xStream |
| = queryStream( |
| xParentStorage, rUri, rPassword, READ_WRITE_CREATE, bTruncate ); |
| |
| if ( !xStream.is() ) |
| { |
| OSL_ENSURE( false, |
| "StorageElementFactory::createOutputStream - " |
| "Unable to create stream!" ); |
| return uno::Reference< io::XOutputStream >(); |
| } |
| |
| // Note: We need a wrapper to hold a reference to the parent storage to |
| // ensure that nobody else owns it at the moment we want to commit |
| // our changes. (There can be only one writable instance at a time |
| // and even no writable instance if there is already another |
| // read-only instance!) |
| return uno::Reference< io::XOutputStream >( |
| new OutputStream( |
| m_xSMgr, rUri, xParentStorage, xStream->getOutputStream() ) ); |
| } |
| |
| //========================================================================= |
| uno::Reference< io::XStream > |
| StorageElementFactory::createStream( const rtl::OUString & rUri, |
| const rtl::OUString & rPassword, |
| bool bTruncate ) |
| throw ( embed::InvalidStorageException, |
| lang::IllegalArgumentException, |
| io::IOException, |
| embed::StorageWrappedTargetException, |
| packages::WrongPasswordException, |
| uno::RuntimeException ) |
| { |
| osl::MutexGuard aGuard( m_aMutex ); |
| |
| uno::Reference< embed::XStorage > xParentStorage |
| = queryParentStorage( rUri, READ_WRITE_CREATE ); |
| |
| // Each stream must have a parent storage. |
| if ( !xParentStorage.is() ) |
| { |
| OSL_ENSURE( false, |
| "StorageElementFactory::createStream - " |
| "Unable to create parent storage!" ); |
| return uno::Reference< io::XStream >(); |
| } |
| |
| uno::Reference< io::XStream > xStream |
| = queryStream( |
| xParentStorage, rUri, rPassword, READ_WRITE_NOCREATE, bTruncate ); |
| |
| if ( !xStream.is() ) |
| { |
| OSL_ENSURE( false, |
| "StorageElementFactory::createStream - " |
| "Unable to create stream!" ); |
| return uno::Reference< io::XStream >(); |
| } |
| |
| return uno::Reference< io::XStream >( |
| new Stream( m_xSMgr, rUri, xParentStorage, xStream ) ); |
| } |
| |
| //========================================================================= |
| void StorageElementFactory::releaseElement( Storage * pElement ) SAL_THROW( () ) |
| { |
| OSL_ASSERT( pElement ); |
| osl::MutexGuard aGuard( m_aMutex ); |
| if ( pElement->m_aContainerIt != m_aMap.end() ) |
| m_aMap.erase( pElement->m_aContainerIt ); |
| } |
| |
| //========================================================================= |
| // |
| // Non-UNO interface |
| // |
| //========================================================================= |
| |
| uno::Reference< embed::XStorage > StorageElementFactory::queryParentStorage( |
| const rtl::OUString & rUri, StorageAccessMode eMode ) |
| throw ( embed::InvalidStorageException, |
| lang::IllegalArgumentException, |
| io::IOException, |
| embed::StorageWrappedTargetException, |
| uno::RuntimeException ) |
| { |
| uno::Reference< embed::XStorage > xParentStorage; |
| |
| Uri aUri( rUri ); |
| Uri aParentUri( aUri.getParentUri() ); |
| if ( !aParentUri.isRoot() ) |
| { |
| xParentStorage = createStorage( aUri.getParentUri(), eMode ); |
| OSL_ENSURE( xParentStorage.is() |
| // requested to create new storage, but failed? |
| || ( eMode != READ_WRITE_CREATE ), |
| "StorageElementFactory::queryParentStorage - No storage!" ); |
| } |
| return xParentStorage; |
| } |
| |
| //========================================================================= |
| uno::Reference< embed::XStorage > StorageElementFactory::queryStorage( |
| const uno::Reference< embed::XStorage > & xParentStorage, |
| const rtl::OUString & rUri, |
| StorageAccessMode eMode ) |
| throw ( embed::InvalidStorageException, |
| lang::IllegalArgumentException, |
| io::IOException, |
| embed::StorageWrappedTargetException, |
| uno::RuntimeException ) |
| { |
| uno::Reference< embed::XStorage > xStorage; |
| |
| Uri aUri( rUri ); |
| |
| if ( !xParentStorage.is() ) |
| { |
| // document storage |
| |
| xStorage = m_xDocsMgr->queryStorage( aUri.getDocumentId() ); |
| |
| if ( !xStorage.is() ) |
| { |
| if ( eMode == READ_WRITE_CREATE ) |
| throw lang::IllegalArgumentException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Invalid open mode: document storages cannot be " |
| "created!" ) ), |
| uno::Reference< uno::XInterface >(), |
| sal_Int16( 2 ) ); |
| else |
| throw embed::InvalidStorageException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Invalid document id!" ) ), |
| uno::Reference< uno::XInterface >() ); |
| } |
| |
| // match xStorage's open mode against requested open mode |
| |
| uno::Reference< beans::XPropertySet > xPropSet( |
| xStorage, uno::UNO_QUERY ); |
| OSL_ENSURE( xPropSet.is(), |
| "StorageElementFactory::queryStorage - " |
| "No XPropertySet interface!" ); |
| try |
| { |
| uno::Any aPropValue = xPropSet->getPropertyValue( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( "OpenMode" ) ) ); |
| |
| sal_Int32 nOpenMode = 0; |
| if ( aPropValue >>= nOpenMode ) |
| { |
| switch ( eMode ) |
| { |
| case READ: |
| if ( !( nOpenMode & embed::ElementModes::READ ) ) |
| { |
| // document opened, but not readable. |
| throw embed::InvalidStorageException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Storage is open, but not readable!" ) ), |
| uno::Reference< uno::XInterface >() ); |
| } |
| // storage okay |
| break; |
| |
| case READ_WRITE_NOCREATE: |
| case READ_WRITE_CREATE: |
| if ( !( nOpenMode & embed::ElementModes::WRITE ) ) |
| { |
| // document opened, but not writable. |
| throw embed::InvalidStorageException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Storage is open, but not writable!" ) ), |
| uno::Reference< uno::XInterface >() ); |
| } |
| // storage okay |
| break; |
| } |
| } |
| else |
| { |
| OSL_ENSURE( |
| false, "Bug! Value of property OpenMode has wrong type!" ); |
| |
| throw uno::RuntimeException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Bug! Value of property OpenMode has wrong type!" ) ), |
| uno::Reference< uno::XInterface >() ); |
| } |
| } |
| catch ( beans::UnknownPropertyException const & e ) |
| { |
| OSL_ENSURE( false, "Property OpenMode not supported!" ); |
| |
| throw embed::StorageWrappedTargetException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Bug! Value of property OpenMode has wrong type!" ) ), |
| uno::Reference< uno::XInterface >(), |
| uno::makeAny( e ) ); |
| } |
| catch ( lang::WrappedTargetException const & e ) |
| { |
| OSL_ENSURE( false, "Caught WrappedTargetException!" ); |
| |
| throw embed::StorageWrappedTargetException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "WrappedTargetException during getPropertyValue!" ) ), |
| uno::Reference< uno::XInterface >(), |
| uno::makeAny( e ) ); |
| } |
| } |
| else |
| { |
| // sub storage |
| |
| const rtl::OUString & rName = aUri.getDecodedName(); |
| |
| if ( eMode == READ ) |
| { |
| try |
| { |
| sal_Int32 nOpenMode = embed::ElementModes::READ |
| | embed::ElementModes::NOCREATE; |
| xStorage |
| = xParentStorage->openStorageElement( rName, nOpenMode ); |
| } |
| catch ( io::IOException const & ) |
| { |
| // Another chance: Try to clone storage. |
| xStorage = createTemporaryStorage(); |
| xParentStorage->copyStorageElementLastCommitTo( rName, |
| xStorage ); |
| } |
| } |
| else |
| { |
| sal_Int32 nOpenMode = embed::ElementModes::READWRITE; |
| if ( eMode == READ_WRITE_NOCREATE ) |
| nOpenMode |= embed::ElementModes::NOCREATE; |
| |
| xStorage = xParentStorage->openStorageElement( rName, nOpenMode ); |
| } |
| } |
| |
| OSL_ENSURE( xStorage.is() || ( eMode != READ_WRITE_CREATE ), |
| "StorageElementFactory::queryStorage - No storage!" ); |
| return xStorage; |
| } |
| |
| //========================================================================= |
| uno::Reference< io::XStream > |
| StorageElementFactory::queryStream( |
| const uno::Reference< embed::XStorage > & xParentStorage, |
| const rtl::OUString & rUri, |
| const rtl::OUString & rPassword, |
| StorageAccessMode eMode, |
| bool bTruncate ) |
| throw ( embed::InvalidStorageException, |
| lang::IllegalArgumentException, |
| io::IOException, |
| embed::StorageWrappedTargetException, |
| packages::WrongPasswordException, |
| uno::RuntimeException ) |
| { |
| osl::MutexGuard aGuard( m_aMutex ); |
| |
| if ( !xParentStorage.is() ) |
| { |
| throw lang::IllegalArgumentException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "No parent storage!" ) ), |
| uno::Reference< uno::XInterface >(), |
| sal_Int16( 2 ) ); |
| } |
| |
| Uri aUri( rUri ); |
| if ( aUri.isRoot() ) |
| { |
| throw lang::IllegalArgumentException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Root never is a stream!" ) ), |
| uno::Reference< uno::XInterface >(), |
| sal_Int16( 2 ) ); |
| } |
| else if ( aUri.isDocument() ) |
| { |
| throw lang::IllegalArgumentException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "A document never is a stream!" ) ), |
| uno::Reference< uno::XInterface >(), |
| sal_Int16( 2 ) ); |
| } |
| |
| sal_Int32 nOpenMode; |
| switch ( eMode ) |
| { |
| case READ: |
| nOpenMode = embed::ElementModes::READ |
| | embed::ElementModes::NOCREATE |
| | embed::ElementModes::SEEKABLE; |
| break; |
| |
| case READ_WRITE_NOCREATE: |
| nOpenMode = embed::ElementModes::READWRITE |
| | embed::ElementModes::NOCREATE |
| | embed::ElementModes::SEEKABLE; |
| |
| if ( bTruncate ) |
| nOpenMode |= embed::ElementModes::TRUNCATE; |
| |
| break; |
| |
| case READ_WRITE_CREATE: |
| nOpenMode = embed::ElementModes::READWRITE |
| | embed::ElementModes::SEEKABLE; |
| |
| if ( bTruncate ) |
| nOpenMode |= embed::ElementModes::TRUNCATE; |
| |
| break; |
| |
| default: |
| OSL_ENSURE( false, |
| "StorageElementFactory::queryStream : Unknown open mode!" ); |
| |
| throw embed::InvalidStorageException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Unknown open mode!" ) ), |
| uno::Reference< uno::XInterface >() ); |
| } |
| |
| // No object re-usage mechanism; streams are seekable => not stateless. |
| |
| uno::Reference< io::XStream > xStream; |
| if ( rPassword.getLength() > 0 ) |
| { |
| if ( eMode == READ ) |
| { |
| try |
| { |
| xStream = xParentStorage->cloneEncryptedStreamElement( |
| aUri.getDecodedName(), |
| rPassword ); |
| } |
| catch ( packages::NoEncryptionException const & ) |
| { |
| xStream |
| = xParentStorage->cloneStreamElement( aUri.getDecodedName() ); |
| } |
| } |
| else |
| { |
| try |
| { |
| xStream = xParentStorage->openEncryptedStreamElement( |
| aUri.getDecodedName(), |
| nOpenMode, |
| rPassword ); |
| } |
| catch ( packages::NoEncryptionException const & ) |
| { |
| xStream |
| = xParentStorage->openStreamElement( aUri.getDecodedName(), |
| nOpenMode ); |
| } |
| } |
| } |
| else |
| { |
| if ( eMode == READ ) |
| { |
| xStream = xParentStorage->cloneStreamElement( aUri.getDecodedName() ); |
| } |
| else |
| { |
| xStream = xParentStorage->openStreamElement( aUri.getDecodedName(), |
| nOpenMode ); |
| } |
| } |
| |
| if ( !xStream.is() ) |
| { |
| throw embed::InvalidStorageException( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "No stream!" ) ), |
| uno::Reference< uno::XInterface >() ); |
| } |
| |
| return xStream; |
| } |