Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / ole / vbaexport.cxx
blobb4f2db17ccf5c38828e545cf9f656bd73bfb7fac
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/.
8 */
10 #include <sal/config.h>
12 #include <cassert>
13 #include <cmath>
14 #include <random>
16 #include <oox/ole/vbaexport.hxx>
18 #include <tools/stream.hxx>
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>
26 #include <ooo/vba/excel/XWorkbook.hpp>
28 #include <oox/helper/binaryoutputstream.hxx>
29 #include <oox/helper/propertyset.hxx>
30 #include <oox/token/properties.hxx>
32 #include <sot/storage.hxx>
34 #include <comphelper/xmltools.hxx>
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
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
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.
54 #define VBA_ENCRYPTION 1
56 namespace {
58 void exportString(SvStream& rStrm, const OUString& rString)
60 OString aStringCorrectCodepage = OUStringToOString(rString, CODEPAGE);
61 rStrm.WriteOString(aStringCorrectCodepage);
64 void exportUTF16String(SvStream& rStrm, const OUString& rString)
66 sal_Int32 n = rString.getLength();
67 const sal_Unicode* pString = rString.getStr();
68 for (sal_Int32 i = 0; i < n; ++i)
70 sal_Unicode character = pString[i];
71 rStrm.WriteUnicode(character);
75 bool isWorkbook(const css::uno::Reference<css::uno::XInterface>& xInterface)
77 css::uno::Reference<ooo::vba::excel::XWorkbook> xWorkbook(xInterface, css::uno::UNO_QUERY);
78 return xWorkbook.is();
81 OUString createHexStringFromDigit(sal_uInt8 nDigit)
83 OUString aString = OUString::number( nDigit, 16 );
84 if(aString.getLength() == 1)
85 aString = OUString::number(0) + aString;
86 return aString.toAsciiUpperCase();
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)
103 static void setUInt16(sal_uInt8* pBuffer, size_t nPos, sal_uInt16 nVal)
105 pBuffer[nPos] = nVal & 0xFF;
106 pBuffer[nPos+1] = (nVal & 0xFF00) >> 8;
109 sal_uInt16 VBACompressionChunk::handleHeader(bool bCompressed)
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);
118 return nHeader;
121 // section 2.4.1.3.7
122 void VBACompressionChunk::write()
125 mnDecompressedCurrent = 0;
126 mnCompressedCurrent = 2;
127 mnCompressedEnd = 4098;
128 mnDecompressedEnd = std::min<sal_uInt64>(4096, mnChunkSize);
130 // if that stream becomes larger than 4096 bytes then
131 // we use the uncompressed stream
132 sal_uInt8 pCompressedChunkStream[4098];
133 mpCompressedChunkStream = pCompressedChunkStream;
135 while (mnDecompressedCurrent < mnDecompressedEnd
136 && mnCompressedCurrent < mnCompressedEnd)
138 // compress token sequence
139 compressTokenSequence();
142 if (mnDecompressedCurrent < mnDecompressedEnd)
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);
151 else
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);
160 // section 2.4.1.3.13
161 void VBACompressionChunk::PackCompressedChunkSize(size_t nSize, sal_uInt16& rHeader)
163 sal_uInt16 nTemp1 = rHeader & 0xF000;
164 sal_uInt16 nTemp2 = nSize - 3;
165 rHeader = nTemp1 | nTemp2;
168 // section 2.4.1.3.16
169 void VBACompressionChunk::PackCompressedChunkFlag(bool bCompressed, sal_uInt16& rHeader)
171 sal_uInt16 nTemp1 = rHeader & 0x7FFF;
172 sal_uInt16 nTemp2 = static_cast<sal_uInt16>(bCompressed) << 15;
173 rHeader = nTemp1 | nTemp2;
176 // section 2.4.1.3.14
177 void VBACompressionChunk::PackCompressedChunkSignature(sal_uInt16& rHeader)
179 sal_Int32 nTemp = rHeader & 0x8FFFF;
180 rHeader = nTemp | 0x3000;
183 // section 2.4.1.3.8
184 void VBACompressionChunk::compressTokenSequence()
186 sal_uInt64 nFlagByteIndex = mnCompressedCurrent;
187 sal_uInt8 nFlagByte = 0;
188 ++mnCompressedCurrent;
189 for (size_t index = 0; index <= 7; ++index)
191 if (mnDecompressedCurrent < mnDecompressedEnd
192 && mnCompressedCurrent < mnCompressedEnd)
194 compressToken(index, nFlagByte);
197 mpCompressedChunkStream[nFlagByteIndex] = nFlagByte;
200 // section 2.4.1.3.9
201 void VBACompressionChunk::compressToken(size_t index, sal_uInt8& nFlagByte)
203 size_t nLength = 0;
204 size_t nOffset = 0;
205 match(nLength, nOffset);
206 if (nOffset != 0)
208 if (mnCompressedCurrent + 1 < mnCompressedEnd)
210 sal_uInt16 nToken = CopyToken(nLength, nOffset);
211 setUInt16(mpCompressedChunkStream, mnCompressedCurrent, nToken);
212 SetFlagBit(index, true, nFlagByte);
213 mnCompressedCurrent += 2;
214 mnDecompressedCurrent += nLength;
216 else
218 mnCompressedCurrent = mnCompressedEnd;
221 else
223 if (mnCompressedCurrent + 1 < mnCompressedEnd)
225 mpCompressedChunkStream[mnCompressedCurrent] = mpUncompressedData[mnDecompressedCurrent];
226 ++mnCompressedCurrent;
227 ++mnDecompressedCurrent;
229 else
231 mnCompressedCurrent = mnCompressedEnd;
236 // section 2.4.1.3.18
237 void VBACompressionChunk::SetFlagBit(size_t index, bool bVal, sal_uInt8& rFlag)
239 size_t nTemp1 = static_cast<int>(bVal) << index;
240 sal_uInt8 nTemp2 = rFlag & (~nTemp1);
241 rFlag = nTemp2 | nTemp1;
244 // section 2.4.1.3.19.3
245 sal_uInt16 VBACompressionChunk::CopyToken(size_t nLength, size_t nOffset)
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;
259 // section 2.4.1.3.19.4
260 void VBACompressionChunk::match(size_t& rLength, size_t& rOffset)
262 size_t nBestLen = 0;
263 sal_Int32 nCandidate = mnDecompressedCurrent - 1;
264 sal_Int32 nBestCandidate = nCandidate;
265 while (nCandidate >= 0)
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])
273 ++nLen;
274 ++nC;
275 ++nD;
277 if (nLen > nBestLen)
279 nBestLen = nLen;
280 nBestCandidate = nCandidate;
282 --nCandidate;
285 if (nBestLen >= 3)
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;
293 else
295 rLength = 0;
296 rOffset = 0;
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)
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;
330 // section 2.4.1.3.10
331 void VBACompressionChunk::writeRawChunk()
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)
339 mrCompressedStream.WriteUInt8(0);
343 VBACompression::VBACompression(SvStream& rCompressedStream,
344 SvMemoryStream& rUncompressedStream):
345 mrCompressedStream(rCompressedStream),
346 mrUncompressedStream(rUncompressedStream)
350 // section 2.4.1.3.6
351 void VBACompression::write()
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)
361 std::size_t nChunkSize = std::min<size_t>(nRemainingSize, 4096);
362 VBACompressionChunk aChunk(mrCompressedStream, &pData[nSize - nRemainingSize], nChunkSize);
363 aChunk.write();
365 // update the uncompressed chunk start marker
366 nRemainingSize -= nChunkSize;
367 bStreamNotEnded = nRemainingSize != 0;
371 // section 2.4.3
372 #if VBA_ENCRYPTION
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)
386 std::random_device rd;
387 std::mt19937 gen(rd());
388 std::uniform_int_distribution<> dis(0, 255);
389 mnSeed = dis(gen);
392 void VBAEncryption::writeSeed()
394 exportString(mrEncryptedData, createHexStringFromDigit(mnSeed));
397 void VBAEncryption::writeVersionEnc()
399 static const sal_uInt8 mnVersion = 2; // the encrypted version
400 mnVersionEnc = mnSeed ^ mnVersion;
401 exportString(mrEncryptedData, createHexStringFromDigit(mnVersionEnc));
404 sal_uInt8 VBAEncryption::calculateProjKey(const OUString& rProjectKey)
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)
411 sal_Unicode character = pString[i];
412 nProjKey += character;
415 return nProjKey;
418 void VBAEncryption::writeProjKeyEnc()
420 sal_uInt8 nProjKeyEnc = mnSeed ^ mnProjKey;
421 exportString(mrEncryptedData, createHexStringFromDigit(nProjKeyEnc));
422 mnUnencryptedByte1 = mnProjKey;
423 mnEncryptedByte1 = nProjKeyEnc; // ProjKeyEnc
424 mnEncryptedByte2 = mnVersionEnc; // VersionEnc
427 void VBAEncryption::writeIgnoredEnc()
429 mnIgnoredLength = (mnSeed & 6) / 2;
430 for(sal_Int32 i = 1; i <= mnIgnoredLength; ++i)
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;
441 void VBAEncryption::writeDataLengthEnc()
443 sal_uInt16 temp = mnLength;
444 for(sal_Int8 i = 0; i < 4; ++i)
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;
456 void VBAEncryption::writeDataEnc()
458 for(sal_Int16 i = 0; i < mnLength; i++)
460 sal_uInt8 nByteEnc = mpData[i] ^ (mnEncryptedByte2 + mnUnencryptedByte1);
461 exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc));
462 mnEncryptedByte2 = mnEncryptedByte1;
463 mnEncryptedByte1 = nByteEnc;
464 mnUnencryptedByte1 = mpData[i];
468 void VBAEncryption::write()
470 writeSeed();
471 writeVersionEnc();
472 writeProjKeyEnc();
473 writeIgnoredEnc();
474 writeDataLengthEnc();
475 writeDataEnc();
478 #endif
480 VbaExport::VbaExport(css::uno::Reference<css::frame::XModel> const & xModel):
481 mxModel(xModel)
485 namespace {
487 // section 2.3.4.2.1.1
488 void writePROJECTSYSKIND(SvStream& rStrm)
490 rStrm.WriteUInt16(0x0001); // id
491 rStrm.WriteUInt32(0x00000004); // size
492 rStrm.WriteUInt32(0x00000001); // SysKind, hard coded to 32-bin windows for now
495 // section 2.3.4.2.1.2
496 void writePROJECTLCID(SvStream& rStrm)
498 rStrm.WriteUInt16(0x0002); // id
499 rStrm.WriteUInt32(0x00000004); // size
500 rStrm.WriteUInt32(0x00000409); // Lcid
503 // section 2.3.4.2.1.3
504 void writePROJECTLCIDINVOKE(SvStream& rStrm)
506 rStrm.WriteUInt16(0x0014); // id
507 rStrm.WriteUInt32(0x00000004); // size
508 rStrm.WriteUInt32(0x00000409); // LcidInvoke
511 // section 2.3.4.2.1.4
512 void writePROJECTCODEPAGE(SvStream& rStrm)
514 rStrm.WriteUInt16(0x0003); // id
515 rStrm.WriteUInt32(0x00000002); // size
516 rStrm.WriteUInt16(CODEPAGE_MS); // CodePage
519 //section 2.3.4.2.1.5
520 void writePROJECTNAME(SvStream& rStrm, const OUString& name)
522 rStrm.WriteUInt16(0x0004); // id
523 sal_uInt32 sizeOfProjectName = name.getLength();
524 rStrm.WriteUInt32(sizeOfProjectName); // sizeOfProjectName
525 exportString(rStrm, name); // ProjectName
528 //section 2.3.4.2.1.6
529 void writePROJECTDOCSTRING(SvStream& rStrm)
531 rStrm.WriteUInt16(0x0005); // id
532 rStrm.WriteUInt32(0x00000000); // sizeOfDocString
533 rStrm.WriteUInt16(0x0040); // Reserved
534 rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode, MUST be even
537 //section 2.3.4.2.1.7
538 void writePROJECTHELPFILEPATH(SvStream& rStrm)
540 rStrm.WriteUInt16(0x0006); // id
541 rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile1
542 rStrm.WriteUInt16(0x003D); // Reserved
543 rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile2
546 //section 2.3.4.2.1.8
547 void writePROJECTHELPCONTEXT(SvStream& rStrm)
549 rStrm.WriteUInt16(0x0007); // id
550 rStrm.WriteUInt32(0x00000004); // size
551 rStrm.WriteUInt32(0x00000000); // HelpContext
554 //section 2.3.4.2.1.9
555 void writePROJECTLIBFLAGS(SvStream& rStrm)
557 rStrm.WriteUInt16(0x0008); // id
558 rStrm.WriteUInt32(0x00000004); // size
559 rStrm.WriteUInt32(0x00000000); // ProjectLibFlags
562 //section 2.3.4.2.1.10
563 void writePROJECTVERSION(SvStream& rStrm)
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
571 //section 2.3.4.2.1.11
572 void writePROJECTCONSTANTS(SvStream& rStrm)
574 rStrm.WriteUInt16(0x000C); // id
575 rStrm.WriteUInt32(0x00000000); // sizeOfConstants
576 rStrm.WriteUInt16(0x003C); // Reserved
577 rStrm.WriteUInt32(0x00000000); // sizeOfConstantsUnicode
580 // section 2.3.4.2.1
581 void writePROJECTINFORMATION(SvStream& rStrm, const OUString& projectName)
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);
596 // section 2.3.4.2.2.2
597 void writeREFERENCENAME(SvStream& rStrm, const OUString& name)
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
609 // section 2.3.4.2.2.5
610 void writeREFERENCEREGISTERED(SvStream& rStrm, const OUString& libid)
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
622 // section 2.3.4.2.2.1
623 void writeREFERENCE(SvStream& rStrm, const OUString& name, const OUString& libid)
625 writeREFERENCENAME(rStrm, name);
626 writeREFERENCEREGISTERED(rStrm, libid);
629 // section 2.3.4.2.2
630 void writePROJECTREFERENCES(SvStream& rStrm)
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");
637 // section 2.3.4.2.3.1
638 void writePROJECTCOOKIE(SvStream& rStrm)
640 rStrm.WriteUInt16(0x0013); // id
641 rStrm.WriteUInt32(0x00000002); // size
642 rStrm.WriteUInt16(0xFFFF); // cookie
645 // section 2.3.4.2.3.2.1
646 void writeMODULENAME(SvStream& rStrm, const OUString& name)
648 rStrm.WriteUInt16(0x0019); // id
649 sal_Int32 n = name.getLength(); // sizeOfModuleName
650 rStrm.WriteUInt32(n);
651 exportString(rStrm, name); // ModuleName
654 // section 2.3.4.2.3.2.2
655 void writeMODULENAMEUNICODE(SvStream& rStrm, const OUString& name)
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
663 // section 2.3.4.2.3.2.3
664 void writeMODULESTREAMNAME(SvStream& rStrm, const OUString& streamName)
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
675 // section 2.3.4.2.3.2.4
676 void writeMODULEDOCSTRING(SvStream& rStrm)
678 rStrm.WriteUInt16(0x001C); // id
679 rStrm.WriteUInt32(0x00000000); // sizeOfDocString
680 rStrm.WriteUInt16(0x0048); // reserved
681 rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode
684 // section 2.3.4.2.3.2.5
685 void writeMODULEOFFSET(SvStream& rStrm)
687 rStrm.WriteUInt16(0x0031); // id
688 rStrm.WriteUInt32(0x00000004); // sizeOfTextOffset
689 rStrm.WriteUInt32(0x00000000); // TextOffset
692 // section 2.3.4.2.3.2.6
693 void writeMODULEHELPCONTEXT(SvStream& rStrm)
695 rStrm.WriteUInt16(0x001E); // id
696 rStrm.WriteUInt32(0x00000004); // sizeOfHelpContext
697 rStrm.WriteUInt32(0x00000000); // HelpContext
700 // section 2.3.4.2.3.2.7
701 void writeMODULECOOKIE(SvStream& rStrm)
703 rStrm.WriteUInt16(0x002C); // id
704 rStrm.WriteUInt32(0x00000002); // sizeOfHelpContext
705 rStrm.WriteUInt16(0xFFFF); // HelpContext
708 // section 2.3.4.2.3.2.8
709 void writeMODULETYPE(SvStream& rStrm, const sal_uInt16 type)
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
718 // section 2.3.4.2.3.2
719 void writePROJECTMODULE(SvStream& rStrm, const OUString& name, const sal_uInt16 type)
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
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)
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());
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);
748 for (sal_Int32 i = 0; i < n; ++i)
750 const OUString& rModuleName = aElementNames[rLibrayMap[i]];
751 css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
752 writePROJECTMODULE(rStrm, rModuleName, aModuleInfo.ModuleType);
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)
759 SvMemoryStream aDirStream(4096, 4096);
761 writePROJECTINFORMATION(aDirStream, projectName);
762 writePROJECTREFERENCES(aDirStream);
763 writePROJECTMODULES(aDirStream, xNameContainer, rLibraryMap);
764 aDirStream.WriteUInt16(0x0010); // terminator
765 aDirStream.WriteUInt32(0x00000000); // reserved
767 aDirStream.Seek(0);
769 #if VBA_EXPORT_DEBUG
770 const OUString aDirFileName("/tmp/vba_dir_out.bin");
771 SvFileStream aDirStreamDebug(aDirFileName, StreamMode::READWRITE);
773 aDirStreamDebug.WriteStream(aDirStream);
774 aDirStream.Seek(0);
775 #endif
777 // the stream for the compression
778 SvMemoryStream aMemoryStream(4096, 4096);
779 aMemoryStream.WriteStream(aDirStream);
781 VBACompression aCompression(rStrm, aDirStream);
782 aCompression.write();
785 // section 2.3.4.3 Module Stream
786 void exportModuleStream(SvStream& rStrm, const OUString& rSourceCode, const OUString& aElementName, css::script::ModuleInfo const & rInfo)
788 SvMemoryStream aModuleStream(4096, 4096);
790 exportString(aModuleStream, "Attribute VB_Name = \"" + aElementName + "\"\r\n");
791 if (rInfo.ModuleType == 4)
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");
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");
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);
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
821 // the stream for the compression
822 SvMemoryStream aMemoryStream(4096, 4096);
823 aMemoryStream.WriteStream(aModuleStream);
825 VBACompression aCompression(rStrm, aModuleStream);
826 aCompression.write();
829 // section 2.3.4.1 _VBA_PROJECT Stream
830 void exportVBAProjectStream(SvStream& rStrm)
832 rStrm.WriteUInt16(0x61CC); // Reserved1
833 rStrm.WriteUInt16(0xFFFF); // Version
834 rStrm.WriteUInt8(0x00); // Reserved2
835 rStrm.WriteUInt16(0x0000); // Undefined
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)
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());
847 // section 2.3.1.1ProjectProperties
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");
856 // section 2.3.1.3 ProjectModule
857 for (sal_Int32 i = 0; i < n; ++i)
859 const OUString& rModuleName = aElementNames[rLibraryMap[i]];
860 css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
861 if(aModuleInfo.ModuleType == 1)
863 exportString(rStrm, "Module=" + rModuleName + "\r\n");
865 else if(aModuleInfo.ModuleType == 4)
867 exportString(rStrm, "Document=" + rModuleName + "/&H00000000\r\n");
871 // section 2.3.1.11 ProjectName
872 exportString(rStrm, "Name=\"" + projectName + "\"\r\n");
874 // section 2.3.1.12 ProjectHelpId
875 exportString(rStrm, "HelpContextID=\"0\"\r\n");
877 // section 2.3.1.14 ProjectVersionCompat32
878 exportString(rStrm, "VersionCompatible32=\"393222000\"\r\n");
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
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
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
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"
925 // section 2.3.1.19 ProjectWorkspace
926 exportString(rStrm, "[Workspace]\r\n");
927 for (sal_Int32 i = 0; i < n; ++i)
929 const OUString& rModuleName = aElementNames[rLibraryMap[i]];
930 css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
931 if(aModuleInfo.ModuleType == 1)
933 exportString(rStrm, rModuleName + "=25, 25, 1439, 639, \r\n");
935 else
937 exportString(rStrm, rModuleName + "=0, 0, 0, 0, C\r\n");
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)
946 int n = rElementNames.getLength();
947 for(sal_Int32 i = 0; i < n; ++i)
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
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)
961 writeNAMEMAP(rStrm, rElementNames, rLibraryMap);
962 rStrm.WriteUInt16(0x0000); // terminator
965 void getCorrectExportOrder(const css::uno::Reference<css::container::XNameContainer>& xNameContainer, std::vector<sal_Int32>& rLibraryMap)
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);
971 sal_Int32 nCurrentId = 0;
972 // first all the non-document modules
973 for (sal_Int32 i = 0; i < n; ++i)
975 css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
976 if (aModuleInfo.ModuleType != 4)
978 rLibraryMap[nCurrentId] = i;
979 ++nCurrentId;
983 sal_Int32 nWorkbookIndex = -1;
984 // then possibly the workbook module
985 for (sal_Int32 i = 0; i < n; ++i)
987 css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
988 bool bWorkbook = isWorkbook(aModuleInfo.ModuleObject);
989 if (bWorkbook)
991 nWorkbookIndex = i;
992 rLibraryMap[nCurrentId] = i;
993 ++nCurrentId;
997 // then the remaining modules
998 for (sal_Int32 i = 0; i < n; ++i)
1000 if (i == nWorkbookIndex)
1001 continue;
1003 css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
1004 if (aModuleInfo.ModuleType == 4)
1006 rLibraryMap[nCurrentId] = i;
1007 ++nCurrentId;
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)
1019 SvFileStream aFileStream(rPath, StreamMode::READWRITE);
1020 pStream->WriteStream(aFileStream);
1022 #endif
1024 void VbaExport::exportVBA(SotStorage* pRootStorage)
1026 css::uno::Reference<css::container::XNameContainer> xNameContainer = getBasicLibrary();
1027 if (!xNameContainer.is()) {
1028 return;
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);
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);
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);
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
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
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
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
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);
1090 pModule1Stream->Commit();
1091 pSheet1Stream->Commit();
1092 pSheet2Stream->Commit();
1093 pSheet3Stream->Commit();
1094 pWorkbookStream->Commit();
1095 #else
1097 css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
1098 for (sal_Int32 i = 0; i < n; ++i)
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();
1110 #endif
1112 pVBAProjectStream->Commit();
1114 pDirStream->Commit();
1115 xVBAStream->Commit();
1116 pPROJECTStream->Commit();
1117 pPROJECTwmStream->Commit();
1118 pRootStorage->Commit();
1121 css::uno::Reference<css::script::XLibraryContainer> VbaExport::getLibraryContainer() const
1123 oox::PropertySet aDocProp(mxModel);
1124 css::uno::Reference<css::script::XLibraryContainer> xLibContainer(aDocProp.getAnyProperty(oox::PROP_BasicLibraries), css::uno::UNO_QUERY);
1126 return xLibContainer;
1129 css::uno::Reference<css::container::XNameContainer> VbaExport::getBasicLibrary() const
1131 css::uno::Reference<css::container::XNameContainer> xLibrary;
1134 css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer();
1135 OUString aProjectName = getProjectName();
1136 xLibrary.set( xLibContainer->getByName(aProjectName), css::uno::UNO_QUERY_THROW );
1138 catch(...)
1142 return xLibrary;
1145 bool VbaExport::containsVBAProject()
1147 css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer();
1148 if (!xLibContainer.is())
1149 return false;
1151 css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility (xLibContainer, css::uno::UNO_QUERY);
1152 if (!xVbaCompatibility.is())
1153 return false;
1155 bool bVBACompatibilty = xVbaCompatibility->getVBACompatibilityMode();
1157 return bVBACompatibilty;
1160 OUString VbaExport::getProjectName() const
1162 css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility(getLibraryContainer(), css::uno::UNO_QUERY);
1163 if (xVbaCompatibility.is())
1164 return xVbaCompatibility->getProjectName();
1166 return OUString();
1169 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */