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 <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <embeddoc.hxx>
25 #include <com/sun/star/uno/Exception.hpp>
26 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
27 #include <com/sun/star/lang/XComponent.hpp>
28 #include <com/sun/star/io/TempFile.hpp>
29 #include <com/sun/star/io/XInputStream.hpp>
30 #include <com/sun/star/io/XOutputStream.hpp>
31 #include <com/sun/star/io/XSeekable.hpp>
32 #include <com/sun/star/frame/XModel.hpp>
33 #include <com/sun/star/frame/XLoadable.hpp>
34 #include <com/sun/star/util/XModifiable.hpp>
35 #include <com/sun/star/frame/XStorable.hpp>
36 #include <com/sun/star/util/URLTransformer.hpp>
37 #include <com/sun/star/util/XURLTransformer.hpp>
38 
39 #include <comphelper/processfactory.hxx>
40 #include <o3tl/char16_t2wchar_t.hxx>
41 #include <osl/mutex.hxx>
42 #include <osl/diagnose.h>
43 #include <sal/types.h>
44 
45 #include "guid.hxx"
46 
47 #include <string.h>
48 
49 #define EXT_STREAM_LENGTH 4
50 
51 namespace {
52 
53 const sal_Int32 nConstBufferSize = 32000;
54 
55 }
56 
57 using namespace ::com::sun::star;
58 
59 const wchar_t aOfficeEmbedStreamName[] = L"package_stream";
60 const wchar_t aExtentStreamName[] = L"properties_stream";
61 
createTempXInStreamFromIStream(uno::Reference<lang::XMultiServiceFactory> const & xFactory,IStream * pStream)62 static uno::Reference< io::XInputStream > createTempXInStreamFromIStream(
63                                         uno::Reference< lang::XMultiServiceFactory > const & xFactory,
64                                         IStream *pStream )
65 {
66     uno::Reference< io::XInputStream > xResult;
67 
68     if ( !pStream )
69         return xResult;
70 
71     uno::Reference < io::XOutputStream > xTempOut( io::TempFile::create(comphelper::getComponentContext(xFactory)),
72                                                             uno::UNO_QUERY_THROW );
73     ULARGE_INTEGER nNewPos;
74     LARGE_INTEGER const aZero = { 0, 0 };
75     HRESULT hr = pStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos );
76     if ( FAILED( hr ) ) return xResult;
77 
78     STATSTG aStat;
79     hr = pStream->Stat( &aStat, STATFLAG_NONAME );
80     if ( FAILED( hr ) ) return xResult;
81 
82     sal_uInt32 nSize = static_cast<sal_uInt32>(aStat.cbSize.QuadPart);
83     sal_uInt32 nCopied = 0;
84     uno::Sequence< sal_Int8 > aBuffer( nConstBufferSize );
85     try
86     {
87         sal_uInt32 nRead = 0;
88         do
89         {
90             pStream->Read( aBuffer.getArray(), nConstBufferSize, &nRead );
91 
92             if ( nRead < nConstBufferSize )
93                 aBuffer.realloc( nRead );
94 
95             xTempOut->writeBytes( aBuffer );
96             nCopied += nRead;
97         } while( nRead == nConstBufferSize );
98 
99         if ( nCopied == nSize )
100         {
101             uno::Reference < io::XSeekable > xTempSeek ( xTempOut, uno::UNO_QUERY );
102             if ( xTempSeek.is() )
103             {
104                 xTempSeek->seek ( 0 );
105                 xResult.set( xTempOut, uno::UNO_QUERY );
106             }
107         }
108     }
109     catch( const uno::Exception& )
110     {
111     }
112 
113     return xResult;
114 }
115 
copyXTempOutToIStream(uno::Reference<io::XOutputStream> const & xTempOut,IStream * pStream)116 static HRESULT copyXTempOutToIStream( uno::Reference< io::XOutputStream > const & xTempOut, IStream* pStream )
117 {
118     if ( !xTempOut.is() || !pStream )
119         return E_FAIL;
120 
121     uno::Reference < io::XSeekable > xTempSeek ( xTempOut, uno::UNO_QUERY );
122     if ( !xTempSeek.is() )
123         return E_FAIL;
124 
125     xTempSeek->seek ( 0 );
126 
127     uno::Reference< io::XInputStream > xTempIn ( xTempOut, uno::UNO_QUERY );
128     if ( !xTempSeek.is() )
129         return E_FAIL;
130 
131     // Seek to zero and truncate the stream
132     ULARGE_INTEGER nNewPos;
133     LARGE_INTEGER const aZero = { 0, 0 };
134     HRESULT hr = pStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos );
135     if ( FAILED( hr ) ) return E_FAIL;
136     ULARGE_INTEGER const aUZero = { 0, 0 };
137     hr = pStream->SetSize( aUZero );
138     if ( FAILED( hr ) ) return E_FAIL;
139 
140     uno::Sequence< sal_Int8 > aBuffer( nConstBufferSize );
141     sal_uInt32 nReadBytes = 0;
142 
143     do
144     {
145         try {
146             nReadBytes = xTempIn->readBytes( aBuffer, nConstBufferSize );
147         }
148         catch( const uno::Exception& )
149         {
150             return E_FAIL;
151         }
152 
153         sal_uInt32 nWritten = 0;
154         hr = pStream->Write( aBuffer.getArray(), nReadBytes, &nWritten );
155         if ( !SUCCEEDED( hr ) || nWritten != nReadBytes )
156             return E_FAIL;
157 
158     } while( nReadBytes == nConstBufferSize );
159 
160     return S_OK;
161 }
162 
163 
164 // EmbedDocument_Impl
165 
166 
EmbedDocument_Impl(const uno::Reference<lang::XMultiServiceFactory> & xFactory,const GUID * guid)167 EmbedDocument_Impl::EmbedDocument_Impl( const uno::Reference< lang::XMultiServiceFactory >& xFactory, const GUID* guid )
168 : m_refCount( 0 )
169 , m_xFactory( xFactory )
170 , m_guid( *guid )
171 , m_bIsDirty( false )
172 , m_nAdviseNum( 0 )
173 , m_bIsInVerbHandling( false )
174 //, m_bLoadedFromFile( sal_False )
175 {
176     m_xOwnAccess = new EmbeddedDocumentInstanceAccess_Impl( this );
177     m_pDocHolder = new DocumentHolder( xFactory, m_xOwnAccess );
178 }
179 
~EmbedDocument_Impl()180 EmbedDocument_Impl::~EmbedDocument_Impl()
181 {
182     m_pDocHolder->FreeOffice();
183 
184     if ( m_pDocHolder->HasFrame() && m_pDocHolder->IsLink() )
185     {
186         // a link with frame should be only disconnected, not closed
187         m_pDocHolder->DisconnectFrameDocument( true );
188     }
189     else
190     {
191         m_pDocHolder->CloseDocument();
192         m_pDocHolder->CloseFrame();
193     }
194 }
195 
fillArgsForLoading_Impl(uno::Reference<io::XInputStream> const & xStream,DWORD,LPCOLESTR pFilePath)196 uno::Sequence< beans::PropertyValue > EmbedDocument_Impl::fillArgsForLoading_Impl( uno::Reference< io::XInputStream > const & xStream, DWORD /*nStreamMode*/, LPCOLESTR pFilePath )
197 {
198     uno::Sequence< beans::PropertyValue > aArgs( 3 );
199 
200     sal_Int32 nInd = 0; // must not be bigger than the preset size
201     aArgs[nInd].Name = "FilterName";
202     aArgs[nInd++].Value <<= getFilterNameFromGUID_Impl( &m_guid );
203 
204     if ( xStream.is() )
205     {
206         aArgs[nInd].Name = "InputStream";
207         aArgs[nInd++].Value <<= xStream;
208         aArgs[nInd].Name = "URL";
209         aArgs[nInd++].Value <<= OUString( "private:stream" );
210     }
211     else
212     {
213         aArgs[nInd].Name = "URL";
214 
215         OUString sDocUrl;
216         if ( pFilePath )
217         {
218             uno::Reference< util::XURLTransformer > aTransformer( util::URLTransformer::create(comphelper::getComponentContext(m_xFactory)) );
219             util::URL aURL;
220 
221             aURL.Complete = o3tl::toU(pFilePath);
222 
223             if ( aTransformer->parseSmart( aURL, OUString() ) )
224                 sDocUrl = aURL.Complete;
225         }
226 
227         aArgs[nInd++].Value <<= sDocUrl;
228     }
229 
230     aArgs.realloc( nInd );
231 
232     // aArgs[].Name = "ReadOnly";
233     // aArgs[].Value <<= sal_False; //( ( nStreamMode & ( STGM_READWRITE | STGM_WRITE ) ) ? sal_True : sal_False );
234 
235     return aArgs;
236 }
237 
fillArgsForStoring_Impl(uno::Reference<io::XOutputStream> const & xStream)238 uno::Sequence< beans::PropertyValue > EmbedDocument_Impl::fillArgsForStoring_Impl( uno::Reference< io::XOutputStream > const & xStream)
239 {
240     uno::Sequence< beans::PropertyValue > aArgs( xStream.is() ? 2 : 1 );
241 
242     aArgs[0].Name = "FilterName";
243     aArgs[0].Value <<= getFilterNameFromGUID_Impl( &m_guid );
244 
245     if ( xStream.is() )
246     {
247         aArgs[1].Name = "OutputStream";
248         aArgs[1].Value <<= xStream;
249     }
250 
251     return aArgs;
252 }
253 
SaveTo_Impl(IStorage * pStg)254 HRESULT EmbedDocument_Impl::SaveTo_Impl( IStorage* pStg )
255 {
256     if ( !pStg || pStg == m_pMasterStorage )
257         return E_FAIL;
258 
259     // for saveto operation the master storage
260     // should not enter NoScribble mode
261     CComPtr< IStream > pOrigOwn = m_pOwnStream;
262     CComPtr< IStream > pOrigExt = m_pExtStream;
263     HRESULT hr = Save( pStg, false );
264     pStg->Commit( STGC_ONLYIFCURRENT );
265     m_pOwnStream = pOrigOwn;
266     m_pExtStream = pOrigExt;
267 
268     return hr;
269 }
270 
271 
272 // IUnknown
273 
QueryInterface(REFIID riid,void ** ppv)274 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::QueryInterface(REFIID riid, void** ppv)
275 {
276     if(IsEqualIID(riid, IID_IUnknown))
277     {
278         AddRef();
279         *ppv = static_cast<IUnknown*>(static_cast<IPersistStorage*>(this));
280         return S_OK;
281     }
282     else if (IsEqualIID(riid, IID_IPersist))
283     {
284         AddRef();
285         *ppv = static_cast<IPersist*>(static_cast<IPersistStorage*>(this));
286         return S_OK;
287     }
288     else if (IsEqualIID(riid, IID_IExternalConnection))
289     {
290         AddRef();
291         *ppv = static_cast<IExternalConnection*>(this);
292         return S_OK;
293     }
294     else if (IsEqualIID(riid, IID_IPersistStorage))
295     {
296         AddRef();
297         *ppv = static_cast<IPersistStorage*>(this);
298         return S_OK;
299     }
300     else if (IsEqualIID(riid, IID_IDataObject))
301     {
302         AddRef();
303         *ppv = static_cast<IDataObject*>(this);
304         return S_OK;
305     }
306     else if (IsEqualIID(riid, IID_IOleObject))
307     {
308         AddRef();
309         *ppv = static_cast<IOleObject*>(this);
310         return S_OK;
311     }
312     else if (IsEqualIID(riid, IID_IOleWindow))
313     {
314         AddRef();
315         *ppv = static_cast<IOleWindow*>(this);
316         return S_OK;
317     }
318     else if (IsEqualIID(riid, IID_IOleInPlaceObject))
319     {
320         AddRef();
321         *ppv = static_cast<IOleInPlaceObject*>(this);
322         return S_OK;
323     }
324     else if (IsEqualIID(riid, IID_IPersistFile))
325     {
326         AddRef();
327         *ppv = static_cast<IPersistFile*>(this);
328         return S_OK;
329     }
330     else if (IsEqualIID(riid, IID_IDispatch))
331     {
332         AddRef();
333         *ppv = static_cast<IDispatch*>(this);
334         return S_OK;
335     }
336 
337     *ppv = nullptr;
338     return ResultFromScode(E_NOINTERFACE);
339 }
340 
STDMETHODIMP_(ULONG)341 COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) EmbedDocument_Impl::AddRef()
342 {
343     return osl_atomic_increment( &m_refCount);
344 }
345 
STDMETHODIMP_(ULONG)346 COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) EmbedDocument_Impl::Release()
347 {
348     // if there is a time when the last reference is destructed, that means that only internal pointers are alive
349     // after the following call either the refcount is increased or the pointers are empty
350     if ( m_refCount == 1 )
351         m_xOwnAccess->ClearEmbedDocument();
352 
353     sal_Int32 nCount = osl_atomic_decrement( &m_refCount );
354     if ( nCount == 0 )
355         delete this;
356     return nCount;
357 }
358 
359 
360 // IPersist
361 
GetClassID(CLSID * pClassId)362 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetClassID( CLSID* pClassId )
363 {
364     *pClassId = m_guid;
365     return S_OK;
366 }
367 
368 
369 // IPersistStorage
370 
IsDirty()371 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::IsDirty()
372 {
373     // the link modified state is controlled by the document
374     if ( m_bIsDirty && !m_aFileName.getLength() )
375         return S_OK;
376 
377     uno::Reference< util::XModifiable > xMod( m_pDocHolder->GetDocument(), uno::UNO_QUERY );
378     if ( xMod.is() )
379         return xMod->isModified() ? S_OK : S_FALSE;
380     return S_FALSE;
381 }
382 
InitNew(IStorage * pStg)383 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::InitNew( IStorage *pStg )
384 {
385     HRESULT hr = CO_E_ALREADYINITIALIZED;
386 
387     if ( !m_pDocHolder->GetDocument().is() )
388     {
389 
390         STATSTG aStat;
391         hr = pStg->Stat( &aStat, STATFLAG_NONAME );
392         if ( FAILED( hr ) ) return E_FAIL;
393 
394         DWORD nStreamMode = aStat.grfMode;
395 
396         hr = E_FAIL;
397         if ( m_xFactory.is() && pStg )
398         {
399             uno::Reference< frame::XModel > aDocument(
400                             m_xFactory->createInstance( OUString(getServiceNameFromGUID_Impl( &m_guid )) ),
401                             uno::UNO_QUERY );
402             if ( aDocument.is() )
403             {
404                 m_pDocHolder->SetDocument( aDocument );
405 
406                 uno::Reference< frame::XLoadable > xLoadable( m_pDocHolder->GetDocument(), uno::UNO_QUERY );
407                 if( xLoadable.is() )
408                 {
409                     try
410                     {
411                         xLoadable->initNew();
412                         // xLoadable->load( fillArgsForLoading_Impl( uno::Reference< io::XInputStream >(), nStreamMode ) );
413                         hr = S_OK;
414                     }
415                     catch( const uno::Exception& )
416                     {
417                     }
418                 }
419 
420                 if ( hr == S_OK )
421                 {
422                     wchar_t const * aCurType = getStorageTypeFromGUID_Impl( &m_guid ); // ???
423                     CLIPFORMAT cf = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" ));
424                     hr = WriteFmtUserTypeStg( pStg,
425                                             cf,                         // ???
426                                             const_cast<wchar_t *>(aCurType) );
427 
428                     if ( hr == S_OK )
429                     {
430                         hr = pStg->CreateStream( aOfficeEmbedStreamName,
431                                                  STGM_CREATE | ( nStreamMode & 0x73 ),
432                                                  0,
433                                                  0,
434                                                  &m_pOwnStream );
435 
436                         if ( hr == S_OK && m_pOwnStream )
437                         {
438                             hr = pStg->CreateStream( aExtentStreamName,
439                                                      STGM_CREATE | ( nStreamMode & 0x73 ),
440                                                      0,
441                                                      0,
442                                                      &m_pExtStream );
443 
444                             if ( hr == S_OK && m_pExtStream )
445                             {
446 
447                                 m_pMasterStorage = pStg;
448                                 m_bIsDirty = true;
449                             }
450                             else
451                                 hr = E_FAIL;
452                         }
453                         else
454                             hr = E_FAIL;
455                     }
456                     else
457                         hr = E_FAIL;
458                 }
459 
460                 if ( hr != S_OK )
461                     m_pDocHolder->CloseDocument();
462             }
463         }
464     }
465 
466     return hr;
467 }
468 
Load(IStorage * pStg)469 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Load( IStorage *pStg )
470 {
471     if ( m_pDocHolder->GetDocument().is() )
472         return CO_E_ALREADYINITIALIZED;
473 
474     if ( !m_xFactory.is() || !pStg )
475         return E_FAIL;
476 
477     HRESULT hr = E_FAIL;
478 
479     STATSTG aStat;
480     hr = pStg->Stat( &aStat, STATFLAG_NONAME );
481     if ( FAILED( hr ) ) return E_FAIL;
482 
483     DWORD nStreamMode = aStat.grfMode;
484     hr = pStg->OpenStream( aOfficeEmbedStreamName,
485                             nullptr,
486                             nStreamMode & 0x73,
487                             0,
488                             &m_pOwnStream );
489     if ( !m_pOwnStream ) hr = E_FAIL;
490 
491     if ( SUCCEEDED( hr ) )
492     {
493         hr = pStg->OpenStream( aExtentStreamName,
494                                 nullptr,
495                                 nStreamMode & 0x73,
496                                 0,
497                                 &m_pExtStream );
498         if ( !m_pExtStream ) hr = E_FAIL;
499     }
500 
501     // RECTL aRectToSet;
502     SIZEL aSizeToSet;
503     if ( SUCCEEDED( hr ) )
504     {
505         ULARGE_INTEGER nNewPos;
506         LARGE_INTEGER const aZero = { 0, 0 };
507         hr = m_pExtStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos );
508         if ( SUCCEEDED( hr ) )
509         {
510             sal_uInt32 nRead;
511             sal_Int32 aInf[EXT_STREAM_LENGTH];
512             hr = m_pExtStream->Read( aInf, sizeof aInf, &nRead );
513             if ( nRead != sizeof aInf ) hr = E_FAIL;
514 
515             if ( SUCCEEDED( hr ) )
516             {
517                 // aRectToSet.left = *((sal_Int32*)aInf);
518                 // aRectToSet.top = *((sal_Int32*)&aInf[4]);
519                 // aRectToSet.right = *((sal_Int32*)&aInf[8]);
520                 // aRectToSet.bottom = *((sal_Int32*)&aInf[12]);
521                 aSizeToSet.cx = aInf[2] - aInf[0];
522                 aSizeToSet.cy = aInf[3] - aInf[1];
523             }
524         }
525     }
526 
527     if ( SUCCEEDED( hr ) )
528     {
529         hr = E_FAIL;
530 
531         uno::Reference < io::XInputStream > xTempIn = createTempXInStreamFromIStream( m_xFactory, m_pOwnStream );
532         if ( xTempIn.is() )
533         {
534             uno::Reference< frame::XModel > aDocument(
535                                                 m_xFactory->createInstance( OUString(getServiceNameFromGUID_Impl( &m_guid )) ),
536                                                 uno::UNO_QUERY );
537             if ( aDocument.is() )
538             {
539                 m_pDocHolder->SetDocument( aDocument );
540 
541                 uno::Reference< frame::XLoadable > xLoadable( m_pDocHolder->GetDocument(), uno::UNO_QUERY );
542                 if( xLoadable.is() )
543                 {
544                     try
545                     {
546                         xLoadable->load( fillArgsForLoading_Impl( xTempIn, nStreamMode ) );
547                         m_pMasterStorage = pStg;
548                         hr = m_pDocHolder->SetExtent( &aSizeToSet );
549                         // hr = m_pDocHolder->SetVisArea( &aRectToSet );
550                     }
551                     catch( const uno::Exception& )
552                     {
553                     }
554                 }
555 
556                 if ( FAILED( hr ) )
557                     m_pDocHolder->CloseDocument();
558             }
559         }
560     }
561 
562     if ( FAILED( hr ) )
563     {
564         m_pOwnStream = CComPtr< IStream >();
565         m_pExtStream = CComPtr< IStream >();
566         hr = pStg->DestroyElement( aOfficeEmbedStreamName );
567         hr = pStg->DestroyElement( aExtentStreamName );
568 
569         OSL_ENSURE( SUCCEEDED( hr ), "Can not destroy created stream!" );
570         if ( FAILED( hr ) )
571             hr = E_FAIL;
572     }
573 
574     return hr;
575 }
576 
Save(IStorage * pStgSave,BOOL fSameAsLoad)577 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Save( IStorage *pStgSave, BOOL fSameAsLoad )
578 {
579     if ( !m_pDocHolder->GetDocument().is() || !m_xFactory.is() || !pStgSave || !m_pOwnStream || !m_pExtStream )
580         return E_FAIL;
581 
582     CComPtr< IStream > pTargetStream;
583     CComPtr< IStream > pNewExtStream;
584 
585     if ( !fSameAsLoad && pStgSave != m_pMasterStorage )
586     {
587         OSL_ENSURE( m_pMasterStorage, "How could the document be initialized without storage!??" );
588         HRESULT hr = m_pMasterStorage->CopyTo( NULL, nullptr, nullptr, pStgSave );
589         if ( FAILED( hr ) ) return E_FAIL;
590 
591         STATSTG aStat;
592         hr = pStgSave->Stat( &aStat, STATFLAG_NONAME );
593         if ( FAILED( hr ) ) return E_FAIL;
594 
595         DWORD nStreamMode = aStat.grfMode;
596         hr = pStgSave->CreateStream( aOfficeEmbedStreamName,
597                                  STGM_CREATE | ( nStreamMode & 0x73 ),
598                                 0,
599                                  0,
600                                  &pTargetStream );
601         if ( FAILED( hr ) || !pTargetStream ) return E_FAIL;
602 
603         hr = pStgSave->CreateStream( aExtentStreamName,
604                                  STGM_CREATE | ( nStreamMode & 0x73 ),
605                                 0,
606                                  0,
607                                  &pNewExtStream );
608         if ( FAILED( hr ) || !pNewExtStream ) return E_FAIL;
609     }
610     else
611     {
612         pTargetStream = m_pOwnStream;
613         pNewExtStream = m_pExtStream;
614     }
615 
616     HRESULT hr = E_FAIL;
617 
618     uno::Reference < io::XOutputStream > xTempOut( io::TempFile::create(comphelper::getComponentContext(m_xFactory)),
619                                                             uno::UNO_QUERY_THROW );
620 
621     uno::Reference< frame::XStorable > xStorable( m_pDocHolder->GetDocument(), uno::UNO_QUERY );
622     if( xStorable.is() )
623     {
624         try
625         {
626             xStorable->storeToURL( "private:stream",
627                                         fillArgsForStoring_Impl( xTempOut ) );
628             hr = copyXTempOutToIStream( xTempOut, pTargetStream );
629             if ( SUCCEEDED( hr ) )
630             {
631                 // no need to truncate the stream, the size of the stream is always the same
632                 ULARGE_INTEGER nNewPos;
633                 LARGE_INTEGER const aZero = { 0, 0 };
634                 hr = pNewExtStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos );
635                 if ( SUCCEEDED( hr ) )
636                 {
637                     SIZEL aSize;
638                     hr = m_pDocHolder->GetExtent( &aSize );
639 
640                     if ( SUCCEEDED( hr ) )
641                     {
642                         sal_uInt32 nWritten;
643                         sal_Int32 aInf[EXT_STREAM_LENGTH] = {0, 0, aSize.cx, aSize.cy};
644 
645                         hr = pNewExtStream->Write( aInf, sizeof aInf, &nWritten );
646                         if ( nWritten != sizeof aInf ) hr = E_FAIL;
647 
648                         if ( SUCCEEDED( hr ) )
649                         {
650                             m_pOwnStream = CComPtr< IStream >();
651                             m_pExtStream = CComPtr< IStream >();
652                             if ( fSameAsLoad || pStgSave == m_pMasterStorage )
653                             {
654                                 uno::Reference< util::XModifiable > xMod( m_pDocHolder->GetDocument(), uno::UNO_QUERY );
655                                 if ( xMod.is() )
656                                     xMod->setModified( false );
657                                 m_bIsDirty = false;
658                             }
659                         }
660                     }
661                 }
662             }
663         }
664         catch( const uno::Exception& )
665         {
666         }
667     }
668 
669     return hr;
670 }
671 
SaveCompleted(IStorage * pStgNew)672 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SaveCompleted( IStorage *pStgNew )
673 {
674     // m_pOwnStream == NULL && m_pMasterStorage != NULL means the object is in NoScribble mode
675     // m_pOwnStream == NULL && m_pMasterStorage == NULL means the object is in HandsOff mode
676 
677     if ( m_pOwnStream || m_pExtStream )
678         return E_UNEXPECTED;
679 
680     if ( !m_pMasterStorage && !pStgNew )
681         return E_INVALIDARG;
682 
683     if ( pStgNew )
684         m_pMasterStorage = pStgNew;
685 
686     STATSTG aStat;
687     HRESULT hr = m_pMasterStorage->Stat( &aStat, STATFLAG_NONAME );
688     if ( FAILED( hr ) ) return E_OUTOFMEMORY;
689 
690     DWORD nStreamMode = aStat.grfMode;
691     hr = m_pMasterStorage->OpenStream( aOfficeEmbedStreamName,
692                                         nullptr,
693                                         nStreamMode & 0x73,
694                                         0,
695                                         &m_pOwnStream );
696     if ( FAILED( hr ) || !m_pOwnStream ) return E_OUTOFMEMORY;
697 
698     hr = m_pMasterStorage->OpenStream( aExtentStreamName,
699                                         nullptr,
700                                         nStreamMode & 0x73,
701                                         0,
702                                         &m_pExtStream );
703     if ( FAILED( hr ) || !m_pExtStream ) return E_OUTOFMEMORY;
704 
705     for (auto const& advise : m_aAdviseHashMap)
706     {
707         if ( advise.second )
708             advise.second->OnSave();
709     }
710 
711     return S_OK;
712 }
713 
HandsOffStorage()714 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::HandsOffStorage()
715 {
716     m_pMasterStorage = CComPtr< IStorage >();
717     m_pOwnStream = CComPtr< IStream >();
718     m_pExtStream = CComPtr< IStream >();
719 
720     return S_OK;
721 }
722 
723 
724 // IPersistFile
725 
Load(LPCOLESTR pszFileName,DWORD)726 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Load( LPCOLESTR pszFileName, DWORD /*dwMode*/ )
727 {
728     if ( m_pDocHolder->GetDocument().is() )
729         return CO_E_ALREADYINITIALIZED;
730 
731     if ( !m_xFactory.is() )
732         return E_FAIL;
733 
734     DWORD nStreamMode = STGM_CREATE | STGM_READWRITE | STGM_DELETEONRELEASE | STGM_SHARE_EXCLUSIVE;
735     HRESULT hr = StgCreateDocfile( nullptr,
736                                      nStreamMode ,
737                                      0,
738                                      &m_pMasterStorage );
739 
740     if ( FAILED( hr ) || !m_pMasterStorage ) return E_FAIL;
741 
742     std::u16string_view aCurType = getServiceNameFromGUID_Impl( &m_guid ); // ???
743     CLIPFORMAT cf = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" ));
744     hr = WriteFmtUserTypeStg( m_pMasterStorage,
745                             cf,                         // ???
746                             const_cast<LPOLESTR>( o3tl::toW(aCurType.data())) );
747     if ( FAILED( hr ) ) return E_FAIL;
748 
749     hr = m_pMasterStorage->SetClass( m_guid );
750     if ( FAILED( hr ) ) return E_FAIL;
751 
752     hr = m_pMasterStorage->CreateStream( aOfficeEmbedStreamName,
753                             STGM_CREATE | ( nStreamMode & 0x73 ),
754                             0,
755                             0,
756                             &m_pOwnStream );
757     if ( FAILED( hr ) || !m_pOwnStream ) return E_FAIL;
758 
759     hr = m_pMasterStorage->CreateStream( aExtentStreamName,
760                             STGM_CREATE | ( nStreamMode & 0x73 ),
761                             0,
762                             0,
763                             &m_pExtStream );
764     if ( FAILED( hr ) || !m_pExtStream ) return E_FAIL;
765 
766 
767     uno::Reference< frame::XModel > aDocument(
768                     m_xFactory->createInstance( OUString(getServiceNameFromGUID_Impl( &m_guid )) ),
769                     uno::UNO_QUERY );
770     if ( aDocument.is() )
771     {
772         m_pDocHolder->SetDocument( aDocument, true );
773 
774         uno::Reference< frame::XLoadable > xLoadable( m_pDocHolder->GetDocument(), uno::UNO_QUERY );
775         if( xLoadable.is() )
776         {
777             try
778             {
779                 xLoadable->load( fillArgsForLoading_Impl( uno::Reference< io::XInputStream >(),
780                                                             STGM_READWRITE,
781                                                             pszFileName ) );
782                 hr = S_OK;
783 
784                 m_aFileName = o3tl::toU(pszFileName);
785             }
786             catch( const uno::Exception& )
787             {
788             }
789         }
790 
791         if ( hr == S_OK )
792         {
793             aCurType = getServiceNameFromGUID_Impl( &m_guid ); // ???
794             cf = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" ));
795             hr = WriteFmtUserTypeStg( m_pMasterStorage,
796                                     cf,                         // ???
797                                     const_cast<LPOLESTR>( o3tl::toW(aCurType.data())) );
798 
799             if ( SUCCEEDED( hr ) )
800             {
801                 // no need to truncate the stream, the size of the stream is always the same
802                 ULARGE_INTEGER nNewPos;
803                 LARGE_INTEGER const aZero = { 0, 0 };
804                 hr = m_pExtStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos );
805                 if ( SUCCEEDED( hr ) )
806                 {
807                     SIZEL aSize;
808                     hr = m_pDocHolder->GetExtent( &aSize );
809 
810                     if ( SUCCEEDED( hr ) )
811                     {
812                         sal_uInt32 nWritten;
813                         sal_Int32 aInf[EXT_STREAM_LENGTH] = {0, 0, aSize.cx, aSize.cy};
814 
815                         hr = m_pExtStream->Write( aInf, sizeof aInf, &nWritten );
816                         if ( nWritten != sizeof aInf ) hr = E_FAIL;
817                     }
818                 }
819             }
820 
821             if ( SUCCEEDED( hr ) )
822                 m_bIsDirty = true;
823             else
824                 hr = E_FAIL;
825         }
826 
827         if ( FAILED( hr ) )
828         {
829             m_pDocHolder->CloseDocument();
830             m_pOwnStream = nullptr;
831             m_pExtStream = nullptr;
832             m_pMasterStorage = nullptr;
833         }
834     }
835 
836     return hr;
837 }
838 
Save(LPCOLESTR pszFileName,BOOL fRemember)839 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Save( LPCOLESTR pszFileName, BOOL fRemember )
840 {
841     if ( !m_pDocHolder->GetDocument().is() || !m_xFactory.is() )
842         return E_FAIL;
843 
844     HRESULT hr = E_FAIL;
845 
846     // TODO/LATER: currently there is no hands off state implemented
847     try
848     {
849         uno::Reference< frame::XStorable > xStorable( m_pDocHolder->GetDocument(), uno::UNO_QUERY_THROW );
850 
851         if ( !pszFileName )
852             xStorable->store();
853         else
854         {
855             util::URL aURL;
856             aURL.Complete = o3tl::toU( pszFileName );
857 
858             uno::Reference< util::XURLTransformer > aTransformer( util::URLTransformer::create(comphelper::getComponentContext(m_xFactory)) );
859 
860             if ( aTransformer->parseSmart( aURL, OUString() ) && aURL.Complete.getLength() )
861             {
862                 if ( fRemember )
863                 {
864                     xStorable->storeAsURL( aURL.Complete, fillArgsForStoring_Impl( uno::Reference< io::XOutputStream >() ) );
865                     m_aFileName = aURL.Complete;
866                 }
867                 else
868                     xStorable->storeToURL( aURL.Complete, fillArgsForStoring_Impl( uno::Reference< io::XOutputStream >() ) );
869             }
870         }
871 
872         hr = S_OK;
873     }
874     catch( const uno::Exception& )
875     {
876     }
877 
878     return hr;
879 }
880 
SaveCompleted(LPCOLESTR pszFileName)881 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SaveCompleted( LPCOLESTR pszFileName )
882 {
883     // the different file name would mean error here
884     m_aFileName = o3tl::toU(pszFileName);
885     return S_OK;
886 }
887 
GetCurFile(LPOLESTR * ppszFileName)888 COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetCurFile( LPOLESTR *ppszFileName )
889 {
890     CComPtr<IMalloc> pMalloc;
891 
892     HRESULT hr = CoGetMalloc( 1, &pMalloc );
893     if ( FAILED( hr ) || !pMalloc ) return E_FAIL;
894 
895     *ppszFileName = static_cast<LPOLESTR>( pMalloc->Alloc( sizeof( sal_Unicode ) * ( m_aFileName.getLength() + 1 ) ) );
896     wcsncpy( *ppszFileName, o3tl::toW(m_aFileName.getStr()), m_aFileName.getLength() + 1 );
897 
898     return m_aFileName.getLength() ? S_OK : S_FALSE;
899 }
900 
901 
GetEmbedDocument()902 LockedEmbedDocument_Impl EmbeddedDocumentInstanceAccess_Impl::GetEmbedDocument()
903 {
904     ::osl::MutexGuard aGuard( m_aMutex );
905     return LockedEmbedDocument_Impl( m_pEmbedDocument );
906 }
907 
ClearEmbedDocument()908 void EmbeddedDocumentInstanceAccess_Impl::ClearEmbedDocument()
909 {
910     ::osl::MutexGuard aGuard( m_aMutex );
911     m_pEmbedDocument = nullptr;
912 }
913 
914 
LockedEmbedDocument_Impl()915 LockedEmbedDocument_Impl::LockedEmbedDocument_Impl()
916 : m_pEmbedDocument( nullptr )
917 {}
918 
LockedEmbedDocument_Impl(EmbedDocument_Impl * pEmbedDocument)919 LockedEmbedDocument_Impl::LockedEmbedDocument_Impl( EmbedDocument_Impl* pEmbedDocument )
920 : m_pEmbedDocument( pEmbedDocument )
921 {
922     if ( m_pEmbedDocument )
923         m_pEmbedDocument->AddRef();
924 }
925 
LockedEmbedDocument_Impl(const LockedEmbedDocument_Impl & aDocLock)926 LockedEmbedDocument_Impl::LockedEmbedDocument_Impl( const LockedEmbedDocument_Impl& aDocLock )
927 : m_pEmbedDocument( aDocLock.m_pEmbedDocument )
928 {
929     if ( m_pEmbedDocument )
930         m_pEmbedDocument->AddRef();
931 }
932 
operator =(const LockedEmbedDocument_Impl & aDocLock)933 LockedEmbedDocument_Impl& LockedEmbedDocument_Impl::operator=( const LockedEmbedDocument_Impl& aDocLock )
934 {
935     if ( m_pEmbedDocument )
936         m_pEmbedDocument->Release();
937 
938     m_pEmbedDocument = aDocLock.m_pEmbedDocument;
939     if ( m_pEmbedDocument )
940         m_pEmbedDocument->AddRef();
941 
942     return *this;
943 }
944 
~LockedEmbedDocument_Impl()945 LockedEmbedDocument_Impl::~LockedEmbedDocument_Impl()
946 {
947     if ( m_pEmbedDocument )
948         m_pEmbedDocument->Release();
949 }
950 
ExecuteMethod(sal_Int16 nId)951 void LockedEmbedDocument_Impl::ExecuteMethod( sal_Int16 nId )
952 {
953     if ( m_pEmbedDocument )
954     {
955         if ( nId == OLESERV_SAVEOBJECT )
956             m_pEmbedDocument->SaveObject();
957         else if ( nId == OLESERV_CLOSE )
958             m_pEmbedDocument->Close( 0 );
959         else if ( nId == OLESERV_NOTIFY )
960             m_pEmbedDocument->notify();
961         else if ( nId == OLESERV_NOTIFYCLOSING )
962             m_pEmbedDocument->OLENotifyClosing();
963         else if ( nId == OLESERV_SHOWOBJECT )
964             m_pEmbedDocument->ShowObject();
965         else if ( nId == OLESERV_DEACTIVATE )
966             m_pEmbedDocument->Deactivate();
967     }
968 }
969 
970 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
971