xref: /core/oox/source/ole/vbaexport.cxx (revision ed5e4405)
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 
10 #include <sal/config.h>
11 
12 #include <cassert>
13 #include <cmath>
14 #include <random>
15 
16 #include <oox/ole/vbaexport.hxx>
17 
18 #include <tools/stream.hxx>
19 
20 #include <com/sun/star/beans/XPropertySet.hpp>
21 #include <com/sun/star/script/XLibraryContainer.hpp>
22 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
23 #include <com/sun/star/script/vba/XVBACompatibility.hpp>
24 #include <com/sun/star/frame/XModel.hpp>
25 
26 #include <ooo/vba/excel/XWorkbook.hpp>
27 
28 #include <oox/helper/binaryoutputstream.hxx>
29 #include <oox/helper/propertyset.hxx>
30 #include <oox/token/properties.hxx>
31 
32 #include <sot/storage.hxx>
33 
34 #include <comphelper/xmltools.hxx>
35 
36 #define USE_UTF8_CODEPAGE 0
37 #if USE_UTF8_CODEPAGE
38 #define CODEPAGE_MS 65001
39 #define CODEPAGE RTL_TEXTENCODING_UTF8
40 #else
41 #define CODEPAGE_MS 1252
42 #define CODEPAGE RTL_TEXTENCODING_MS_1252
43 #endif
44 
45 #define VBA_EXPORT_DEBUG 0
46 #define VBA_USE_ORIGINAL_WM_STREAM 0
47 #define VBA_USE_ORIGINAL_DIR_STREAM 0
48 #define VBA_USE_ORIGINAL_PROJECT_STREAM 0
49 #define VBA_USE_ORIGINAL_VBA_PROJECT 0
50 
51 /* Enable to see VBA Encryption work. For now the input data and length values
52  * for encryption correspond to the case when the VBA macro is not protected.
53  */
54 #define VBA_ENCRYPTION 1
55 
56 namespace {
57 
58 void exportString(SvStream& rStrm, const OUString& rString)
59 {
60     OString aStringCorrectCodepage = OUStringToOString(rString, CODEPAGE);
61     rStrm.WriteOString(aStringCorrectCodepage);
62 }
63 
64 void exportUTF16String(SvStream& rStrm, const OUString& rString)
65 {
66     sal_Int32 n = rString.getLength();
67     const sal_Unicode* pString = rString.getStr();
68     for (sal_Int32 i = 0; i < n; ++i)
69     {
70         sal_Unicode character = pString[i];
71         rStrm.WriteUnicode(character);
72     }
73 }
74 
75 bool isWorkbook(const css::uno::Reference<css::uno::XInterface>& xInterface)
76 {
77     css::uno::Reference<ooo::vba::excel::XWorkbook> xWorkbook(xInterface, css::uno::UNO_QUERY);
78     return xWorkbook.is();
79 }
80 
81 OUString createHexStringFromDigit(sal_uInt8 nDigit)
82 {
83     OUString aString = OUString::number( nDigit, 16 );
84     if(aString.getLength() == 1)
85         aString = OUString::number(0) + aString;
86     return aString.toAsciiUpperCase();
87 }
88 
89 }
90 
91 VBACompressionChunk::VBACompressionChunk(SvStream& rCompressedStream, const sal_uInt8* pData, std::size_t nChunkSize)
92     : mrCompressedStream(rCompressedStream)
93     , mpUncompressedData(pData)
94     , mpCompressedChunkStream(nullptr)
95     , mnChunkSize(nChunkSize)
96     , mnCompressedCurrent(0)
97     , mnCompressedEnd(0)
98     , mnDecompressedCurrent(0)
99     , mnDecompressedEnd(0)
100 {
101 }
102 
103 static void setUInt16(sal_uInt8* pBuffer, size_t nPos, sal_uInt16 nVal)
104 {
105     pBuffer[nPos] = nVal & 0xFF;
106     pBuffer[nPos+1] = (nVal & 0xFF00) >> 8;
107 }
108 
109 sal_uInt16 VBACompressionChunk::handleHeader(bool bCompressed)
110 {
111     // handle header bytes
112     size_t nSize = mnCompressedCurrent;
113     sal_uInt16 nHeader = 0;
114     PackCompressedChunkSize(nSize, nHeader);
115     PackCompressedChunkFlag(bCompressed, nHeader);
116     PackCompressedChunkSignature(nHeader);
117 
118     return nHeader;
119 }
120 
121 // section 2.4.1.3.7
122 void VBACompressionChunk::write()
123 {
124 
125     mnDecompressedCurrent = 0;
126     mnCompressedCurrent = 2;
127     mnCompressedEnd = 4098;
128     mnDecompressedEnd = std::min<sal_uInt64>(4096, mnChunkSize);
129 
130     // if that stream becomes larger than 4096 bytes then
131     // we use the uncompressed stream
132     sal_uInt8 pCompressedChunkStream[4098];
133     mpCompressedChunkStream = pCompressedChunkStream;
134 
135     while (mnDecompressedCurrent < mnDecompressedEnd
136             && mnCompressedCurrent < mnCompressedEnd)
137     {
138         // compress token sequence
139         compressTokenSequence();
140     }
141 
142     if (mnDecompressedCurrent < mnDecompressedEnd)
143     {
144         sal_uInt64 nChunkStart = mrCompressedStream.Tell();
145         mrCompressedStream.WriteUInt16(0);
146         writeRawChunk();
147         mrCompressedStream.Seek(nChunkStart);
148         sal_uInt16 nHeader = handleHeader(false);
149         mrCompressedStream.WriteUInt16(nHeader);
150     }
151     else
152     {
153         sal_uInt16 nHeader = handleHeader(true);
154         setUInt16(pCompressedChunkStream, 0, nHeader);
155         // copy the compressed stream to our output stream
156         mrCompressedStream.WriteBytes(pCompressedChunkStream, mnCompressedCurrent);
157     }
158 }
159 
160 // section 2.4.1.3.13
161 void VBACompressionChunk::PackCompressedChunkSize(size_t nSize, sal_uInt16& rHeader)
162 {
163     sal_uInt16 nTemp1 = rHeader & 0xF000;
164     sal_uInt16 nTemp2 = nSize - 3;
165     rHeader = nTemp1 | nTemp2;
166 }
167 
168 // section 2.4.1.3.16
169 void VBACompressionChunk::PackCompressedChunkFlag(bool bCompressed, sal_uInt16& rHeader)
170 {
171     sal_uInt16 nTemp1 = rHeader & 0x7FFF;
172     sal_uInt16 nTemp2 = static_cast<sal_uInt16>(bCompressed) << 15;
173     rHeader = nTemp1 | nTemp2;
174 }
175 
176 // section 2.4.1.3.14
177 void VBACompressionChunk::PackCompressedChunkSignature(sal_uInt16& rHeader)
178 {
179     sal_Int32 nTemp = rHeader & 0x8FFFF;
180     rHeader = nTemp | 0x3000;
181 }
182 
183 // section 2.4.1.3.8
184 void VBACompressionChunk::compressTokenSequence()
185 {
186     sal_uInt64 nFlagByteIndex = mnCompressedCurrent;
187     sal_uInt8 nFlagByte = 0;
188     ++mnCompressedCurrent;
189     for (size_t index = 0; index <= 7; ++index)
190     {
191         if (mnDecompressedCurrent < mnDecompressedEnd
192                 && mnCompressedCurrent < mnCompressedEnd)
193         {
194             compressToken(index, nFlagByte);
195         }
196     }
197     mpCompressedChunkStream[nFlagByteIndex] = nFlagByte;
198 }
199 
200 // section 2.4.1.3.9
201 void VBACompressionChunk::compressToken(size_t index, sal_uInt8& nFlagByte)
202 {
203     size_t nLength = 0;
204     size_t nOffset = 0;
205     match(nLength, nOffset);
206     if (nOffset != 0)
207     {
208         if (mnCompressedCurrent + 1 < mnCompressedEnd)
209         {
210             sal_uInt16 nToken = CopyToken(nLength, nOffset);
211             setUInt16(mpCompressedChunkStream, mnCompressedCurrent, nToken);
212             SetFlagBit(index, true, nFlagByte);
213             mnCompressedCurrent += 2;
214             mnDecompressedCurrent += nLength;
215         }
216         else
217         {
218             mnCompressedCurrent = mnCompressedEnd;
219         }
220     }
221     else
222     {
223         if (mnCompressedCurrent + 1 < mnCompressedEnd)
224         {
225             mpCompressedChunkStream[mnCompressedCurrent] = mpUncompressedData[mnDecompressedCurrent];
226             ++mnCompressedCurrent;
227             ++mnDecompressedCurrent;
228         }
229         else
230         {
231             mnCompressedCurrent = mnCompressedEnd;
232         }
233     }
234 }
235 
236 // section 2.4.1.3.18
237 void VBACompressionChunk::SetFlagBit(size_t index, bool bVal, sal_uInt8& rFlag)
238 {
239     size_t nTemp1 = static_cast<int>(bVal) << index;
240     sal_uInt8 nTemp2 = rFlag & (~nTemp1);
241     rFlag = nTemp2 | nTemp1;
242 }
243 
244 // section 2.4.1.3.19.3
245 sal_uInt16 VBACompressionChunk::CopyToken(size_t nLength, size_t nOffset)
246 {
247     sal_uInt16 nLengthMask = 0;
248     sal_uInt16 nOffsetMask = 0;
249     sal_uInt16 nBitCount = 0;
250     sal_uInt16 nMaxLength;
251     CopyTokenHelp(nLengthMask, nOffsetMask, nBitCount, nMaxLength);
252     sal_uInt16 nTemp1 = nOffset -1;
253     sal_uInt16 nTemp2 = 16 - nBitCount;
254     sal_uInt16 nTemp3 = nLength - 3;
255     sal_uInt16 nToken = (nTemp1 << nTemp2) | nTemp3;
256     return nToken;
257 }
258 
259 // section 2.4.1.3.19.4
260 void VBACompressionChunk::match(size_t& rLength, size_t& rOffset)
261 {
262     size_t nBestLen = 0;
263     sal_Int32 nCandidate = mnDecompressedCurrent - 1;
264     sal_Int32 nBestCandidate = nCandidate;
265     while (nCandidate >= 0)
266     {
267         sal_Int32 nC = nCandidate;
268         sal_Int32 nD = mnDecompressedCurrent;
269         size_t nLen = 0;
270         while (nD < static_cast<sal_Int32>(mnChunkSize) // TODO: check if this needs to be including a minus -1
271                 && mpUncompressedData[nC] == mpUncompressedData[nD])
272         {
273             ++nLen;
274             ++nC;
275             ++nD;
276         }
277         if (nLen > nBestLen)
278         {
279             nBestLen = nLen;
280             nBestCandidate = nCandidate;
281         }
282         --nCandidate;
283     }
284 
285     if (nBestLen >= 3)
286     {
287         sal_uInt16 nMaximumLength = 0;
288         sal_uInt16 nLengthMask, nOffsetMask, nBitCount;
289         CopyTokenHelp(nLengthMask, nOffsetMask, nBitCount, nMaximumLength);
290         rLength = std::min<sal_uInt16>(nMaximumLength, nBestLen);
291         rOffset = mnDecompressedCurrent - nBestCandidate;
292     }
293     else
294     {
295         rLength = 0;
296         rOffset = 0;
297     }
298 }
299 
300 // section 2.4.1.3.19.1
301 void VBACompressionChunk::CopyTokenHelp(sal_uInt16& rLengthMask, sal_uInt16& rOffsetMask,
302         sal_uInt16& rBitCount, sal_uInt16& rMaximumLength)
303 {
304     sal_uInt16 nDifference = mnDecompressedCurrent;
305     assert(nDifference <= 4096);
306     assert(nDifference >= 1);
307     if (nDifference >= 2049)
308         rBitCount = 12;
309     else if (nDifference >= 1025)
310         rBitCount = 11;
311     else if (nDifference >= 513)
312         rBitCount = 10;
313     else if (nDifference >= 257)
314         rBitCount = 9;
315     else if (nDifference >= 129)
316         rBitCount = 8;
317     else if (nDifference >= 65)
318         rBitCount = 7;
319     else if (nDifference >= 33)
320         rBitCount = 6;
321     else if (nDifference >= 17)
322         rBitCount = 5;
323     else
324         rBitCount = 4;
325     rLengthMask = 0xffff >> rBitCount;
326     rOffsetMask = ~rLengthMask;
327     rMaximumLength = rLengthMask + 3;
328 }
329 
330 // section 2.4.1.3.10
331 void VBACompressionChunk::writeRawChunk()
332 {
333     // we need to use up to 4096 bytes of the original stream
334     // and fill the rest with padding
335     mrCompressedStream.WriteBytes(mpUncompressedData, mnChunkSize);
336     std::size_t nPadding = 4096 - mnChunkSize;
337     for (size_t i = 0; i < nPadding; ++i)
338     {
339         mrCompressedStream.WriteUInt8(0);
340     }
341 }
342 
343 VBACompression::VBACompression(SvStream& rCompressedStream,
344         SvMemoryStream& rUncompressedStream):
345     mrCompressedStream(rCompressedStream),
346     mrUncompressedStream(rUncompressedStream)
347 {
348 }
349 
350 // section 2.4.1.3.6
351 void VBACompression::write()
352 {
353     // section 2.4.1.1.1
354     mrCompressedStream.WriteUInt8(0x01); // signature byte of a compressed container
355     bool bStreamNotEnded = true;
356     const sal_uInt8* pData = static_cast<const sal_uInt8*>(mrUncompressedStream.GetData());
357     std::size_t nSize = mrUncompressedStream.GetEndOfData();
358     std::size_t nRemainingSize = nSize;
359     while(bStreamNotEnded)
360     {
361         std::size_t nChunkSize = std::min<size_t>(nRemainingSize, 4096);
362         VBACompressionChunk aChunk(mrCompressedStream, &pData[nSize - nRemainingSize], nChunkSize);
363         aChunk.write();
364 
365         // update the uncompressed chunk start marker
366         nRemainingSize -= nChunkSize;
367         bStreamNotEnded = nRemainingSize != 0;
368     }
369 }
370 
371 // section 2.4.3
372 #if VBA_ENCRYPTION
373 
374 VBAEncryption::VBAEncryption(const sal_uInt8* pData, const sal_uInt16 length, SvStream& rEncryptedData, sal_uInt8 nProjKey)
375     :mpData(pData)
376     ,mnLength(length)
377     ,mrEncryptedData(rEncryptedData)
378     ,mnUnencryptedByte1(0)
379     ,mnEncryptedByte1(0)
380     ,mnEncryptedByte2(0)
381     ,mnProjKey(nProjKey)
382     ,mnIgnoredLength(0)
383     ,mnSeed(0x00)
384     ,mnVersionEnc(0)
385 {
386     std::random_device rd;
387     std::mt19937 gen(rd());
388     std::uniform_int_distribution<> dis(0, 255);
389     mnSeed = dis(gen);
390 }
391 
392 void VBAEncryption::writeSeed()
393 {
394     exportString(mrEncryptedData, createHexStringFromDigit(mnSeed));
395 }
396 
397 void VBAEncryption::writeVersionEnc()
398 {
399     static const sal_uInt8 mnVersion = 2; // the encrypted version
400     mnVersionEnc = mnSeed ^ mnVersion;
401     exportString(mrEncryptedData, createHexStringFromDigit(mnVersionEnc));
402 }
403 
404 sal_uInt8 VBAEncryption::calculateProjKey(const OUString& rProjectKey)
405 {
406     sal_uInt8 nProjKey = 0;
407     sal_Int32 n = rProjectKey.getLength();
408     const sal_Unicode* pString = rProjectKey.getStr();
409     for (sal_Int32 i = 0; i < n; ++i)
410     {
411         sal_Unicode character = pString[i];
412         nProjKey += character;
413     }
414 
415     return nProjKey;
416 }
417 
418 void VBAEncryption::writeProjKeyEnc()
419 {
420     sal_uInt8 nProjKeyEnc = mnSeed ^ mnProjKey;
421     exportString(mrEncryptedData, createHexStringFromDigit(nProjKeyEnc));
422     mnUnencryptedByte1 = mnProjKey;
423     mnEncryptedByte1 = nProjKeyEnc; // ProjKeyEnc
424     mnEncryptedByte2 = mnVersionEnc; // VersionEnc
425 }
426 
427 void VBAEncryption::writeIgnoredEnc()
428 {
429     mnIgnoredLength = (mnSeed & 6) / 2;
430     for(sal_Int32 i = 1; i <= mnIgnoredLength; ++i)
431     {
432         sal_uInt8 nTempValue = 0xBE; // Any value can be assigned here
433         sal_uInt8 nByteEnc = nTempValue ^ (mnEncryptedByte2 + mnUnencryptedByte1);
434         exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc));
435         mnEncryptedByte2 = mnEncryptedByte1;
436         mnEncryptedByte1 = nByteEnc;
437         mnUnencryptedByte1 = nTempValue;
438     }
439 }
440 
441 void VBAEncryption::writeDataLengthEnc()
442 {
443     sal_uInt16 temp = mnLength;
444     for(sal_Int8 i = 0; i < 4; ++i)
445     {
446         sal_uInt8 nByte = temp & 0xFF;
447         sal_uInt8 nByteEnc = nByte ^ (mnEncryptedByte2 + mnUnencryptedByte1);
448         exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc));
449         mnEncryptedByte2 = mnEncryptedByte1;
450         mnEncryptedByte1 = nByteEnc;
451         mnUnencryptedByte1 = nByte;
452         temp >>= 8;
453     }
454 }
455 
456 void VBAEncryption::writeDataEnc()
457 {
458     for(sal_Int16 i = 0; i < mnLength; i++)
459     {
460         sal_uInt8 nByteEnc = mpData[i] ^ (mnEncryptedByte2 + mnUnencryptedByte1);
461         exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc));
462         mnEncryptedByte2 = mnEncryptedByte1;
463         mnEncryptedByte1 = nByteEnc;
464         mnUnencryptedByte1 = mpData[i];
465     }
466 }
467 
468 void VBAEncryption::write()
469 {
470     writeSeed();
471     writeVersionEnc();
472     writeProjKeyEnc();
473     writeIgnoredEnc();
474     writeDataLengthEnc();
475     writeDataEnc();
476 }
477 
478 #endif
479 
480 VbaExport::VbaExport(css::uno::Reference<css::frame::XModel> const & xModel):
481     mxModel(xModel)
482 {
483 }
484 
485 namespace {
486 
487 // section 2.3.4.2.1.1
488 void writePROJECTSYSKIND(SvStream& rStrm)
489 {
490     rStrm.WriteUInt16(0x0001); // id
491     rStrm.WriteUInt32(0x00000004); // size
492     rStrm.WriteUInt32(0x00000001); // SysKind, hard coded to 32-bin windows for now
493 }
494 
495 // section 2.3.4.2.1.2
496 void writePROJECTLCID(SvStream& rStrm)
497 {
498     rStrm.WriteUInt16(0x0002); // id
499     rStrm.WriteUInt32(0x00000004); // size
500     rStrm.WriteUInt32(0x00000409); // Lcid
501 }
502 
503 // section 2.3.4.2.1.3
504 void writePROJECTLCIDINVOKE(SvStream& rStrm)
505 {
506     rStrm.WriteUInt16(0x0014); // id
507     rStrm.WriteUInt32(0x00000004); // size
508     rStrm.WriteUInt32(0x00000409); // LcidInvoke
509 }
510 
511 // section 2.3.4.2.1.4
512 void writePROJECTCODEPAGE(SvStream& rStrm)
513 {
514     rStrm.WriteUInt16(0x0003); // id
515     rStrm.WriteUInt32(0x00000002); // size
516     rStrm.WriteUInt16(CODEPAGE_MS); // CodePage
517 }
518 
519 //section 2.3.4.2.1.5
520 void writePROJECTNAME(SvStream& rStrm, const OUString& name)
521 {
522     rStrm.WriteUInt16(0x0004); // id
523     sal_uInt32 sizeOfProjectName = name.getLength();
524     rStrm.WriteUInt32(sizeOfProjectName); // sizeOfProjectName
525     exportString(rStrm, name); // ProjectName
526 }
527 
528 //section 2.3.4.2.1.6
529 void writePROJECTDOCSTRING(SvStream& rStrm)
530 {
531     rStrm.WriteUInt16(0x0005); // id
532     rStrm.WriteUInt32(0x00000000); // sizeOfDocString
533     rStrm.WriteUInt16(0x0040); // Reserved
534     rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode, MUST be even
535 }
536 
537 //section 2.3.4.2.1.7
538 void writePROJECTHELPFILEPATH(SvStream& rStrm)
539 {
540     rStrm.WriteUInt16(0x0006); // id
541     rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile1
542     rStrm.WriteUInt16(0x003D); // Reserved
543     rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile2
544 }
545 
546 //section 2.3.4.2.1.8
547 void writePROJECTHELPCONTEXT(SvStream& rStrm)
548 {
549     rStrm.WriteUInt16(0x0007); // id
550     rStrm.WriteUInt32(0x00000004); // size
551     rStrm.WriteUInt32(0x00000000); // HelpContext
552 }
553 
554 //section 2.3.4.2.1.9
555 void writePROJECTLIBFLAGS(SvStream& rStrm)
556 {
557     rStrm.WriteUInt16(0x0008); // id
558     rStrm.WriteUInt32(0x00000004); // size
559     rStrm.WriteUInt32(0x00000000); // ProjectLibFlags
560 }
561 
562 //section 2.3.4.2.1.10
563 void writePROJECTVERSION(SvStream& rStrm)
564 {
565     rStrm.WriteUInt16(0x0009); // id
566     rStrm.WriteUInt32(0x00000004); // Reserved
567     rStrm.WriteUInt32(1467127224); // VersionMajor // TODO: where is this magic number coming from
568     rStrm.WriteUInt16(5); // VersionMinor // TODO: where is this magic number coming from
569 }
570 
571 //section 2.3.4.2.1.11
572 void writePROJECTCONSTANTS(SvStream& rStrm)
573 {
574     rStrm.WriteUInt16(0x000C); // id
575     rStrm.WriteUInt32(0x00000000); // sizeOfConstants
576     rStrm.WriteUInt16(0x003C); // Reserved
577     rStrm.WriteUInt32(0x00000000); // sizeOfConstantsUnicode
578 }
579 
580 // section 2.3.4.2.1
581 void writePROJECTINFORMATION(SvStream& rStrm, const OUString& projectName)
582 {
583     writePROJECTSYSKIND(rStrm);
584     writePROJECTLCID(rStrm);
585     writePROJECTLCIDINVOKE(rStrm);
586     writePROJECTCODEPAGE(rStrm);
587     writePROJECTNAME(rStrm, projectName);
588     writePROJECTDOCSTRING(rStrm);
589     writePROJECTHELPFILEPATH(rStrm);
590     writePROJECTHELPCONTEXT(rStrm);
591     writePROJECTLIBFLAGS(rStrm);
592     writePROJECTVERSION(rStrm);
593     writePROJECTCONSTANTS(rStrm);
594 }
595 
596 // section 2.3.4.2.2.2
597 void writeREFERENCENAME(SvStream& rStrm, const OUString& name)
598 {
599     rStrm.WriteUInt16(0x0016); // id
600     sal_Int32 size = name.getLength();
601     rStrm.WriteUInt32(size); // sizeOfName
602     exportString(rStrm, name); // name
603     rStrm.WriteUInt16(0x003E); // reserved
604     sal_Int32 unicodesize = size * 2;
605     rStrm.WriteUInt32(unicodesize); // sizeOfNameUnicode
606     exportUTF16String(rStrm, name); // nameUnicode
607 }
608 
609 // section 2.3.4.2.2.5
610 void writeREFERENCEREGISTERED(SvStream& rStrm, const OUString& libid)
611 {
612     rStrm.WriteUInt16(0x000D); // id
613     sal_Int32 sizeOfLibid = libid.getLength();
614     sal_Int32 size = sizeOfLibid + 10; // size of Libid, sizeOfLibid(4 bytes), reserved 1(4 bytes) and reserved 2(2 bytes)
615     rStrm.WriteUInt32(size); // size
616     rStrm.WriteUInt32(sizeOfLibid); // sizeOfLibid
617     exportString(rStrm, libid); // Libid
618     rStrm.WriteUInt32(0x00000000); // reserved 1
619     rStrm.WriteUInt16(0x0000); // reserved 2
620 }
621 
622 // section 2.3.4.2.2.1
623 void writeREFERENCE(SvStream& rStrm, const OUString& name, const OUString& libid)
624 {
625     writeREFERENCENAME(rStrm, name);
626     writeREFERENCEREGISTERED(rStrm, libid);
627 }
628 
629 // section 2.3.4.2.2
630 void writePROJECTREFERENCES(SvStream& rStrm)
631 {
632     // TODO: find out where these references are coming from
633     writeREFERENCE(rStrm, "stdole", "*\\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\\Windows\\SysWOW64\\stdole2.tlb#OLE Automation");
634     writeREFERENCE(rStrm, "Office", "*\\G{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}#2.0#0#C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\OFFICE14\\MSO.DLL#Microsoft Office 14.0 Object Library");
635 }
636 
637 // section 2.3.4.2.3.1
638 void writePROJECTCOOKIE(SvStream& rStrm)
639 {
640     rStrm.WriteUInt16(0x0013); // id
641     rStrm.WriteUInt32(0x00000002); // size
642     rStrm.WriteUInt16(0xFFFF); // cookie
643 }
644 
645 // section 2.3.4.2.3.2.1
646 void writeMODULENAME(SvStream& rStrm, const OUString& name)
647 {
648     rStrm.WriteUInt16(0x0019); // id
649     sal_Int32 n = name.getLength(); // sizeOfModuleName
650     rStrm.WriteUInt32(n);
651     exportString(rStrm, name); // ModuleName
652 }
653 
654 // section 2.3.4.2.3.2.2
655 void writeMODULENAMEUNICODE(SvStream& rStrm, const OUString& name)
656 {
657     rStrm.WriteUInt16(0x0047); // id
658     sal_Int32 n = name.getLength() * 2; // sizeOfModuleNameUnicode // TODO: better calculation for unicode string length
659     rStrm.WriteUInt32(n);
660     exportUTF16String(rStrm, name); // ModuleNameUnicode
661 }
662 
663 // section 2.3.4.2.3.2.3
664 void writeMODULESTREAMNAME(SvStream& rStrm, const OUString& streamName)
665 {
666     rStrm.WriteUInt16(0x001A); // id
667     sal_Int32 n = streamName.getLength(); // sizeOfStreamName
668     rStrm.WriteUInt32(n);
669     exportString(rStrm, streamName); // StreamName
670     rStrm.WriteUInt16(0x0032); // reserved
671     rStrm.WriteUInt32(n * 2); // sizeOfStreamNameUnicode // TODO: better calculation for unicode string length
672     exportUTF16String(rStrm, streamName); // StreamNameUnicode
673 }
674 
675 // section 2.3.4.2.3.2.4
676 void writeMODULEDOCSTRING(SvStream& rStrm)
677 {
678     rStrm.WriteUInt16(0x001C); // id
679     rStrm.WriteUInt32(0x00000000); // sizeOfDocString
680     rStrm.WriteUInt16(0x0048); // reserved
681     rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode
682 }
683 
684 // section 2.3.4.2.3.2.5
685 void writeMODULEOFFSET(SvStream& rStrm)
686 {
687     rStrm.WriteUInt16(0x0031); // id
688     rStrm.WriteUInt32(0x00000004); // sizeOfTextOffset
689     rStrm.WriteUInt32(0x00000000); // TextOffset
690 }
691 
692 // section 2.3.4.2.3.2.6
693 void writeMODULEHELPCONTEXT(SvStream& rStrm)
694 {
695     rStrm.WriteUInt16(0x001E); // id
696     rStrm.WriteUInt32(0x00000004); // sizeOfHelpContext
697     rStrm.WriteUInt32(0x00000000); // HelpContext
698 }
699 
700 // section 2.3.4.2.3.2.7
701 void writeMODULECOOKIE(SvStream& rStrm)
702 {
703     rStrm.WriteUInt16(0x002C); // id
704     rStrm.WriteUInt32(0x00000002); // sizeOfHelpContext
705     rStrm.WriteUInt16(0xFFFF); // HelpContext
706 }
707 
708 // section 2.3.4.2.3.2.8
709 void writeMODULETYPE(SvStream& rStrm, const sal_uInt16 type)
710 {
711     if(type == 1)
712         rStrm.WriteUInt16(0x0021); // id for a procedural module
713     else
714         rStrm.WriteUInt16(0x0022); // id for document, class or design module
715     rStrm.WriteUInt32(0x00000000); // reserved
716 }
717 
718 // section 2.3.4.2.3.2
719 void writePROJECTMODULE(SvStream& rStrm, const OUString& name, const sal_uInt16 type)
720 {
721     writeMODULENAME(rStrm, name);
722     writeMODULENAMEUNICODE(rStrm, name);
723     writeMODULESTREAMNAME(rStrm, name);
724     writeMODULEDOCSTRING(rStrm);
725     writeMODULEOFFSET(rStrm);
726     writeMODULEHELPCONTEXT(rStrm);
727     writeMODULECOOKIE(rStrm);
728     writeMODULETYPE(rStrm, type);
729     rStrm.WriteUInt16(0x002B); // terminator
730     rStrm.WriteUInt32(0x00000000); // reserved
731 }
732 
733 // section 2.3.4.2.3
734 void writePROJECTMODULES(SvStream& rStrm, const css::uno::Reference<css::container::XNameContainer>& xNameContainer, const std::vector<sal_Int32>& rLibrayMap)
735 {
736     const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
737     sal_Int32 n = aElementNames.getLength();
738     css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
739     assert(xModuleInfo.is());
740 
741     // TODO: this whole part is document specific
742     rStrm.WriteUInt16(0x000F); // id
743     rStrm.WriteUInt32(0x00000002); // size of Count
744     sal_Int16 count = n; // Number of modules // TODO: this is dependent on the document
745     rStrm.WriteUInt16(count); // Count
746     writePROJECTCOOKIE(rStrm);
747 
748     for (sal_Int32 i = 0; i < n; ++i)
749     {
750         const OUString& rModuleName = aElementNames[rLibrayMap[i]];
751         css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
752         writePROJECTMODULE(rStrm, rModuleName, aModuleInfo.ModuleType);
753     }
754 }
755 
756 // section 2.3.4.2
757 void exportDirStream(SvStream& rStrm, const css::uno::Reference<css::container::XNameContainer>& xNameContainer, const std::vector<sal_Int32>& rLibraryMap, const OUString& projectName)
758 {
759     SvMemoryStream aDirStream(4096, 4096);
760 
761     writePROJECTINFORMATION(aDirStream, projectName);
762     writePROJECTREFERENCES(aDirStream);
763     writePROJECTMODULES(aDirStream, xNameContainer, rLibraryMap);
764     aDirStream.WriteUInt16(0x0010); // terminator
765     aDirStream.WriteUInt32(0x00000000); // reserved
766 
767     aDirStream.Seek(0);
768 
769 #if VBA_EXPORT_DEBUG
770     const OUString aDirFileName("/tmp/vba_dir_out.bin");
771     SvFileStream aDirStreamDebug(aDirFileName, StreamMode::READWRITE);
772 
773     aDirStreamDebug.WriteStream(aDirStream);
774     aDirStream.Seek(0);
775 #endif
776 
777     // the stream for the compression
778     SvMemoryStream aMemoryStream(4096, 4096);
779     aMemoryStream.WriteStream(aDirStream);
780 
781     VBACompression aCompression(rStrm, aDirStream);
782     aCompression.write();
783 }
784 
785 // section 2.3.4.3 Module Stream
786 void exportModuleStream(SvStream& rStrm, const OUString& rSourceCode, const OUString& aElementName, css::script::ModuleInfo const & rInfo)
787 {
788     SvMemoryStream aModuleStream(4096, 4096);
789 
790     exportString(aModuleStream, "Attribute VB_Name = \"" + aElementName + "\"\r\n");
791     if (rInfo.ModuleType == 4)
792     {
793         if (isWorkbook(rInfo.ModuleObject))
794             exportString(aModuleStream, "Attribute VB_Base = \"0{00020819-0000-0000-C000-000000000046}\"\r\n");
795         else
796             exportString(aModuleStream, "Attribute VB_Base = \"0{00020820-0000-0000-C000-000000000046}\"\r\n");
797 
798         exportString(aModuleStream, "Attribute VB_GlobalNameSpace = False\r\n");
799         exportString(aModuleStream, "Attribute VB_Creatable = False\r\n");
800         exportString(aModuleStream, "Attribute VB_PredeclaredId = True\r\n");
801         exportString(aModuleStream, "Attribute VB_Exposed = True\r\n");
802         exportString(aModuleStream, "Attribute VB_TemplateDerived = False\r\n");
803         exportString(aModuleStream, "Attribute VB_Customizable = True\r\n");
804     }
805     OUString aSourceCode = rSourceCode.replaceFirst("Option VBASupport 1\n", "");
806     const sal_Int32 nPos = aSourceCode.indexOf("Rem Attribute VBA_ModuleType=");
807     const sal_Int32 nEndPos = nPos != -1 ? aSourceCode.indexOf("\n", nPos) : -1;
808     if (nPos != -1 && nEndPos != -1)
809         aSourceCode = aSourceCode.replaceAt(nPos, nEndPos - nPos+1, "");
810     aSourceCode = aSourceCode.replaceAll("\n", "\r\n");
811     exportString(aModuleStream, aSourceCode);
812     aModuleStream.Seek(0);
813 
814 #if VBA_EXPORT_DEBUG
815     OUString aModuleFileName("/tmp/vba_" + aElementName + "_out.bin");
816     SvFileStream aModuleStreamDebug(aModuleFileName, StreamMode::READWRITE);
817     aModuleStreamDebug.WriteStream(aModuleStream);
818     aModuleStream.Seek(0);
819 #endif
820 
821     // the stream for the compression
822     SvMemoryStream aMemoryStream(4096, 4096);
823     aMemoryStream.WriteStream(aModuleStream);
824 
825     VBACompression aCompression(rStrm, aModuleStream);
826     aCompression.write();
827 }
828 
829 // section 2.3.4.1 _VBA_PROJECT Stream
830 void exportVBAProjectStream(SvStream& rStrm)
831 {
832     rStrm.WriteUInt16(0x61CC); // Reserved1
833     rStrm.WriteUInt16(0xFFFF); // Version
834     rStrm.WriteUInt8(0x00); // Reserved2
835     rStrm.WriteUInt16(0x0000); // Undefined
836 }
837 
838 // section 2.3.1 PROJECT Stream
839 void exportPROJECTStream(SvStream& rStrm, const css::uno::Reference<css::container::XNameContainer>& xNameContainer,
840         const OUString& projectName, const std::vector<sal_Int32>& rLibraryMap)
841 {
842     const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
843     sal_Int32 n = aElementNames.getLength();
844     css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
845     assert(xModuleInfo.is());
846 
847     // section 2.3.1.1ProjectProperties
848 
849     // section 2.3.1.2 ProjectId
850     exportString(rStrm, "ID=\"");
851     OUString aProjectID
852         = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
853     exportString(rStrm, aProjectID);
854     exportString(rStrm, "\"\r\n");
855 
856     // section 2.3.1.3 ProjectModule
857     for (sal_Int32 i = 0; i < n; ++i)
858     {
859         const OUString& rModuleName = aElementNames[rLibraryMap[i]];
860         css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
861         if(aModuleInfo.ModuleType == 1)
862         {
863             exportString(rStrm, "Module=" + rModuleName + "\r\n");
864         }
865         else if(aModuleInfo.ModuleType == 4)
866         {
867             exportString(rStrm, "Document=" + rModuleName + "/&H00000000\r\n");
868         }
869     }
870 
871     // section 2.3.1.11 ProjectName
872     exportString(rStrm, "Name=\"" + projectName + "\"\r\n");
873 
874     // section 2.3.1.12 ProjectHelpId
875     exportString(rStrm, "HelpContextID=\"0\"\r\n");
876 
877     // section 2.3.1.14 ProjectVersionCompat32
878     exportString(rStrm, "VersionCompatible32=\"393222000\"\r\n");
879 
880     // section 2.3.1.15 ProjectProtectionState
881 #if VBA_ENCRYPTION
882     exportString(rStrm, "CMG=\"");
883     SvMemoryStream aProtectedStream(4096, 4096);
884     aProtectedStream.WriteUInt32(0x00000000);
885     const sal_uInt8* pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData());
886     sal_uInt8 nProjKey = VBAEncryption::calculateProjKey(aProjectID);
887     VBAEncryption aProtectionState(pData, 4, rStrm, nProjKey);
888     aProtectionState.write();
889     exportString(rStrm, "\"\r\n");
890 #else
891     exportString(rStrm, "CMG=\"BEBC9256EEAAA8AEA8AEA8AEA8AE\"\r\n");
892 #endif
893 
894     // section 2.3.1.16 ProjectPassword
895 #if VBA_ENCRYPTION
896     exportString(rStrm, "DPB=\"");
897     aProtectedStream.Seek(0);
898     aProtectedStream.WriteUInt8(0x00);
899     pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData());
900     VBAEncryption aProjectPassword(pData, 1, rStrm, nProjKey);
901     aProjectPassword.write();
902     exportString(rStrm, "\"\r\n");
903 #else
904     exportString(rStrm, "DPB=\"7C7E5014B0D3B1D3B1D3\"\r\n");
905 #endif
906 
907     // section 2.3.1.17 ProjectVisibilityState
908 #if VBA_ENCRYPTION
909     exportString(rStrm, "GC=\"");
910     aProtectedStream.Seek(0);
911     aProtectedStream.WriteUInt8(0xFF);
912     pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData());
913     VBAEncryption aVisibilityState(pData, 1, rStrm, nProjKey);
914     aVisibilityState.write();
915     exportString(rStrm, "\"\r\n\r\n");
916 #else
917     exportString(rStrm, "GC=\"3A3816DAD5DBD5DB2A\"\r\n\r\n");
918 #endif
919 
920     // section 2.3.1.18 HostExtenders
921     exportString(rStrm, "[Host Extender Info]\r\n"
922                         "&H00000001={3832D640-CF90-11CF-8E43-00A0C911005A};VBE;&H00000000\r\n\r\n"
923     );
924 
925     // section 2.3.1.19 ProjectWorkspace
926     exportString(rStrm, "[Workspace]\r\n");
927     for (sal_Int32 i = 0; i < n; ++i)
928     {
929         const OUString& rModuleName = aElementNames[rLibraryMap[i]];
930         css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
931         if(aModuleInfo.ModuleType == 1)
932         {
933             exportString(rStrm,  rModuleName + "=25, 25, 1439, 639, \r\n");
934         }
935         else
936         {
937             exportString(rStrm, rModuleName + "=0, 0, 0, 0, C\r\n");
938         }
939     }
940 }
941 
942 // section 2.3.3.1 NAMEMAP
943 void writeNAMEMAP(SvStream& rStrm, const css::uno::Sequence<OUString>& rElementNames,
944         const std::vector<sal_Int32>& rLibraryMap)
945 {
946     int n = rElementNames.getLength();
947     for(sal_Int32 i = 0; i < n; ++i)
948     {
949         const OUString& rModuleName = rElementNames[rLibraryMap[i]];
950         exportString(rStrm, rModuleName);
951         rStrm.WriteUInt8(0x00); // terminator
952         exportUTF16String(rStrm, rModuleName);
953         rStrm.WriteUInt16(0x0000); // terminator
954     }
955 }
956 
957 // section 2.3.3 PROJECTwm Stream
958 void exportPROJECTwmStream(SvStream& rStrm, const css::uno::Sequence<OUString>& rElementNames,
959         const std::vector<sal_Int32>& rLibraryMap)
960 {
961     writeNAMEMAP(rStrm, rElementNames, rLibraryMap);
962     rStrm.WriteUInt16(0x0000); // terminator
963 }
964 
965 void getCorrectExportOrder(const css::uno::Reference<css::container::XNameContainer>& xNameContainer, std::vector<sal_Int32>& rLibraryMap)
966 {
967     const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
968     sal_Int32 n = aElementNames.getLength();
969     css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
970 
971     sal_Int32 nCurrentId = 0;
972     // first all the non-document modules
973     for (sal_Int32 i = 0; i < n; ++i)
974     {
975         css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
976         if (aModuleInfo.ModuleType != 4)
977         {
978             rLibraryMap[nCurrentId] = i;
979             ++nCurrentId;
980         }
981     }
982 
983     sal_Int32 nWorkbookIndex = -1;
984     // then possibly the workbook module
985     for (sal_Int32 i = 0; i < n; ++i)
986     {
987         css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
988         bool bWorkbook = isWorkbook(aModuleInfo.ModuleObject);
989         if (bWorkbook)
990         {
991             nWorkbookIndex = i;
992             rLibraryMap[nCurrentId] = i;
993             ++nCurrentId;
994         }
995     }
996 
997     // then the remaining modules
998     for (sal_Int32 i = 0; i < n; ++i)
999     {
1000         if (i == nWorkbookIndex)
1001             continue;
1002 
1003         css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
1004         if (aModuleInfo.ModuleType == 4)
1005         {
1006             rLibraryMap[nCurrentId] = i;
1007             ++nCurrentId;
1008         }
1009     }
1010 }
1011 
1012 }
1013 
1014 #if VBA_USE_ORIGINAL_WM_STREAM || VBA_USE_ORIGINAL_DIR_STREAM \
1015     || VBA_USE_ORIGINAL_PROJECT_STREAM || VBA_USE_ORIGINAL_VBA_PROJECT \
1016     || VBA_USE_ORIGINAL_DIR_STREAM
1017 void addFileStreamToSotStream(const OUString& rPath, SotStorageStream* pStream)
1018 {
1019     SvFileStream aFileStream(rPath, StreamMode::READWRITE);
1020     pStream->WriteStream(aFileStream);
1021 }
1022 #endif
1023 
1024 void VbaExport::exportVBA(SotStorage* pRootStorage)
1025 {
1026     css::uno::Reference<css::container::XNameContainer> xNameContainer = getBasicLibrary();
1027     if (!xNameContainer.is()) {
1028         return;
1029     }
1030     const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
1031     sal_Int32 n = aElementNames.getLength(); // get the number of modules
1032     // export the elements in the order MSO expects them
1033     // we store the index of the
1034     std::vector<sal_Int32> aLibraryMap(n, 0);
1035     getCorrectExportOrder(xNameContainer, aLibraryMap);
1036 
1037     // start here with the VBA export
1038     tools::SvRef<SotStorage> xVBAStream = pRootStorage->OpenSotStorage("VBA", StreamMode::READWRITE);
1039     SotStorageStream* pDirStream = xVBAStream->OpenSotStream("dir", StreamMode::READWRITE);
1040 
1041     SotStorageStream* pVBAProjectStream = xVBAStream->OpenSotStream("_VBA_PROJECT", StreamMode::READWRITE);
1042     SotStorageStream* pPROJECTStream = pRootStorage->OpenSotStream("PROJECT", StreamMode::READWRITE);
1043     SotStorageStream* pPROJECTwmStream = pRootStorage->OpenSotStream("PROJECTwm", StreamMode::READWRITE);
1044 
1045 #if VBA_USE_ORIGINAL_WM_STREAM
1046     OUString aProjectwmPath = "/home/moggi/Documents/testfiles/vba/PROJECTwm";
1047     addFileStreamToSotStream(aProjectwmPath, pPROJECTwmStream);
1048 #else
1049     exportPROJECTwmStream(*pPROJECTwmStream, aElementNames, aLibraryMap);
1050 #endif
1051 
1052 #if VBA_USE_ORIGINAL_DIR_STREAM
1053     OUString aDirPath = "/home/moggi/Documents/testfiles/vba/VBA/dir";
1054     addFileStreamToSotStream(aDirPath, pDirStream);
1055 #else
1056     exportDirStream(*pDirStream, xNameContainer, aLibraryMap, getProjectName());
1057 #endif
1058 
1059 #if VBA_USE_ORIGINAL_PROJECT_STREAM
1060     OUString aProjectPath = "/home/moggi/Documents/testfiles/vba/PROJECT";
1061     addFileStreamToSotStream(aProjectPath, pPROJECTStream);
1062 #else
1063     exportPROJECTStream(*pPROJECTStream, xNameContainer, getProjectName(), aLibraryMap);
1064 #endif
1065 
1066 #if VBA_USE_ORIGINAL_VBA_PROJECT
1067     OUString a_VBA_ProjectPath = "/home/moggi/Documents/testfiles/vba/VBA/_VBA_PROJECT";
1068     addFileStreamToSotStream(a_VBA_ProjectPath, pVBAProjectStream);
1069 #else
1070     exportVBAProjectStream(*pVBAProjectStream);
1071 #endif
1072 
1073 #if VBA_USE_ORIGINAL_DIR_STREAM
1074     OUString aModule1Path = "/home/moggi/Documents/testfiles/vba/VBA/Module1";
1075     OUString aSheet1Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet1";
1076     OUString aSheet2Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet2";
1077     OUString aSheet3Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet3";
1078     OUString aWorkbookPath = "/home/moggi/Documents/testfiles/vba/VBA/ThisWorkbook";
1079     SotStorageStream* pModule1Stream = xVBAStream->OpenSotStream("Module1", StreamMode::READWRITE);
1080     SotStorageStream* pSheet1Stream = xVBAStream->OpenSotStream("Sheet1", StreamMode::READWRITE);
1081     SotStorageStream* pSheet2Stream = xVBAStream->OpenSotStream("Sheet2", StreamMode::READWRITE);
1082     SotStorageStream* pSheet3Stream = xVBAStream->OpenSotStream("Sheet3", StreamMode::READWRITE);
1083     SotStorageStream* pWorkbookStream = xVBAStream->OpenSotStream("ThisWorkbook", StreamMode::READWRITE);
1084     addFileStreamToSotStream(aModule1Path, pModule1Stream);
1085     addFileStreamToSotStream(aSheet1Path, pSheet1Stream);
1086     addFileStreamToSotStream(aSheet2Path, pSheet2Stream);
1087     addFileStreamToSotStream(aSheet3Path, pSheet3Stream);
1088     addFileStreamToSotStream(aWorkbookPath, pWorkbookStream);
1089 
1090     pModule1Stream->Commit();
1091     pSheet1Stream->Commit();
1092     pSheet2Stream->Commit();
1093     pSheet3Stream->Commit();
1094     pWorkbookStream->Commit();
1095 #else
1096 
1097     css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
1098     for (sal_Int32 i = 0; i < n; ++i)
1099     {
1100         const OUString& rModuleName = aElementNames[aLibraryMap[i]];
1101         SotStorageStream* pModuleStream = xVBAStream->OpenSotStream(rModuleName, StreamMode::READWRITE);
1102         css::uno::Any aCode = xNameContainer->getByName(rModuleName);
1103         css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
1104         OUString aSourceCode;
1105         aCode >>= aSourceCode;
1106         exportModuleStream(*pModuleStream, aSourceCode, rModuleName, aModuleInfo);
1107         pModuleStream->Commit();
1108     }
1109 
1110 #endif
1111 
1112     pVBAProjectStream->Commit();
1113 
1114     pDirStream->Commit();
1115     xVBAStream->Commit();
1116     pPROJECTStream->Commit();
1117     pPROJECTwmStream->Commit();
1118     pRootStorage->Commit();
1119 }
1120 
1121 css::uno::Reference<css::script::XLibraryContainer> VbaExport::getLibraryContainer() const
1122 {
1123     oox::PropertySet aDocProp(mxModel);
1124     css::uno::Reference<css::script::XLibraryContainer> xLibContainer(aDocProp.getAnyProperty(oox::PROP_BasicLibraries), css::uno::UNO_QUERY);
1125 
1126     return xLibContainer;
1127 }
1128 
1129 css::uno::Reference<css::container::XNameContainer> VbaExport::getBasicLibrary() const
1130 {
1131     css::uno::Reference<css::container::XNameContainer> xLibrary;
1132     try
1133     {
1134         css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer();
1135         OUString aProjectName = getProjectName();
1136         xLibrary.set( xLibContainer->getByName(aProjectName), css::uno::UNO_QUERY_THROW );
1137     }
1138     catch(...)
1139     {
1140     }
1141 
1142     return xLibrary;
1143 }
1144 
1145 bool VbaExport::containsVBAProject()
1146 {
1147     css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer();
1148     if (!xLibContainer.is())
1149         return false;
1150 
1151     css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility (xLibContainer, css::uno::UNO_QUERY);
1152     if (!xVbaCompatibility.is())
1153         return false;
1154 
1155     bool bVBACompatibilty = xVbaCompatibility->getVBACompatibilityMode();
1156 
1157     return bVBACompatibilty;
1158 }
1159 
1160 OUString VbaExport::getProjectName() const
1161 {
1162     css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility(getLibraryContainer(), css::uno::UNO_QUERY);
1163     if (xVbaCompatibility.is())
1164         return xVbaCompatibility->getProjectName();
1165 
1166     return OUString();
1167 }
1168 
1169 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1170