LibreOffice Module embeddedobj (master) 1
ownview.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
32
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>
42
43#include "olepersist.hxx"
44#include "ownview.hxx"
45
46
47using namespace ::com::sun::star;
48using namespace ::comphelper;
49
50namespace {
51
52class DummyHandler_Impl : public ::cppu::WeakImplHelper< task::XInteractionHandler >
53{
54public:
55 DummyHandler_Impl() {}
56
57 virtual void SAL_CALL handle( const uno::Reference< task::XInteractionRequest >& xRequest ) override;
58};
59
60}
61
62void SAL_CALL DummyHandler_Impl::handle( const uno::Reference< task::XInteractionRequest >& )
63{
64}
65
66
67// Object viewer
68
69
70OwnView_Impl::OwnView_Impl( const uno::Reference< uno::XComponentContext >& xContext,
71 const uno::Reference< io::XInputStream >& xInputStream )
72: m_xContext( xContext )
73, m_bBusy( false )
74, m_bUseNative( false )
75{
76 if ( !xContext.is() || !xInputStream.is() )
77 throw uno::RuntimeException();
78
80}
81
82
84{
85 try {
87 } catch( uno::Exception& ) {}
88
89 try {
90 if ( !m_aNativeTempURL.isEmpty() )
92 } catch( uno::Exception& ) {}
93}
94
95
96bool OwnView_Impl::CreateModelFromURL( const OUString& aFileURL )
97{
98 bool bResult = false;
99
100 if ( !aFileURL.isEmpty() )
101 {
102 try {
103 uno::Reference < frame::XDesktop2 > xDocumentLoader = frame::Desktop::create(m_xContext);
104
105 uno::Sequence< beans::PropertyValue > aArgs( m_aFilterName.isEmpty() ? 4 : 5 );
106 auto pArgs = aArgs.getArray();
107
108 pArgs[0].Name = "URL";
109 pArgs[0].Value <<= aFileURL;
110
111 pArgs[1].Name = "ReadOnly";
112 pArgs[1].Value <<= true;
113
114 pArgs[2].Name = "InteractionHandler";
115 pArgs[2].Value <<= uno::Reference< task::XInteractionHandler >( new DummyHandler_Impl() );
116
117 pArgs[3].Name = "DontEdit";
118 pArgs[3].Value <<= true;
119
120 if ( !m_aFilterName.isEmpty() )
121 {
122 pArgs[4].Name = "FilterName";
123 pArgs[4].Value <<= m_aFilterName;
124 }
125
126 uno::Reference< frame::XModel > xModel( xDocumentLoader->loadComponentFromURL(
127 aFileURL,
128 "_blank",
129 0,
130 aArgs ),
131 uno::UNO_QUERY );
132
133 if ( xModel.is() )
134 {
135 uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY );
136 if ( xBroadCaster.is() )
137 xBroadCaster->addEventListener( uno::Reference< document::XEventListener >(this) );
138
139 uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY );
140 if ( xCloseable.is() )
141 {
142 xCloseable->addCloseListener( uno::Reference< util::XCloseListener >(this) );
143
144 ::osl::MutexGuard aGuard( m_aMutex );
146 bResult = true;
147 }
148 }
149 }
150 catch (uno::Exception const&)
151 {
152 TOOLS_WARN_EXCEPTION("embeddedobj.ole", "OwnView_Impl::CreateModelFromURL:");
153 }
154 }
155
156 return bResult;
157}
158
159
160bool OwnView_Impl::CreateModel( bool bUseNative )
161{
162 bool bResult = false;
163
164 try {
165 bResult = CreateModelFromURL( bUseNative ? m_aNativeTempURL : m_aTempFileURL );
166 }
167 catch( uno::Exception& )
168 {
169 }
170
171 return bResult;
172}
173
174
176 const css::uno::Reference< css::uno::XComponentContext >& xContext,
177 std::u16string_view aNameWithExtention,
178 const uno::Reference< io::XInputStream >& xInputStream )
179{
180 if ( !xInputStream.is() )
181 throw uno::RuntimeException();
182
183 uno::Reference< document::XTypeDetection > xTypeDetection(
184 xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext),
185 uno::UNO_QUERY_THROW );
186
187 OUString aTypeName;
188
189 if ( !aNameWithExtention.empty() )
190 {
191 OUString aURLToAnalyze = OUString::Concat("file:///") + aNameWithExtention;
192 aTypeName = xTypeDetection->queryTypeByURL( aURLToAnalyze );
193 }
194
195 uno::Sequence< beans::PropertyValue > aArgs( aTypeName.isEmpty() ? 2 : 3 );
196 auto pArgs = aArgs.getArray();
197 pArgs[0].Name = "URL";
198 pArgs[0].Value <<= OUString( "private:stream" );
199 pArgs[1].Name = "InputStream";
200 pArgs[1].Value <<= xInputStream;
201 if ( !aTypeName.isEmpty() )
202 {
203 pArgs[2].Name = "TypeName";
204 pArgs[2].Value <<= aTypeName;
205 }
206
207 aTypeName = xTypeDetection->queryTypeByDescriptor( aArgs, true );
208
209 OUString aFilterName;
210 for ( beans::PropertyValue const & prop : std::as_const(aArgs) )
211 if ( prop.Name == "FilterName" )
212 prop.Value >>= aFilterName;
213
214 if ( aFilterName.isEmpty() && !aTypeName.isEmpty() )
215 {
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;
219
220 if ( xNameAccess.is() && ( xNameAccess->getByName( aTypeName ) >>= aTypes ) )
221 {
222 for ( beans::PropertyValue const & prop : std::as_const(aTypes) )
223 {
224 if ( prop.Name == "PreferredFilter" && ( prop.Value >>= aFilterName ) )
225 {
226 prop.Value >>= aFilterName;
227 break;
228 }
229 }
230 }
231 }
232
233 return aFilterName;
234}
235
236
237bool OwnView_Impl::ReadContentsAndGenerateTempFile( const uno::Reference< io::XInputStream >& xInStream,
238 bool bParseHeader )
239{
240 uno::Reference< io::XSeekable > xSeekable( xInStream, uno::UNO_QUERY_THROW );
241 xSeekable->seek( 0 );
242
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();
252
253 try {
254 xNativeTempFile->setRemoveFile( false );
255 aNativeTempURL = xNativeTempFile->getUri();
256 }
257 catch ( uno::Exception& )
258 {
259 }
260
261 bool bFailed = false;
262 OUString aFileSuffix;
263
264 if ( bParseHeader )
265 {
266 uno::Sequence< sal_Int8 > aReadSeq( 4 );
267 // read the complete size of the Object Package
268 if ( xInStream->readBytes( aReadSeq, 4 ) != 4 )
269 return false;
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 )
272 return false;
273
274 // read file name
275 // only extension is interesting so only subset of symbols is accepted
276 do
277 {
278 if ( xInStream->readBytes( aReadSeq, 1 ) != 1 )
279 return false;
280
281 if (
282 (aReadSeq[0] >= '0' && aReadSeq[0] <= '9') ||
283 (aReadSeq[0] >= 'a' && aReadSeq[0] <= 'z') ||
284 (aReadSeq[0] >= 'A' && aReadSeq[0] <= 'Z') ||
285 aReadSeq[0] == '.'
286 )
287 {
288 aFileSuffix += OUStringChar( sal_Unicode(aReadSeq[0]) );
289 }
290
291 } while( aReadSeq[0] );
292
293 // skip url
294 do
295 {
296 if ( xInStream->readBytes( aReadSeq, 1 ) != 1 )
297 return false;
298 } while( aReadSeq[0] );
299
300 // check the next header
301 if ( xInStream->readBytes( aReadSeq, 4 ) != 4
302 || aReadSeq[0] || aReadSeq[1] || aReadSeq[2] != 3 || aReadSeq[3] )
303 return false;
304
305 // get the size of the next entry
306 if ( xInStream->readBytes( aReadSeq, 4 ) != 4 )
307 return false;
308
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;
314
315 xSeekable->seek( nTargetPos );
316
317 // get the size of stored data
318 if ( xInStream->readBytes( aReadSeq, 4 ) != 4 )
319 return false;
320
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;
325
326 aReadSeq.realloc( 32000 );
327 sal_uInt32 nRead = 0;
328 while ( nRead < nDataSize )
329 {
330 sal_uInt32 nToRead = std::min<sal_uInt32>( nDataSize - nRead, 32000 );
331 sal_uInt32 nLocalRead = xInStream->readBytes( aReadSeq, nToRead );
332
333
334 if ( !nLocalRead )
335 {
336 bFailed = true;
337 break;
338 }
339 else if ( nLocalRead == 32000 )
340 xNativeOutTemp->writeBytes( aReadSeq );
341 else
342 {
343 uno::Sequence< sal_Int8 > aToWrite( aReadSeq );
344 aToWrite.realloc( nLocalRead );
345 xNativeOutTemp->writeBytes( aToWrite );
346 }
347
348 nRead += nLocalRead;
349 }
350 }
351 else
352 {
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 )
357 {
358 // the header has to be removed
359 xSeekable->seek( 40 );
360 }
361 else
362 {
363 // the usual Ole10Native format
364 xSeekable->seek( 4 );
365 }
366
367 ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xNativeOutTemp );
368 }
369
370 xNativeOutTemp->closeOutput();
371
372 // The temporary native file is created, now the filter must be detected
373 if ( !bFailed )
374 {
375 m_aFilterName = GetFilterNameFromExtentionAndInStream( m_xContext, aFileSuffix, xNativeInTemp );
376 m_aNativeTempURL = aNativeTempURL;
377 }
378
379 return !bFailed;
380}
381
382
384{
385 if ( !m_aNativeTempURL.isEmpty() )
386 return;
387
388 try
389 {
390 uno::Reference < ucb::XSimpleFileAccess3 > xAccess(
391 ucb::SimpleFileAccess::create( m_xContext ) );
392
393 uno::Reference< io::XInputStream > xInStream = xAccess->openFileRead( m_aTempFileURL );
394 if ( !xInStream.is() )
395 throw uno::RuntimeException();
396
397 uno::Sequence< uno::Any > aArgs{ uno::Any(xInStream) };
398 uno::Reference< container::XNameAccess > xNameAccess(
399 m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
400 "com.sun.star.embed.OLESimpleStorage",
401 aArgs, m_xContext ),
402 uno::UNO_QUERY_THROW );
403
404 OUString aSubStreamName = "\1Ole10Native";
405 uno::Reference< embed::XClassifiedObject > xStor( xNameAccess, uno::UNO_QUERY_THROW );
406 uno::Sequence< sal_Int8 > aStorClassID = xStor->getClassID();
407
408 if ( xNameAccess->hasByName( aSubStreamName ) )
409 {
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 );
414
415 uno::Reference< io::XStream > xSubStream;
416 xNameAccess->getByName( aSubStreamName ) >>= xSubStream;
417 if ( xSubStream.is() )
418 {
419 bool bOk = false;
420
421 if ( MimeConfigurationHelper::ClassIDsEqual( aPackageClassID, aStorClassID ) )
422 {
423 // the storage represents Object Package
424
425 bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), true );
426
427 if ( !bOk && !m_aNativeTempURL.isEmpty() )
428 {
430 m_aNativeTempURL.clear();
431 }
432 }
433
434 if ( !bOk )
435 {
436 bOk = ReadContentsAndGenerateTempFile( xSubStream->getInputStream(), false );
437
438 if ( !bOk && !m_aNativeTempURL.isEmpty() )
439 {
441 m_aNativeTempURL.clear();
442 }
443 }
444 }
445 }
446 else
447 {
448 // TODO/LATER: No native stream, needs a new solution
449 }
450 }
451 catch( uno::Exception& )
452 {}
453}
454
455
457{
458 bool bResult = false;
459
460 uno::Reference< frame::XModel > xExistingModel;
461
462 {
463 ::osl::MutexGuard aGuard( m_aMutex );
464 xExistingModel = m_xModel;
465 if ( m_bBusy )
466 return false;
467
468 m_bBusy = true;
469 }
470
471 if ( xExistingModel.is() )
472 {
473 try {
474 uno::Reference< frame::XController > xController = xExistingModel->getCurrentController();
475 if ( xController.is() )
476 {
477 uno::Reference< frame::XFrame > xFrame = xController->getFrame();
478 if ( xFrame.is() )
479 {
480 xFrame->activate();
481 uno::Reference<awt::XTopWindow> xTopWindow( xFrame->getContainerWindow(), uno::UNO_QUERY );
482 if(xTopWindow.is())
483 xTopWindow->toFront();
484
485 bResult = true;
486 }
487 }
488 }
489 catch( uno::Exception& )
490 {
491 }
492 }
493 else
494 {
495 bResult = CreateModel( m_bUseNative );
496
497 if ( !bResult && !m_bUseNative )
498 {
499 // the original storage can not be recognized
500 if ( m_aNativeTempURL.isEmpty() )
501 {
502 // create a temporary file for the native representation if there is no
503 CreateNative();
504 }
505
506 if ( !m_aNativeTempURL.isEmpty() )
507 {
508 bResult = CreateModel( true );
509 if ( bResult )
510 m_bUseNative = true;
511 }
512 }
513 }
514
515 m_bBusy = false;
516
517 return bResult;
518}
519
520
522{
523 uno::Reference< frame::XModel > xModel;
524
525 {
526 ::osl::MutexGuard aGuard( m_aMutex );
527 if ( !m_xModel.is() )
528 return;
530 m_xModel.clear();
531
532 if ( m_bBusy )
533 return;
534
535 m_bBusy = true;
536 }
537
538 try {
539 uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY );
540 if ( xBroadCaster.is() )
541 xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) );
542
543 uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY );
544 if ( xCloseable.is() )
545 {
546 xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) );
547 xCloseable->close( true );
548 }
549 }
550 catch( uno::Exception& )
551 {}
552
553 m_bBusy = false;
554}
555
556
557void SAL_CALL OwnView_Impl::notifyEvent( const document::EventObject& aEvent )
558{
559
560 uno::Reference< frame::XModel > xModel;
561
562 {
563 ::osl::MutexGuard aGuard( m_aMutex );
564 if ( aEvent.Source == m_xModel && aEvent.EventName == "OnSaveAsDone" )
565 {
566 // SaveAs operation took place, so just forget the model and deregister listeners
568 m_xModel.clear();
569 }
570 }
571
572 if ( !xModel.is() )
573 return;
574
575 try {
576 uno::Reference< document::XEventBroadcaster > xBroadCaster( xModel, uno::UNO_QUERY );
577 if ( xBroadCaster.is() )
578 xBroadCaster->removeEventListener( uno::Reference< document::XEventListener >( this ) );
579
580 uno::Reference< util::XCloseable > xCloseable( xModel, uno::UNO_QUERY );
581 if ( xCloseable.is() )
582 xCloseable->removeCloseListener( uno::Reference< util::XCloseListener >( this ) );
583 }
584 catch( uno::Exception& )
585 {}
586}
587
588
589void SAL_CALL OwnView_Impl::queryClosing( const lang::EventObject&, sal_Bool )
590{
591}
592
593
594void SAL_CALL OwnView_Impl::notifyClosing( const lang::EventObject& Source )
595{
596 ::osl::MutexGuard aGuard( m_aMutex );
597 if ( Source.Source == m_xModel )
598 m_xModel.clear();
599}
600
601
602void SAL_CALL OwnView_Impl::disposing( const lang::EventObject& Source )
603{
604 ::osl::MutexGuard aGuard( m_aMutex );
605 if ( Source.Source == m_xModel )
606 m_xModel.clear();
607};
608
609/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
AnyEventRef aEvent
bool m_bBusy
Definition: ownview.hxx:48
bool CreateModel(bool bUseNative)
Definition: ownview.cxx:160
OwnView_Impl(const css::uno::Reference< css::uno::XComponentContext > &xContext, const css::uno::Reference< css::io::XInputStream > &xStream)
Definition: ownview.cxx:70
bool Open()
Definition: ownview.cxx:456
virtual void SAL_CALL notifyEvent(const css::document::EventObject &Event) override
Definition: ownview.cxx:557
void Close()
Definition: ownview.cxx:521
bool m_bUseNative
Definition: ownview.hxx:50
bool CreateModelFromURL(const OUString &aFileURL)
Definition: ownview.cxx:96
void CreateNative()
Definition: ownview.cxx:383
static OUString GetFilterNameFromExtentionAndInStream(const css::uno::Reference< css::uno::XComponentContext > &xContext, std::u16string_view aNameWithExtention, const css::uno::Reference< css::io::XInputStream > &xInputStream)
Definition: ownview.cxx:175
::osl::Mutex m_aMutex
Definition: ownview.hxx:38
css::uno::Reference< css::uno::XComponentContext > m_xContext
Definition: ownview.hxx:40
OUString m_aTempFileURL
Definition: ownview.hxx:43
css::uno::Reference< css::frame::XModel > m_xModel
Definition: ownview.hxx:41
OUString m_aNativeTempURL
Definition: ownview.hxx:44
virtual void SAL_CALL notifyClosing(const css::lang::EventObject &Source) override
Definition: ownview.cxx:594
virtual ~OwnView_Impl() override
Definition: ownview.cxx:83
OUString m_aFilterName
Definition: ownview.hxx:46
virtual void SAL_CALL queryClosing(const css::lang::EventObject &Source, sal_Bool GetsOwnership) override
Definition: ownview.cxx:589
bool ReadContentsAndGenerateTempFile(const css::uno::Reference< css::io::XInputStream > &xStream, bool bParseHeader)
Definition: ownview.cxx:237
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
Definition: ownview.cxx:602
static void CopyInputToOutput(const css::uno::Reference< css::io::XInputStream > &xInput, const css::uno::Reference< css::io::XOutputStream > &xOutput)
#define TOOLS_WARN_EXCEPTION(area, stream)
constexpr OUStringLiteral aData
bool KillFile_Impl(const OUString &aURL, const uno::Reference< uno::XComponentContext > &xContext)
Definition: olepersist.cxx:62
OUString GetNewFilledTempFile_Impl(const uno::Reference< io::XInputStream > &xInStream, const uno::Reference< uno::XComponentContext > &xContext)
Definition: olepersist.cxx:110
const char *const aClassID
Reference< XController > xController
Reference< XFrame > xFrame
Reference< XModel > xModel
unsigned char sal_uInt8
unsigned char sal_Bool
sal_uInt16 sal_Unicode
signed char sal_Int8
const SvXMLTokenMapEntry aTypes[]