diff options
Diffstat (limited to 'embeddedobj/source/msole/ownview.cxx')
-rw-r--r-- | embeddedobj/source/msole/ownview.cxx | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/embeddedobj/source/msole/ownview.cxx b/embeddedobj/source/msole/ownview.cxx new file mode 100644 index 000000000..26f74f039 --- /dev/null +++ b/embeddedobj/source/msole/ownview.cxx @@ -0,0 +1,609 @@ +/* -*- 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/frame/Desktop.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/embed/XClassifiedObject.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +#include <com/sun/star/document/XEventBroadcaster.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <cppuhelper/implbase.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/mimeconfighelper.hxx> +#include <tools/diagnose_ex.h> + +#include "olepersist.hxx" +#include "ownview.hxx" + + +using namespace ::com::sun::star; +using namespace ::comphelper; + +namespace { + +class DummyHandler_Impl : public ::cppu::WeakImplHelper< task::XInteractionHandler > +{ +public: + DummyHandler_Impl() {} + + virtual void SAL_CALL handle( const uno::Reference< task::XInteractionRequest >& xRequest ) override; +}; + +} + +void SAL_CALL DummyHandler_Impl::handle( const uno::Reference< task::XInteractionRequest >& ) +{ +} + + +// Object viewer + + +OwnView_Impl::OwnView_Impl( const uno::Reference< uno::XComponentContext >& xContext, + const uno::Reference< io::XInputStream >& xInputStream ) +: m_xContext( xContext ) +, m_bBusy( false ) +, m_bUseNative( false ) +{ + if ( !xContext.is() || !xInputStream.is() ) + throw uno::RuntimeException(); + + m_aTempFileURL = GetNewFilledTempFile_Impl( xInputStream, m_xContext ); +} + + +OwnView_Impl::~OwnView_Impl() +{ + try { + KillFile_Impl( m_aTempFileURL, m_xContext ); + } catch( uno::Exception& ) {} + + try { + if ( !m_aNativeTempURL.isEmpty() ) + KillFile_Impl( m_aNativeTempURL, m_xContext ); + } catch( uno::Exception& ) {} +} + + +bool OwnView_Impl::CreateModelFromURL( const OUString& aFileURL ) +{ + bool bResult = false; + + if ( !aFileURL.isEmpty() ) + { + try { + uno::Reference < frame::XDesktop2 > xDocumentLoader = frame::Desktop::create(m_xContext); + + uno::Sequence< beans::PropertyValue > aArgs( m_aFilterName.isEmpty() ? 4 : 5 ); + auto pArgs = aArgs.getArray(); + + pArgs[0].Name = "URL"; + pArgs[0].Value <<= aFileURL; + + pArgs[1].Name = "ReadOnly"; + pArgs[1].Value <<= true; + + pArgs[2].Name = "InteractionHandler"; + pArgs[2].Value <<= uno::Reference< task::XInteractionHandler >( new DummyHandler_Impl() ); + + pArgs[3].Name = "DontEdit"; + pArgs[3].Value <<= true; + + if ( !m_aFilterName.isEmpty() ) + { + pArgs[4].Name = "FilterName"; + pArgs[4].Value <<= m_aFilterName; + } + + uno::Reference< frame::XModel > xModel( xDocumentLoader->loadComponentFromURL( + aFileURL, + "_blank", + 0, + aArgs ), + uno::UNO_QUERY ); + + if ( xModel.is() ) + { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->addEventListener( uno::Reference< document::XEventListener >(this) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + { + xCloseable->addCloseListener( uno::Reference< util::XCloseListener >(this) ); + + ::osl::MutexGuard aGuard( m_aMutex ); + m_xModel = xModel; + bResult = true; + } + } + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OwnView_Impl::CreateModelFromURL:"); + } + } + + return bResult; +} + + +bool OwnView_Impl::CreateModel( bool bUseNative ) +{ + bool bResult = false; + + try { + bResult = CreateModelFromURL( bUseNative ? m_aNativeTempURL : m_aTempFileURL ); + } + catch( uno::Exception& ) + { + } + + return bResult; +} + + +OUString OwnView_Impl::GetFilterNameFromExtentionAndInStream( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + std::u16string_view aNameWithExtention, + const uno::Reference< io::XInputStream >& xInputStream ) +{ + if ( !xInputStream.is() ) + throw uno::RuntimeException(); + + uno::Reference< document::XTypeDetection > xTypeDetection( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext), + uno::UNO_QUERY_THROW ); + + OUString aTypeName; + + if ( !aNameWithExtention.empty() ) + { + OUString aURLToAnalyze = OUString::Concat("file:///") + aNameWithExtention; + aTypeName = xTypeDetection->queryTypeByURL( aURLToAnalyze ); + } + + uno::Sequence< beans::PropertyValue > aArgs( aTypeName.isEmpty() ? 2 : 3 ); + auto pArgs = aArgs.getArray(); + pArgs[0].Name = "URL"; + pArgs[0].Value <<= OUString( "private:stream" ); + pArgs[1].Name = "InputStream"; + pArgs[1].Value <<= xInputStream; + if ( !aTypeName.isEmpty() ) + { + pArgs[2].Name = "TypeName"; + pArgs[2].Value <<= aTypeName; + } + + aTypeName = xTypeDetection->queryTypeByDescriptor( aArgs, true ); + + OUString aFilterName; + for ( beans::PropertyValue const & prop : std::as_const(aArgs) ) + if ( prop.Name == "FilterName" ) + prop.Value >>= aFilterName; + + if ( aFilterName.isEmpty() && !aTypeName.isEmpty() ) + { + // get the default filter name for the type + uno::Reference< container::XNameAccess > xNameAccess( xTypeDetection, uno::UNO_QUERY_THROW ); + uno::Sequence< beans::PropertyValue > aTypes; + + if ( xNameAccess.is() && ( xNameAccess->getByName( aTypeName ) >>= aTypes ) ) + { + for ( beans::PropertyValue const & prop : std::as_const(aTypes) ) + { + if ( prop.Name == "PreferredFilter" && ( prop.Value >>= aFilterName ) ) + { + prop.Value >>= aFilterName; + break; + } + } + } + } + + return aFilterName; +} + + +bool OwnView_Impl::ReadContentsAndGenerateTempFile( const uno::Reference< io::XInputStream >& xInStream, + bool bParseHeader ) +{ + uno::Reference< io::XSeekable > xSeekable( xInStream, uno::UNO_QUERY_THROW ); + xSeekable->seek( 0 ); + + // create m_aNativeTempURL + OUString aNativeTempURL; + uno::Reference < io::XTempFile > xNativeTempFile( + io::TempFile::create(m_xContext), + uno::UNO_SET_THROW ); + uno::Reference < io::XOutputStream > xNativeOutTemp = xNativeTempFile->getOutputStream(); + uno::Reference < io::XInputStream > xNativeInTemp = xNativeTempFile->getInputStream(); + if ( !xNativeOutTemp.is() || !xNativeInTemp.is() ) + throw uno::RuntimeException(); + + try { + xNativeTempFile->setRemoveFile( false ); + aNativeTempURL = xNativeTempFile->getUri(); + } + catch ( uno::Exception& ) + { + } + + bool bFailed = false; + OUString aFileSuffix; + + if ( bParseHeader ) + { + uno::Sequence< sal_Int8 > aReadSeq( 4 ); + // read the complete size of the Object Package + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + // read the first header ( have no idea what does this header mean ) + if ( xInStream->readBytes( aReadSeq, 2 ) != 2 || aReadSeq[0] != 2 || aReadSeq[1] != 0 ) + return false; + + // read file name + // only extension is interesting so only subset of symbols is accepted + do + { + if ( xInStream->readBytes( aReadSeq, 1 ) != 1 ) + return false; + + if ( + (aReadSeq[0] >= '0' && aReadSeq[0] <= '9') || + (aReadSeq[0] >= 'a' && aReadSeq[0] <= 'z') || + (aReadSeq[0] >= 'A' && aReadSeq[0] <= 'Z') || + aReadSeq[0] == '.' + ) + { + aFileSuffix += OUStringChar( sal_Unicode(aReadSeq[0]) ); + } + + } while( aReadSeq[0] ); + + // skip url + do + { + if ( xInStream->readBytes( aReadSeq, 1 ) != 1 ) + return false; + } while( aReadSeq[0] ); + + // check the next header + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 + || aReadSeq[0] || aReadSeq[1] || aReadSeq[2] != 3 || aReadSeq[3] ) + return false; + + // get the size of the next entry + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + + sal_uInt32 nUrlSize = static_cast<sal_uInt8>(aReadSeq[0]) + + static_cast<sal_uInt8>(aReadSeq[1]) * 0x100 + + static_cast<sal_uInt8>(aReadSeq[2]) * 0x10000 + + static_cast<sal_uInt8>(aReadSeq[3]) * 0x1000000; + sal_Int64 nTargetPos = xSeekable->getPosition() + nUrlSize; + + xSeekable->seek( nTargetPos ); + + // get the size of stored data + if ( xInStream->readBytes( aReadSeq, 4 ) != 4 ) + return false; + + sal_uInt32 nDataSize = static_cast<sal_uInt8>(aReadSeq[0]) + + static_cast<sal_uInt8>(aReadSeq[1]) * 0x100 + + static_cast<sal_uInt8>(aReadSeq[2]) * 0x10000 + + static_cast<sal_uInt8>(aReadSeq[3]) * 0x1000000; + + aReadSeq.realloc( 32000 ); + sal_uInt32 nRead = 0; + while ( nRead < nDataSize ) + { + sal_uInt32 nToRead = std::min<sal_uInt32>( nDataSize - nRead, 32000 ); + sal_uInt32 nLocalRead = xInStream->readBytes( aReadSeq, nToRead ); + + + if ( !nLocalRead ) + { + bFailed = true; + break; + } + else if ( nLocalRead == 32000 ) + xNativeOutTemp->writeBytes( aReadSeq ); + else + { + uno::Sequence< sal_Int8 > aToWrite( aReadSeq ); + aToWrite.realloc( nLocalRead ); + xNativeOutTemp->writeBytes( aToWrite ); + } + + nRead += nLocalRead; + } + } + else + { + uno::Sequence< sal_Int8 > aData( 8 ); + if ( xInStream->readBytes( aData, 8 ) == 8 + && aData[0] == -1 && aData[1] == -1 && aData[2] == -1 && aData[3] == -1 + && ( aData[4] == 2 || aData[4] == 3 ) && aData[5] == 0 && aData[6] == 0 && aData[7] == 0 ) + { + // the header has to be removed + xSeekable->seek( 40 ); + } + else + { + // the usual Ole10Native format + xSeekable->seek( 4 ); + } + + ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xNativeOutTemp ); + } + + xNativeOutTemp->closeOutput(); + + // The temporary native file is created, now the filter must be detected + if ( !bFailed ) + { + m_aFilterName = GetFilterNameFromExtentionAndInStream( m_xContext, aFileSuffix, xNativeInTemp ); + m_aNativeTempURL = aNativeTempURL; + } + + return !bFailed; +} + + +void OwnView_Impl::CreateNative() +{ + if ( !m_aNativeTempURL.isEmpty() ) + return; + + try + { + uno::Reference < ucb::XSimpleFileAccess3 > xAccess( + ucb::SimpleFileAccess::create( m_xContext ) ); + + uno::Reference< io::XInputStream > xInStream = xAccess->openFileRead( m_aTempFileURL ); + if ( !xInStream.is() ) + throw uno::RuntimeException(); + + uno::Sequence< uno::Any > aArgs{ uno::Any(xInStream) }; + uno::Reference< container::XNameAccess > xNameAccess( + m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.embed.OLESimpleStorage", + aArgs, m_xContext ), + uno::UNO_QUERY_THROW ); + + OUString aSubStreamName = "\1Ole10Native"; + uno::Reference< embed::XClassifiedObject > xStor( xNameAccess, uno::UNO_QUERY_THROW ); + uno::Sequence< sal_Int8 > aStorClassID = xStor->getClassID(); + + if ( xNameAccess->hasByName( aSubStreamName ) ) + { + sal_uInt8 const aClassID[] = + { 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + uno::Sequence< sal_Int8 > aPackageClassID( reinterpret_cast<sal_Int8 const *>(aClassID), 16 ); + + uno::Reference< io::XStream > xSubStream; + xNameAccess->getByName( aSubStreamName ) >>= xSubStream; + if ( xSubStream.is() ) + { + bool bOk = false; + + if ( MimeConfigurationHelper::ClassIDsEqual( aPackageClassID, aStorClassID ) ) + { + // the storage represents Object Package + + bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), true ); + + if ( !bOk && !m_aNativeTempURL.isEmpty() ) + { + KillFile_Impl( m_aNativeTempURL, m_xContext ); + m_aNativeTempURL.clear(); + } + } + + if ( !bOk ) + { + bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), false ); + + if ( !bOk && !m_aNativeTempURL.isEmpty() ) + { + KillFile_Impl( m_aNativeTempURL, m_xContext ); + m_aNativeTempURL.clear(); + } + } + } + } + else + { + // TODO/LATER: No native stream, needs a new solution + } + } + catch( uno::Exception& ) + {} +} + + +bool OwnView_Impl::Open() +{ + bool bResult = false; + + uno::Reference< frame::XModel > xExistingModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + xExistingModel = m_xModel; + if ( m_bBusy ) + return false; + + m_bBusy = true; + } + + if ( xExistingModel.is() ) + { + try { + uno::Reference< frame::XController > xController = xExistingModel->getCurrentController(); + if ( xController.is() ) + { + uno::Reference< frame::XFrame > xFrame = xController->getFrame(); + if ( xFrame.is() ) + { + xFrame->activate(); + uno::Reference<awt::XTopWindow> xTopWindow( xFrame->getContainerWindow(), uno::UNO_QUERY ); + if(xTopWindow.is()) + xTopWindow->toFront(); + + bResult = true; + } + } + } + catch( uno::Exception& ) + { + } + } + else + { + bResult = CreateModel( m_bUseNative ); + + if ( !bResult && !m_bUseNative ) + { + // the original storage can not be recognized + if ( m_aNativeTempURL.isEmpty() ) + { + // create a temporary file for the native representation if there is no + CreateNative(); + } + + if ( !m_aNativeTempURL.isEmpty() ) + { + bResult = CreateModel( true ); + if ( bResult ) + m_bUseNative = true; + } + } + } + + m_bBusy = false; + + return bResult; +} + + +void OwnView_Impl::Close() +{ + uno::Reference< frame::XModel > xModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_xModel.is() ) + return; + xModel = m_xModel; + m_xModel.clear(); + + if ( m_bBusy ) + return; + + m_bBusy = true; + } + + try { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + { + xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) ); + xCloseable->close( true ); + } + } + catch( uno::Exception& ) + {} + + m_bBusy = false; +} + + +void SAL_CALL OwnView_Impl::notifyEvent( const document::EventObject& aEvent ) +{ + + uno::Reference< frame::XModel > xModel; + + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( aEvent.Source == m_xModel && aEvent.EventName == "OnSaveAsDone" ) + { + // SaveAs operation took place, so just forget the model and deregister listeners + xModel = m_xModel; + m_xModel.clear(); + } + } + + if ( !xModel.is() ) + return; + + try { + uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY ); + if ( xBroadCaster.is() ) + xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) ); + + uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY ); + if ( xCloseable.is() ) + xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) ); + } + catch( uno::Exception& ) + {} +} + + +void SAL_CALL OwnView_Impl::queryClosing( const lang::EventObject&, sal_Bool ) +{ +} + + +void SAL_CALL OwnView_Impl::notifyClosing( const lang::EventObject& Source ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( Source.Source == m_xModel ) + m_xModel.clear(); +} + + +void SAL_CALL OwnView_Impl::disposing( const lang::EventObject& Source ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( Source.Source == m_xModel ) + m_xModel.clear(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |