blob: b275c50316861bb3f90cb8786af7633a3842c871 [file] [log] [blame]
/**************************************************************
*
* 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.
*
*************************************************************/
#include "oox/core/filterbase.hxx"
#include <set>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/task/XStatusIndicator.hpp>
#include <com/sun/star/task/XInteractionHandler.hpp>
#include <comphelper/docpasswordhelper.hxx>
#include <comphelper/mediadescriptor.hxx>
#include <osl/mutex.hxx>
#include <rtl/instance.hxx>
#include <rtl/uri.hxx>
#include "oox/helper/binaryinputstream.hxx"
#include "oox/helper/binaryoutputstream.hxx"
#include "oox/helper/graphichelper.hxx"
#include "oox/helper/modelobjecthelper.hxx"
#include "oox/ole/oleobjecthelper.hxx"
#include "oox/ole/vbaproject.hxx"
namespace oox {
namespace core {
// ============================================================================
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::graphic;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::uno;
using ::comphelper::MediaDescriptor;
using ::comphelper::SequenceAsHashMap;
using ::oox::ole::OleObjectHelper;
using ::oox::ole::VbaProject;
using ::rtl::OUString;
// ============================================================================
namespace {
struct UrlPool
{
::osl::Mutex maMutex;
::std::set< OUString > maUrls;
};
struct StaticUrlPool : public ::rtl::Static< UrlPool, StaticUrlPool > {};
// ----------------------------------------------------------------------------
/** This guard prevents recursive loading/saving of the same document. */
class DocumentOpenedGuard
{
public:
explicit DocumentOpenedGuard( const OUString& rUrl );
~DocumentOpenedGuard();
inline bool isValid() const { return mbValid; }
private:
DocumentOpenedGuard( const DocumentOpenedGuard& );
DocumentOpenedGuard& operator=( const DocumentOpenedGuard& );
OUString maUrl;
bool mbValid;
};
DocumentOpenedGuard::DocumentOpenedGuard( const OUString& rUrl )
{
UrlPool& rUrlPool = StaticUrlPool::get();
::osl::MutexGuard aGuard( rUrlPool.maMutex );
mbValid = (rUrl.getLength() == 0) || (rUrlPool.maUrls.count( rUrl ) == 0);
if( mbValid && (rUrl.getLength() > 0) )
{
rUrlPool.maUrls.insert( rUrl );
maUrl = rUrl;
}
}
DocumentOpenedGuard::~DocumentOpenedGuard()
{
UrlPool& rUrlPool = StaticUrlPool::get();
::osl::MutexGuard aGuard( rUrlPool.maMutex );
if( maUrl.getLength() > 0 )
rUrlPool.maUrls.erase( maUrl );
}
} // namespace
// ============================================================================
/** Specifies whether this filter is an import or export filter. */
enum FilterDirection
{
FILTERDIRECTION_UNKNOWN,
FILTERDIRECTION_IMPORT,
FILTERDIRECTION_EXPORT
};
// ----------------------------------------------------------------------------
struct FilterBaseImpl
{
typedef ::boost::shared_ptr< GraphicHelper > GraphicHelperRef;
typedef ::boost::shared_ptr< ModelObjectHelper > ModelObjHelperRef;
typedef ::boost::shared_ptr< OleObjectHelper > OleObjHelperRef;
typedef ::boost::shared_ptr< VbaProject > VbaProjectRef;
FilterDirection meDirection;
SequenceAsHashMap maArguments;
MediaDescriptor maMediaDesc;
OUString maFileUrl;
StorageRef mxStorage;
GraphicHelperRef mxGraphicHelper; /// Graphic and graphic object handling.
ModelObjHelperRef mxModelObjHelper; /// Tables to create new named drawing objects.
OleObjHelperRef mxOleObjHelper; /// OLE object handling.
VbaProjectRef mxVbaProject; /// VBA project manager.
Reference< XComponentContext > mxComponentContext;
Reference< XMultiComponentFactory > mxComponentFactory;
Reference< XMultiServiceFactory > mxServiceFactory;
Reference< XModel > mxModel;
Reference< XMultiServiceFactory > mxModelFactory;
Reference< XFrame > mxTargetFrame;
Reference< XInputStream > mxInStream;
Reference< XStream > mxOutStream;
Reference< XStatusIndicator > mxStatusIndicator;
Reference< XInteractionHandler > mxInteractionHandler;
explicit FilterBaseImpl( const Reference< XComponentContext >& rxContext ) throw( RuntimeException );
void setDocumentModel( const Reference< XComponent >& rxComponent ) throw( IllegalArgumentException );
void initializeFilter();
void finalizeFilter();
};
// ----------------------------------------------------------------------------
FilterBaseImpl::FilterBaseImpl( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
meDirection( FILTERDIRECTION_UNKNOWN ),
mxComponentContext( rxContext, UNO_SET_THROW ),
mxComponentFactory( rxContext->getServiceManager(), UNO_SET_THROW ),
mxServiceFactory( rxContext->getServiceManager(), UNO_QUERY_THROW )
{
}
void FilterBaseImpl::setDocumentModel( const Reference< XComponent >& rxComponent ) throw( IllegalArgumentException )
{
try
{
mxModel.set( rxComponent, UNO_QUERY_THROW );
mxModelFactory.set( rxComponent, UNO_QUERY_THROW );
}
catch( Exception& )
{
throw IllegalArgumentException();
}
}
void FilterBaseImpl::initializeFilter()
{
try
{
// lock the model controllers
mxModel->lockControllers();
}
catch( Exception& )
{
}
}
void FilterBaseImpl::finalizeFilter()
{
try
{
// write the descriptor back to the document model (adds the passwords)
mxModel->attachResource( maFileUrl, maMediaDesc.getAsConstPropertyValueList() );
// unlock the model controllers
mxModel->unlockControllers();
}
catch( Exception& )
{
}
}
// ============================================================================
FilterBase::FilterBase( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
mxImpl( new FilterBaseImpl( rxContext ) )
{
}
FilterBase::~FilterBase()
{
}
bool FilterBase::isImportFilter() const
{
return mxImpl->meDirection == FILTERDIRECTION_IMPORT;
}
bool FilterBase::isExportFilter() const
{
return mxImpl->meDirection == FILTERDIRECTION_EXPORT;
}
// ----------------------------------------------------------------------------
Any FilterBase::getArgument( const OUString& rArgName ) const
{
SequenceAsHashMap::const_iterator aIt = mxImpl->maArguments.find( rArgName );
return (aIt == mxImpl->maArguments.end()) ? Any() : aIt->second;
}
const Reference< XComponentContext >& FilterBase::getComponentContext() const
{
return mxImpl->mxComponentContext;
}
const Reference< XMultiComponentFactory >& FilterBase::getComponentFactory() const
{
return mxImpl->mxComponentFactory;
}
const Reference< XMultiServiceFactory >& FilterBase::getServiceFactory() const
{
return mxImpl->mxServiceFactory;
}
const Reference< XModel >& FilterBase::getModel() const
{
return mxImpl->mxModel;
}
const Reference< XMultiServiceFactory >& FilterBase::getModelFactory() const
{
return mxImpl->mxModelFactory;
}
const Reference< XFrame >& FilterBase::getTargetFrame() const
{
return mxImpl->mxTargetFrame;
}
const Reference< XStatusIndicator >& FilterBase::getStatusIndicator() const
{
return mxImpl->mxStatusIndicator;
}
const Reference< XInteractionHandler >& FilterBase::getInteractionHandler() const
{
return mxImpl->mxInteractionHandler;
}
MediaDescriptor& FilterBase::getMediaDescriptor() const
{
return mxImpl->maMediaDesc;
}
const OUString& FilterBase::getFileUrl() const
{
return mxImpl->maFileUrl;
}
namespace {
inline bool lclIsDosDrive( const OUString& rUrl, sal_Int32 nPos = 0 )
{
return
(rUrl.getLength() >= nPos + 3) &&
((('A' <= rUrl[ nPos ]) && (rUrl[ nPos ] <= 'Z')) || (('a' <= rUrl[ nPos ]) && (rUrl[ nPos ] <= 'z'))) &&
(rUrl[ nPos + 1 ] == ':') &&
(rUrl[ nPos + 2 ] == '/');
}
} // namespace
OUString FilterBase::getAbsoluteUrl( const OUString& rUrl ) const
{
// handle some special cases before calling ::rtl::Uri::convertRelToAbs()
const OUString aFileSchema = CREATE_OUSTRING( "file:" );
const OUString aFilePrefix = CREATE_OUSTRING( "file:///" );
const sal_Int32 nFilePrefixLen = aFilePrefix.getLength();
const OUString aUncPrefix = CREATE_OUSTRING( "//" );
/* (1) convert all backslashes to slashes, and check that passed URL is
not empty. */
OUString aUrl = rUrl.replace( '\\', '/' );
if( aUrl.getLength() == 0 )
return aUrl;
/* (2) add 'file:///' to absolute Windows paths, e.g. convert
'C:/path/file' to 'file:///c:/path/file'. */
if( lclIsDosDrive( aUrl ) )
return aFilePrefix + aUrl;
/* (3) add 'file:' to UNC paths, e.g. convert '//server/path/file' to
'file://server/path/file'. */
if( aUrl.match( aUncPrefix ) )
return aFileSchema + aUrl;
/* (4) remove additional slashes from UNC paths, e.g. convert
'file://///server/path/file' to 'file://server/path/file'. */
if( (aUrl.getLength() >= nFilePrefixLen + 2) &&
aUrl.match( aFilePrefix ) &&
aUrl.match( aUncPrefix, nFilePrefixLen ) )
{
return aFileSchema + aUrl.copy( nFilePrefixLen );
}
/* (5) handle URLs relative to current drive, e.g. the URL '/path1/file1'
relative to the base URL 'file:///C:/path2/file2' does not result in
the expected 'file:///C:/path1/file1', but in 'file:///path1/file1'. */
if( (aUrl.getLength() >= 1) && (aUrl[ 0 ] == '/') &&
mxImpl->maFileUrl.match( aFilePrefix ) &&
lclIsDosDrive( mxImpl->maFileUrl, nFilePrefixLen ) )
{
return mxImpl->maFileUrl.copy( 0, nFilePrefixLen + 3 ) + aUrl.copy( 1 );
}
try
{
return ::rtl::Uri::convertRelToAbs( mxImpl->maFileUrl, aUrl );
}
catch( ::rtl::MalformedUriException& )
{
}
return aUrl;
}
StorageRef FilterBase::getStorage() const
{
return mxImpl->mxStorage;
}
StorageRef FilterBase::openSubStorage( const OUString& rStorageName, bool bCreateMissing ) const
{
return mxImpl->mxStorage->openSubStorage( rStorageName, bCreateMissing );
}
Reference< XInputStream > FilterBase::openInputStream( const OUString& rStreamName ) const
{
return mxImpl->mxStorage->openInputStream( rStreamName );
}
Reference< XOutputStream > FilterBase::openOutputStream( const OUString& rStreamName ) const
{
return mxImpl->mxStorage->openOutputStream( rStreamName );
}
void FilterBase::commitStorage() const
{
mxImpl->mxStorage->commit();
}
// helpers --------------------------------------------------------------------
GraphicHelper& FilterBase::getGraphicHelper() const
{
if( !mxImpl->mxGraphicHelper )
mxImpl->mxGraphicHelper.reset( implCreateGraphicHelper() );
return *mxImpl->mxGraphicHelper;
}
ModelObjectHelper& FilterBase::getModelObjectHelper() const
{
if( !mxImpl->mxModelObjHelper )
mxImpl->mxModelObjHelper.reset( new ModelObjectHelper( mxImpl->mxModelFactory ) );
return *mxImpl->mxModelObjHelper;
}
OleObjectHelper& FilterBase::getOleObjectHelper() const
{
if( !mxImpl->mxOleObjHelper )
mxImpl->mxOleObjHelper.reset( new OleObjectHelper( mxImpl->mxModelFactory ) );
return *mxImpl->mxOleObjHelper;
}
VbaProject& FilterBase::getVbaProject() const
{
if( !mxImpl->mxVbaProject )
mxImpl->mxVbaProject.reset( implCreateVbaProject() );
return *mxImpl->mxVbaProject;
}
Sequence< NamedValue > FilterBase::requestEncryptionData( ::comphelper::IDocPasswordVerifier& rVerifier ) const
{
::std::vector< OUString > aDefaultPasswords;
aDefaultPasswords.push_back( CREATE_OUSTRING( "VelvetSweatshop" ) );
return ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
rVerifier, mxImpl->maMediaDesc, ::comphelper::DocPasswordRequestType_MS, &aDefaultPasswords );
}
bool FilterBase::importBinaryData( StreamDataSequence& orDataSeq, const OUString& rStreamName )
{
OSL_ENSURE( rStreamName.getLength() > 0, "FilterBase::importBinaryData - empty stream name" );
if( rStreamName.getLength() == 0 )
return false;
// try to open the stream (this may fail - do not assert)
BinaryXInputStream aInStrm( openInputStream( rStreamName ), true );
if( aInStrm.isEof() )
return false;
// copy the entire stream to the passed sequence
SequenceOutputStream aOutStrm( orDataSeq );
aInStrm.copyToStream( aOutStrm );
return true;
}
// com.sun.star.lang.XServiceInfo interface -----------------------------------
OUString SAL_CALL FilterBase::getImplementationName() throw( RuntimeException )
{
return implGetImplementationName();
}
sal_Bool SAL_CALL FilterBase::supportsService( const OUString& rServiceName ) throw( RuntimeException )
{
return
(rServiceName == CREATE_OUSTRING( "com.sun.star.document.ImportFilter" )) ||
(rServiceName == CREATE_OUSTRING( "com.sun.star.document.ExportFilter" ));
}
Sequence< OUString > SAL_CALL FilterBase::getSupportedServiceNames() throw( RuntimeException )
{
Sequence< OUString > aServiceNames( 2 );
aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.document.ImportFilter" );
aServiceNames[ 1 ] = CREATE_OUSTRING( "com.sun.star.document.ExportFilter" );
return aServiceNames;
}
// com.sun.star.lang.XInitialization interface --------------------------------
void SAL_CALL FilterBase::initialize( const Sequence< Any >& rArgs ) throw( Exception, RuntimeException )
{
if( rArgs.getLength() >= 2 ) try
{
mxImpl->maArguments << rArgs[ 1 ];
}
catch( Exception& )
{
}
}
// com.sun.star.document.XImporter interface ----------------------------------
void SAL_CALL FilterBase::setTargetDocument( const Reference< XComponent >& rxDocument ) throw( IllegalArgumentException, RuntimeException )
{
mxImpl->setDocumentModel( rxDocument );
mxImpl->meDirection = FILTERDIRECTION_IMPORT;
}
// com.sun.star.document.XExporter interface ----------------------------------
void SAL_CALL FilterBase::setSourceDocument( const Reference< XComponent >& rxDocument ) throw( IllegalArgumentException, RuntimeException )
{
mxImpl->setDocumentModel( rxDocument );
mxImpl->meDirection = FILTERDIRECTION_EXPORT;
}
// com.sun.star.document.XFilter interface ------------------------------------
sal_Bool SAL_CALL FilterBase::filter( const Sequence< PropertyValue >& rMediaDescSeq ) throw( RuntimeException )
{
if( !mxImpl->mxModel.is() || !mxImpl->mxModelFactory.is() || (mxImpl->meDirection == FILTERDIRECTION_UNKNOWN) )
throw RuntimeException();
sal_Bool bRet = sal_False;
setMediaDescriptor( rMediaDescSeq );
DocumentOpenedGuard aOpenedGuard( mxImpl->maFileUrl );
if( aOpenedGuard.isValid() || !mxImpl->maFileUrl.getLength() )
{
mxImpl->initializeFilter();
switch( mxImpl->meDirection )
{
case FILTERDIRECTION_UNKNOWN:
break;
case FILTERDIRECTION_IMPORT:
if( mxImpl->mxInStream.is() )
{
mxImpl->mxStorage = implCreateStorage( mxImpl->mxInStream );
bRet = mxImpl->mxStorage.get() && importDocument();
}
break;
case FILTERDIRECTION_EXPORT:
if( mxImpl->mxOutStream.is() )
{
mxImpl->mxStorage = implCreateStorage( mxImpl->mxOutStream );
bRet = mxImpl->mxStorage.get() && exportDocument();
}
break;
}
mxImpl->finalizeFilter();
}
return bRet;
}
void SAL_CALL FilterBase::cancel() throw( RuntimeException )
{
}
// protected ------------------------------------------------------------------
Reference< XInputStream > FilterBase::implGetInputStream( MediaDescriptor& rMediaDesc ) const
{
return rMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_INPUTSTREAM(), Reference< XInputStream >() );
}
Reference< XStream > FilterBase::implGetOutputStream( MediaDescriptor& rMediaDesc ) const
{
return rMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_STREAMFOROUTPUT(), Reference< XStream >() );
}
// private --------------------------------------------------------------------
void FilterBase::setMediaDescriptor( const Sequence< PropertyValue >& rMediaDescSeq )
{
mxImpl->maMediaDesc << rMediaDescSeq;
switch( mxImpl->meDirection )
{
case FILTERDIRECTION_UNKNOWN:
OSL_ENSURE( false, "FilterBase::setMediaDescriptor - invalid filter direction" );
break;
case FILTERDIRECTION_IMPORT:
mxImpl->maMediaDesc.addInputStream();
mxImpl->mxInStream = implGetInputStream( mxImpl->maMediaDesc );
OSL_ENSURE( mxImpl->mxInStream.is(), "FilterBase::setMediaDescriptor - missing input stream" );
break;
case FILTERDIRECTION_EXPORT:
mxImpl->mxOutStream = implGetOutputStream( mxImpl->maMediaDesc );
OSL_ENSURE( mxImpl->mxOutStream.is(), "FilterBase::setMediaDescriptor - missing output stream" );
break;
}
mxImpl->maFileUrl = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_URL(), OUString() );
mxImpl->mxTargetFrame = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_FRAME(), Reference< XFrame >() );
mxImpl->mxStatusIndicator = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_STATUSINDICATOR(), Reference< XStatusIndicator >() );
mxImpl->mxInteractionHandler = mxImpl->maMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_INTERACTIONHANDLER(), Reference< XInteractionHandler >() );
}
GraphicHelper* FilterBase::implCreateGraphicHelper() const
{
// default: return base implementation without any special behaviour
return new GraphicHelper( mxImpl->mxComponentContext, mxImpl->mxTargetFrame, mxImpl->mxStorage );
}
// ============================================================================
} // namespace core
} // namespace oox