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/uno/Reference.hxx>
21 #include <com/sun/star/uno/Sequence.hxx>
22 #include <com/sun/star/uno/Any.hxx>
23 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
24 #include <com/sun/star/configuration/theDefaultProvider.hpp>
25 #include <com/sun/star/container/XNameAccess.hpp>
26 #include <com/sun/star/embed/XEmbeddedObject.hpp>
27 #include <com/sun/star/embed/XEmbedPersist.hpp>
28 #include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
29 #include <com/sun/star/embed/EmbedStates.hpp>
30 #include <com/sun/star/frame/XStorable.hpp>
31 #include <com/sun/star/awt/Size.hpp>
32 #include <com/sun/star/embed/Aspects.hpp>
33 #include <comphelper/classids.hxx>
34 #include <sfx2/docfilt.hxx>
35 #include <sfx2/fcontnr.hxx>
36 #include <sot/formats.hxx>
37 #include <sot/storage.hxx>
38 #include <tools/diagnose_ex.h>
39 #include <comphelper/fileformat.h>
40 #include <comphelper/processfactory.hxx>
41 #include <unotools/streamwrap.hxx>
42 #include <comphelper/storagehelper.hxx>
43 #include <svtools/embedhlp.hxx>
44 #include <filter/msfilter/msdffimp.hxx>
45 
46 #include <filter/msfilter/msoleexp.hxx>
47 
48 using namespace ::com::sun::star;
49 
GetEmbeddedVersion(const SvGlobalName & aAppName)50 static SvGlobalName GetEmbeddedVersion( const SvGlobalName& aAppName )
51 {
52     if ( aAppName == SvGlobalName( SO3_SM_CLASSID_60 ) )
53             return SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_8 );
54     else if ( aAppName == SvGlobalName( SO3_SW_CLASSID_60 ) )
55             return SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_8 );
56     else if ( aAppName == SvGlobalName( SO3_SC_CLASSID_60 ) )
57             return SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_8 );
58     else if ( aAppName == SvGlobalName( SO3_SDRAW_CLASSID_60 ) )
59             return SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_8 );
60     else if ( aAppName == SvGlobalName( SO3_SIMPRESS_CLASSID_60 ) )
61             return SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 );
62     else if ( aAppName == SvGlobalName( SO3_SCH_CLASSID_60 ) )
63             return SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_8 );
64 
65     return SvGlobalName();
66 }
67 
GetStorageType(const SvGlobalName & aEmbName)68 static OUString GetStorageType( const SvGlobalName& aEmbName )
69 {
70     if ( aEmbName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_8 ) )
71         return "LibreOffice.MathDocument.1";
72     else if ( aEmbName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_8 ) )
73         return "LibreOffice.WriterDocument.1";
74     else if ( aEmbName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_8 ) )
75         return "LibreOffice.CalcDocument.1";
76     else if ( aEmbName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) )
77         return "LibreOffice.DrawDocument.1";
78     else if ( aEmbName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) )
79         return "LibreOffice.ImpressDocument.1";
80     else if ( aEmbName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_8 ) )
81         return "LibreOffice.ChartDocument.1";
82     return OUString();
83 }
84 
UseOldMSExport()85 static bool UseOldMSExport()
86 {
87     uno::Reference< lang::XMultiServiceFactory > xProvider(
88         configuration::theDefaultProvider::get(
89             comphelper::getProcessComponentContext()));
90     try {
91         uno::Sequence< uno::Any > aArg( 1 );
92         aArg[0] <<= OUString( "/org.openoffice.Office.Common/InternalMSExport" );
93         uno::Reference< container::XNameAccess > xNameAccess(
94             xProvider->createInstanceWithArguments(
95                 "com.sun.star.configuration.ConfigurationUpdateAccess",
96                 aArg ),
97             uno::UNO_QUERY );
98         if ( xNameAccess.is() )
99         {
100             uno::Any aResult = xNameAccess->getByName( "UseOldExport" );
101 
102             bool bResult;
103             if ( aResult >>= bResult )
104                 return bResult;
105         }
106     }
107     catch( const uno::Exception& )
108     {
109     }
110 
111     OSL_FAIL( "Could not get access to configuration entry!" );
112     return false;
113 }
114 
ExportOLEObject(const css::uno::Reference<css::embed::XEmbeddedObject> & rObj,SotStorage & rDestStg)115 void SvxMSExportOLEObjects::ExportOLEObject( const css::uno::Reference < css::embed::XEmbeddedObject>& rObj, SotStorage& rDestStg )
116 {
117     svt::EmbeddedObjectRef aObj( rObj, embed::Aspects::MSOLE_CONTENT );
118     ExportOLEObject( aObj, rDestStg );
119 }
120 
ExportOLEObject(svt::EmbeddedObjectRef const & rObj,SotStorage & rDestStg)121 void SvxMSExportOLEObjects::ExportOLEObject( svt::EmbeddedObjectRef const & rObj, SotStorage& rDestStg )
122 {
123     SvGlobalName aOwnGlobalName;
124     SvGlobalName aObjName( rObj->getClassID() );
125     std::shared_ptr<const SfxFilter> pExpFilter;
126     {
127         static struct ObjExpType {
128             sal_uInt32 nFlag;
129             const char* pFilterNm;
130             // GlobalNameId
131             struct GlobalNameIds {
132                 sal_uInt32 n1;
133                 sal_uInt16 n2, n3;
134                 sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
135             }
136             aGlNmIds[4];
137         } const aArr[] = {
138             { OLE_STARMATH_2_MATHTYPE, "MathType 3.x",
139                 {{SO3_SM_CLASSID_60}, {SO3_SM_CLASSID_50},
140                  {SO3_SM_CLASSID_40}, {SO3_SM_CLASSID_30 }}},
141             { OLE_STARWRITER_2_WINWORD, "MS Word 97",
142                 {{SO3_SW_CLASSID_60}, {SO3_SW_CLASSID_50},
143                  {SO3_SW_CLASSID_40}, {SO3_SW_CLASSID_30 }}},
144             { OLE_STARCALC_2_EXCEL, "MS Excel 97",
145                 {{SO3_SC_CLASSID_60}, {SO3_SC_CLASSID_50},
146                  {SO3_SC_CLASSID_40}, {SO3_SC_CLASSID_30 }}},
147             { OLE_STARIMPRESS_2_POWERPOINT, "MS PowerPoint 97",
148                 {{SO3_SIMPRESS_CLASSID_60}, {SO3_SIMPRESS_CLASSID_50},
149                  {SO3_SIMPRESS_CLASSID_40}, {SO3_SIMPRESS_CLASSID_30 }}},
150             { 0, "",
151                 {{SO3_SCH_CLASSID_60}, {SO3_SCH_CLASSID_50},
152                  {SO3_SCH_CLASSID_40}, {SO3_SCH_CLASSID_30 }}},
153             { 0, "",
154                 {{SO3_SDRAW_CLASSID_60}, {SO3_SDRAW_CLASSID_50},    // SJ: !!!! SO3_SDRAW_CLASSID is only available up from
155                  {SO3_SDRAW_CLASSID_60}, {SO3_SDRAW_CLASSID_50 }}}, // ver 5.0, it is purpose to have double entries here.
156 
157             { 0xffff,nullptr,
158                 {{SO3_SDRAW_CLASSID_60}, {SO3_SDRAW_CLASSID_50},
159                 {SO3_SDRAW_CLASSID_60}, {SO3_SDRAW_CLASSID_50}}}
160         };
161 
162         for( const ObjExpType* pArr = aArr; !pExpFilter && ( pArr->nFlag != 0xffff ); ++pArr )
163         {
164             for (const ObjExpType::GlobalNameIds& rId : pArr->aGlNmIds)
165             {
166                 SvGlobalName aGlbNm( rId.n1, rId.n2, rId.n3,
167                             rId.b8, rId.b9, rId.b10, rId.b11,
168                             rId.b12, rId.b13, rId.b14, rId.b15 );
169                 if( aObjName == aGlbNm )
170                 {
171                     aOwnGlobalName = aGlbNm;
172 
173                     // flags for checking if conversion is wanted at all (SaveOptions?!)
174                     if( nConvertFlags & pArr->nFlag )
175                     {
176                         pExpFilter = SfxFilterMatcher().GetFilter4FilterName(OUString::createFromAscii(pArr->pFilterNm));
177                         break;
178                     }
179                 }
180             }
181         }
182     }
183 
184     if( pExpFilter )                        // use this filter for the export
185     {
186         try
187         {
188             if ( rObj->getCurrentState() == embed::EmbedStates::LOADED )
189                 rObj->changeState( embed::EmbedStates::RUNNING );
190             //TODO/LATER: is stream instead of outputstream a better choice?!
191             //TODO/LATER: a "StoreTo" method at embedded object would be nice
192             uno::Sequence < beans::PropertyValue > aSeq(2);
193             SvStream* pStream = new SvMemoryStream;
194             aSeq[0].Name = "OutputStream";
195             ::uno::Reference < io::XOutputStream > xOut = new ::utl::OOutputStreamWrapper( *pStream );
196             aSeq[0].Value <<= xOut;
197             aSeq[1].Name = "FilterName";
198             aSeq[1].Value <<= pExpFilter->GetName();
199             uno::Reference < frame::XStorable > xStor( rObj->getComponent(), uno::UNO_QUERY );
200             try
201             {
202                 xStor->storeToURL( "private:stream", aSeq );
203             }
204             catch( const uno::Exception& ) {} // #TODO really handle exceptions - interactionalhandler etc. ?
205 
206             tools::SvRef<SotStorage> xOLEStor = new SotStorage( pStream, true );
207             xOLEStor->CopyTo( &rDestStg );
208             rDestStg.Commit();
209         }
210         catch( const uno::Exception& )
211         {
212             // TODO/LATER: Error handling
213             OSL_FAIL( "The object could not be exported!" );
214         }
215     }
216     else if( aOwnGlobalName != SvGlobalName() )
217     {
218         // own format, maybe SO6 format or lower
219         SvGlobalName aEmbName = GetEmbeddedVersion( aOwnGlobalName );
220         if ( aEmbName != SvGlobalName() && !UseOldMSExport() )
221         {
222             // this is a SO6 embedded object, save in old binary format
223             rDestStg.SetVersion( SOFFICE_FILEFORMAT_31 );
224             rDestStg.SetClass( aEmbName,
225                                 SotClipboardFormatId::EMBEDDED_OBJ_OLE,
226                                 GetStorageType( aEmbName ) );
227             tools::SvRef<SotStorageStream> xExtStm = rDestStg.OpenSotStream(
228                                             "properties_stream");
229 
230             bool bExtentSuccess = false;
231             if( !xExtStm->GetError() )
232             {
233                 // write extent
234                 //TODO/MBA: check if writing a size is enough
235                 if( rObj.GetObject().is() )
236                 {
237                     // MSOLE objects don't need to be in running state for VisualArea access
238                     awt::Size aSize;
239                     try
240                     {
241                         // this is an own object, the content size must be stored in the
242                         // extension stream
243                         aSize = rObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
244                     }
245                     catch( const embed::NoVisualAreaSizeException& )
246                     {
247                         OSL_FAIL( "Could not get visual area size!" );
248                         aSize.Width = 5000;
249                         aSize.Height = 5000;
250                     }
251                     catch( const uno::Exception& )
252                     {
253                         TOOLS_WARN_EXCEPTION(
254                             "filter.ms", "Unexpected exception while getting visual area size!");
255                         aSize.Width = 5000;
256                         aSize.Height = 5000;
257                     }
258 
259                     sal_Int32 pRect[4];
260                     pRect[0] = 0;
261                     pRect[1] = aSize.Width;
262                     pRect[2] = 0;
263                     pRect[3] = aSize.Height;
264 
265                     sal_Int8 aWriteSet[16];
266                     for ( int ind = 0; ind < 4; ind++ )
267                     {
268                         sal_Int32 nVal = pRect[ind];
269                         for ( int nByte = 0; nByte < 4; nByte++ )
270                         {
271                             aWriteSet[ind*4+nByte] = static_cast<sal_Int8>(nVal) % 0x100;
272                             nVal /= 0x100;
273                         }
274                     }
275 
276                     bExtentSuccess = (xExtStm->WriteBytes(aWriteSet, 16) == 16);
277                 }
278             }
279 
280             if ( bExtentSuccess )
281             {
282                 tools::SvRef<SotStorageStream> xEmbStm = rDestStg.OpenSotStream(
283                                                 "package_stream");
284                 if( !xEmbStm->GetError() )
285                 {
286                     try
287                     {
288                         if ( rObj->getCurrentState() == embed::EmbedStates::LOADED )
289                             rObj->changeState( embed::EmbedStates::RUNNING );
290                         //TODO/LATER: is stream instead of outputstream a better choice?!
291                         //TODO/LATER: a "StoreTo" method at embedded object would be nice
292                         uno::Sequence < beans::PropertyValue > aSeq(1);
293                         aSeq[0].Name = "OutputStream";
294                         ::uno::Reference < io::XOutputStream > xOut = new ::utl::OOutputStreamWrapper( *xEmbStm );
295                         aSeq[0].Value <<= xOut;
296                         uno::Reference < frame::XStorable > xStor( rObj->getComponent(), uno::UNO_QUERY );
297                         xStor->storeToURL( "private:stream", aSeq );
298                     }
299                     catch( const uno::Exception& )
300                     {
301                         // TODO/LATER: Error handling
302                         OSL_FAIL( "The object could not be exported!" );
303                     }
304                 }
305             }
306         }
307         else
308         {
309             OSL_FAIL("Own binary format inside own container document!");
310         }
311     }
312     else
313     {
314         // alien objects
315         //TODO/LATER: a "StoreTo" method at embedded object would be nice
316         rDestStg.SetVersion( SOFFICE_FILEFORMAT_31 );
317         uno::Reference < embed::XStorage > xStor = ::comphelper::OStorageHelper::GetTemporaryStorage();
318         uno::Reference < embed::XEmbedPersist > xPers( rObj.GetObject(), uno::UNO_QUERY );
319         if ( xPers.is() )
320         {
321             uno::Sequence < beans::PropertyValue > aEmptySeq;
322             OUString aTempName( "bla" );
323             try
324             {
325                 xPers->storeToEntry( xStor, aTempName, aEmptySeq, aEmptySeq );
326             }
327             catch ( const uno::Exception& )
328             {}
329 
330             tools::SvRef<SotStorage> xOLEStor = SotStorage::OpenOLEStorage( xStor, aTempName, StreamMode::STD_READ );
331             xOLEStor->CopyTo( &rDestStg );
332             rDestStg.Commit();
333         }
334     }
335 
336     //We never need this stream: See #99809# and #i2179#
337     rDestStg.Remove( SVEXT_PERSIST_STREAM );
338 }
339 
340 
341 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
342