/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "oox/helper/propertyset.hxx" #include "oox/token/properties.hxx" #include #include #include #define USE_UTF8_CODEPAGE 0 #if USE_UTF8_CODEPAGE #define CODEPAGE_MS 65001 #define CODEPAGE RTL_TEXTENCODING_UTF8 #else #define CODEPAGE_MS 1252 #define CODEPAGE RTL_TEXTENCODING_MS_1252 #endif #define VBA_EXPORT_DEBUG 0 #define VBA_USE_ORIGINAL_WM_STREAM 0 #define VBA_USE_ORIGINAL_DIR_STREAM 0 #define VBA_USE_ORIGINAL_PROJECT_STREAM 0 #define VBA_USE_ORIGINAL_VBA_PROJECT 0 /* Enable to see VBA Encryption work. For now the input data and length values * for encryption correspond to the case when the VBA macro is not protected. */ #define VBA_ENCRYPTION 1 namespace { void exportString(SvStream& rStrm, const OUString& rString) { OString aStringCorrectCodepage = OUStringToOString(rString, CODEPAGE); rStrm.WriteOString(aStringCorrectCodepage); } void exportUTF16String(SvStream& rStrm, const OUString& rString) { sal_Int32 n = rString.getLength(); const sal_Unicode* pString = rString.getStr(); for (sal_Int32 i = 0; i < n; ++i) { sal_Unicode character = pString[i]; rStrm.WriteUnicode(character); } } bool isWorkbook(const css::uno::Reference& xInterface) { css::uno::Reference xWorkbook(xInterface, css::uno::UNO_QUERY); return xWorkbook.is(); } OUString createHexStringFromDigit(sal_uInt8 nDigit) { OUString aString = OUString::number( nDigit, 16 ); if(aString.getLength() == 1) aString = OUString::number(0) + aString; return aString.toAsciiUpperCase(); } OUString createGuidStringFromInt(sal_uInt8 nGuid[16]) { OUStringBuffer aBuffer; aBuffer.append('{'); for(size_t i = 0; i < 16; ++i) { aBuffer.append(createHexStringFromDigit(nGuid[i])); if(i == 3|| i == 5 || i == 7 || i == 9 ) aBuffer.append('-'); } aBuffer.append('}'); OUString aString = aBuffer.makeStringAndClear(); return aString.toAsciiUpperCase(); } OUString generateGUIDString() { sal_uInt8 nGuid[16]; rtl_createUuid(nGuid, nullptr, true); return createGuidStringFromInt(nGuid); } } VBACompressionChunk::VBACompressionChunk(SvStream& rCompressedStream, const sal_uInt8* pData, std::size_t nChunkSize) : mrCompressedStream(rCompressedStream) , mpUncompressedData(pData) , mpCompressedChunkStream(nullptr) , mnChunkSize(nChunkSize) , mnCompressedCurrent(0) , mnCompressedEnd(0) , mnDecompressedCurrent(0) , mnDecompressedEnd(0) { } void setUInt16(sal_uInt8* pBuffer, size_t nPos, sal_uInt16 nVal) { pBuffer[nPos] = nVal & 0xFF; pBuffer[nPos+1] = (nVal & 0xFF00) >> 8; } sal_uInt16 VBACompressionChunk::handleHeader(bool bCompressed) { // handle header bytes size_t nSize = mnCompressedCurrent; sal_uInt16 nHeader = 0; PackCompressedChunkSize(nSize, nHeader); PackCompressedChunkFlag(bCompressed, nHeader); PackCompressedChunkSignature(nHeader); return nHeader; } // section 2.4.1.3.7 void VBACompressionChunk::write() { mnDecompressedCurrent = 0; mnCompressedCurrent = 2; mnCompressedEnd = 4098; mnDecompressedEnd = std::min(4096, mnChunkSize); // if that stream becomes larger than 4096 bytes then // we use the uncompressed stream sal_uInt8 pCompressedChunkStream[4098]; mpCompressedChunkStream = pCompressedChunkStream; while (mnDecompressedCurrent < mnDecompressedEnd && mnCompressedCurrent < mnCompressedEnd) { // compress token sequence compressTokenSequence(); } if (mnDecompressedCurrent < mnDecompressedEnd) { sal_uInt64 nChunkStart = mrCompressedStream.Tell(); mrCompressedStream.WriteUInt16(0); writeRawChunk(); mrCompressedStream.Seek(nChunkStart); sal_uInt16 nHeader = handleHeader(false); mrCompressedStream.WriteUInt16(nHeader); } else { sal_uInt16 nHeader = handleHeader(true); setUInt16(pCompressedChunkStream, 0, nHeader); // copy the compressed stream to our output stream mrCompressedStream.WriteBytes(pCompressedChunkStream, mnCompressedCurrent); } } // section 2.4.1.3.13 void VBACompressionChunk::PackCompressedChunkSize(size_t nSize, sal_uInt16& rHeader) { sal_uInt16 nTemp1 = rHeader & 0xF000; sal_uInt16 nTemp2 = nSize - 3; rHeader = nTemp1 | nTemp2; } // section 2.4.1.3.16 void VBACompressionChunk::PackCompressedChunkFlag(bool bCompressed, sal_uInt16& rHeader) { sal_uInt16 nTemp1 = rHeader & 0x7FFF; sal_uInt16 nTemp2 = ((sal_uInt16)bCompressed) << 15; rHeader = nTemp1 | nTemp2; } // section 2.4.1.3.14 void VBACompressionChunk::PackCompressedChunkSignature(sal_uInt16& rHeader) { sal_Int32 nTemp = rHeader & 0x8FFFF; rHeader = nTemp | 0x3000; } // section 2.4.1.3.8 void VBACompressionChunk::compressTokenSequence() { sal_uInt64 nFlagByteIndex = mnCompressedCurrent; sal_uInt8 nFlagByte = 0; ++mnCompressedCurrent; for (size_t index = 0; index <= 7; ++index) { if (mnDecompressedCurrent < mnDecompressedEnd && mnCompressedCurrent < mnCompressedEnd) { compressToken(index, nFlagByte); } } mpCompressedChunkStream[nFlagByteIndex] = nFlagByte; } // section 2.4.1.3.9 void VBACompressionChunk::compressToken(size_t index, sal_uInt8& nFlagByte) { size_t nLength = 0; size_t nOffset = 0; match(nLength, nOffset); if (nOffset != 0) { if (mnCompressedCurrent + 1 < mnCompressedEnd) { sal_uInt16 nToken = CopyToken(nLength, nOffset); setUInt16(mpCompressedChunkStream, mnCompressedCurrent, nToken); SetFlagBit(index, true, nFlagByte); mnCompressedCurrent += 2; mnDecompressedCurrent += nLength; } else { mnCompressedCurrent = mnCompressedEnd; } } else { if (mnCompressedCurrent + 1 < mnCompressedEnd) { mpCompressedChunkStream[mnCompressedCurrent] = mpUncompressedData[mnDecompressedCurrent]; ++mnCompressedCurrent; ++mnDecompressedCurrent; } else { mnCompressedCurrent = mnCompressedEnd; } } } // section 2.4.1.3.18 void VBACompressionChunk::SetFlagBit(size_t index, bool bVal, sal_uInt8& rFlag) { size_t nTemp1 = ((int)bVal) << index; sal_uInt8 nTemp2 = rFlag & (~nTemp1); rFlag = nTemp2 | nTemp1; } // section 2.4.1.3.19.3 sal_uInt16 VBACompressionChunk::CopyToken(size_t nLength, size_t nOffset) { sal_uInt16 nLengthMask = 0; sal_uInt16 nOffsetMask = 0; sal_uInt16 nBitCount = 0; sal_uInt16 nMaxLength; CopyTokenHelp(nLengthMask, nOffsetMask, nBitCount, nMaxLength); sal_uInt16 nTemp1 = nOffset -1; sal_uInt16 nTemp2 = 16 - nBitCount; sal_uInt16 nTemp3 = nLength - 3; sal_uInt16 nToken = (nTemp1 << nTemp2) | nTemp3; return nToken; } // section 2.4.1.3.19.4 void VBACompressionChunk::match(size_t& rLength, size_t& rOffset) { size_t nBestLen = 0; sal_Int32 nCandidate = mnDecompressedCurrent - 1; sal_Int32 nBestCandidate = nCandidate; while (nCandidate >= 0) { sal_Int32 nC = nCandidate; sal_Int32 nD = mnDecompressedCurrent; size_t nLen = 0; while (nD < static_cast(mnChunkSize) // TODO: check if this needs to be including a minus -1 && mpUncompressedData[nC] == mpUncompressedData[nD]) { ++nLen; ++nC; ++nD; } if (nLen > nBestLen) { nBestLen = nLen; nBestCandidate = nCandidate; } --nCandidate; } if (nBestLen >= 3) { sal_uInt16 nMaximumLength = 0; sal_uInt16 nLengthMask, nOffsetMask, nBitCount; CopyTokenHelp(nLengthMask, nOffsetMask, nBitCount, nMaximumLength); rLength = std::min(nMaximumLength, nBestLen); rOffset = mnDecompressedCurrent - nBestCandidate; } else { rLength = 0; rOffset = 0; } } // section 2.4.1.3.19.1 void VBACompressionChunk::CopyTokenHelp(sal_uInt16& rLengthMask, sal_uInt16& rOffsetMask, sal_uInt16& rBitCount, sal_uInt16& rMaximumLength) { sal_uInt16 nDifference = mnDecompressedCurrent; assert(nDifference <= 4096); assert(nDifference >= 1); if (nDifference >= 2049) rBitCount = 12; else if (nDifference >= 1025) rBitCount = 11; else if (nDifference >= 513) rBitCount = 10; else if (nDifference >= 257) rBitCount = 9; else if (nDifference >= 129) rBitCount = 8; else if (nDifference >= 65) rBitCount = 7; else if (nDifference >= 33) rBitCount = 6; else if (nDifference >= 17) rBitCount = 5; else rBitCount = 4; rLengthMask = 0xffff >> rBitCount; rOffsetMask = ~rLengthMask; rMaximumLength = rLengthMask + 3; } // section 2.4.1.3.10 void VBACompressionChunk::writeRawChunk() { // we need to use up to 4096 bytes of the original stream // and fill the rest with padding mrCompressedStream.WriteBytes(mpUncompressedData, mnChunkSize); std::size_t nPadding = 4096 - mnChunkSize; for (size_t i = 0; i < nPadding; ++i) { mrCompressedStream.WriteUInt8(0); } } VBACompression::VBACompression(SvStream& rCompressedStream, SvMemoryStream& rUncompressedStream): mrCompressedStream(rCompressedStream), mrUncompressedStream(rUncompressedStream) { } // section 2.4.1.3.6 void VBACompression::write() { // section 2.4.1.1.1 mrCompressedStream.WriteUInt8(0x01); // signature byte of a compressed container bool bStreamNotEnded = true; const sal_uInt8* pData = static_cast(mrUncompressedStream.GetData()); std::size_t nSize = mrUncompressedStream.GetEndOfData(); std::size_t nRemainingSize = nSize; while(bStreamNotEnded) { std::size_t nChunkSize = nRemainingSize > 4096 ? 4096 : nRemainingSize; VBACompressionChunk aChunk(mrCompressedStream, &pData[nSize - nRemainingSize], nChunkSize); aChunk.write(); // update the uncompressed chunk start marker nRemainingSize -= nChunkSize; bStreamNotEnded = nRemainingSize != 0; } } // section 2.4.3 #if VBA_ENCRYPTION VBAEncryption::VBAEncryption(const sal_uInt8* pData, const sal_uInt16 length, SvStream& rEncryptedData, sal_uInt8 nProjKey) :mpData(pData) ,mnLength(length) ,mrEncryptedData(rEncryptedData) ,mnUnencryptedByte1(0) ,mnEncryptedByte1(0) ,mnEncryptedByte2(0) ,mnProjKey(nProjKey) ,mnIgnoredLength(0) ,mnSeed(0x00) ,mnVersionEnc(0) { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> dis(0, 255); mnSeed = dis(gen); } void VBAEncryption::writeSeed() { exportString(mrEncryptedData, createHexStringFromDigit(mnSeed)); } void VBAEncryption::writeVersionEnc() { static const sal_uInt8 mnVersion = 2; // the encrypted version mnVersionEnc = mnSeed ^ mnVersion; exportString(mrEncryptedData, createHexStringFromDigit(mnVersionEnc)); } sal_uInt8 VBAEncryption::calculateProjKey(const OUString& rProjectKey) { sal_uInt32 nProjKey = 0; // use sal_uInt32 instead of sal_uInt8 to avoid miscompilation at least // under "Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 // for x64" with --enable-64-bit and non-debug, causing // CppunitTest_oox_vba_encryption's TestVbaEncryption::testProjKey1 to // fail with actual 53 vs. expected 223 sal_Int32 n = rProjectKey.getLength(); const sal_Unicode* pString = rProjectKey.getStr(); for (sal_Int32 i = 0; i < n; ++i) { sal_Unicode character = pString[i]; nProjKey += character; } return nProjKey; } void VBAEncryption::writeProjKeyEnc() { sal_uInt8 nProjKeyEnc = mnSeed ^ mnProjKey; exportString(mrEncryptedData, createHexStringFromDigit(nProjKeyEnc)); mnUnencryptedByte1 = mnProjKey; mnEncryptedByte1 = nProjKeyEnc; // ProjKeyEnc mnEncryptedByte2 = mnVersionEnc; // VersionEnc } void VBAEncryption::writeIgnoredEnc() { mnIgnoredLength = (mnSeed & 6) / 2; for(sal_Int32 i = 1; i <= mnIgnoredLength; ++i) { sal_uInt8 nTempValue = 0xBE; // Any value can be assigned here sal_uInt8 nByteEnc = nTempValue ^ (mnEncryptedByte2 + mnUnencryptedByte1); exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc)); mnEncryptedByte2 = mnEncryptedByte1; mnEncryptedByte1 = nByteEnc; mnUnencryptedByte1 = nTempValue; } } void VBAEncryption::writeDataLengthEnc() { sal_uInt16 temp = mnLength; for(sal_Int8 i = 0; i < 4; ++i) { sal_uInt8 nByte = temp & 0xFF; sal_uInt8 nByteEnc = nByte ^ (mnEncryptedByte2 + mnUnencryptedByte1); exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc)); mnEncryptedByte2 = mnEncryptedByte1; mnEncryptedByte1 = nByteEnc; mnUnencryptedByte1 = nByte; temp >>= 8; } } void VBAEncryption::writeDataEnc() { for(sal_Int16 i = 0; i < mnLength; i++) { sal_uInt8 nByteEnc = mpData[i] ^ (mnEncryptedByte2 + mnUnencryptedByte1); exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc)); mnEncryptedByte2 = mnEncryptedByte1; mnEncryptedByte1 = nByteEnc; mnUnencryptedByte1 = mpData[i]; } } void VBAEncryption::write() { writeSeed(); writeVersionEnc(); writeProjKeyEnc(); writeIgnoredEnc(); writeDataLengthEnc(); writeDataEnc(); } #endif VbaExport::VbaExport(css::uno::Reference const & xModel): mxModel(xModel) { } namespace { // section 2.3.4.2.1.1 void writePROJECTSYSKIND(SvStream& rStrm) { rStrm.WriteUInt16(0x0001); // id rStrm.WriteUInt32(0x00000004); // size rStrm.WriteUInt32(0x00000001); // SysKind, hard coded to 32-bin windows for now } // section 2.3.4.2.1.2 void writePROJECTLCID(SvStream& rStrm) { rStrm.WriteUInt16(0x0002); // id rStrm.WriteUInt32(0x00000004); // size rStrm.WriteUInt32(0x00000409); // Lcid } // section 2.3.4.2.1.3 void writePROJECTLCIDINVOKE(SvStream& rStrm) { rStrm.WriteUInt16(0x0014); // id rStrm.WriteUInt32(0x00000004); // size rStrm.WriteUInt32(0x00000409); // LcidInvoke } // section 2.3.4.2.1.4 void writePROJECTCODEPAGE(SvStream& rStrm) { rStrm.WriteUInt16(0x0003); // id rStrm.WriteUInt32(0x00000002); // size rStrm.WriteUInt16(CODEPAGE_MS); // CodePage } //section 2.3.4.2.1.5 void writePROJECTNAME(SvStream& rStrm, const OUString& name) { rStrm.WriteUInt16(0x0004); // id sal_uInt32 sizeOfProjectName = name.getLength(); rStrm.WriteUInt32(sizeOfProjectName); // sizeOfProjectName exportString(rStrm, name); // ProjectName } //section 2.3.4.2.1.6 void writePROJECTDOCSTRING(SvStream& rStrm) { rStrm.WriteUInt16(0x0005); // id rStrm.WriteUInt32(0x00000000); // sizeOfDocString rStrm.WriteUInt16(0x0040); // Reserved rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode, MUST be even } //section 2.3.4.2.1.7 void writePROJECTHELPFILEPATH(SvStream& rStrm) { rStrm.WriteUInt16(0x0006); // id rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile1 rStrm.WriteUInt16(0x003D); // Reserved rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile2 } //section 2.3.4.2.1.8 void writePROJECTHELPCONTEXT(SvStream& rStrm) { rStrm.WriteUInt16(0x0007); // id rStrm.WriteUInt32(0x00000004); // size rStrm.WriteUInt32(0x00000000); // HelpContext } //section 2.3.4.2.1.9 void writePROJECTLIBFLAGS(SvStream& rStrm) { rStrm.WriteUInt16(0x0008); // id rStrm.WriteUInt32(0x00000004); // size rStrm.WriteUInt32(0x00000000); // ProjectLibFlags } //section 2.3.4.2.1.10 void writePROJECTVERSION(SvStream& rStrm) { rStrm.WriteUInt16(0x0009); // id rStrm.WriteUInt32(0x00000004); // Reserved rStrm.WriteUInt32(1467127224); // VersionMajor // TODO: where is this magic number coming from rStrm.WriteUInt16(5); // VersionMinor // TODO: where is this magic number coming from } //section 2.3.4.2.1.11 void writePROJECTCONSTANTS(SvStream& rStrm) { rStrm.WriteUInt16(0x000C); // id rStrm.WriteUInt32(0x00000000); // sizeOfConstants rStrm.WriteUInt16(0x003C); // Reserved rStrm.WriteUInt32(0x00000000); // sizeOfConstantsUnicode } // section 2.3.4.2.1 void writePROJECTINFORMATION(SvStream& rStrm, const OUString& projectName) { writePROJECTSYSKIND(rStrm); writePROJECTLCID(rStrm); writePROJECTLCIDINVOKE(rStrm); writePROJECTCODEPAGE(rStrm); writePROJECTNAME(rStrm, projectName); writePROJECTDOCSTRING(rStrm); writePROJECTHELPFILEPATH(rStrm); writePROJECTHELPCONTEXT(rStrm); writePROJECTLIBFLAGS(rStrm); writePROJECTVERSION(rStrm); writePROJECTCONSTANTS(rStrm); } // section 2.3.4.2.2.2 void writeREFERENCENAME(SvStream& rStrm, const OUString& name) { rStrm.WriteUInt16(0x0016); // id sal_Int32 size = name.getLength(); rStrm.WriteUInt32(size); // sizeOfName exportString(rStrm, name); // name rStrm.WriteUInt16(0x003E); // reserved sal_Int32 unicodesize = size * 2; rStrm.WriteUInt32(unicodesize); // sizeOfNameUnicode exportUTF16String(rStrm, name); // nameUnicode } // section 2.3.4.2.2.5 void writeREFERENCEREGISTERED(SvStream& rStrm, const OUString& libid) { rStrm.WriteUInt16(0x000D); // id sal_Int32 sizeOfLibid = libid.getLength(); sal_Int32 size = sizeOfLibid + 10; // size of Libid, sizeOfLibid(4 bytes), reserved 1(4 bytes) and reserved 2(2 bytes) rStrm.WriteUInt32(size); // size rStrm.WriteUInt32(sizeOfLibid); // sizeOfLibid exportString(rStrm, libid); // Libid rStrm.WriteUInt32(0x00000000); // reserved 1 rStrm.WriteUInt16(0x0000); // reserved 2 } // section 2.3.4.2.2.1 void writeREFERENCE(SvStream& rStrm, const OUString& name, const OUString& libid) { writeREFERENCENAME(rStrm, name); writeREFERENCEREGISTERED(rStrm, libid); } // section 2.3.4.2.2 void writePROJECTREFERENCES(SvStream& rStrm) { // TODO: find out where these references are coming from writeREFERENCE(rStrm, "stdole", "*\\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\\Windows\\SysWOW64\\stdole2.tlb#OLE Automation"); 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"); } // section 2.3.4.2.3.1 void writePROJECTCOOKIE(SvStream& rStrm) { rStrm.WriteUInt16(0x0013); // id rStrm.WriteUInt32(0x00000002); // size rStrm.WriteUInt16(0xFFFF); // cookie } // section 2.3.4.2.3.2.1 void writeMODULENAME(SvStream& rStrm, const OUString& name) { rStrm.WriteUInt16(0x0019); // id sal_Int32 n = name.getLength(); // sizeOfModuleName rStrm.WriteUInt32(n); exportString(rStrm, name); // ModuleName } // section 2.3.4.2.3.2.2 void writeMODULENAMEUNICODE(SvStream& rStrm, const OUString& name) { rStrm.WriteUInt16(0x0047); // id sal_Int32 n = name.getLength() * 2; // sizeOfModuleNameUnicode // TODO: better calculation for unicode string length rStrm.WriteUInt32(n); exportUTF16String(rStrm, name); // ModuleNameUnicode } // section 2.3.4.2.3.2.3 void writeMODULESTREAMNAME(SvStream& rStrm, const OUString& streamName) { rStrm.WriteUInt16(0x001A); // id sal_Int32 n = streamName.getLength(); // sizeOfStreamName rStrm.WriteUInt32(n); exportString(rStrm, streamName); // StreamName rStrm.WriteUInt16(0x0032); // reserved rStrm.WriteUInt32(n * 2); // sizeOfStreamNameUnicode // TODO: better calculation for unicode string length exportUTF16String(rStrm, streamName); // StreamNameUnicode } // section 2.3.4.2.3.2.4 void writeMODULEDOCSTRING(SvStream& rStrm) { rStrm.WriteUInt16(0x001C); // id rStrm.WriteUInt32(0x00000000); // sizeOfDocString rStrm.WriteUInt16(0x0048); // reserved rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode } // section 2.3.4.2.3.2.5 void writeMODULEOFFSET(SvStream& rStrm) { rStrm.WriteUInt16(0x0031); // id rStrm.WriteUInt32(0x00000004); // sizeOfTextOffset rStrm.WriteUInt32(0x00000000); // TextOffset } // section 2.3.4.2.3.2.6 void writeMODULEHELPCONTEXT(SvStream& rStrm) { rStrm.WriteUInt16(0x001E); // id rStrm.WriteUInt32(0x00000004); // sizeOfHelpContext rStrm.WriteUInt32(0x00000000); // HelpContext } // section 2.3.4.2.3.2.7 void writeMODULECOOKIE(SvStream& rStrm) { rStrm.WriteUInt16(0x002C); // id rStrm.WriteUInt32(0x00000002); // sizeOfHelpContext rStrm.WriteUInt16(0xFFFF); // HelpContext } // section 2.3.4.2.3.2.8 void writeMODULETYPE(SvStream& rStrm, const sal_uInt16 type) { if(type == 1) rStrm.WriteUInt16(0x0021); // id for a procedural module else rStrm.WriteUInt16(0x0022); // id for document, class or design module rStrm.WriteUInt32(0x00000000); // reserved } // section 2.3.4.2.3.2 void writePROJECTMODULE(SvStream& rStrm, const OUString& name, const sal_uInt16 type) { writeMODULENAME(rStrm, name); writeMODULENAMEUNICODE(rStrm, name); writeMODULESTREAMNAME(rStrm, name); writeMODULEDOCSTRING(rStrm); writeMODULEOFFSET(rStrm); writeMODULEHELPCONTEXT(rStrm); writeMODULECOOKIE(rStrm); writeMODULETYPE(rStrm, type); rStrm.WriteUInt16(0x002B); // terminator rStrm.WriteUInt32(0x00000000); // reserved } // section 2.3.4.2.3 void writePROJECTMODULES(SvStream& rStrm, const css::uno::Reference& xNameContainer, const std::vector& rLibrayMap) { css::uno::Sequence aElementNames = xNameContainer->getElementNames(); sal_Int32 n = aElementNames.getLength(); css::uno::Reference xModuleInfo(xNameContainer, css::uno::UNO_QUERY); assert(xModuleInfo.is()); // TODO: this whole part is document specific rStrm.WriteUInt16(0x000F); // id rStrm.WriteUInt32(0x00000002); // size of Count sal_Int16 count = n; // Number of modules // TODO: this is dependent on the document rStrm.WriteUInt16(count); // Count writePROJECTCOOKIE(rStrm); for (sal_Int32 i = 0; i < n; ++i) { const OUString& rModuleName = aElementNames[rLibrayMap[i]]; css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName); writePROJECTMODULE(rStrm, rModuleName, aModuleInfo.ModuleType); } } // section 2.3.4.2 void exportDirStream(SvStream& rStrm, const css::uno::Reference& xNameContainer, const std::vector& rLibraryMap, const OUString& projectName) { SvMemoryStream aDirStream(4096, 4096); writePROJECTINFORMATION(aDirStream, projectName); writePROJECTREFERENCES(aDirStream); writePROJECTMODULES(aDirStream, xNameContainer, rLibraryMap); aDirStream.WriteUInt16(0x0010); // terminator aDirStream.WriteUInt32(0x00000000); // reserved aDirStream.Seek(0); #if VBA_EXPORT_DEBUG const OUString aDirFileName("/tmp/vba_dir_out.bin"); SvFileStream aDirStreamDebug(aDirFileName, StreamMode::READWRITE); aDirStreamDebug.WriteStream(aDirStream); aDirStream.Seek(0); #endif // the stream for the compression SvMemoryStream aMemoryStream(4096, 4096); aMemoryStream.WriteStream(aDirStream); VBACompression aCompression(rStrm, aDirStream); aCompression.write(); } // section 2.3.4.3 Module Stream void exportModuleStream(SvStream& rStrm, const OUString& rSourceCode, const OUString& aElementName, css::script::ModuleInfo const & rInfo) { SvMemoryStream aModuleStream(4096, 4096); exportString(aModuleStream, "Attribute VB_Name = \"" + aElementName + "\"\r\n"); if (rInfo.ModuleType == 4) { if (isWorkbook(rInfo.ModuleObject)) exportString(aModuleStream, "Attribute VB_Base = \"0{00020819-0000-0000-C000-000000000046}\"\r\n"); else exportString(aModuleStream, "Attribute VB_Base = \"0{00020820-0000-0000-C000-000000000046}\"\r\n"); exportString(aModuleStream, "Attribute VB_GlobalNameSpace = False\r\n"); exportString(aModuleStream, "Attribute VB_Creatable = False\r\n"); exportString(aModuleStream, "Attribute VB_PredeclaredId = True\r\n"); exportString(aModuleStream, "Attribute VB_Exposed = True\r\n"); exportString(aModuleStream, "Attribute VB_TemplateDerived = False\r\n"); exportString(aModuleStream, "Attribute VB_Customizable = True\r\n"); } OUString aSourceCode = rSourceCode.replaceFirst("Option VBASupport 1\n", ""); const sal_Int32 nPos = aSourceCode.indexOf("Rem Attribute VBA_ModuleType="); const sal_Int32 nEndPos = nPos != -1 ? aSourceCode.indexOf("\n", nPos) : -1; if (nPos != -1 && nEndPos != -1) aSourceCode = aSourceCode.replaceAt(nPos, nEndPos - nPos+1, ""); aSourceCode = aSourceCode.replaceAll("\n", "\r\n"); exportString(aModuleStream, aSourceCode); aModuleStream.Seek(0); #if VBA_EXPORT_DEBUG OUString aModuleFileName("/tmp/vba_" + aElementName + "_out.bin"); SvFileStream aModuleStreamDebug(aModuleFileName, StreamMode::READWRITE); aModuleStreamDebug.WriteStream(aModuleStream); aModuleStream.Seek(0); #endif // the stream for the compression SvMemoryStream aMemoryStream(4096, 4096); aMemoryStream.WriteStream(aModuleStream); VBACompression aCompression(rStrm, aModuleStream); aCompression.write(); } // section 2.3.4.1 _VBA_PROJECT Stream void exportVBAProjectStream(SvStream& rStrm) { rStrm.WriteUInt16(0x61CC); // Reserved1 rStrm.WriteUInt16(0xFFFF); // Version rStrm.WriteUInt8(0x00); // Reserved2 rStrm.WriteUInt16(0x0000); // Undefined } // section 2.3.1 PROJECT Stream void exportPROJECTStream(SvStream& rStrm, const css::uno::Reference& xNameContainer, const OUString& projectName, const std::vector& rLibraryMap) { css::uno::Sequence aElementNames = xNameContainer->getElementNames(); sal_Int32 n = aElementNames.getLength(); css::uno::Reference xModuleInfo(xNameContainer, css::uno::UNO_QUERY); assert(xModuleInfo.is()); // section 2.3.1.1ProjectProperties // section 2.3.1.2 ProjectId exportString(rStrm, "ID=\""); OUString aProjectID = generateGUIDString(); exportString(rStrm, aProjectID); exportString(rStrm, "\"\r\n"); // section 2.3.1.3 ProjectModule for (sal_Int32 i = 0; i < n; ++i) { const OUString& rModuleName = aElementNames[rLibraryMap[i]]; css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName); if(aModuleInfo.ModuleType == 1) { exportString(rStrm, "Module=" + rModuleName + "\r\n"); } else if(aModuleInfo.ModuleType == 4) { exportString(rStrm, "Document=" + rModuleName + "/&H00000000\r\n"); } } // section 2.3.1.11 ProjectName exportString(rStrm, "Name=\"" + projectName + "\"\r\n"); // section 2.3.1.12 ProjectHelpId exportString(rStrm, "HelpContextID=\"0\"\r\n"); // section 2.3.1.14 ProjectVersionCompat32 exportString(rStrm, "VersionCompatible32=\"393222000\"\r\n"); // section 2.3.1.15 ProjectProtectionState #if VBA_ENCRYPTION exportString(rStrm, "CMG=\""); SvMemoryStream aProtectedStream(4096, 4096); aProtectedStream.WriteUInt32(0x00000000); const sal_uInt8* pData = static_cast(aProtectedStream.GetData()); sal_uInt8 nProjKey = VBAEncryption::calculateProjKey(aProjectID); VBAEncryption aProtectionState(pData, 4, rStrm, nProjKey); aProtectionState.write(); exportString(rStrm, "\"\r\n"); #else exportString(rStrm, "CMG=\"BEBC9256EEAAA8AEA8AEA8AEA8AE\"\r\n"); #endif // section 2.3.1.16 ProjectPassword #if VBA_ENCRYPTION exportString(rStrm, "DPB=\""); aProtectedStream.Seek(0); aProtectedStream.WriteUInt8(0x00); pData = static_cast(aProtectedStream.GetData()); VBAEncryption aProjectPassword(pData, 1, rStrm, nProjKey); aProjectPassword.write(); exportString(rStrm, "\"\r\n"); #else exportString(rStrm, "DPB=\"7C7E5014B0D3B1D3B1D3\"\r\n"); #endif // section 2.3.1.17 ProjectVisibilityState #if VBA_ENCRYPTION exportString(rStrm, "GC=\""); aProtectedStream.Seek(0); aProtectedStream.WriteUInt8(0xFF); pData = static_cast(aProtectedStream.GetData()); VBAEncryption aVisibilityState(pData, 1, rStrm, nProjKey); aVisibilityState.write(); exportString(rStrm, "\"\r\n\r\n"); #else exportString(rStrm, "GC=\"3A3816DAD5DBD5DB2A\"\r\n\r\n"); #endif // section 2.3.1.18 HostExtenders exportString(rStrm, "[Host Extender Info]\r\n" "&H00000001={3832D640-CF90-11CF-8E43-00A0C911005A};VBE;&H00000000\r\n\r\n" ); // section 2.3.1.19 ProjectWorkspace exportString(rStrm, "[Workspace]\r\n"); for (sal_Int32 i = 0; i < n; ++i) { const OUString& rModuleName = aElementNames[rLibraryMap[i]]; css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName); if(aModuleInfo.ModuleType == 1) { exportString(rStrm, rModuleName + "=25, 25, 1439, 639, \r\n"); } else { exportString(rStrm, rModuleName + "=0, 0, 0, 0, C\r\n"); } } } // section 2.3.3.1 NAMEMAP void writeNAMEMAP(SvStream& rStrm, const css::uno::Sequence& rElementNames, const std::vector& rLibraryMap) { int n = rElementNames.getLength(); for(sal_Int32 i = 0; i < n; ++i) { const OUString& rModuleName = rElementNames[rLibraryMap[i]]; exportString(rStrm, rModuleName); rStrm.WriteUInt8(0x00); // terminator exportUTF16String(rStrm, rModuleName); rStrm.WriteUInt16(0x0000); // terminator } } // section 2.3.3 PROJECTwm Stream void exportPROJECTwmStream(SvStream& rStrm, const css::uno::Sequence& rElementNames, const std::vector& rLibraryMap) { writeNAMEMAP(rStrm, rElementNames, rLibraryMap); rStrm.WriteUInt16(0x0000); // terminator } void getCorrectExportOrder(const css::uno::Reference& xNameContainer, std::vector& rLibraryMap) { css::uno::Sequence aElementNames = xNameContainer->getElementNames(); sal_Int32 n = aElementNames.getLength(); css::uno::Reference xModuleInfo(xNameContainer, css::uno::UNO_QUERY); sal_Int32 nCurrentId = 0; // first all the non-document modules for (sal_Int32 i = 0; i < n; ++i) { css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]); if (aModuleInfo.ModuleType != 4) { rLibraryMap[nCurrentId] = i; ++nCurrentId; } } sal_Int32 nWorkbookIndex = -1; // then possibly the workbook module for (sal_Int32 i = 0; i < n; ++i) { css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]); bool bWorkbook = isWorkbook(aModuleInfo.ModuleObject); if (bWorkbook) { nWorkbookIndex = i; rLibraryMap[nCurrentId] = i; ++nCurrentId; } } // then the remaining modules for (sal_Int32 i = 0; i < n; ++i) { if (i == nWorkbookIndex) continue; css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]); if (aModuleInfo.ModuleType == 4) { rLibraryMap[nCurrentId] = i; ++nCurrentId; } } } } #if VBA_USE_ORIGINAL_WM_STREAM || VBA_USE_ORIGINAL_DIR_STREAM \ || VBA_USE_ORIGINAL_PROJECT_STREAM || VBA_USE_ORIGINAL_VBA_PROJECT \ || VBA_USE_ORIGINAL_DIR_STREAM void addFileStreamToSotStream(const OUString& rPath, SotStorageStream* pStream) { SvFileStream aFileStream(rPath, StreamMode::READWRITE); pStream->WriteStream(aFileStream); } #endif void VbaExport::exportVBA(SotStorage* pRootStorage) { css::uno::Reference xNameContainer = getBasicLibrary(); if (!xNameContainer.is()) { return; } css::uno::Sequence aElementNames = xNameContainer->getElementNames(); sal_Int32 n = aElementNames.getLength(); // get the number of modules // export the elements in the order MSO expects them // we store the index of the std::vector aLibraryMap(n, 0); getCorrectExportOrder(xNameContainer, aLibraryMap); // start here with the VBA export tools::SvRef xVBAStream = pRootStorage->OpenSotStorage("VBA", StreamMode::READWRITE); SotStorageStream* pDirStream = xVBAStream->OpenSotStream("dir", StreamMode::READWRITE); SotStorageStream* pVBAProjectStream = xVBAStream->OpenSotStream("_VBA_PROJECT", StreamMode::READWRITE); SotStorageStream* pPROJECTStream = pRootStorage->OpenSotStream("PROJECT", StreamMode::READWRITE); SotStorageStream* pPROJECTwmStream = pRootStorage->OpenSotStream("PROJECTwm", StreamMode::READWRITE); #if VBA_USE_ORIGINAL_WM_STREAM OUString aProjectwmPath = "/home/moggi/Documents/testfiles/vba/PROJECTwm"; addFileStreamToSotStream(aProjectwmPath, pPROJECTwmStream); #else exportPROJECTwmStream(*pPROJECTwmStream, aElementNames, aLibraryMap); #endif #if VBA_USE_ORIGINAL_DIR_STREAM OUString aDirPath = "/home/moggi/Documents/testfiles/vba/VBA/dir"; addFileStreamToSotStream(aDirPath, pDirStream); #else exportDirStream(*pDirStream, xNameContainer, aLibraryMap, getProjectName()); #endif #if VBA_USE_ORIGINAL_PROJECT_STREAM OUString aProjectPath = "/home/moggi/Documents/testfiles/vba/PROJECT"; addFileStreamToSotStream(aProjectPath, pPROJECTStream); #else exportPROJECTStream(*pPROJECTStream, xNameContainer, getProjectName(), aLibraryMap); #endif #if VBA_USE_ORIGINAL_VBA_PROJECT OUString a_VBA_ProjectPath = "/home/moggi/Documents/testfiles/vba/VBA/_VBA_PROJECT"; addFileStreamToSotStream(a_VBA_ProjectPath, pVBAProjectStream); #else exportVBAProjectStream(*pVBAProjectStream); #endif #if VBA_USE_ORIGINAL_DIR_STREAM OUString aModule1Path = "/home/moggi/Documents/testfiles/vba/VBA/Module1"; OUString aSheet1Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet1"; OUString aSheet2Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet2"; OUString aSheet3Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet3"; OUString aWorkbookPath = "/home/moggi/Documents/testfiles/vba/VBA/ThisWorkbook"; SotStorageStream* pModule1Stream = xVBAStream->OpenSotStream("Module1", StreamMode::READWRITE); SotStorageStream* pSheet1Stream = xVBAStream->OpenSotStream("Sheet1", StreamMode::READWRITE); SotStorageStream* pSheet2Stream = xVBAStream->OpenSotStream("Sheet2", StreamMode::READWRITE); SotStorageStream* pSheet3Stream = xVBAStream->OpenSotStream("Sheet3", StreamMode::READWRITE); SotStorageStream* pWorkbookStream = xVBAStream->OpenSotStream("ThisWorkbook", StreamMode::READWRITE); addFileStreamToSotStream(aModule1Path, pModule1Stream); addFileStreamToSotStream(aSheet1Path, pSheet1Stream); addFileStreamToSotStream(aSheet2Path, pSheet2Stream); addFileStreamToSotStream(aSheet3Path, pSheet3Stream); addFileStreamToSotStream(aWorkbookPath, pWorkbookStream); pModule1Stream->Commit(); pSheet1Stream->Commit(); pSheet2Stream->Commit(); pSheet3Stream->Commit(); pWorkbookStream->Commit(); #else css::uno::Reference xModuleInfo(xNameContainer, css::uno::UNO_QUERY); for (sal_Int32 i = 0; i < n; ++i) { const OUString& rModuleName = aElementNames[aLibraryMap[i]]; SotStorageStream* pModuleStream = xVBAStream->OpenSotStream(rModuleName, StreamMode::READWRITE); css::uno::Any aCode = xNameContainer->getByName(rModuleName); css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName); OUString aSourceCode; aCode >>= aSourceCode; exportModuleStream(*pModuleStream, aSourceCode, rModuleName, aModuleInfo); pModuleStream->Commit(); } #endif pVBAProjectStream->Commit(); pDirStream->Commit(); xVBAStream->Commit(); pPROJECTStream->Commit(); pPROJECTwmStream->Commit(); pRootStorage->Commit(); } css::uno::Reference VbaExport::getLibraryContainer() { oox::PropertySet aDocProp(mxModel); css::uno::Reference xLibContainer(aDocProp.getAnyProperty(oox::PROP_BasicLibraries), css::uno::UNO_QUERY); return xLibContainer; } css::uno::Reference VbaExport::getBasicLibrary() { css::uno::Reference xLibrary; try { css::uno::Reference xLibContainer = getLibraryContainer(); OUString aProjectName = getProjectName(); xLibrary.set( xLibContainer->getByName(aProjectName), css::uno::UNO_QUERY_THROW ); } catch(...) { } return xLibrary; } bool VbaExport::containsVBAProject() { css::uno::Reference xLibContainer = getLibraryContainer(); if (!xLibContainer.is()) return false; css::uno::Reference xVbaCompatibility (xLibContainer, css::uno::UNO_QUERY); if (!xVbaCompatibility.is()) return false; bool bVBACompatibilty = xVbaCompatibility->getVBACompatibilityMode(); return bVBACompatibilty; } OUString VbaExport::getProjectName() { css::uno::Reference xVbaCompatibility(getLibraryContainer(), css::uno::UNO_QUERY); if (xVbaCompatibility.is()) return xVbaCompatibility->getProjectName(); return OUString(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */