use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / excel.cxx
blobaae72134bf0d7fa636fadfd6700d1740fd9bcb9b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
36 #include <filter.hxx>
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>
44 #include <docsh.hxx>
45 #include <scerrors.hxx>
46 #include <imp_op.hxx>
47 #include <excimp8.hxx>
48 #include <exp_op.hxx>
49 #include <scdll.hxx>
51 #include <memory>
53 using namespace css;
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);
67 else
69 // Read stream
70 rtl::Reference<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL);
71 if (rStream.is())
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
98 return aNewStorage;
101 comphelper::SequenceAsHashMap aStreamsData;
102 lcl_getListOfStreams(rStorage.get(), aStreamsData, u"");
104 try {
105 uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList();
106 if (!xPackageEncryption->readEncryptionInfo(aStreams))
108 // We failed with decryption
109 return aNewStorage;
112 rtl::Reference<SotStorageStream> rContentStream = rStorage->OpenSotStream(u"\011DRMContent"_ustr, StreamMode::READ | StreamMode::SHARE_DENYALL);
113 if (!rContentStream.is())
115 return aNewStorage;
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
126 return aNewStorage;
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&)
140 return aNewStorage;
143 return aNewStorage;
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() )
178 xRootStrg = nullptr;
181 // try to open "Book" or "Workbook" stream in OLE storage
182 if( xRootStrg.is() )
184 // Check if there is DRM encryption in storage
185 rtl::Reference<SotStorageStream> xDRMStrm = ScfTools::OpenStorageStreamRead(xRootStrg, u"\011DRMContent"_ustr);
186 if (xDRMStrm.is())
188 if (auto xDecryptedStorage = lcl_DRMDecrypt(rMedium, xRootStrg, aNewStorageStrm))
189 xRootStrg = std::move(xDecryptedStorage);
190 else
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);
217 eBiff = eBookBiff;
220 pBookStrm = xStrgStrm.get();
223 // no "Book" or "Workbook" stream found, try plain input stream from medium (even for BIFF5+)
224 if( !pBookStrm )
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;
233 if( pBookStrm )
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;
241 switch( eBiff )
243 case EXC_BIFF2:
244 case EXC_BIFF3:
245 case EXC_BIFF4:
246 case EXC_BIFF5:
247 xFilter.reset( new ImportExcel( aImpData, *pBookStrm ) );
248 break;
249 case EXC_BIFF8:
250 xFilter.reset( new ImportExcel8( aImpData, *pBookStrm ) );
251 break;
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;
261 return eRet;
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;
304 if( bBiff8 )
306 aStrmName = EXC_STREAM_WORKBOOK;
307 aClipName = "Biff8";
308 aClassName = "Microsoft Excel 97-Tabelle";
310 else
312 aStrmName = EXC_STREAM_BOOK;
313 aClipName = "Biff5";
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 );
325 if ( bBiff8 )
327 ExportBiff8 aFilter( aExpData, *xStrgStrm );
328 eRet = aFilter.Write();
330 else
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 );
343 xStrgStrm->Commit();
344 xRootStrg->Commit();
346 if (xPackageEncryption.is())
348 // Perform DRM encryption
349 pMedStrm->Seek(0);
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;
362 OUString sFileName;
363 sal_Int32 idx = 0;
366 OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx);
367 if (!sPathElem.isEmpty())
369 if (idx < 0)
371 sFileName = sPathElem;
373 else
375 pStorage = pStorage->OpenSotStorage(sPathElem);
378 } while (pStorage && idx >= 0);
380 if (!pStorage)
382 eRet = ERRCODE_IO_GENERAL;
383 break;
386 rtl::Reference<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName);
387 if (!pStream)
389 eRet = ERRCODE_IO_GENERAL;
390 break;
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;
398 break;
401 xEncryptedRootStrg->Commit();
403 // Restore encryption data
404 rMedium.GetItemSet().Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
407 return eRet;
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);
426 return eRet;
429 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportCalcRTF(SvStream &rStream)
431 ScDLL::Init();
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);
439 ScRange aRange;
441 bool bRet;
445 bRet = ScFormatFilter::Get().ScImportRTF(rStream, OUString(), &aDocument, aRange) == ERRCODE_NONE;
447 catch (const std::range_error&)
449 return false;
452 return bRet;
455 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportXLS(SvStream& rStream)
457 ScDLL::Init();
458 SfxMedium aMedium;
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);
475 rDoc.MakeTable(0);
476 rDoc.EnableExecuteLink(false);
477 rDoc.SetInsertingFromOtherDoc(true);
478 rDoc.InitDrawLayer(xDocShell.get());
479 bool bRet(false);
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();
491 xDocShell.clear();
492 return bRet;
495 extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDIF(SvStream &rStream)
497 ScDLL::Init();
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: */