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 <sfx2/docfile.hxx>
21 #include <sfx2/frame.hxx>
22 #include <sfx2/sfxsids.hrc>
23 #include <sot/storage.hxx>
24 #include <sot/exchange.hxx>
25 #include <svl/intitem.hxx>
26 #include <filter/msfilter/classids.hxx>
27 #include <tools/globname.hxx>
28 #include <com/sun/star/document/UpdateDocMode.hpp>
29 #include <com/sun/star/packages/XPackageEncryption.hpp>
30 #include <com/sun/star/ucb/ContentCreationException.hpp>
31 #include <com/sun/star/uno/XComponentContext.hpp>
32 #include <unotools/streamwrap.hxx>
33 #include <unotools/defaultencoding.hxx>
34 #include <unotools/wincodepage.hxx>
35 #include <osl/diagnose.h>
37 #include <document.hxx>
38 #include <xistream.hxx>
39 #include <xltools.hxx>
40 #include <docoptio.hxx>
41 #include <comphelper/sequenceashashmap.hxx>
42 #include <comphelper/processfactory.hxx>
45 #include <scerrors.hxx>
47 #include <excimp8.hxx>
55 static void lcl_getListOfStreams(SotStorage
* pStorage
, comphelper::SequenceAsHashMap
& aStreamsData
, std::u16string_view sPrefix
)
57 SvStorageInfoList aElements
;
58 pStorage
->FillInfoList(&aElements
);
59 for (const auto & aElement
: aElements
)
61 OUString sStreamFullName
= sPrefix
.size() ? OUString::Concat(sPrefix
) + "/" + aElement
.GetName() : aElement
.GetName();
62 if (aElement
.IsStorage())
64 rtl::Reference
<SotStorage
> xSubStorage
= pStorage
->OpenSotStorage(aElement
.GetName(), StreamMode::STD_READ
| StreamMode::SHARE_DENYALL
);
65 lcl_getListOfStreams(xSubStorage
.get(), aStreamsData
, sStreamFullName
);
70 rtl::Reference
<SotStorageStream
> rStream
= pStorage
->OpenSotStream(aElement
.GetName(), StreamMode::READ
| StreamMode::SHARE_DENYALL
);
73 sal_Int32 nStreamSize
= rStream
->GetSize();
74 uno::Sequence
< sal_Int8
> oData
;
75 oData
.realloc(nStreamSize
);
76 sal_Int32 nReadBytes
= rStream
->ReadBytes(oData
.getArray(), nStreamSize
);
77 if (nStreamSize
== nReadBytes
)
78 aStreamsData
[sStreamFullName
] <<= oData
;
84 static rtl::Reference
<SotStorage
> lcl_DRMDecrypt(const SfxMedium
& rMedium
, const rtl::Reference
<SotStorage
>& rStorage
, std::shared_ptr
<SvStream
>& rNewStorageStrm
)
86 rtl::Reference
<SotStorage
> aNewStorage
;
88 // We have DRM encrypted storage. We should try to decrypt it first, if we can
89 uno::Sequence
< uno::Any
> aArguments
;
90 const uno::Reference
<uno::XComponentContext
>& xComponentContext(comphelper::getProcessComponentContext());
91 uno::Reference
< packages::XPackageEncryption
> xPackageEncryption(
92 xComponentContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
93 u
"com.sun.star.comp.oox.crypto.DRMDataSpace"_ustr
, aArguments
, xComponentContext
), uno::UNO_QUERY
);
95 if (!xPackageEncryption
.is())
97 // We do not know how to decrypt this
101 comphelper::SequenceAsHashMap aStreamsData
;
102 lcl_getListOfStreams(rStorage
.get(), aStreamsData
, u
"");
105 uno::Sequence
<beans::NamedValue
> aStreams
= aStreamsData
.getAsConstNamedValueList();
106 if (!xPackageEncryption
->readEncryptionInfo(aStreams
))
108 // We failed with decryption
112 rtl::Reference
<SotStorageStream
> rContentStream
= rStorage
->OpenSotStream(u
"\011DRMContent"_ustr
, StreamMode::READ
| StreamMode::SHARE_DENYALL
);
113 if (!rContentStream
.is())
118 rNewStorageStrm
= std::make_shared
<SvMemoryStream
>();
120 uno::Reference
<io::XInputStream
> xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream
.get(), false));
121 uno::Reference
<io::XOutputStream
> xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*rNewStorageStrm
));
123 if (!xPackageEncryption
->decrypt(xInputStream
, xDecryptedStream
))
125 // We failed with decryption
129 rNewStorageStrm
->Seek(0);
131 // Further reading is done from new document
132 aNewStorage
= new SotStorage(*rNewStorageStrm
);
134 // Set the media descriptor data
135 uno::Sequence
<beans::NamedValue
> aEncryptionData
= xPackageEncryption
->createEncryptionData(u
""_ustr
);
136 rMedium
.GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA
, uno::Any(aEncryptionData
)));
138 catch (const std::exception
&)
146 ErrCode
ScFormatFilterPluginImpl::ScImportExcel( SfxMedium
& rMedium
, ScDocument
* pDocument
, const EXCIMPFORMAT eFormat
)
148 // check the passed Calc document
149 OSL_ENSURE( pDocument
, "::ScImportExcel - no document" );
150 if( !pDocument
) return SCERR_IMPORT_INTERNAL
; // should not happen
152 /* Import all BIFF versions regardless on eFormat, needed for import of
153 external cells (file type detection returns Excel4.0). */
154 if( (eFormat
!= EIF_AUTO
) && (eFormat
!= EIF_BIFF_LE4
) && (eFormat
!= EIF_BIFF5
) && (eFormat
!= EIF_BIFF8
) )
156 OSL_FAIL( "::ScImportExcel - wrong file format specification" );
157 return SCERR_IMPORT_FORMAT
;
160 // check the input stream from medium
161 SvStream
* pMedStrm
= rMedium
.GetInStream();
162 OSL_ENSURE( pMedStrm
, "::ScImportExcel - medium without input stream" );
163 if( !pMedStrm
) return SCERR_IMPORT_OPEN
; // should not happen
165 SvStream
* pBookStrm
= nullptr; // The "Book"/"Workbook" stream containing main data.
166 XclBiff eBiff
= EXC_BIFF_UNKNOWN
; // The BIFF version of the main stream.
168 bool bUnableToDecryptContent
= false;
170 // try to open an OLE storage
171 rtl::Reference
<SotStorage
> xRootStrg
;
172 rtl::Reference
<SotStorageStream
> xStrgStrm
;
173 std::shared_ptr
<SvStream
> aNewStorageStrm
;
174 if( SotStorage::IsStorageFile( pMedStrm
) )
176 xRootStrg
= new SotStorage( pMedStrm
, false );
177 if( xRootStrg
->GetError() )
181 // try to open "Book" or "Workbook" stream in OLE storage
184 // Check if there is DRM encryption in storage
185 rtl::Reference
<SotStorageStream
> xDRMStrm
= ScfTools::OpenStorageStreamRead(xRootStrg
, u
"\011DRMContent"_ustr
);
188 if (auto xDecryptedStorage
= lcl_DRMDecrypt(rMedium
, xRootStrg
, aNewStorageStrm
))
189 xRootStrg
= std::move(xDecryptedStorage
);
192 bUnableToDecryptContent
= true;
196 // try to open the "Book" stream
197 rtl::Reference
<SotStorageStream
> xBookStrm
= ScfTools::OpenStorageStreamRead( xRootStrg
, EXC_STREAM_BOOK
);
198 XclBiff eBookBiff
= xBookStrm
.is() ? XclImpStream::DetectBiffVersion( *xBookStrm
) : EXC_BIFF_UNKNOWN
;
200 // try to open the "Workbook" stream
201 rtl::Reference
<SotStorageStream
> xWorkbookStrm
= ScfTools::OpenStorageStreamRead( xRootStrg
, EXC_STREAM_WORKBOOK
);
202 XclBiff eWorkbookBiff
= xWorkbookStrm
.is() ? XclImpStream::DetectBiffVersion( *xWorkbookStrm
) : EXC_BIFF_UNKNOWN
;
204 // decide which stream to use
205 if( (eWorkbookBiff
!= EXC_BIFF_UNKNOWN
) && ((eBookBiff
== EXC_BIFF_UNKNOWN
) || (eWorkbookBiff
> eBookBiff
)) )
207 /* Only "Workbook" stream exists; or both streams exist,
208 and "Workbook" has higher BIFF version than "Book" stream. */
209 xStrgStrm
= std::move(xWorkbookStrm
);
210 eBiff
= eWorkbookBiff
;
212 else if( eBookBiff
!= EXC_BIFF_UNKNOWN
)
214 /* Only "Book" stream exists; or both streams exist,
215 and "Book" has higher BIFF version than "Workbook" stream. */
216 xStrgStrm
= std::move(xBookStrm
);
220 pBookStrm
= xStrgStrm
.get();
223 // no "Book" or "Workbook" stream found, try plain input stream from medium (even for BIFF5+)
226 eBiff
= XclImpStream::DetectBiffVersion( *pMedStrm
);
227 if( eBiff
!= EXC_BIFF_UNKNOWN
)
228 pBookStrm
= pMedStrm
;
231 // try to import the file
232 ErrCode eRet
= SCERR_IMPORT_UNKNOWN_BIFF
;
235 pBookStrm
->SetBufferSize( 0x8000 ); // still needed?
237 XclImpRootData
aImpData(
238 eBiff
, rMedium
, xRootStrg
, *pDocument
,
239 utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding()));
240 std::unique_ptr
< ImportExcel
> xFilter
;
247 xFilter
.reset( new ImportExcel( aImpData
, *pBookStrm
) );
250 xFilter
.reset( new ImportExcel8( aImpData
, *pBookStrm
) );
252 default: DBG_ERROR_BIFF();
255 eRet
= xFilter
? xFilter
->Read() : SCERR_IMPORT_INTERNAL
;
258 if (bUnableToDecryptContent
&& eRet
== ERRCODE_NONE
)
259 eRet
= SCWARN_IMPORT_UNKNOWN_ENCRYPTION
;
264 static ErrCode
lcl_ExportExcelBiff( SfxMedium
& rMedium
, ScDocument
*pDocument
,
265 SvStream
* pMedStrm
, bool bBiff8
, rtl_TextEncoding eNach
)
267 uno::Reference
< packages::XPackageEncryption
> xPackageEncryption
;
268 uno::Sequence
< beans::NamedValue
> aEncryptionData
;
269 const SfxUnoAnyItem
* pEncryptionDataItem
= rMedium
.GetItemSet().GetItem(SID_ENCRYPTIONDATA
, false);
270 SvStream
* pOriginalMediaStrm
= pMedStrm
;
271 std::shared_ptr
<SvStream
> pMediaStrm
;
272 if (pEncryptionDataItem
&& (pEncryptionDataItem
->GetValue() >>= aEncryptionData
))
274 ::comphelper::SequenceAsHashMap
aHashData(aEncryptionData
);
275 OUString sCryptoType
= aHashData
.getUnpackedValueOrDefault(u
"CryptoType"_ustr
, OUString());
277 if (sCryptoType
.getLength())
279 const uno::Reference
<uno::XComponentContext
>& xComponentContext(comphelper::getProcessComponentContext());
280 uno::Sequence
<uno::Any
> aArguments
{
281 uno::Any(beans::NamedValue(u
"Binary"_ustr
, uno::Any(true))) };
282 xPackageEncryption
.set(
283 xComponentContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
284 "com.sun.star.comp.oox.crypto." + sCryptoType
, aArguments
, xComponentContext
), uno::UNO_QUERY
);
286 if (xPackageEncryption
.is())
288 // We have an encryptor. Export document into memory stream and encrypt it later
289 pMediaStrm
= std::make_shared
<SvMemoryStream
>();
290 pMedStrm
= pMediaStrm
.get();
292 // Temp removal of EncryptionData to avoid password protection triggering
293 rMedium
.GetItemSet().ClearItem(SID_ENCRYPTIONDATA
);
298 // try to open an OLE storage
299 rtl::Reference
<SotStorage
> xRootStrg
= new SotStorage(pMedStrm
, false);
300 if( xRootStrg
->GetError() ) return SCERR_IMPORT_OPEN
;
302 // create BIFF dependent strings
303 OUString aStrmName
, aClipName
, aClassName
;
306 aStrmName
= EXC_STREAM_WORKBOOK
;
308 aClassName
= "Microsoft Excel 97-Tabelle";
312 aStrmName
= EXC_STREAM_BOOK
;
314 aClassName
= "Microsoft Excel 5.0-Tabelle";
317 // open the "Book"/"Workbook" stream
318 rtl::Reference
<SotStorageStream
> xStrgStrm
= ScfTools::OpenStorageStreamWrite( xRootStrg
, aStrmName
);
319 if( !xStrgStrm
.is() || xStrgStrm
->GetError() ) return SCERR_IMPORT_OPEN
;
321 xStrgStrm
->SetBufferSize( 0x8000 ); // still needed?
323 ErrCode eRet
= SCERR_IMPORT_UNKNOWN_BIFF
;
324 XclExpRootData
aExpData( bBiff8
? EXC_BIFF8
: EXC_BIFF5
, rMedium
, xRootStrg
, *pDocument
, eNach
);
327 ExportBiff8
aFilter( aExpData
, *xStrgStrm
);
328 eRet
= aFilter
.Write();
332 ExportBiff5
aFilter( aExpData
, *xStrgStrm
);
333 eRet
= aFilter
.Write();
336 if( eRet
== SCWARN_IMPORT_RANGE_OVERFLOW
)
337 eRet
= SCWARN_EXPORT_MAXROW
;
339 SvGlobalName
aGlobName(MSO_EXCEL5_CLASSID
);
340 SotClipboardFormatId nClip
= SotExchange::RegisterFormatName( aClipName
);
341 xRootStrg
->SetClass( aGlobName
, nClip
, aClassName
);
346 if (xPackageEncryption
.is())
348 // Perform DRM encryption
351 xPackageEncryption
->setupEncryption(aEncryptionData
);
353 uno::Reference
<io::XInputStream
> xInputStream(new utl::OSeekableInputStreamWrapper(pMedStrm
, false));
354 uno::Sequence
<beans::NamedValue
> aStreams
= xPackageEncryption
->encrypt(xInputStream
);
356 rtl::Reference
<SotStorage
> xEncryptedRootStrg
= new SotStorage(pOriginalMediaStrm
, false);
357 for (const beans::NamedValue
& aStreamData
: aStreams
)
359 // To avoid long paths split and open substorages recursively
360 // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09
361 rtl::Reference
<SotStorage
> pStorage
= xEncryptedRootStrg
;
366 OUString sPathElem
= aStreamData
.Name
.getToken(0, L
'/', idx
);
367 if (!sPathElem
.isEmpty())
371 sFileName
= sPathElem
;
375 pStorage
= pStorage
->OpenSotStorage(sPathElem
);
378 } while (pStorage
&& idx
>= 0);
382 eRet
= ERRCODE_IO_GENERAL
;
386 rtl::Reference
<SotStorageStream
> pStream
= pStorage
->OpenSotStream(sFileName
);
389 eRet
= ERRCODE_IO_GENERAL
;
392 uno::Sequence
<sal_Int8
> aStreamContent
;
393 aStreamData
.Value
>>= aStreamContent
;
394 size_t nBytesWritten
= pStream
->WriteBytes(aStreamContent
.getConstArray(), aStreamContent
.getLength());
395 if (nBytesWritten
!= static_cast<size_t>(aStreamContent
.getLength()))
397 eRet
= ERRCODE_IO_CANTWRITE
;
401 xEncryptedRootStrg
->Commit();
403 // Restore encryption data
404 rMedium
.GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA
, uno::Any(aEncryptionData
)));
410 ErrCode
ScFormatFilterPluginImpl::ScExportExcel5( SfxMedium
& rMedium
, ScDocument
*pDocument
,
411 ExportFormatExcel eFormat
, rtl_TextEncoding eNach
)
413 if( eFormat
!= ExpBiff5
&& eFormat
!= ExpBiff8
)
414 return SCERR_IMPORT_NI
;
416 // check the passed Calc document
417 OSL_ENSURE( pDocument
, "::ScExportExcel5 - no document" );
418 if( !pDocument
) return SCERR_IMPORT_INTERNAL
; // should not happen
420 // check the output stream from medium
421 SvStream
* pMedStrm
= rMedium
.GetOutStream();
422 OSL_ENSURE( pMedStrm
, "::ScExportExcel5 - medium without output stream" );
423 if( !pMedStrm
) return SCERR_IMPORT_OPEN
; // should not happen
425 ErrCode eRet
= lcl_ExportExcelBiff(rMedium
, pDocument
, pMedStrm
, eFormat
== ExpBiff8
, eNach
);
429 extern "C" SAL_DLLPUBLIC_EXPORT
bool TestImportCalcRTF(SvStream
&rStream
)
432 ScDocument aDocument
;
433 ScDocOptions aDocOpt
= aDocument
.GetDocOptions();
434 aDocOpt
.SetLookUpColRowNames(false);
435 aDocument
.SetDocOptions(aDocOpt
);
436 aDocument
.MakeTable(0);
437 aDocument
.EnableExecuteLink(false);
438 aDocument
.SetInsertingFromOtherDoc(true);
445 bRet
= ScFormatFilter::Get().ScImportRTF(rStream
, OUString(), &aDocument
, aRange
) == ERRCODE_NONE
;
447 catch (const std::range_error
&)
455 extern "C" SAL_DLLPUBLIC_EXPORT
bool TestImportXLS(SvStream
& rStream
)
459 css::uno::Reference
<css::io::XInputStream
> xStm(new utl::OInputStreamWrapper(rStream
));
460 aMedium
.GetItemSet().Put(SfxUnoAnyItem(SID_INPUTSTREAM
, css::uno::Any(xStm
)));
461 aMedium
.GetItemSet().Put(SfxUInt16Item(SID_UPDATEDOCMODE
, css::document::UpdateDocMode::NO_UPDATE
));
463 ScDocShellRef xDocShell
= new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT
|
464 SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS
|
465 SfxModelFlags::DISABLE_DOCUMENT_RECOVERY
);
467 xDocShell
->DoInitNew();
468 xDocShell
->SetInitialLinkUpdate(&aMedium
);
470 ScDocument
& rDoc
= xDocShell
->GetDocument();
472 ScDocOptions aDocOpt
= rDoc
.GetDocOptions();
473 aDocOpt
.SetLookUpColRowNames(false);
474 rDoc
.SetDocOptions(aDocOpt
);
476 rDoc
.EnableExecuteLink(false);
477 rDoc
.SetInsertingFromOtherDoc(true);
478 rDoc
.InitDrawLayer(xDocShell
.get());
482 bRet
= ScFormatFilter::Get().ScImportExcel(aMedium
, &rDoc
, EIF_AUTO
) == ERRCODE_NONE
;
484 catch (const css::ucb::ContentCreationException
&)
487 catch (const std::out_of_range
&)
490 xDocShell
->DoClose();
495 extern "C" SAL_DLLPUBLIC_EXPORT
bool TestImportDIF(SvStream
&rStream
)
498 ScDocument aDocument
;
499 ScDocOptions aDocOpt
= aDocument
.GetDocOptions();
500 aDocOpt
.SetLookUpColRowNames(false);
501 aDocument
.SetDocOptions(aDocOpt
);
502 aDocument
.MakeTable(0);
503 aDocument
.EnableExecuteLink(false);
504 aDocument
.SetInsertingFromOtherDoc(true);
505 return ScFormatFilter::Get().ScImportDif(rStream
, &aDocument
, ScAddress(0, 0, 0), RTL_TEXTENCODING_IBM_850
) == ERRCODE_NONE
;
508 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */