xref: /core/sc/source/filter/excel/xestream.cxx (revision 09cd0e36)
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 <stdarg.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <utility>
24 
25 #include <filter/msfilter/util.hxx>
26 #include <rtl/ustring.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <rtl/random.h>
29 #include <sax/fshelper.hxx>
30 #include <unotools/streamwrap.hxx>
31 #include <sot/storage.hxx>
32 #include <tools/urlobj.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/settings.hxx>
35 
36 #include <docuno.hxx>
37 #include <xestream.hxx>
38 #include <xladdress.hxx>
39 #include <xlstring.hxx>
40 #include <xltools.hxx>
41 #include <xeroot.hxx>
42 #include <xcl97rec.hxx>
43 #include <rangelst.hxx>
44 #include <compiler.hxx>
45 #include <formulacell.hxx>
46 #include <tokenarray.hxx>
47 #include <tokenstringcontext.hxx>
48 #include <refreshtimerprotector.hxx>
49 #include <globstr.hrc>
50 #include <scresid.hxx>
51 #include <root.hxx>
52 
53 #include <docsh.hxx>
54 #include <viewdata.hxx>
55 #include <excdoc.hxx>
56 
57 #include <oox/token/tokens.hxx>
58 #include <oox/token/relationship.hxx>
59 #include <oox/export/utils.hxx>
60 #include <formula/grammar.hxx>
61 #include <oox/ole/vbaexport.hxx>
62 #include <excelvbaproject.hxx>
63 
64 #include <com/sun/star/task/XStatusIndicator.hpp>
65 #include <memory>
66 #include <comphelper/storagehelper.hxx>
67 
68 #define DEBUG_XL_ENCRYPTION 0
69 
70 using ::com::sun::star::uno::XInterface;
71 using ::std::vector;
72 
73 using namespace com::sun::star;
74 using namespace ::com::sun::star::beans;
75 using namespace ::com::sun::star::io;
76 using namespace ::com::sun::star::lang;
77 using namespace ::com::sun::star::sheet;
78 using namespace ::com::sun::star::uno;
79 using namespace ::formula;
80 using namespace ::oox;
81 
82 XclExpStream::XclExpStream( SvStream& rOutStrm, const XclExpRoot& rRoot, sal_uInt16 nMaxRecSize ) :
83     mrStrm( rOutStrm ),
84     mrRoot( rRoot ),
85     mbUseEncrypter( false ),
86     mnMaxRecSize( nMaxRecSize ),
87     mnCurrMaxSize( 0 ),
88     mnMaxSliceSize( 0 ),
89     mnHeaderSize( 0 ),
90     mnCurrSize( 0 ),
91     mnSliceSize( 0 ),
92     mnPredictSize( 0 ),
93     mnLastSizePos( 0 ),
94     mbInRec( false )
95 {
96     if( mnMaxRecSize == 0 )
97         mnMaxRecSize = (mrRoot.GetBiff() <= EXC_BIFF5) ? EXC_MAXRECSIZE_BIFF5 : EXC_MAXRECSIZE_BIFF8;
98     mnMaxContSize = mnMaxRecSize;
99 }
100 
101 XclExpStream::~XclExpStream()
102 {
103     mrStrm.Flush();
104 }
105 
106 void XclExpStream::StartRecord( sal_uInt16 nRecId, std::size_t nRecSize )
107 {
108     OSL_ENSURE( !mbInRec, "XclExpStream::StartRecord - another record still open" );
109     DisableEncryption();
110     mnMaxContSize = mnCurrMaxSize = mnMaxRecSize;
111     mnPredictSize = nRecSize;
112     mbInRec = true;
113     InitRecord( nRecId );
114     SetSliceSize( 0 );
115     EnableEncryption();
116 }
117 
118 void XclExpStream::EndRecord()
119 {
120     OSL_ENSURE( mbInRec, "XclExpStream::EndRecord - no record open" );
121     DisableEncryption();
122     UpdateRecSize();
123     mrStrm.Seek( STREAM_SEEK_TO_END );
124     mbInRec = false;
125 }
126 
127 void XclExpStream::SetSliceSize( sal_uInt16 nSize )
128 {
129     mnMaxSliceSize = nSize;
130     mnSliceSize = 0;
131 }
132 
133 XclExpStream& XclExpStream::operator<<( sal_Int8 nValue )
134 {
135     PrepareWrite( 1 );
136     if (mbUseEncrypter && HasValidEncrypter())
137         mxEncrypter->Encrypt(mrStrm, nValue);
138     else
139         mrStrm.WriteSChar( nValue );
140     return *this;
141 }
142 
143 XclExpStream& XclExpStream::operator<<( sal_uInt8 nValue )
144 {
145     PrepareWrite( 1 );
146     if (mbUseEncrypter && HasValidEncrypter())
147         mxEncrypter->Encrypt(mrStrm, nValue);
148     else
149         mrStrm.WriteUChar( nValue );
150     return *this;
151 }
152 
153 XclExpStream& XclExpStream::operator<<( sal_Int16 nValue )
154 {
155     PrepareWrite( 2 );
156     if (mbUseEncrypter && HasValidEncrypter())
157         mxEncrypter->Encrypt(mrStrm, nValue);
158     else
159         mrStrm.WriteInt16( nValue );
160     return *this;
161 }
162 
163 XclExpStream& XclExpStream::operator<<( sal_uInt16 nValue )
164 {
165     PrepareWrite( 2 );
166     if (mbUseEncrypter && HasValidEncrypter())
167         mxEncrypter->Encrypt(mrStrm, nValue);
168     else
169         mrStrm.WriteUInt16( nValue );
170     return *this;
171 }
172 
173 XclExpStream& XclExpStream::operator<<( sal_Int32 nValue )
174 {
175     PrepareWrite( 4 );
176     if (mbUseEncrypter && HasValidEncrypter())
177         mxEncrypter->Encrypt(mrStrm, nValue);
178     else
179         mrStrm.WriteInt32( nValue );
180     return *this;
181 }
182 
183 XclExpStream& XclExpStream::operator<<( sal_uInt32 nValue )
184 {
185     PrepareWrite( 4 );
186     if (mbUseEncrypter && HasValidEncrypter())
187         mxEncrypter->Encrypt(mrStrm, nValue);
188     else
189         mrStrm.WriteUInt32( nValue );
190     return *this;
191 }
192 
193 XclExpStream& XclExpStream::operator<<( float fValue )
194 {
195     PrepareWrite( 4 );
196     if (mbUseEncrypter && HasValidEncrypter())
197         mxEncrypter->Encrypt(mrStrm, fValue);
198     else
199         mrStrm.WriteFloat( fValue );
200     return *this;
201 }
202 
203 XclExpStream& XclExpStream::operator<<( double fValue )
204 {
205     PrepareWrite( 8 );
206     if (mbUseEncrypter && HasValidEncrypter())
207         mxEncrypter->Encrypt(mrStrm, fValue);
208     else
209         mrStrm.WriteDouble( fValue );
210     return *this;
211 }
212 
213 std::size_t XclExpStream::Write( const void* pData, std::size_t nBytes )
214 {
215     std::size_t nRet = 0;
216     if( pData && (nBytes > 0) )
217     {
218         if( mbInRec )
219         {
220             const sal_uInt8* pBuffer = static_cast< const sal_uInt8* >( pData );
221             std::size_t nBytesLeft = nBytes;
222             bool bValid = true;
223 
224             while( bValid && (nBytesLeft > 0) )
225             {
226                 std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
227                 std::size_t nWriteRet = nWriteLen;
228                 if (mbUseEncrypter && HasValidEncrypter())
229                 {
230                     OSL_ENSURE(nWriteLen > 0, "XclExpStream::Write: write length is 0!");
231                     vector<sal_uInt8> aBytes(nWriteLen);
232                     memcpy(aBytes.data(), pBuffer, nWriteLen);
233                     mxEncrypter->EncryptBytes(mrStrm, aBytes);
234                     // TODO: How do I check if all the bytes have been successfully written ?
235                 }
236                 else
237                 {
238                     nWriteRet = mrStrm.WriteBytes(pBuffer, nWriteLen);
239                     bValid = (nWriteLen == nWriteRet);
240                     OSL_ENSURE( bValid, "XclExpStream::Write - stream write error" );
241                 }
242                 pBuffer += nWriteRet;
243                 nRet += nWriteRet;
244                 nBytesLeft -= nWriteRet;
245                 UpdateSizeVars( nWriteRet );
246             }
247         }
248         else
249             nRet = mrStrm.WriteBytes(pData, nBytes);
250     }
251     return nRet;
252 }
253 
254 void XclExpStream::WriteZeroBytes( std::size_t nBytes )
255 {
256     if( mbInRec )
257     {
258         std::size_t nBytesLeft = nBytes;
259         while( nBytesLeft > 0 )
260         {
261             std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
262             WriteRawZeroBytes( nWriteLen );
263             nBytesLeft -= nWriteLen;
264             UpdateSizeVars( nWriteLen );
265         }
266     }
267     else
268         WriteRawZeroBytes( nBytes );
269 }
270 
271 void XclExpStream::WriteZeroBytesToRecord( std::size_t nBytes )
272 {
273     if (!mbInRec)
274         // not in record.
275         return;
276 
277     for (std::size_t i = 0; i < nBytes; ++i)
278         *this << sal_uInt8(0)/*nZero*/;
279 }
280 
281 void XclExpStream::CopyFromStream(SvStream& rInStrm, sal_uInt64 const nBytes)
282 {
283     sal_uInt64 const nRemaining(rInStrm.remainingSize());
284     sal_uInt64 nBytesLeft = ::std::min(nBytes, nRemaining);
285     if( nBytesLeft > 0 )
286     {
287         const std::size_t nMaxBuffer = 4096;
288         std::unique_ptr<sal_uInt8[]> pBuffer(
289             new sal_uInt8[ ::std::min<std::size_t>(nBytesLeft, nMaxBuffer) ]);
290         bool bValid = true;
291 
292         while( bValid && (nBytesLeft > 0) )
293         {
294             std::size_t nWriteLen = ::std::min<std::size_t>(nBytesLeft, nMaxBuffer);
295             rInStrm.ReadBytes(pBuffer.get(), nWriteLen);
296             std::size_t nWriteRet = Write( pBuffer.get(), nWriteLen );
297             bValid = (nWriteLen == nWriteRet);
298             nBytesLeft -= nWriteRet;
299         }
300     }
301 }
302 
303 void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags )
304 {
305     SetSliceSize( 0 );
306     nFlags &= EXC_STRF_16BIT;   // repeat only 16bit flag
307     sal_uInt16 nCharLen = nFlags ? 2 : 1;
308 
309     for( const auto& rItem : rBuffer )
310     {
311         if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) )
312         {
313             StartContinue();
314             operator<<( nFlags );
315         }
316         if( nCharLen == 2 )
317             operator<<( rItem );
318         else
319             operator<<( static_cast< sal_uInt8 >( rItem ) );
320     }
321 }
322 
323 // Xcl has an obscure sense of whether starting a new record or not,
324 // and crashes if it encounters the string header at the very end of a record.
325 // Thus we add 1 to give some room, seems like they do it that way but with another count (10?)
326 void XclExpStream::WriteByteString( const OString& rString )
327 {
328     SetSliceSize( 0 );
329     std::size_t nLen = ::std::min< std::size_t >( rString.getLength(), 0x00FF );
330     nLen = ::std::min< std::size_t >( nLen, 0xFF );
331 
332     sal_uInt16 nLeft = PrepareWrite();
333     if( mbInRec && (nLeft <= 1) )
334         StartContinue();
335 
336     operator<<( static_cast< sal_uInt8 >( nLen ) );
337     Write( rString.getStr(), nLen );
338 }
339 
340 void XclExpStream::WriteCharBuffer( const ScfUInt8Vec& rBuffer )
341 {
342     SetSliceSize( 0 );
343     Write( rBuffer.data(), rBuffer.size() );
344 }
345 
346 void XclExpStream::SetEncrypter( XclExpEncrypterRef const & xEncrypter )
347 {
348     mxEncrypter = xEncrypter;
349 }
350 
351 bool XclExpStream::HasValidEncrypter() const
352 {
353     return mxEncrypter && mxEncrypter->IsValid();
354 }
355 
356 void XclExpStream::EnableEncryption( bool bEnable )
357 {
358     mbUseEncrypter = bEnable && HasValidEncrypter();
359 }
360 
361 void XclExpStream::DisableEncryption()
362 {
363     EnableEncryption(false);
364 }
365 
366 void XclExpStream::SetSvStreamPos(sal_uInt64 const nPos)
367 {
368     OSL_ENSURE( !mbInRec, "XclExpStream::SetSvStreamPos - not allowed inside of a record" );
369     mbInRec ? 0 : mrStrm.Seek( nPos );
370 }
371 
372 // private --------------------------------------------------------------------
373 
374 void XclExpStream::InitRecord( sal_uInt16 nRecId )
375 {
376     mrStrm.Seek( STREAM_SEEK_TO_END );
377     mrStrm.WriteUInt16( nRecId );
378 
379     mnLastSizePos = mrStrm.Tell();
380     mnHeaderSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( mnPredictSize, mnCurrMaxSize ) );
381     mrStrm.WriteUInt16( mnHeaderSize );
382     mnCurrSize = mnSliceSize = 0;
383 }
384 
385 void XclExpStream::UpdateRecSize()
386 {
387     if( mnCurrSize != mnHeaderSize )
388     {
389         mrStrm.Seek( mnLastSizePos );
390         mrStrm.WriteUInt16( mnCurrSize );
391     }
392 }
393 
394 void XclExpStream::UpdateSizeVars( std::size_t nSize )
395 {
396     OSL_ENSURE( mnCurrSize + nSize <= mnCurrMaxSize, "XclExpStream::UpdateSizeVars - record overwritten" );
397     mnCurrSize = mnCurrSize + static_cast< sal_uInt16 >( nSize );
398 
399     if( mnMaxSliceSize > 0 )
400     {
401         OSL_ENSURE( mnSliceSize + nSize <= mnMaxSliceSize, "XclExpStream::UpdateSizeVars - slice overwritten" );
402         mnSliceSize = mnSliceSize + static_cast< sal_uInt16 >( nSize );
403         if( mnSliceSize >= mnMaxSliceSize )
404             mnSliceSize = 0;
405     }
406 }
407 
408 void XclExpStream::StartContinue()
409 {
410     UpdateRecSize();
411     mnCurrMaxSize = mnMaxContSize;
412     mnPredictSize -= mnCurrSize;
413     InitRecord( EXC_ID_CONT );
414 }
415 
416 void XclExpStream::PrepareWrite( sal_uInt16 nSize )
417 {
418     if( mbInRec )
419     {
420         if( (mnCurrSize + nSize > mnCurrMaxSize) ||
421             ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
422             StartContinue();
423         UpdateSizeVars( nSize );
424     }
425 }
426 
427 sal_uInt16 XclExpStream::PrepareWrite()
428 {
429     sal_uInt16 nRet = 0;
430     if( mbInRec )
431     {
432         if( (mnCurrSize >= mnCurrMaxSize) ||
433             ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
434             StartContinue();
435         UpdateSizeVars( 0 );
436 
437         nRet = (mnMaxSliceSize > 0) ? (mnMaxSliceSize - mnSliceSize) : (mnCurrMaxSize - mnCurrSize);
438     }
439     return nRet;
440 }
441 
442 void XclExpStream::WriteRawZeroBytes( std::size_t nBytes )
443 {
444     const sal_uInt32 nData = 0;
445     std::size_t nBytesLeft = nBytes;
446     while( nBytesLeft >= sizeof( nData ) )
447     {
448         mrStrm.WriteUInt32( nData );
449         nBytesLeft -= sizeof( nData );
450     }
451     if( nBytesLeft )
452         mrStrm.WriteBytes(&nData, nBytesLeft);
453 }
454 
455 XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot& rRoot ) :
456     mnOldPos(STREAM_SEEK_TO_END),
457     mbValid(false)
458 {
459     Sequence< NamedValue > aEncryptionData = rRoot.GetEncryptionData();
460     if( !aEncryptionData.hasElements() )
461         // Empty password.  Get the default biff8 password.
462         aEncryptionData = XclExpRoot::GenerateDefaultEncryptionData();
463     Init( aEncryptionData );
464 }
465 
466 XclExpBiff8Encrypter::~XclExpBiff8Encrypter()
467 {
468 }
469 
470 void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 pnSaltDigest[16] ) const
471 {
472     if ( sizeof( mpnSaltDigest ) == 16 )
473         memcpy( pnSaltDigest, mpnSaltDigest, 16 );
474 }
475 
476 void XclExpBiff8Encrypter::GetSalt( sal_uInt8 pnSalt[16] ) const
477 {
478     if ( sizeof( mpnSalt ) == 16 )
479         memcpy( pnSalt, mpnSalt, 16 );
480 }
481 
482 void XclExpBiff8Encrypter::GetDocId( sal_uInt8 pnDocId[16] ) const
483 {
484     if ( sizeof( mpnDocId ) == 16 )
485     memcpy( pnDocId, mpnDocId, 16 );
486 }
487 
488 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt8 nData )
489 {
490     vector<sal_uInt8> aByte(1);
491     aByte[0] = nData;
492     EncryptBytes(rStrm, aByte);
493 }
494 
495 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt16 nData )
496 {
497     ::std::vector<sal_uInt8> pnBytes(2);
498     pnBytes[0] = nData & 0xFF;
499     pnBytes[1] = (nData >> 8) & 0xFF;
500     EncryptBytes(rStrm, pnBytes);
501 }
502 
503 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt32 nData )
504 {
505     ::std::vector<sal_uInt8> pnBytes(4);
506     pnBytes[0] = nData & 0xFF;
507     pnBytes[1] = (nData >>  8) & 0xFF;
508     pnBytes[2] = (nData >> 16) & 0xFF;
509     pnBytes[3] = (nData >> 24) & 0xFF;
510     EncryptBytes(rStrm, pnBytes);
511 }
512 
513 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, float fValue )
514 {
515     ::std::vector<sal_uInt8> pnBytes(4);
516     memcpy(pnBytes.data(), &fValue, 4);
517     EncryptBytes(rStrm, pnBytes);
518 }
519 
520 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, double fValue )
521 {
522     ::std::vector<sal_uInt8> pnBytes(8);
523     memcpy(pnBytes.data(), &fValue, 8);
524     EncryptBytes(rStrm, pnBytes);
525 }
526 
527 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int8 nData )
528 {
529     Encrypt(rStrm, static_cast<sal_uInt8>(nData));
530 }
531 
532 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int16 nData )
533 {
534     Encrypt(rStrm, static_cast<sal_uInt16>(nData));
535 }
536 
537 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int32 nData )
538 {
539     Encrypt(rStrm, static_cast<sal_uInt32>(nData));
540 }
541 
542 void XclExpBiff8Encrypter::Init( const Sequence< NamedValue >& rEncryptionData )
543 {
544     mbValid = false;
545 
546     if( maCodec.InitCodec( rEncryptionData ) )
547     {
548         maCodec.GetDocId( mpnDocId );
549 
550         // generate the salt here
551         rtlRandomPool aRandomPool = rtl_random_createPool ();
552         rtl_random_getBytes( aRandomPool, mpnSalt, 16 );
553         rtl_random_destroyPool( aRandomPool );
554 
555         memset( mpnSaltDigest, 0, sizeof( mpnSaltDigest ) );
556 
557         // generate salt hash.
558         ::msfilter::MSCodec_Std97 aCodec;
559         aCodec.InitCodec( rEncryptionData );
560         aCodec.CreateSaltDigest( mpnSalt, mpnSaltDigest );
561 
562         // verify to make sure it's in good shape.
563         mbValid = maCodec.VerifyKey( mpnSalt, mpnSaltDigest );
564     }
565 }
566 
567 sal_uInt32 XclExpBiff8Encrypter::GetBlockPos( std::size_t nStrmPos )
568 {
569     return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
570 }
571 
572 sal_uInt16 XclExpBiff8Encrypter::GetOffsetInBlock( std::size_t nStrmPos )
573 {
574     return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
575 }
576 
577 void XclExpBiff8Encrypter::EncryptBytes( SvStream& rStrm, vector<sal_uInt8>& aBytes )
578 {
579     sal_uInt64 nStrmPos = rStrm.Tell();
580     sal_uInt16 nBlockOffset = GetOffsetInBlock(nStrmPos);
581     sal_uInt32 nBlockPos = GetBlockPos(nStrmPos);
582 
583 #if DEBUG_XL_ENCRYPTION
584     fprintf(stdout, "XclExpBiff8Encrypter::EncryptBytes: stream pos = %ld  offset in block = %d  block pos = %ld\n",
585             nStrmPos, nBlockOffset, nBlockPos);
586 #endif
587 
588     sal_uInt16 nSize = static_cast< sal_uInt16 >( aBytes.size() );
589     if (nSize == 0)
590         return;
591 
592 #if DEBUG_XL_ENCRYPTION
593     fprintf(stdout, "RAW: ");
594     for (sal_uInt16 i = 0; i < nSize; ++i)
595         fprintf(stdout, "%2.2X ", aBytes[i]);
596     fprintf(stdout, "\n");
597 #endif
598 
599     if (mnOldPos != nStrmPos)
600     {
601         sal_uInt16 nOldOffset = GetOffsetInBlock(mnOldPos);
602         sal_uInt32 nOldBlockPos = GetBlockPos(mnOldPos);
603 
604         if ( (nBlockPos != nOldBlockPos) || (nBlockOffset < nOldOffset) )
605         {
606             maCodec.InitCipher(nBlockPos);
607             nOldOffset = 0;
608         }
609 
610         if (nBlockOffset > nOldOffset)
611             maCodec.Skip(nBlockOffset - nOldOffset);
612     }
613 
614     sal_uInt16 nBytesLeft = nSize;
615     sal_uInt16 nPos = 0;
616     while (nBytesLeft > 0)
617     {
618         sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - nBlockOffset;
619         sal_uInt16 nEncBytes = ::std::min(nBlockLeft, nBytesLeft);
620 
621         bool bRet = maCodec.Encode(&aBytes[nPos], nEncBytes, &aBytes[nPos], nEncBytes);
622         OSL_ENSURE(bRet, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!");
623 
624         std::size_t nRet = rStrm.WriteBytes(&aBytes[nPos], nEncBytes);
625         OSL_ENSURE(nRet == nEncBytes, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!");
626 
627         nStrmPos = rStrm.Tell();
628         nBlockOffset = GetOffsetInBlock(nStrmPos);
629         nBlockPos = GetBlockPos(nStrmPos);
630         if (nBlockOffset == 0)
631             maCodec.InitCipher(nBlockPos);
632 
633         nBytesLeft -= nEncBytes;
634         nPos += nEncBytes;
635     }
636     mnOldPos = nStrmPos;
637 }
638 
639 static const char* lcl_GetErrorString( FormulaError nScErrCode )
640 {
641     sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode );
642     switch( nXclErrCode )
643     {
644         case EXC_ERR_NULL:  return "#NULL!";
645         case EXC_ERR_DIV0:  return "#DIV/0!";
646         case EXC_ERR_VALUE: return "#VALUE!";
647         case EXC_ERR_REF:   return "#REF!";
648         case EXC_ERR_NAME:  return "#NAME?";
649         case EXC_ERR_NUM:   return "#NUM!";
650         case EXC_ERR_NA:
651         default:            return "#N/A";
652     }
653 }
654 
655 void XclXmlUtils::GetFormulaTypeAndValue( ScFormulaCell& rCell, const char*& rsType, OUString& rsValue )
656 {
657     sc::FormulaResultValue aResValue = rCell.GetResult();
658 
659     switch (aResValue.meType)
660     {
661         case sc::FormulaResultValue::Error:
662             rsType = "e";
663             rsValue = ToOUString(lcl_GetErrorString(aResValue.mnError));
664         break;
665         case sc::FormulaResultValue::Value:
666             rsType = "n";
667             rsValue = OUString::number(aResValue.mfValue);
668         break;
669         case sc::FormulaResultValue::String:
670             rsType = "str";
671             rsValue = rCell.GetString().getString();
672         break;
673         case sc::FormulaResultValue::Invalid:
674         default:
675             // TODO : double-check this to see if this is correct.
676             rsType = "inlineStr";
677             rsValue = rCell.GetString().getString();
678     }
679 }
680 
681 OUString XclXmlUtils::GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId )
682 {
683     OUStringBuffer sBuf;
684     if( sStreamDir )
685         sBuf.appendAscii( sStreamDir );
686     sBuf.appendAscii( sStream );
687     if( nId )
688         sBuf.append( nId );
689     if( strstr(sStream, "vml") )
690         sBuf.append( ".vml" );
691     else
692         sBuf.append( ".xml" );
693     return sBuf.makeStringAndClear();
694 }
695 
696 OString XclXmlUtils::ToOString( const Color& rColor )
697 {
698     char buf[9];
699     sprintf( buf, "%.2X%.2X%.2X%.2X", 0xFF-rColor.GetTransparency(), rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
700     buf[8] = '\0';
701     return buf;
702 }
703 
704 OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const ScAddress& rAddress )
705 {
706     rAddress.Format(s, ScRefFlags::VALID, nullptr, ScAddress::Details( FormulaGrammar::CONV_XL_A1));
707     return s;
708 }
709 
710 OString XclXmlUtils::ToOString( const ScfUInt16Vec& rBuffer )
711 {
712     if(rBuffer.empty())
713         return OString();
714 
715     const sal_uInt16* pBuffer = rBuffer.data();
716     return OString(
717         reinterpret_cast<sal_Unicode const *>(pBuffer), rBuffer.size(),
718         RTL_TEXTENCODING_UTF8);
719 }
720 
721 OString XclXmlUtils::ToOString( const ScRange& rRange, bool bFullAddressNotation )
722 {
723     OUString sRange(rRange.Format( ScRefFlags::VALID, nullptr,
724                                    ScAddress::Details( FormulaGrammar::CONV_XL_A1 ),
725                                    bFullAddressNotation ) );
726     return sRange.toUtf8();
727 }
728 
729 OString XclXmlUtils::ToOString( const ScRangeList& rRangeList )
730 {
731     OUString s;
732     rRangeList.Format(s, ScRefFlags::VALID, nullptr, FormulaGrammar::CONV_XL_OOX, ' ');
733     return s.toUtf8();
734 }
735 
736 static ScAddress lcl_ToAddress( const XclAddress& rAddress )
737 {
738     ScAddress aAddress;
739 
740     // For some reason, ScRange::Format() returns omits row numbers if
741     // the row is >= MAXROW or the column is >= MAXCOL, and Excel doesn't
742     // like "A:IV" (i.e. no row numbers).  Prevent this.
743     // KOHEI: Find out if the above comment is still true.
744     aAddress.SetRow( std::min<sal_Int32>( rAddress.mnRow, MAXROW ) );
745     aAddress.SetCol( static_cast<sal_Int16>(std::min<sal_Int32>( rAddress.mnCol, MAXCOL )) );
746 
747     return aAddress;
748 }
749 
750 OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const XclAddress& rAddress )
751 {
752     return ToOString( s, lcl_ToAddress( rAddress ));
753 }
754 
755 OString XclXmlUtils::ToOString( const XclExpString& s )
756 {
757     OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
758     return ToOString( s.GetUnicodeBuffer() );
759 }
760 
761 static ScRange lcl_ToRange( const XclRange& rRange )
762 {
763     ScRange aRange;
764 
765     aRange.aStart = lcl_ToAddress( rRange.maFirst );
766     aRange.aEnd   = lcl_ToAddress( rRange.maLast );
767 
768     return aRange;
769 }
770 
771 OString XclXmlUtils::ToOString( const XclRangeList& rRanges )
772 {
773     ScRangeList aRanges;
774     for( const auto& rRange : rRanges )
775     {
776         aRanges.push_back( lcl_ToRange( rRange ) );
777     }
778     return ToOString( aRanges );
779 }
780 
781 OUString XclXmlUtils::ToOUString( const char* s )
782 {
783     return OUString( s, static_cast<sal_Int32>(strlen( s )), RTL_TEXTENCODING_ASCII_US );
784 }
785 
786 OUString XclXmlUtils::ToOUString( const ScfUInt16Vec& rBuf, sal_Int32 nStart, sal_Int32 nLength )
787 {
788     if( nLength == -1 || ( nLength > (static_cast<sal_Int32>(rBuf.size()) - nStart) ) )
789         nLength = (rBuf.size() - nStart);
790 
791     return nLength > 0
792         ? OUString(
793             reinterpret_cast<sal_Unicode const *>(&rBuf[nStart]), nLength)
794         : OUString();
795 }
796 
797 OUString XclXmlUtils::ToOUString(
798     sc::CompileFormulaContext& rCtx, const ScAddress& rAddress, const ScTokenArray* pTokenArray )
799 {
800     ScCompiler aCompiler( rCtx, rAddress, const_cast<ScTokenArray&>(*pTokenArray));
801 
802     /* TODO: isn't this the same as passed in rCtx and thus superfluous? */
803     aCompiler.SetGrammar(FormulaGrammar::GRAM_OOXML);
804 
805     OUStringBuffer aBuffer( pTokenArray->GetLen() * 5 );
806     aCompiler.CreateStringFromTokenArray( aBuffer );
807     return aBuffer.makeStringAndClear();
808 }
809 
810 OUString XclXmlUtils::ToOUString( const XclExpString& s )
811 {
812     OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
813     return ToOUString( s.GetUnicodeBuffer() );
814 }
815 
816 sax_fastparser::FSHelperPtr XclXmlUtils::WriteElement( sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, sal_Int32 nValue )
817 {
818     pStream->startElement(nElement);
819     pStream->write( nValue );
820     pStream->endElement( nElement );
821 
822     return pStream;
823 }
824 
825 sax_fastparser::FSHelperPtr XclXmlUtils::WriteElement( sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, sal_Int64 nValue )
826 {
827     pStream->startElement(nElement);
828     pStream->write( nValue );
829     pStream->endElement( nElement );
830 
831     return pStream;
832 }
833 
834 sax_fastparser::FSHelperPtr XclXmlUtils::WriteElement( sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, const char* sValue )
835 {
836     pStream->startElement(nElement);
837     pStream->write( sValue );
838     pStream->endElement( nElement );
839 
840     return pStream;
841 }
842 
843 static void lcl_WriteValue( const sax_fastparser::FSHelperPtr& rStream, sal_Int32 nElement, const char* pValue )
844 {
845     if( !pValue )
846         return;
847     rStream->singleElement(nElement, XML_val, pValue);
848 }
849 
850 static const char* lcl_GetUnderlineStyle( FontLineStyle eUnderline, bool& bHaveUnderline )
851 {
852     bHaveUnderline = true;
853     switch( eUnderline )
854     {
855         // OOXTODO: doubleAccounting, singleAccounting
856         // OOXTODO: what should be done with the other FontLineStyle values?
857         case LINESTYLE_SINGLE:  return "single";
858         case LINESTYLE_DOUBLE:  return "double";
859         case LINESTYLE_NONE:
860         default:                bHaveUnderline = false; return "none";
861     }
862 }
863 
864 static const char* lcl_ToVerticalAlignmentRun( SvxEscapement eEscapement, bool& bHaveAlignment )
865 {
866     bHaveAlignment = true;
867     switch( eEscapement )
868     {
869         case SvxEscapement::Superscript:    return "superscript";
870         case SvxEscapement::Subscript:      return "subscript";
871         case SvxEscapement::Off:
872         default:                            bHaveAlignment = false; return "baseline";
873     }
874 }
875 
876 sax_fastparser::FSHelperPtr XclXmlUtils::WriteFontData( sax_fastparser::FSHelperPtr pStream, const XclFontData& rFontData, sal_Int32 nFontId )
877 {
878     bool bHaveUnderline, bHaveVertAlign;
879     const char* pUnderline = lcl_GetUnderlineStyle( rFontData.GetScUnderline(), bHaveUnderline );
880     const char* pVertAlign = lcl_ToVerticalAlignmentRun( rFontData.GetScEscapement(), bHaveVertAlign );
881 
882     lcl_WriteValue( pStream, XML_b,          rFontData.mnWeight > 400 ? ToPsz( true ) : nullptr );
883     lcl_WriteValue( pStream, XML_i,          rFontData.mbItalic       ? ToPsz( true ) : nullptr );
884     lcl_WriteValue( pStream, XML_strike,     rFontData.mbStrikeout    ? ToPsz( true ) : nullptr );
885     // OOXTODO: lcl_WriteValue( rStream, XML_condense, );    // mac compatibility setting
886     // OOXTODO: lcl_WriteValue( rStream, XML_extend, );      // compatibility setting
887     lcl_WriteValue( pStream, XML_outline,    rFontData.mbOutline      ? ToPsz( true ) : nullptr );
888     lcl_WriteValue( pStream, XML_shadow,     rFontData.mbShadow       ? ToPsz( true ) : nullptr );
889     lcl_WriteValue( pStream, XML_u,          bHaveUnderline           ? pUnderline    : nullptr );
890     lcl_WriteValue( pStream, XML_vertAlign,  bHaveVertAlign           ? pVertAlign    : nullptr );
891     lcl_WriteValue( pStream, XML_sz,         OString::number( rFontData.mnHeight / 20.0 ).getStr() );  // Twips->Pt
892     if( rFontData.maColor != Color( 0xFF, 0xFF, 0xFF, 0xFF ) )
893         pStream->singleElement( XML_color,
894                 // OOXTODO: XML_auto,       bool
895                 // OOXTODO: XML_indexed,    uint
896                 XML_rgb, XclXmlUtils::ToOString(rFontData.maColor)
897                 // OOXTODO: XML_theme,      index into <clrScheme/>
898                 // OOXTODO: XML_tint,       double
899         );
900     lcl_WriteValue( pStream, nFontId,        rFontData.maName.toUtf8().getStr() );
901     lcl_WriteValue( pStream, XML_family,     OString::number(  rFontData.mnFamily ).getStr() );
902     lcl_WriteValue( pStream, XML_charset,    rFontData.mnCharSet != 0 ? OString::number(  rFontData.mnCharSet ).getStr() : nullptr );
903 
904     return pStream;
905 }
906 
907 XclExpXmlStream::XclExpXmlStream( const uno::Reference< XComponentContext >& rCC, bool bExportVBA, bool bExportTemplate )
908     : XmlFilterBase( rCC ),
909       mpRoot( nullptr ),
910       mbExportVBA(bExportVBA),
911       mbExportTemplate(bExportTemplate)
912 {
913 }
914 
915 XclExpXmlStream::~XclExpXmlStream()
916 {
917     assert(maStreams.empty() && "Forgotten PopStream()?");
918 }
919 
920 sax_fastparser::FSHelperPtr& XclExpXmlStream::GetCurrentStream()
921 {
922     OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" );
923     return maStreams.top();
924 }
925 
926 void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr const & aStream )
927 {
928     maStreams.push( aStream );
929 }
930 
931 void XclExpXmlStream::PopStream()
932 {
933     OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::PopStream - stack is empty!" );
934     maStreams.pop();
935 }
936 
937 sax_fastparser::FSHelperPtr XclExpXmlStream::GetStreamForPath( const OUString& sPath )
938 {
939     if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() )
940         return sax_fastparser::FSHelperPtr();
941     return maOpenedStreamMap[ sPath ].second;
942 }
943 
944 void XclExpXmlStream::WriteAttribute(sal_Int32 nAttr, const OUString& sVal)
945 {
946     GetCurrentStream()->write(" ")->writeId(nAttr)->write("=\"")->writeEscaped(sVal)->write("\"");
947 }
948 
949 sax_fastparser::FSHelperPtr XclExpXmlStream::CreateOutputStream (
950     const OUString& sFullStream,
951     const OUString& sRelativeStream,
952     const uno::Reference< XOutputStream >& xParentRelation,
953     const char* sContentType,
954     const char* sRelationshipType,
955     OUString* pRelationshipId )
956 {
957     OUString sRelationshipId;
958     if (xParentRelation.is())
959         sRelationshipId = addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream );
960     else
961         sRelationshipId = addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream );
962 
963     if( pRelationshipId )
964         *pRelationshipId = sRelationshipId;
965 
966     sax_fastparser::FSHelperPtr p = openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
967 
968     maOpenedStreamMap[ sFullStream ] = std::make_pair( sRelationshipId, p );
969 
970     return p;
971 }
972 
973 bool XclExpXmlStream::importDocument() throw()
974 {
975     return false;
976 }
977 
978 oox::vml::Drawing* XclExpXmlStream::getVmlDrawing()
979 {
980     return nullptr;
981 }
982 
983 const oox::drawingml::Theme* XclExpXmlStream::getCurrentTheme() const
984 {
985     return nullptr;
986 }
987 
988 oox::drawingml::table::TableStyleListPtr XclExpXmlStream::getTableStyles()
989 {
990     return oox::drawingml::table::TableStyleListPtr();
991 }
992 
993 oox::drawingml::chart::ChartConverter* XclExpXmlStream::getChartConverter()
994 {
995     // DO NOT CALL
996     return nullptr;
997 }
998 
999 ScDocShell* XclExpXmlStream::getDocShell()
1000 {
1001     uno::Reference< XInterface > xModel( getModel(), UNO_QUERY );
1002 
1003     ScModelObj *pObj = dynamic_cast < ScModelObj* >( xModel.get() );
1004 
1005     if ( pObj )
1006         return static_cast < ScDocShell* >( pObj->GetEmbeddedObject() );
1007 
1008     return nullptr;
1009 }
1010 
1011 bool XclExpXmlStream::exportDocument()
1012 {
1013     ScDocShell* pShell = getDocShell();
1014     ScDocument& rDoc = pShell->GetDocument();
1015     ScRefreshTimerProtector aProt(rDoc.GetRefreshTimerControlAddress());
1016 
1017     uno::Reference<task::XStatusIndicator> xStatusIndicator = getStatusIndicator();
1018 
1019     if (xStatusIndicator.is())
1020         xStatusIndicator->start(ScResId(STR_SAVE_DOC), 100);
1021 
1022     // NOTE: Don't use SotStorage or SvStream any more, and never call
1023     // SfxMedium::GetOutStream() anywhere in the xlsx export filter code!
1024     // Instead, write via XOutputStream instance.
1025     tools::SvRef<SotStorage> rStorage = static_cast<SotStorage*>(nullptr);
1026     XclExpObjList::ResetCounters();
1027 
1028     XclExpRootData aData(
1029         EXC_BIFF8, *pShell->GetMedium (), rStorage, rDoc,
1030         msfilter::util::getBestTextEncodingFromLocale(
1031             Application::GetSettings().GetLanguageTag().getLocale()));
1032     aData.meOutput = EXC_OUTPUT_XML_2007;
1033     aData.maXclMaxPos.Set( EXC_MAXCOL_XML_2007, EXC_MAXROW_XML_2007, EXC_MAXTAB_XML_2007 );
1034     aData.maMaxPos.SetCol( ::std::min( aData.maScMaxPos.Col(), aData.maXclMaxPos.Col() ) );
1035     aData.maMaxPos.SetRow( ::std::min( aData.maScMaxPos.Row(), aData.maXclMaxPos.Row() ) );
1036     aData.maMaxPos.SetTab( ::std::min( aData.maScMaxPos.Tab(), aData.maXclMaxPos.Tab() ) );
1037     aData.mpCompileFormulaCxt.reset( new sc::CompileFormulaContext(&rDoc) );
1038     // set target path to get correct relative links to target document, not source
1039     INetURLObject aPath(getFileUrl());
1040     aData.maBasePath = aPath.GetPath() + "\\";
1041     aData.maBasePath = "file:///" + aData.maBasePath.replace('\\', '/');
1042 
1043     XclExpRoot aRoot( aData );
1044 
1045     mpRoot = &aRoot;
1046     aRoot.GetOldRoot().pER = &aRoot;
1047     aRoot.GetOldRoot().eDateiTyp = Biff8;
1048     // Get the viewsettings before processing
1049     if( ScDocShell::GetViewData() )
1050         ScDocShell::GetViewData()->WriteExtOptions( mpRoot->GetExtDocOptions() );
1051 
1052     OUString const workbook = "xl/workbook.xml";
1053     const char* pWorkbookContentType = nullptr;
1054     if (mbExportVBA)
1055     {
1056         if (mbExportTemplate)
1057         {
1058             pWorkbookContentType = "application/vnd.ms-excel.template.macroEnabled.main+xml";
1059         }
1060         else
1061         {
1062             pWorkbookContentType = "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
1063         }
1064     }
1065     else
1066     {
1067         if (mbExportTemplate)
1068         {
1069             pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
1070         }
1071         else
1072         {
1073             pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
1074         }
1075     }
1076 
1077     PushStream( CreateOutputStream( workbook, workbook,
1078                                     uno::Reference <XOutputStream>(),
1079                                     pWorkbookContentType,
1080                                     OUStringToOString(oox::getRelationship(Relationship::OFFICEDOCUMENT), RTL_TEXTENCODING_UTF8).getStr() ) );
1081 
1082     if (mbExportVBA)
1083     {
1084         VbaExport aExport(getModel());
1085         if (aExport.containsVBAProject())
1086         {
1087             SvMemoryStream aVbaStream(4096, 4096);
1088             tools::SvRef<SotStorage> pVBAStorage(new SotStorage(aVbaStream));
1089             aExport.exportVBA( pVBAStorage.get() );
1090             aVbaStream.Seek(0);
1091             css::uno::Reference<css::io::XInputStream> xVBAStream(
1092                     new utl::OInputStreamWrapper(aVbaStream));
1093             css::uno::Reference<css::io::XOutputStream> xVBAOutput =
1094                 openFragmentStream("xl/vbaProject.bin", "application/vnd.ms-office.vbaProject");
1095             comphelper::OStorageHelper::CopyInputToOutput(xVBAStream, xVBAOutput);
1096 
1097             addRelation(GetCurrentStream()->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), "vbaProject.bin");
1098         }
1099     }
1100 
1101     // destruct at the end of the block
1102     {
1103         ExcDocument aDocRoot( aRoot );
1104         if (xStatusIndicator.is())
1105             xStatusIndicator->setValue(10);
1106         aDocRoot.ReadDoc();
1107         if (xStatusIndicator.is())
1108             xStatusIndicator->setValue(40);
1109         aDocRoot.WriteXml( *this );
1110     }
1111 
1112     PopStream();
1113     // Free all FSHelperPtr, to flush data before committing storage
1114     maOpenedStreamMap.clear();
1115 
1116     commitStorage();
1117 
1118     if (xStatusIndicator.is())
1119         xStatusIndicator->end();
1120     mpRoot = nullptr;
1121     return true;
1122 }
1123 
1124 ::oox::ole::VbaProject* XclExpXmlStream::implCreateVbaProject() const
1125 {
1126     return new ::oox::xls::ExcelVbaProject( getComponentContext(), uno::Reference< XSpreadsheetDocument >( getModel(), UNO_QUERY ) );
1127 }
1128 
1129 OUString XclExpXmlStream::getImplementationName()
1130 {
1131     return "TODO";
1132 }
1133 
1134 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1135