1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/config.h>
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
38 #define CODEPAGE_MS 65001
39 #define CODEPAGE RTL_TEXTENCODING_UTF8
41 #define CODEPAGE_MS 1252
42 #define CODEPAGE RTL_TEXTENCODING_MS_1252
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
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)
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
);
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);
147 mrCompressedStream
.Seek(nChunkStart
);
148 sal_uInt16 nHeader
= handleHeader(false);
149 mrCompressedStream
.WriteUInt16(nHeader
);
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;
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
;
201 void VBACompressionChunk::compressToken(size_t index
, sal_uInt8
& nFlagByte
)
205 match(nLength
, nOffset
);
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
;
218 mnCompressedCurrent
= mnCompressedEnd
;
223 if (mnCompressedCurrent
+ 1 < mnCompressedEnd
)
225 mpCompressedChunkStream
[mnCompressedCurrent
] = mpUncompressedData
[mnDecompressedCurrent
];
226 ++mnCompressedCurrent
;
227 ++mnDecompressedCurrent
;
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
;
259 // section 2.4.1.3.19.4
260 void VBACompressionChunk::match(size_t& rLength
, size_t& rOffset
)
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
;
270 while (nD
< static_cast<sal_Int32
>(mnChunkSize
) // TODO: check if this needs to be including a minus -1
271 && mpUncompressedData
[nC
] == mpUncompressedData
[nD
])
280 nBestCandidate
= nCandidate
;
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
;
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)
309 else if (nDifference
>= 1025)
311 else if (nDifference
>= 513)
313 else if (nDifference
>= 257)
315 else if (nDifference
>= 129)
317 else if (nDifference
>= 65)
319 else if (nDifference
>= 33)
321 else if (nDifference
>= 17)
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
)
351 void VBACompression::write()
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
);
365 // update the uncompressed chunk start marker
366 nRemainingSize
-= nChunkSize
;
367 bStreamNotEnded
= nRemainingSize
!= 0;
374 VBAEncryption::VBAEncryption(const sal_uInt8
* pData
, const sal_uInt16 length
, SvStream
& rEncryptedData
, sal_uInt8 nProjKey
)
377 ,mrEncryptedData(rEncryptedData
)
378 ,mnUnencryptedByte1(0)
386 std::random_device rd
;
387 std::mt19937
gen(rd());
388 std::uniform_int_distribution
<> dis(0, 255);
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
;
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
;
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()
474 writeDataLengthEnc();
480 VbaExport::VbaExport(css::uno::Reference
<css::frame::XModel
> const & xModel
):
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
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
);
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
)
712 rStrm
.WriteUInt16(0x0021); // id for a procedural module
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
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
);
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
770 const OUString
aDirFileName("/tmp/vba_dir_out.bin");
771 SvFileStream
aDirStreamDebug(aDirFileName
, StreamMode::READWRITE
);
773 aDirStreamDebug
.WriteStream(aDirStream
);
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");
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);
815 OUString
aModuleFileName("/tmp/vba_" + aElementName
+ "_out.bin");
816 SvFileStream
aModuleStreamDebug(aModuleFileName
, StreamMode::READWRITE
);
817 aModuleStreamDebug
.WriteStream(aModuleStream
);
818 aModuleStream
.Seek(0);
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=\"");
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
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");
891 exportString(rStrm
, "CMG=\"BEBC9256EEAAA8AEA8AEA8AEA8AE\"\r\n");
894 // section 2.3.1.16 ProjectPassword
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");
904 exportString(rStrm
, "DPB=\"7C7E5014B0D3B1D3B1D3\"\r\n");
907 // section 2.3.1.17 ProjectVisibilityState
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");
917 exportString(rStrm
, "GC=\"3A3816DAD5DBD5DB2A\"\r\n\r\n");
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");
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
;
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
);
992 rLibraryMap
[nCurrentId
] = i
;
997 // then the remaining modules
998 for (sal_Int32 i
= 0; i
< n
; ++i
)
1000 if (i
== nWorkbookIndex
)
1003 css::script::ModuleInfo aModuleInfo
= xModuleInfo
->getModuleInfo(aElementNames
[i
]);
1004 if (aModuleInfo
.ModuleType
== 4)
1006 rLibraryMap
[nCurrentId
] = i
;
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
);
1024 void VbaExport::exportVBA(SotStorage
* pRootStorage
)
1026 css::uno::Reference
<css::container::XNameContainer
> xNameContainer
= getBasicLibrary();
1027 if (!xNameContainer
.is()) {
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
);
1049 exportPROJECTwmStream(*pPROJECTwmStream
, aElementNames
, aLibraryMap
);
1052 #if VBA_USE_ORIGINAL_DIR_STREAM
1053 OUString aDirPath
= "/home/moggi/Documents/testfiles/vba/VBA/dir";
1054 addFileStreamToSotStream(aDirPath
, pDirStream
);
1056 exportDirStream(*pDirStream
, xNameContainer
, aLibraryMap
, getProjectName());
1059 #if VBA_USE_ORIGINAL_PROJECT_STREAM
1060 OUString aProjectPath
= "/home/moggi/Documents/testfiles/vba/PROJECT";
1061 addFileStreamToSotStream(aProjectPath
, pPROJECTStream
);
1063 exportPROJECTStream(*pPROJECTStream
, xNameContainer
, getProjectName(), aLibraryMap
);
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
);
1070 exportVBAProjectStream(*pVBAProjectStream
);
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();
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();
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
);
1145 bool VbaExport::containsVBAProject()
1147 css::uno::Reference
<css::script::XLibraryContainer
> xLibContainer
= getLibraryContainer();
1148 if (!xLibContainer
.is())
1151 css::uno::Reference
<css::script::vba::XVBACompatibility
> xVbaCompatibility (xLibContainer
, css::uno::UNO_QUERY
);
1152 if (!xVbaCompatibility
.is())
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();
1169 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */