1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/frame/Desktop.hpp>
21 #include <com/sun/star/frame/XFrame.hpp>
22 #include <com/sun/star/frame/XController.hpp>
23 #include <com/sun/star/awt/XTopWindow.hpp>
24 #include <com/sun/star/embed/XClassifiedObject.hpp>
25 #include <com/sun/star/io/TempFile.hpp>
26 #include <com/sun/star/io/XStream.hpp>
27 #include <com/sun/star/io/XOutputStream.hpp>
28 #include <com/sun/star/io/XSeekable.hpp>
29 #include <com/sun/star/task/XInteractionHandler.hpp>
30 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
31 #include <com/sun/star/util/XCloseable.hpp>
33 #include <com/sun/star/document/XEventBroadcaster.hpp>
34 #include <com/sun/star/document/XEventListener.hpp>
35 #include <com/sun/star/document/XTypeDetection.hpp>
36 #include <com/sun/star/container/XNameAccess.hpp>
37 #include <cppuhelper/implbase.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <comphelper/storagehelper.hxx>
40 #include <comphelper/mimeconfighelper.hxx>
41 #include <comphelper/diagnose_ex.hxx>
43 #include "olepersist.hxx"
44 #include "ownview.hxx"
47 using namespace ::com::sun::star
;
48 using namespace ::comphelper
;
52 class DummyHandler_Impl
: public ::cppu::WeakImplHelper
< task::XInteractionHandler
>
55 DummyHandler_Impl() {}
57 virtual void SAL_CALL
handle( const uno::Reference
< task::XInteractionRequest
>& xRequest
) override
;
62 void SAL_CALL
DummyHandler_Impl::handle( const uno::Reference
< task::XInteractionRequest
>& )
70 OwnView_Impl::OwnView_Impl( const uno::Reference
< uno::XComponentContext
>& xContext
,
71 const uno::Reference
< io::XInputStream
>& xInputStream
)
72 : m_xContext( xContext
)
74 , m_bUseNative( false )
76 if ( !xContext
.is() || !xInputStream
.is() )
77 throw uno::RuntimeException();
79 m_aTempFileURL
= GetNewFilledTempFile_Impl( xInputStream
, m_xContext
);
83 OwnView_Impl::~OwnView_Impl()
86 KillFile_Impl( m_aTempFileURL
, m_xContext
);
87 } catch( uno::Exception
& ) {}
90 if ( !m_aNativeTempURL
.isEmpty() )
91 KillFile_Impl( m_aNativeTempURL
, m_xContext
);
92 } catch( uno::Exception
& ) {}
96 bool OwnView_Impl::CreateModelFromURL( const OUString
& aFileURL
)
100 if ( !aFileURL
.isEmpty() )
103 uno::Reference
< frame::XDesktop2
> xDocumentLoader
= frame::Desktop::create(m_xContext
);
105 uno::Sequence
< beans::PropertyValue
> aArgs( m_aFilterName
.isEmpty() ? 4 : 5 );
106 auto pArgs
= aArgs
.getArray();
108 pArgs
[0].Name
= "URL";
109 pArgs
[0].Value
<<= aFileURL
;
111 pArgs
[1].Name
= "ReadOnly";
112 pArgs
[1].Value
<<= true;
114 pArgs
[2].Name
= "InteractionHandler";
115 pArgs
[2].Value
<<= uno::Reference
< task::XInteractionHandler
>( new DummyHandler_Impl() );
117 pArgs
[3].Name
= "DontEdit";
118 pArgs
[3].Value
<<= true;
120 if ( !m_aFilterName
.isEmpty() )
122 pArgs
[4].Name
= "FilterName";
123 pArgs
[4].Value
<<= m_aFilterName
;
126 uno::Reference
< frame::XModel
> xModel( xDocumentLoader
->loadComponentFromURL(
135 uno::Reference
< document::XEventBroadcaster
> xBroadCaster( xModel
, uno::UNO_QUERY
);
136 if ( xBroadCaster
.is() )
137 xBroadCaster
->addEventListener( uno::Reference
< document::XEventListener
>(this) );
139 uno::Reference
< util::XCloseable
> xCloseable( xModel
, uno::UNO_QUERY
);
140 if ( xCloseable
.is() )
142 xCloseable
->addCloseListener( uno::Reference
< util::XCloseListener
>(this) );
144 ::osl::MutexGuard
aGuard( m_aMutex
);
145 m_xModel
= std::move(xModel
);
150 catch (uno::Exception
const&)
152 TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OwnView_Impl::CreateModelFromURL:");
160 bool OwnView_Impl::CreateModel( bool bUseNative
)
162 bool bResult
= false;
165 bResult
= CreateModelFromURL( bUseNative
? m_aNativeTempURL
: m_aTempFileURL
);
167 catch( uno::Exception
& )
175 OUString
OwnView_Impl::GetFilterNameFromExtentionAndInStream(
176 const css::uno::Reference
< css::uno::XComponentContext
>& xContext
,
177 std::u16string_view aNameWithExtention
,
178 const uno::Reference
< io::XInputStream
>& xInputStream
)
180 if ( !xInputStream
.is() )
181 throw uno::RuntimeException();
183 uno::Reference
< document::XTypeDetection
> xTypeDetection(
184 xContext
->getServiceManager()->createInstanceWithContext(u
"com.sun.star.document.TypeDetection"_ustr
, xContext
),
185 uno::UNO_QUERY_THROW
);
189 if ( !aNameWithExtention
.empty() )
191 OUString aURLToAnalyze
= OUString::Concat("file:///") + aNameWithExtention
;
192 aTypeName
= xTypeDetection
->queryTypeByURL( aURLToAnalyze
);
195 uno::Sequence
< beans::PropertyValue
> aArgs( aTypeName
.isEmpty() ? 2 : 3 );
196 auto pArgs
= aArgs
.getArray();
197 pArgs
[0].Name
= "URL";
198 pArgs
[0].Value
<<= u
"private:stream"_ustr
;
199 pArgs
[1].Name
= "InputStream";
200 pArgs
[1].Value
<<= xInputStream
;
201 if ( !aTypeName
.isEmpty() )
203 pArgs
[2].Name
= "TypeName";
204 pArgs
[2].Value
<<= aTypeName
;
207 aTypeName
= xTypeDetection
->queryTypeByDescriptor( aArgs
, true );
209 OUString aFilterName
;
210 for (beans::PropertyValue
const& prop
: aArgs
)
211 if ( prop
.Name
== "FilterName" )
212 prop
.Value
>>= aFilterName
;
214 if ( aFilterName
.isEmpty() && !aTypeName
.isEmpty() )
216 // get the default filter name for the type
217 uno::Reference
< container::XNameAccess
> xNameAccess( xTypeDetection
, uno::UNO_QUERY_THROW
);
218 uno::Sequence
< beans::PropertyValue
> aTypes
;
220 if ( xNameAccess
.is() && ( xNameAccess
->getByName( aTypeName
) >>= aTypes
) )
222 for (beans::PropertyValue
const& prop
: aTypes
)
224 if ( prop
.Name
== "PreferredFilter" && ( prop
.Value
>>= aFilterName
) )
226 prop
.Value
>>= aFilterName
;
237 bool OwnView_Impl::ReadContentsAndGenerateTempFile( const uno::Reference
< io::XInputStream
>& xInStream
,
240 uno::Reference
< io::XSeekable
> xSeekable( xInStream
, uno::UNO_QUERY_THROW
);
241 xSeekable
->seek( 0 );
243 // create m_aNativeTempURL
244 OUString aNativeTempURL
;
245 uno::Reference
< io::XTempFile
> xNativeTempFile(
246 io::TempFile::create(m_xContext
),
247 uno::UNO_SET_THROW
);
248 uno::Reference
< io::XOutputStream
> xNativeOutTemp
= xNativeTempFile
->getOutputStream();
249 uno::Reference
< io::XInputStream
> xNativeInTemp
= xNativeTempFile
->getInputStream();
250 if ( !xNativeOutTemp
.is() || !xNativeInTemp
.is() )
251 throw uno::RuntimeException();
254 xNativeTempFile
->setRemoveFile( false );
255 aNativeTempURL
= xNativeTempFile
->getUri();
257 catch ( uno::Exception
& )
261 bool bFailed
= false;
262 OUString aFileSuffix
;
266 uno::Sequence
< sal_Int8
> aReadSeq( 4 );
267 // read the complete size of the Object Package
268 if ( xInStream
->readBytes( aReadSeq
, 4 ) != 4 )
270 // read the first header ( have no idea what does this header mean )
271 if ( xInStream
->readBytes( aReadSeq
, 2 ) != 2 || aReadSeq
[0] != 2 || aReadSeq
[1] != 0 )
275 // only extension is interesting so only subset of symbols is accepted
278 if ( xInStream
->readBytes( aReadSeq
, 1 ) != 1 )
282 (aReadSeq
[0] >= '0' && aReadSeq
[0] <= '9') ||
283 (aReadSeq
[0] >= 'a' && aReadSeq
[0] <= 'z') ||
284 (aReadSeq
[0] >= 'A' && aReadSeq
[0] <= 'Z') ||
288 aFileSuffix
+= OUStringChar( sal_Unicode(aReadSeq
[0]) );
291 } while( aReadSeq
[0] );
296 if ( xInStream
->readBytes( aReadSeq
, 1 ) != 1 )
298 } while( aReadSeq
[0] );
300 // check the next header
301 if ( xInStream
->readBytes( aReadSeq
, 4 ) != 4
302 || aReadSeq
[0] || aReadSeq
[1] || aReadSeq
[2] != 3 || aReadSeq
[3] )
305 // get the size of the next entry
306 if ( xInStream
->readBytes( aReadSeq
, 4 ) != 4 )
309 sal_uInt32 nUrlSize
= static_cast<sal_uInt8
>(aReadSeq
[0])
310 + static_cast<sal_uInt8
>(aReadSeq
[1]) * 0x100
311 + static_cast<sal_uInt8
>(aReadSeq
[2]) * 0x10000
312 + static_cast<sal_uInt8
>(aReadSeq
[3]) * 0x1000000;
313 sal_Int64 nTargetPos
= xSeekable
->getPosition() + nUrlSize
;
315 xSeekable
->seek( nTargetPos
);
317 // get the size of stored data
318 if ( xInStream
->readBytes( aReadSeq
, 4 ) != 4 )
321 sal_uInt32 nDataSize
= static_cast<sal_uInt8
>(aReadSeq
[0])
322 + static_cast<sal_uInt8
>(aReadSeq
[1]) * 0x100
323 + static_cast<sal_uInt8
>(aReadSeq
[2]) * 0x10000
324 + static_cast<sal_uInt8
>(aReadSeq
[3]) * 0x1000000;
326 aReadSeq
.realloc( 32000 );
327 sal_uInt32 nRead
= 0;
328 while ( nRead
< nDataSize
)
330 sal_uInt32 nToRead
= std::min
<sal_uInt32
>( nDataSize
- nRead
, 32000 );
331 sal_uInt32 nLocalRead
= xInStream
->readBytes( aReadSeq
, nToRead
);
339 else if ( nLocalRead
== 32000 )
340 xNativeOutTemp
->writeBytes( aReadSeq
);
343 uno::Sequence
< sal_Int8
> aToWrite( aReadSeq
);
344 aToWrite
.realloc( nLocalRead
);
345 xNativeOutTemp
->writeBytes( aToWrite
);
353 uno::Sequence
< sal_Int8
> aData( 8 );
354 if ( xInStream
->readBytes( aData
, 8 ) == 8
355 && aData
[0] == -1 && aData
[1] == -1 && aData
[2] == -1 && aData
[3] == -1
356 && ( aData
[4] == 2 || aData
[4] == 3 ) && aData
[5] == 0 && aData
[6] == 0 && aData
[7] == 0 )
358 // the header has to be removed
359 xSeekable
->seek( 40 );
363 // the usual Ole10Native format
364 xSeekable
->seek( 4 );
367 ::comphelper::OStorageHelper::CopyInputToOutput( xInStream
, xNativeOutTemp
);
370 xNativeOutTemp
->closeOutput();
372 // The temporary native file is created, now the filter must be detected
375 m_aFilterName
= GetFilterNameFromExtentionAndInStream( m_xContext
, aFileSuffix
, xNativeInTemp
);
376 m_aNativeTempURL
= aNativeTempURL
;
383 void OwnView_Impl::CreateNative()
385 if ( !m_aNativeTempURL
.isEmpty() )
390 uno::Reference
< ucb::XSimpleFileAccess3
> xAccess(
391 ucb::SimpleFileAccess::create( m_xContext
) );
393 uno::Reference
< io::XInputStream
> xInStream
= xAccess
->openFileRead( m_aTempFileURL
);
394 if ( !xInStream
.is() )
395 throw uno::RuntimeException();
397 uno::Sequence
< uno::Any
> aArgs
{ uno::Any(xInStream
) };
398 uno::Reference
< container::XNameAccess
> xNameAccess(
399 m_xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
400 u
"com.sun.star.embed.OLESimpleStorage"_ustr
,
402 uno::UNO_QUERY_THROW
);
404 static constexpr OUString
aSubStreamName(u
"\1Ole10Native"_ustr
);
405 uno::Reference
< embed::XClassifiedObject
> xStor( xNameAccess
, uno::UNO_QUERY_THROW
);
406 uno::Sequence
< sal_Int8
> aStorClassID
= xStor
->getClassID();
408 if ( xNameAccess
->hasByName( aSubStreamName
) )
410 sal_uInt8
const aClassID
[] =
411 { 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
412 // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
413 uno::Sequence
< sal_Int8
> aPackageClassID( reinterpret_cast<sal_Int8
const *>(aClassID
), 16 );
415 uno::Reference
< io::XStream
> xSubStream
;
416 xNameAccess
->getByName( aSubStreamName
) >>= xSubStream
;
417 if ( xSubStream
.is() )
421 if ( MimeConfigurationHelper::ClassIDsEqual( aPackageClassID
, aStorClassID
) )
423 // the storage represents Object Package
425 bOk
= ReadContentsAndGenerateTempFile( xSubStream
->getInputStream(), true );
427 if ( !bOk
&& !m_aNativeTempURL
.isEmpty() )
429 KillFile_Impl( m_aNativeTempURL
, m_xContext
);
430 m_aNativeTempURL
.clear();
436 bOk
= ReadContentsAndGenerateTempFile( xSubStream
->getInputStream(), false );
438 if ( !bOk
&& !m_aNativeTempURL
.isEmpty() )
440 KillFile_Impl( m_aNativeTempURL
, m_xContext
);
441 m_aNativeTempURL
.clear();
448 // TODO/LATER: No native stream, needs a new solution
451 catch( uno::Exception
& )
456 bool OwnView_Impl::Open()
458 bool bResult
= false;
460 uno::Reference
< frame::XModel
> xExistingModel
;
463 ::osl::MutexGuard
aGuard( m_aMutex
);
464 xExistingModel
= m_xModel
;
471 if ( xExistingModel
.is() )
474 uno::Reference
< frame::XController
> xController
= xExistingModel
->getCurrentController();
475 if ( xController
.is() )
477 uno::Reference
< frame::XFrame
> xFrame
= xController
->getFrame();
481 uno::Reference
<awt::XTopWindow
> xTopWindow( xFrame
->getContainerWindow(), uno::UNO_QUERY
);
483 xTopWindow
->toFront();
489 catch( uno::Exception
& )
495 bResult
= CreateModel( m_bUseNative
);
497 if ( !bResult
&& !m_bUseNative
)
499 // the original storage can not be recognized
500 if ( m_aNativeTempURL
.isEmpty() )
502 // create a temporary file for the native representation if there is no
506 if ( !m_aNativeTempURL
.isEmpty() )
508 bResult
= CreateModel( true );
521 void OwnView_Impl::Close()
523 uno::Reference
< frame::XModel
> xModel
;
526 ::osl::MutexGuard
aGuard( m_aMutex
);
527 if ( !m_xModel
.is() )
539 uno::Reference
< document::XEventBroadcaster
> xBroadCaster( xModel
, uno::UNO_QUERY
);
540 if ( xBroadCaster
.is() )
541 xBroadCaster
->removeEventListener( uno::Reference
< document::XEventListener
>( this ) );
543 uno::Reference
< util::XCloseable
> xCloseable( xModel
, uno::UNO_QUERY
);
544 if ( xCloseable
.is() )
546 xCloseable
->removeCloseListener( uno::Reference
< util::XCloseListener
>( this ) );
547 xCloseable
->close( true );
550 catch( uno::Exception
& )
557 void SAL_CALL
OwnView_Impl::notifyEvent( const document::EventObject
& aEvent
)
560 uno::Reference
< frame::XModel
> xModel
;
563 ::osl::MutexGuard
aGuard( m_aMutex
);
564 if ( aEvent
.Source
== m_xModel
&& aEvent
.EventName
== "OnSaveAsDone" )
566 // SaveAs operation took place, so just forget the model and deregister listeners
576 uno::Reference
< document::XEventBroadcaster
> xBroadCaster( xModel
, uno::UNO_QUERY
);
577 if ( xBroadCaster
.is() )
578 xBroadCaster
->removeEventListener( uno::Reference
< document::XEventListener
>( this ) );
580 uno::Reference
< util::XCloseable
> xCloseable( xModel
, uno::UNO_QUERY
);
581 if ( xCloseable
.is() )
582 xCloseable
->removeCloseListener( uno::Reference
< util::XCloseListener
>( this ) );
584 catch( uno::Exception
& )
589 void SAL_CALL
OwnView_Impl::queryClosing( const lang::EventObject
&, sal_Bool
)
594 void SAL_CALL
OwnView_Impl::notifyClosing( const lang::EventObject
& Source
)
596 ::osl::MutexGuard
aGuard( m_aMutex
);
597 if ( Source
.Source
== m_xModel
)
602 void SAL_CALL
OwnView_Impl::disposing( const lang::EventObject
& Source
)
604 ::osl::MutexGuard
aGuard( m_aMutex
);
605 if ( Source
.Source
== m_xModel
)
609 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */