office-gobmx/oox/source/ole/vbaexport.cxx
Jorenz Paragas 4bf7614503 tdf#42949: clean up includes in include/oox/helper with iwyu
...and fix the many compiler errors that occurred as a result.

Change-Id: I497c326272b2f02737ca3765720d6815b735423b
Reviewed-on: https://gerrit.libreoffice.org/24735
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Michael Stahl <mstahl@redhat.com>
2016-05-10 19:20:40 +00:00

1200 lines
38 KiB
C++

/* -*- 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 <sal/config.h>
#include <cassert>
#include <cmath>
#include <random>
#include <oox/ole/vbaexport.hxx>
#include <tools/stream.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/script/XLibraryContainer.hpp>
#include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
#include <com/sun/star/script/vba/XVBACompatibility.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <ooo/vba/excel/XWorkbook.hpp>
#include <oox/helper/binaryoutputstream.hxx>
#include "oox/helper/propertyset.hxx"
#include "oox/token/properties.hxx"
#include <sot/storage.hxx>
#include <rtl/uuid.h>
#include <comphelper/string.hxx>
#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<css::uno::XInterface>& xInterface)
{
css::uno::Reference<ooo::vba::excel::XWorkbook> 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, sal_Size 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<sal_uInt64>(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.Write(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<sal_Int32>(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<sal_uInt16>(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.Write(mpUncompressedData, mnChunkSize);
sal_Size 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<const sal_uInt8*>(mrUncompressedStream.GetData());
sal_Size nSize = mrUncompressedStream.GetEndOfData();
sal_Size nRemainingSize = nSize;
while(bStreamNotEnded)
{
sal_Size 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* pSeed, sal_uInt8 nProjKey)
:mpData(pData)
,mnLength(length)
,mrEncryptedData(rEncryptedData)
,mnUnencryptedByte1(0)
,mnEncryptedByte1(0)
,mnEncryptedByte2(0)
,mnVersion(2)
,mnProjKey(nProjKey)
,mnIgnoredLength(0)
,mnSeed(pSeed ? *pSeed : 0x00)
,mnVersionEnc(0)
{
if (!pSeed)
{
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()
{
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<css::frame::XModel> 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<css::container::XNameContainer>& xNameContainer, const std::vector<sal_Int32>& rLibrayMap)
{
css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
sal_Int32 n = aElementNames.getLength();
css::uno::Reference<css::script::vba::XVBAModuleInfo> 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<css::container::XNameContainer>& xNameContainer, const std::vector<sal_Int32>& 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, STREAM_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& 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, STREAM_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<css::container::XNameContainer>& xNameContainer,
const OUString& projectName, const std::vector<sal_Int32>& rLibraryMap)
{
css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
sal_Int32 n = aElementNames.getLength();
css::uno::Reference<css::script::vba::XVBAModuleInfo> 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<const sal_uInt8*>(aProtectedStream.GetData());
sal_uInt8 nProjKey = VBAEncryption::calculateProjKey(aProjectID);
VBAEncryption aProtectionState(pData, 4, rStrm, nullptr, 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<const sal_uInt8*>(aProtectedStream.GetData());
VBAEncryption aProjectPassword(pData, 1, rStrm, nullptr, 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<const sal_uInt8*>(aProtectedStream.GetData());
VBAEncryption aVisibilityState(pData, 1, rStrm, nullptr, 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<OUString>& rElementNames,
const std::vector<sal_Int32>& 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<OUString>& rElementNames,
const std::vector<sal_Int32>& rLibraryMap)
{
writeNAMEMAP(rStrm, rElementNames, rLibraryMap);
rStrm.WriteUInt16(0x0000); // terminator
}
void getCorrectExportOrder(const css::uno::Reference<css::container::XNameContainer>& xNameContainer, std::vector<sal_Int32>& rLibraryMap)
{
css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
sal_Int32 n = aElementNames.getLength();
css::uno::Reference<css::script::vba::XVBAModuleInfo> 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, STREAM_READWRITE);
pStream->WriteStream(aFileStream);
}
#endif
void VbaExport::exportVBA(SotStorage* pRootStorage)
{
css::uno::Reference<css::container::XNameContainer> xNameContainer = getBasicLibrary();
if (!xNameContainer.is()) {
return;
}
css::uno::Sequence<OUString> 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<sal_Int32> aLibraryMap(n, 0);
getCorrectExportOrder(xNameContainer, aLibraryMap);
// start here with the VBA export
SotStorage* pVBAStream = pRootStorage->OpenSotStorage("VBA", STREAM_READWRITE);
SotStorageStream* pDirStream = pVBAStream->OpenSotStream("dir", STREAM_READWRITE);
SotStorageStream* pVBAProjectStream = pVBAStream->OpenSotStream("_VBA_PROJECT", STREAM_READWRITE);
SotStorageStream* pPROJECTStream = pRootStorage->OpenSotStream("PROJECT", STREAM_READWRITE);
SotStorageStream* pPROJECTwmStream = pRootStorage->OpenSotStream("PROJECTwm", STREAM_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 = pVBAStream->OpenSotStream("Module1", STREAM_READWRITE);
SotStorageStream* pSheet1Stream = pVBAStream->OpenSotStream("Sheet1", STREAM_READWRITE);
SotStorageStream* pSheet2Stream = pVBAStream->OpenSotStream("Sheet2", STREAM_READWRITE);
SotStorageStream* pSheet3Stream = pVBAStream->OpenSotStream("Sheet3", STREAM_READWRITE);
SotStorageStream* pWorkbookStream = pVBAStream->OpenSotStream("ThisWorkbook", STREAM_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<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
for (sal_Int32 i = 0; i < n; ++i)
{
const OUString& rModuleName = aElementNames[aLibraryMap[i]];
SotStorageStream* pModuleStream = pVBAStream->OpenSotStream(rModuleName, STREAM_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();
pVBAStream->Commit();
pPROJECTStream->Commit();
pPROJECTwmStream->Commit();
pRootStorage->Commit();
}
css::uno::Reference<css::script::XLibraryContainer> VbaExport::getLibraryContainer()
{
oox::PropertySet aDocProp(mxModel);
css::uno::Reference<css::script::XLibraryContainer> xLibContainer(aDocProp.getAnyProperty(oox::PROP_BasicLibraries), css::uno::UNO_QUERY);
return xLibContainer;
}
css::uno::Reference<css::container::XNameContainer> VbaExport::getBasicLibrary()
{
css::uno::Reference<css::container::XNameContainer> xLibrary;
try
{
css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer();
OUString aProjectName = getProjectName();
xLibrary.set( xLibContainer->getByName(aProjectName), css::uno::UNO_QUERY_THROW );
}
catch(...)
{
}
return xLibrary;
}
bool VbaExport::containsVBAProject()
{
css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer();
if (!xLibContainer.is())
return false;
css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility (xLibContainer, css::uno::UNO_QUERY);
if (!xVbaCompatibility.is())
return false;
bool bVBACompatibilty = xVbaCompatibility->getVBACompatibilityMode();
return bVBACompatibilty;
}
OUString VbaExport::getProjectName()
{
css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility(getLibraryContainer(), css::uno::UNO_QUERY);
if (xVbaCompatibility.is())
return xVbaCompatibility->getProjectName();
return OUString();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */