blob: 456ad7edb4866281dbb25203551f3dac0bbd7ce3 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <string.h> // memset(), ...
#ifndef UNX
#include <io.h> // access()
#endif
#include <msvbasic.hxx>
/* class VBA_Impl:
* The VBA class provides a set of methods to handle Visual Basic For
* Applications streams, the constructor is given the root ole2 stream
* of the document, Open reads the VBA project file and figures out
* the number of VBA streams, and the offset of the data within them.
* Decompress decompresses a particular numbered stream, NoStreams returns
* this number, and StreamName can give you the streams name. Decompress
* will call Output when it has a 4096 byte collection of data to output,
* and also with the final remainder of data if there is still some left
* at the end of compression. Output is virtual to allow custom handling
* of each chunk of decompressed data. So inherit from this to do something
* useful with the data.
*
* cmc
* */
sal_uInt8 VBA_Impl::ReadPString(SvStorageStreamRef &xVBAProject)
{
sal_uInt16 idlen;
sal_uInt8 type=0;
*xVBAProject >> idlen;
sal_uInt8 out;
int i=0;
if (idlen < 6)
{
type=0;
xVBAProject->SeekRel(-2);
return(type);
}
for(i=0;i<idlen/2;i++)
{
*xVBAProject >> out;
xVBAProject->SeekRel(1);
if (i==2)
{
type=out;
if ((type != 'G') && (type != 'C'))
type=0;
if (type == 0)
{
xVBAProject->SeekRel(-8);
break;
}
}
}
return(type);
}
void VBA_Impl::ConfirmFixedOctect(SvStorageStreamRef &xVBAProject)
{
static const sal_uInt8 stest[8] =
{
0x06, 0x02, 0x01, 0x00, 0x08, 0x02, 0x00, 0x00
};
sal_uInt8 test[8];
xVBAProject->Read(test,8);
if (memcmp(stest,test,8) != 0)
DBG_WARNING("Found a different octect, please report");
}
void VBA_Impl::Confirm12Zeros(SvStorageStreamRef &xVBAProject)
{
static const sal_uInt8 stest[12]={0};
sal_uInt8 test[12];
xVBAProject->Read(test,12);
if (memcmp(stest,test,12) != 0)
DBG_WARNING("Found a Non Zero block, please report");
}
void VBA_Impl::ConfirmHalfWayMarker(SvStorageStreamRef &xVBAProject)
{
static const sal_uInt8 stest[12]={0,0,0,0,0,0,0,0,0,0,1,0};
sal_uInt8 test[12];
xVBAProject->Read(test,12);
if (memcmp(stest,test,12) != 0)
DBG_WARNING("Found a different halfway marker, please report");
}
void VBA_Impl::ConfirmFixedMiddle(SvStorageStreamRef &xVBAProject)
{
static const sal_uInt8 stest[20] =
{
0x00, 0x00, 0xe1, 0x2e, 0x45, 0x0d, 0x8f, 0xe0,
0x1a, 0x10, 0x85, 0x2e, 0x02, 0x60, 0x8c, 0x4d,
0x0b, 0xb4, 0x00, 0x00
};
sal_uInt8 test[20];
xVBAProject->Read(test,20);
if (memcmp(stest,test,20) != 0)
{
DBG_WARNING("Found a different middle marker, please report");
xVBAProject->SeekRel(-20);
}
}
void VBA_Impl::ConfirmFixedMiddle2(SvStorageStreamRef &xVBAProject)
{
static const sal_uInt8 stest[20] =
{
0x00, 0x00, 0x2e, 0xc9, 0x27, 0x8e, 0x64, 0x12,
0x1c, 0x10, 0x8a, 0x2f, 0x04, 0x02, 0x24, 0x00,
0x9c, 0x02, 0x00, 0x00
};
sal_uInt8 test[20];
xVBAProject->Read(test,20);
if (memcmp(stest,test,20) != 0)
{
DBG_WARNING("Found a different middle2 marker, please report");
xVBAProject->SeekRel(-20);
}
}
void VBA_Impl::Output( int nLen, const sal_uInt8 *pData)
{
sVBAString += String( (const sal_Char *)pData, nLen );
/*
//For debugging purposes
for(int i=0;i<len;i++)
*pOut << data[i];
*/
}
int VBA_Impl::ReadVBAProject(const SvStorageRef &rxVBAStorage)
{
SvStorageStreamRef xVBAProject;
xVBAProject = rxVBAStorage->OpenStream(
String::CreateFromAscii( "_VBA_PROJECT" ),
STREAM_STD_READ | STREAM_NOCREATE );
if( !xVBAProject.Is() || SVSTREAM_OK != xVBAProject->GetError() )
{
DBG_WARNING("Not able to find vba project, cannot find macros");
return(0);
}
xVBAProject->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
//*pOut << hex;
sal_uInt8 header[30] =
{
0xcc, 0x61, 0x5e, 0x00, 0x00, 0x01, 0x00, 0xff,
0x07, 0x04, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00,
0xe4, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00
};
sal_uInt8 headerin[30];
xVBAProject->Read(headerin,30);
if (memcmp(header,headerin,30) != 0)
DBG_WARNING("Warning VBA header is different, please report");
sal_uInt16 value;
*xVBAProject >> value;
//*pOut << "Trigger value 1 is " << value << endl;
sal_uInt16 svalue;
*xVBAProject >> svalue;
if (svalue != 0x02)
DBG_WARNING("Warning VBA number is different, please report");
int count=0;
sal_uInt8 testc=0;
//*pOut << "Other strings after the middle are..." << endl;
//There appears to be almost any number of strings acceptable
//most begin with */G , and sometimes with
//*/C. Those with G always have a trailer of 12 bytes, those
//with C come in pairs, the first with no trailer, and the
//second with one of 12 bytes. The following code attemts
//to read these strings and ends when it reaches a sequence of
//bytes which fails a test to be a valid string. So this
//while loop here is the particular piece of code which is
//very suspect and likely to be the cause of any crashes and
//problems.
while ((testc = ReadPString(xVBAProject)) != 0)
{
//*pOut << endl;
//*pOut << "testcharacter is " << testc << endl;
switch (testc)
{
case 'C':
count++;
if (count == 2)
{
Confirm12Zeros(xVBAProject);
count=0;
}
break;
default:
case 'G':
Confirm12Zeros(xVBAProject);
break;
}
}
//appears to be a fixed 20 byte sequence here, and then the strings
//continue
ConfirmFixedMiddle(xVBAProject);
count=0;
testc=0;
while ((testc = ReadPString(xVBAProject)) != 0)
{
//*pOut << endl;
//*pOut << "testcharacter is " << testc << endl;
switch (testc)
{
case 'C':
count++;
if (count == 2)
{
Confirm12Zeros(xVBAProject);
count=0;
}
break;
default:
case 'G':
Confirm12Zeros(xVBAProject);
break;
}
}
//there *may* be another different 20byte fixed string
ConfirmFixedMiddle2(xVBAProject);
//*pOut << "testc is " << testc << endl;
//*pOut << "position is " << xVBAProject->Tell() << endl;
sal_uInt16 nModules;
*xVBAProject >> nModules;
//begin section, this section isn't really 100% correct
//*pOut << nModules << hex << " vba modules" << endl;
xVBAProject->SeekRel(2*nModules);
xVBAProject->SeekRel(4);
//*pOut << "position is " << xVBAProject->Tell() << endl;
ConfirmFixedOctect(xVBAProject);
sal_uInt16 junksize;
while(junksize != 0xFFFF)
{
xVBAProject->Read(&junksize,2); // usually 18 02, sometimes 1e 02
//but sometimes its a run of numbers until 0xffff, gagh!!!
//*pOut << "position is " << xVBAProject->Tell() << "len is "
// << junksize << endl;
}
sal_uInt16 ftest;
*xVBAProject >> ftest;
if (ftest != 0xFFFF)
xVBAProject->SeekRel(ftest);
*xVBAProject >> ftest;
if (ftest != 0xFFFF)
xVBAProject->SeekRel(ftest);
xVBAProject->SeekRel(100);
//*pOut << "position is " << xVBAProject->Tell() << endl;
//end section
*xVBAProject >> nOffsets;
pOffsets = new VBAOffset_Impl[nOffsets];
int i;
for (i=0;i<nOffsets;i++)
{
sal_uInt8 discard;
sal_uInt16 len;
*xVBAProject >> len;
int j;
for (j=0;j<len/2;j++)
{
*xVBAProject >> discard;
pOffsets[i].sName += discard;
*xVBAProject >> discard;
}
*xVBAProject >> len;
xVBAProject->SeekRel(len);
//begin section, another problem area
*xVBAProject >> len;
if (len == 0xFFFF)
{
xVBAProject->SeekRel(2);
*xVBAProject >> len;
xVBAProject->SeekRel(len);
}
else
xVBAProject->SeekRel(len+2);
//
/* I have a theory that maybe you read a 16bit len, and
* if it has 0x02 for the second byte then it is a special
* token of its own that affects nothing else, otherwise
* it is a len of the following data. C. I must test this
* theory later.
*/
//end section
xVBAProject->SeekRel(8);
sal_uInt8 no_of_octects;
*xVBAProject >> no_of_octects;
for(j=0;j<no_of_octects;j++)
xVBAProject->SeekRel(8);
xVBAProject->SeekRel(6);
*xVBAProject >> pOffsets[i].nOffset;
//*pOut << pOffsets[i].pName.GetStr() << " at 0x" << hex << pOffsets[i].nOffset << endl;
xVBAProject->SeekRel(2);
}
//*pOut << endl;
return(nOffsets);
}
sal_Bool VBA_Impl::Open( const String &rToplevel,const String &rSublevel )
{
/* beginning test for vba stuff */
sal_Bool bRet = sal_False;
SvStorageRef xMacros= xStor->OpenStorage(rToplevel);
if( !xMacros.Is() || SVSTREAM_OK != xMacros->GetError() )
{
DBG_WARNING("No Macros Storage");
}
else
{
xVBA = xMacros->OpenStorage(rSublevel);
if( !xVBA.Is() || SVSTREAM_OK != xVBA->GetError() )
{
DBG_WARNING("No Visual Basic in Storage");
}
else
{
if (ReadVBAProject(xVBA))
bRet = sal_True;
}
}
/* end test for vba stuff */
return bRet;
}
const String &VBA_Impl::Decompress( sal_uInt16 nIndex, int *pOverflow)
{
SvStorageStreamRef xVBAStream;
sVBAString.Erase();
DBG_ASSERT( nIndex < nOffsets, "Index out of range" );
xVBAStream = xVBA->OpenStream( pOffsets[nIndex].sName,
STREAM_STD_READ | STREAM_NOCREATE );
if (pOverflow)
*pOverflow=0;
if( !xVBAStream.Is() || SVSTREAM_OK !=
xVBAStream->GetError() )
{
DBG_WARNING("Not able to open vb module ");
// DBG_WARNING((pOffsets[nIndex].sName).GetStr());
}
else
{
xVBAStream->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
DecompressVBA(nIndex,xVBAStream);
/*
* if len was too big for a single string set that variable ?
* if ((len > XX) && (pOverflow))
*pOverflow=1;
*/
if (bCommented)
{
String sTempStringa(String::CreateFromAscii( "\x0D\x0A"));
String sTempStringb(String::CreateFromAscii( "\x0D\x0ARem "));
sVBAString.SearchAndReplaceAll(sTempStringa,sTempStringb);
sVBAString.InsertAscii("Rem ",0);
}
}
return sVBAString;
}
int VBA_Impl::DecompressVBA( int nIndex, SvStorageStreamRef &xVBAStream )
{
sal_uInt8 leadbyte;
unsigned int pos = 0;
//*pOut << "jumping to " << hex << offsets[nIndex].offset << endl;
xVBAStream->Seek(pOffsets[nIndex].nOffset+3);
int len;
sal_uInt16 token;
int distance, shift, clean=1;
while(xVBAStream->Read(&leadbyte,1))
{
//*pOut << "reading 8 data unit block beginning with " << leadbyte << int(leadbyte) << " at pos " << xVBAStream->Tell() << " real pos " << pos << endl;
for(int position=0x01;position < 0x100;position=position<<1)
{
//we see if the leadbyte has flagged this location as a dataunit
//which is actually a token which must be looked up in the history
if (leadbyte & position)
{
*xVBAStream >> token;
if (clean == 0)
clean=1;
//For some reason the division of the token into the length
//field of the data to be inserted, and the distance back into
//the history differs depending on how full the history is
int pos2 = pos%WINDOWLEN;
if (pos2 <= 0x10)
shift = 12;
else if (pos2 <= 0x20)
shift = 11;
else if (pos2 <= 0x40)
shift = 10;
else if (pos2 <= 0x80)
shift = 9;
else if (pos2 <= 0x100)
shift = 8;
else if (pos2 <= 0x200)
shift = 7;
else if (pos2 <= 0x400)
shift = 6;
else if (pos2 <= 0x800)
shift = 5;
else
shift = 4;
int i;
len=0;
for(i=0;i<shift;i++)
len |= token & (1<<i);
//*pOut << endl << "match lookup token " << int(token) << "len " << int(len) << endl;
len += 3;
//*pOut << endl << "len is " << len << "shift is " << shift << endl;
distance = token >> shift;
//*pOut << "distance token shift is " << distance << " " << int(token) << " " << shift << "pos is " << pos << " " << xVBAStream->Tell() << endl;
//read the len of data from the history, wrapping around the
//WINDOWLEN boundary if necessary
//data read from the history is also copied into the recent
//part of the history as well.
for (i = 0; i < len; i++)
{
unsigned char c;
//*pOut << endl << (pos%WINDOWLEN)-distance-1 << " " << pos << " " << distance << endl;
c = aHistory[(pos-distance-1)%WINDOWLEN];
aHistory[pos%WINDOWLEN] = c;
pos++;
//*pOut << "real pos is " << pos << endl;
//
//temp removed
//*pOut << c ;
}
}
else
{
// special boundary case code, not guarantueed to be correct
// seems to work though, there is something wrong with the
// compression scheme (or maybe a feature) where when
// the data ends on a WINDOWLEN boundary and the excess
// bytes in the 8 dataunit list are discarded, and not
// interpreted as tokens or normal data.
if ((pos != 0) && ((pos%WINDOWLEN) == 0) && (clean))
{
//*pOut << "at boundary position is " << position << " " << xVBAStream->Tell() << " pos is " << pos << endl;
//if (position != 0x01)
//*pOut << "must restart by eating remainder single byte data units" << endl;
xVBAStream->SeekRel(2);
clean=0;
Output(WINDOWLEN,aHistory);
break;
}
//This is the normal case for when the data unit is not a
//token to be looked up, but instead some normal data which
//can be output, and placed in the history.
if (xVBAStream->Read(&aHistory[pos%WINDOWLEN],1))
{
pos++;
//temp removed
//*pOut << aHistory[pos++%WINDOWLEN];
}
if (clean == 0)
clean=1;
//*pOut << "pos is " << pos << " " << xVBAStream->Tell() << endl;
}
}
}
if (pos%WINDOWLEN)
Output(pos%WINDOWLEN,aHistory);
return(pos);
}