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