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/frame/XComponentLoader.hpp>
24 #include <com/sun/star/awt/XTopWindow.hpp>
25 #include <com/sun/star/embed/XClassifiedObject.hpp>
26 #include <com/sun/star/io/TempFile.hpp>
27 #include <com/sun/star/io/XStream.hpp>
28 #include <com/sun/star/io/XInputStream.hpp>
29 #include <com/sun/star/io/XOutputStream.hpp>
30 #include <com/sun/star/io/XSeekable.hpp>
31 #include <com/sun/star/task/XInteractionHandler.hpp>
32 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
33 #include <com/sun/star/util/XCloseable.hpp>
34 #include <com/sun/star/beans/XPropertySet.hpp>
36 #include <com/sun/star/document/XEventBroadcaster.hpp>
37 #include <com/sun/star/document/XEventListener.hpp>
38 #include <com/sun/star/document/XTypeDetection.hpp>
39 #include <com/sun/star/container/XNameAccess.hpp>
40 #include <cppuhelper/implbase1.hxx>
41 #include <comphelper/processfactory.hxx>
42 #include <comphelper/storagehelper.hxx>
43 #include <comphelper/mimeconfighelper.hxx>
45 #include "ownview.hxx"
48 using namespace ::com::sun::star
;
49 using namespace ::comphelper
;
51 OUString
GetNewTempFileURL_Impl( const uno::Reference
< lang::XMultiServiceFactory
>& xFactory
) throw( io::IOException
);
52 OUString
GetNewFilledTempFile_Impl( const uno::Reference
< io::XInputStream
>& xInStream
, const uno::Reference
< lang::XMultiServiceFactory
>& xFactory
) throw( io::IOException
);
53 sal_Bool
KillFile_Impl( const OUString
& aURL
, const uno::Reference
< lang::XMultiServiceFactory
>& xFactory
);
54 uno::Reference
< io::XStream
> TryToGetAcceptableFormat_Impl( const uno::Reference
< io::XStream
>& xStream
, const uno::Reference
< lang::XMultiServiceFactory
>& xFactory
) throw ( uno::Exception
);
56 //========================================================
57 // Dummy interaction handler
58 //========================================================
59 //--------------------------------------------------------
60 class DummyHandler_Impl
: public ::cppu::WeakImplHelper1
< task::XInteractionHandler
>
63 DummyHandler_Impl() {}
66 virtual void SAL_CALL
handle( const uno::Reference
< task::XInteractionRequest
>& xRequest
)
67 throw( uno::RuntimeException
);
70 //--------------------------------------------------------
71 DummyHandler_Impl::~DummyHandler_Impl()
75 //--------------------------------------------------------
76 void SAL_CALL
DummyHandler_Impl::handle( const uno::Reference
< task::XInteractionRequest
>& )
77 throw( uno::RuntimeException
)
82 //========================================================
84 //========================================================
85 //--------------------------------------------------------
86 OwnView_Impl::OwnView_Impl( const uno::Reference
< lang::XMultiServiceFactory
>& xFactory
,
87 const uno::Reference
< io::XInputStream
>& xInputStream
)
88 : m_xFactory( xFactory
)
89 , m_bBusy( sal_False
)
90 , m_bUseNative( sal_False
)
92 if ( !xFactory
.is() || !xInputStream
.is() )
93 throw uno::RuntimeException();
95 m_aTempFileURL
= GetNewFilledTempFile_Impl( xInputStream
, m_xFactory
);
98 //--------------------------------------------------------
99 OwnView_Impl::~OwnView_Impl()
102 KillFile_Impl( m_aTempFileURL
, m_xFactory
);
103 } catch( uno::Exception
& ) {}
106 if ( !m_aNativeTempURL
.isEmpty() )
107 KillFile_Impl( m_aNativeTempURL
, m_xFactory
);
108 } catch( uno::Exception
& ) {}
111 //--------------------------------------------------------
112 sal_Bool
OwnView_Impl::CreateModelFromURL( const OUString
& aFileURL
)
114 sal_Bool bResult
= sal_False
;
116 if ( !aFileURL
.isEmpty() )
119 uno::Reference
< frame::XDesktop2
> xDocumentLoader
= frame::Desktop::create(comphelper::getComponentContext(m_xFactory
));
121 uno::Sequence
< beans::PropertyValue
> aArgs( m_aFilterName
.isEmpty() ? 4 : 5 );
123 aArgs
[0].Name
= "URL";
124 aArgs
[0].Value
<<= aFileURL
;
126 aArgs
[1].Name
= "ReadOnly";
127 aArgs
[1].Value
<<= sal_True
;
129 aArgs
[2].Name
= "InteractionHandler";
130 aArgs
[2].Value
<<= uno::Reference
< task::XInteractionHandler
>(
131 static_cast< ::cppu::OWeakObject
* >( new DummyHandler_Impl() ), uno::UNO_QUERY
);
133 aArgs
[3].Name
= "DontEdit";
134 aArgs
[3].Value
<<= sal_True
;
136 if ( !m_aFilterName
.isEmpty() )
138 aArgs
[4].Name
= "FilterName";
139 aArgs
[4].Value
<<= m_aFilterName
;
142 uno::Reference
< frame::XModel
> xModel( xDocumentLoader
->loadComponentFromURL(
144 OUString( "_blank" ),
151 uno::Reference
< document::XEventBroadcaster
> xBroadCaster( xModel
, uno::UNO_QUERY
);
152 if ( xBroadCaster
.is() )
153 xBroadCaster
->addEventListener( uno::Reference
< document::XEventListener
>(
154 static_cast< ::cppu::OWeakObject
* >( this ),
157 uno::Reference
< util::XCloseable
> xCloseable( xModel
, uno::UNO_QUERY
);
158 if ( xCloseable
.is() )
160 xCloseable
->addCloseListener( uno::Reference
< util::XCloseListener
>(
161 static_cast< ::cppu::OWeakObject
* >( this ),
164 ::osl::MutexGuard
aGuard( m_aMutex
);
170 catch (uno::Exception
const& e
)
172 SAL_WARN("embeddedobj.ole", "OwnView_Impl::CreateModelFromURL:"
173 " exception caught: " << e
.Message
);
180 //--------------------------------------------------------
181 sal_Bool
OwnView_Impl::CreateModel( sal_Bool bUseNative
)
183 sal_Bool bResult
= sal_False
;
186 bResult
= CreateModelFromURL( bUseNative
? m_aNativeTempURL
: m_aTempFileURL
);
188 catch( uno::Exception
& )
195 //--------------------------------------------------------
196 OUString
OwnView_Impl::GetFilterNameFromExtentionAndInStream(
197 const ::com::sun::star::uno::Reference
< ::com::sun::star::lang::XMultiServiceFactory
>& xFactory
,
198 const OUString
& aNameWithExtention
,
199 const uno::Reference
< io::XInputStream
>& xInputStream
)
201 if ( !xInputStream
.is() )
202 throw uno::RuntimeException();
204 uno::Reference
< document::XTypeDetection
> xTypeDetection(
205 xFactory
->createInstance("com.sun.star.document.TypeDetection"),
206 uno::UNO_QUERY_THROW
);
210 if ( !aNameWithExtention
.isEmpty() )
212 OUString aURLToAnalyze
=
213 ( OUString( "file:///" ) + aNameWithExtention
);
214 aTypeName
= xTypeDetection
->queryTypeByURL( aURLToAnalyze
);
217 uno::Sequence
< beans::PropertyValue
> aArgs( aTypeName
.isEmpty() ? 2 : 3 );
218 aArgs
[0].Name
= "URL";
219 aArgs
[0].Value
<<= OUString( "private:stream" );
220 aArgs
[1].Name
= "InputStream";
221 aArgs
[1].Value
<<= xInputStream
;
222 if ( !aTypeName
.isEmpty() )
224 aArgs
[2].Name
= "TypeName";
225 aArgs
[2].Value
<<= aTypeName
;
228 aTypeName
= xTypeDetection
->queryTypeByDescriptor( aArgs
, sal_True
);
230 OUString aFilterName
;
231 for ( sal_Int32 nInd
= 0; nInd
< aArgs
.getLength(); nInd
++ )
232 if ( aArgs
[nInd
].Name
== "FilterName" )
233 aArgs
[nInd
].Value
>>= aFilterName
;
235 if ( aFilterName
.isEmpty() && !aTypeName
.isEmpty() )
237 // get the default filter name for the type
238 uno::Reference
< container::XNameAccess
> xNameAccess( xTypeDetection
, uno::UNO_QUERY_THROW
);
239 uno::Sequence
< beans::PropertyValue
> aTypes
;
241 if ( xNameAccess
.is() && ( xNameAccess
->getByName( aTypeName
) >>= aTypes
) )
243 for ( sal_Int32 nInd
= 0; nInd
< aTypes
.getLength(); nInd
++ )
245 if ( aTypes
[nInd
].Name
.startsWith( "PreferredFilter" ) && ( aTypes
[nInd
].Value
>>= aFilterName
) )
247 aTypes
[nInd
].Value
>>= aFilterName
;
257 //--------------------------------------------------------
258 sal_Bool
OwnView_Impl::ReadContentsAndGenerateTempFile( const uno::Reference
< io::XInputStream
>& xInStream
,
259 sal_Bool bParseHeader
)
261 uno::Reference
< io::XSeekable
> xSeekable( xInStream
, uno::UNO_QUERY_THROW
);
262 xSeekable
->seek( 0 );
264 // create m_aNativeTempURL
265 OUString aNativeTempURL
;
266 uno::Reference
< beans::XPropertySet
> xNativeTempFile(
267 io::TempFile::create(comphelper::getComponentContext(m_xFactory
)),
268 uno::UNO_QUERY_THROW
);
269 uno::Reference
< io::XStream
> xNativeTempStream( xNativeTempFile
, uno::UNO_QUERY_THROW
);
270 uno::Reference
< io::XOutputStream
> xNativeOutTemp
= xNativeTempStream
->getOutputStream();
271 uno::Reference
< io::XInputStream
> xNativeInTemp
= xNativeTempStream
->getInputStream();
272 if ( !xNativeOutTemp
.is() || !xNativeInTemp
.is() )
273 throw uno::RuntimeException();
276 xNativeTempFile
->setPropertyValue("RemoveFile", uno::makeAny( sal_False
) );
277 uno::Any aUrl
= xNativeTempFile
->getPropertyValue("Uri");
278 aUrl
>>= aNativeTempURL
;
280 catch ( uno::Exception
& )
284 sal_Bool bFailed
= sal_False
;
285 OUString aFileSuffix
;
289 uno::Sequence
< sal_Int8
> aReadSeq( 4 );
290 // read the complete size of the Object Package
291 if ( xInStream
->readBytes( aReadSeq
, 4 ) != 4 )
293 // read the first header ( have no idea what does this header mean )
294 if ( xInStream
->readBytes( aReadSeq
, 2 ) != 2 || aReadSeq
[0] != 2 || aReadSeq
[1] != 0 )
298 // only extension is interesting so only subset of symbols is accepted
301 if ( xInStream
->readBytes( aReadSeq
, 1 ) != 1 )
305 (aReadSeq
[0] >= '0' && aReadSeq
[0] <= '9') ||
306 (aReadSeq
[0] >= 'a' && aReadSeq
[0] <= 'z') ||
307 (aReadSeq
[0] >= 'A' && aReadSeq
[0] <= 'Z') ||
311 aFileSuffix
+= OUString( (sal_Unicode
) aReadSeq
[0] );
314 } while( aReadSeq
[0] );
319 if ( xInStream
->readBytes( aReadSeq
, 1 ) != 1 )
321 } while( aReadSeq
[0] );
323 // check the next header
324 if ( xInStream
->readBytes( aReadSeq
, 4 ) != 4
325 || aReadSeq
[0] || aReadSeq
[1] || aReadSeq
[2] != 3 || aReadSeq
[3] )
328 // get the size of the next entry
329 if ( xInStream
->readBytes( aReadSeq
, 4 ) != 4 )
332 sal_uInt32 nUrlSize
= (sal_uInt8
)aReadSeq
[0]
333 + (sal_uInt8
)aReadSeq
[1] * 0x100
334 + (sal_uInt8
)aReadSeq
[2] * 0x10000
335 + (sal_uInt8
)aReadSeq
[3] * 0x1000000;
336 sal_Int64 nTargetPos
= xSeekable
->getPosition() + nUrlSize
;
338 xSeekable
->seek( nTargetPos
);
340 // get the size of stored data
341 if ( xInStream
->readBytes( aReadSeq
, 4 ) != 4 )
344 sal_uInt32 nDataSize
= (sal_uInt8
)aReadSeq
[0]
345 + (sal_uInt8
)aReadSeq
[1] * 0x100
346 + (sal_uInt8
)aReadSeq
[2] * 0x10000
347 + (sal_uInt8
)aReadSeq
[3] * 0x1000000;
349 aReadSeq
.realloc( 32000 );
350 sal_uInt32 nRead
= 0;
351 while ( nRead
< nDataSize
)
353 sal_uInt32 nToRead
= ( nDataSize
- nRead
> 32000 ) ? 32000 : nDataSize
- nRead
;
354 sal_uInt32 nLocalRead
= xInStream
->readBytes( aReadSeq
, nToRead
);
362 else if ( nLocalRead
== 32000 )
363 xNativeOutTemp
->writeBytes( aReadSeq
);
366 uno::Sequence
< sal_Int8
> aToWrite( aReadSeq
);
367 aToWrite
.realloc( nLocalRead
);
368 xNativeOutTemp
->writeBytes( aToWrite
);
376 uno::Sequence
< sal_Int8
> aData( 8 );
377 if ( xInStream
->readBytes( aData
, 8 ) == 8
378 && aData
[0] == -1 && aData
[1] == -1 && aData
[2] == -1 && aData
[3] == -1
379 && ( aData
[4] == 2 || aData
[4] == 3 ) && aData
[5] == 0 && aData
[6] == 0 && aData
[7] == 0 )
381 // the header has to be removed
382 xSeekable
->seek( 40 );
386 // the usual Ole10Native format
387 xSeekable
->seek( 4 );
390 ::comphelper::OStorageHelper::CopyInputToOutput( xInStream
, xNativeOutTemp
);
393 xNativeOutTemp
->closeOutput();
395 // The temporary native file is created, now the filter must be detected
398 m_aFilterName
= GetFilterNameFromExtentionAndInStream( m_xFactory
, aFileSuffix
, xNativeInTemp
);
399 m_aNativeTempURL
= aNativeTempURL
;
405 //--------------------------------------------------------
406 void OwnView_Impl::CreateNative()
408 if ( !m_aNativeTempURL
.isEmpty() )
413 uno::Reference
< ucb::XSimpleFileAccess3
> xAccess(
414 ucb::SimpleFileAccess::create( comphelper::getComponentContext(m_xFactory
) ) );
416 uno::Reference
< io::XInputStream
> xInStream
= xAccess
->openFileRead( m_aTempFileURL
);
417 if ( !xInStream
.is() )
418 throw uno::RuntimeException();
420 uno::Sequence
< uno::Any
> aArgs( 1 );
421 aArgs
[0] <<= xInStream
;
422 uno::Reference
< container::XNameAccess
> xNameAccess(
423 m_xFactory
->createInstanceWithArguments(
424 OUString( "com.sun.star.embed.OLESimpleStorage" ),
426 uno::UNO_QUERY_THROW
);
428 OUString aSubStreamName
= "\1Ole10Native";
429 uno::Reference
< embed::XClassifiedObject
> xStor( xNameAccess
, uno::UNO_QUERY_THROW
);
430 uno::Sequence
< sal_Int8
> aStorClassID
= xStor
->getClassID();
432 if ( xNameAccess
->hasByName( aSubStreamName
) )
434 sal_uInt8 aClassID
[] =
435 { 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 };
436 uno::Sequence
< sal_Int8
> aPackageClassID( (sal_Int8
*)aClassID
, 16 );
438 uno::Reference
< io::XStream
> xSubStream
;
439 xNameAccess
->getByName( aSubStreamName
) >>= xSubStream
;
440 if ( xSubStream
.is() )
442 sal_Bool bOk
= sal_False
;
444 if ( MimeConfigurationHelper::ClassIDsEqual( aPackageClassID
, aStorClassID
) )
446 // the storage represents Object Package
448 bOk
= ReadContentsAndGenerateTempFile( xSubStream
->getInputStream(), sal_True
);
450 if ( !bOk
&& !m_aNativeTempURL
.isEmpty() )
452 KillFile_Impl( m_aNativeTempURL
, m_xFactory
);
453 m_aNativeTempURL
= OUString();
459 bOk
= ReadContentsAndGenerateTempFile( xSubStream
->getInputStream(), sal_False
);
461 if ( !bOk
&& !m_aNativeTempURL
.isEmpty() )
463 KillFile_Impl( m_aNativeTempURL
, m_xFactory
);
464 m_aNativeTempURL
= OUString();
471 // TODO/LATER: No native stream, needs a new solution
474 catch( uno::Exception
& )
478 //--------------------------------------------------------
479 sal_Bool
OwnView_Impl::Open()
481 sal_Bool bResult
= sal_False
;
483 uno::Reference
< frame::XModel
> xExistingModel
;
486 ::osl::MutexGuard
aGuard( m_aMutex
);
487 xExistingModel
= m_xModel
;
494 if ( xExistingModel
.is() )
497 uno::Reference
< frame::XController
> xController
= xExistingModel
->getCurrentController();
498 if ( xController
.is() )
500 uno::Reference
< frame::XFrame
> xFrame
= xController
->getFrame();
504 uno::Reference
<awt::XTopWindow
> xTopWindow( xFrame
->getContainerWindow(), uno::UNO_QUERY
);
506 xTopWindow
->toFront();
512 catch( uno::Exception
& )
518 bResult
= CreateModel( m_bUseNative
);
520 if ( !bResult
&& !m_bUseNative
)
522 // the original storage can not be recognized
523 if ( m_aNativeTempURL
.isEmpty() )
525 // create a temporary file for the native representation if there is no
529 if ( !m_aNativeTempURL
.isEmpty() )
531 bResult
= CreateModel( sal_True
);
533 m_bUseNative
= sal_True
;
543 //--------------------------------------------------------
544 void OwnView_Impl::Close()
546 uno::Reference
< frame::XModel
> xModel
;
549 ::osl::MutexGuard
aGuard( m_aMutex
);
550 if ( !m_xModel
.is() )
553 m_xModel
= uno::Reference
< frame::XModel
>();
562 uno::Reference
< document::XEventBroadcaster
> xBroadCaster( xModel
, uno::UNO_QUERY
);
563 if ( xBroadCaster
.is() )
564 xBroadCaster
->removeEventListener( uno::Reference
< document::XEventListener
>(
565 static_cast< ::cppu::OWeakObject
* >( this ),
568 uno::Reference
< util::XCloseable
> xCloseable( xModel
, uno::UNO_QUERY
);
569 if ( xCloseable
.is() )
571 xCloseable
->removeCloseListener( uno::Reference
< util::XCloseListener
>(
572 static_cast< ::cppu::OWeakObject
* >( this ),
574 xCloseable
->close( sal_True
);
577 catch( uno::Exception
& )
583 //--------------------------------------------------------
584 void SAL_CALL
OwnView_Impl::notifyEvent( const document::EventObject
& aEvent
)
585 throw ( uno::RuntimeException
)
588 uno::Reference
< frame::XModel
> xModel
;
591 ::osl::MutexGuard
aGuard( m_aMutex
);
592 if ( aEvent
.Source
== m_xModel
&& aEvent
.EventName
.startsWith( "OnSaveAsDone" ) )
594 // SaveAs operation took place, so just forget the model and deregister listeners
596 m_xModel
= uno::Reference
< frame::XModel
>();
603 uno::Reference
< document::XEventBroadcaster
> xBroadCaster( xModel
, uno::UNO_QUERY
);
604 if ( xBroadCaster
.is() )
605 xBroadCaster
->removeEventListener( uno::Reference
< document::XEventListener
>(
606 static_cast< ::cppu::OWeakObject
* >( this ),
609 uno::Reference
< util::XCloseable
> xCloseable( xModel
, uno::UNO_QUERY
);
610 if ( xCloseable
.is() )
611 xCloseable
->removeCloseListener( uno::Reference
< util::XCloseListener
>(
612 static_cast< ::cppu::OWeakObject
* >( this ),
615 catch( uno::Exception
& )
620 //--------------------------------------------------------
621 void SAL_CALL
OwnView_Impl::queryClosing( const lang::EventObject
&, sal_Bool
)
622 throw ( util::CloseVetoException
,
623 uno::RuntimeException
)
627 //--------------------------------------------------------
628 void SAL_CALL
OwnView_Impl::notifyClosing( const lang::EventObject
& Source
)
629 throw ( uno::RuntimeException
)
631 ::osl::MutexGuard
aGuard( m_aMutex
);
632 if ( Source
.Source
== m_xModel
)
633 m_xModel
= uno::Reference
< frame::XModel
>();
636 //--------------------------------------------------------
637 void SAL_CALL
OwnView_Impl::disposing( const lang::EventObject
& Source
)
638 throw (uno::RuntimeException
)
640 ::osl::MutexGuard
aGuard( m_aMutex
);
641 if ( Source
.Source
== m_xModel
)
642 m_xModel
= uno::Reference
< frame::XModel
>();
645 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */