1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include "oox/ole/vbamodule.hxx" 25 26 #include <com/sun/star/container/XNameContainer.hpp> 27 #include <com/sun/star/script/ModuleInfo.hpp> 28 #include <com/sun/star/script/ModuleType.hpp> 29 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp> 30 #include "oox/helper/binaryinputstream.hxx" 31 #include "oox/helper/storagebase.hxx" 32 #include "oox/helper/textinputstream.hxx" 33 #include "oox/ole/vbahelper.hxx" 34 #include "oox/ole/vbainputstream.hxx" 35 36 namespace oox { 37 namespace ole { 38 39 // ============================================================================ 40 41 using namespace ::com::sun::star::container; 42 using namespace ::com::sun::star::frame; 43 using namespace ::com::sun::star::lang; 44 using namespace ::com::sun::star::script; 45 using namespace ::com::sun::star::script::vba; 46 using namespace ::com::sun::star::uno; 47 48 using ::rtl::OUString; 49 using ::rtl::OUStringBuffer; 50 51 // ============================================================================ 52 53 VbaModule::VbaModule( const Reference< XComponentContext >& rxContext, const Reference< XModel >& rxDocModel, 54 const OUString& rName, rtl_TextEncoding eTextEnc, bool bExecutable ) : 55 mxContext( rxContext ), 56 mxDocModel( rxDocModel ), 57 maName( rName ), 58 meTextEnc( eTextEnc ), 59 mnType( ModuleType::UNKNOWN ), 60 mnOffset( SAL_MAX_UINT32 ), 61 mbReadOnly( false ), 62 mbPrivate( false ), 63 mbExecutable( bExecutable ) 64 { 65 } 66 67 void VbaModule::importDirRecords( BinaryInputStream& rDirStrm ) 68 { 69 sal_uInt16 nRecId = 0; 70 StreamDataSequence aRecData; 71 while( VbaHelper::readDirRecord( nRecId, aRecData, rDirStrm ) && (nRecId != VBA_ID_MODULEEND) ) 72 { 73 SequenceInputStream aRecStrm( aRecData ); 74 sal_Int32 nRecSize = aRecData.getLength(); 75 switch( nRecId ) 76 { 77 #define OOX_ENSURE_RECORDSIZE( cond ) OSL_ENSURE( cond, "VbaModule::importDirRecords - invalid record size" ) 78 case VBA_ID_MODULENAME: 79 OSL_ENSURE( false, "VbaModule::importDirRecords - unexpected MODULENAME record" ); 80 maName = aRecStrm.readCharArrayUC( nRecSize, meTextEnc ); 81 break; 82 case VBA_ID_MODULENAMEUNICODE: 83 break; 84 case VBA_ID_MODULESTREAMNAME: 85 maStreamName = aRecStrm.readCharArrayUC( nRecSize, meTextEnc ); 86 break; 87 case VBA_ID_MODULESTREAMNAMEUNICODE: 88 break; 89 case VBA_ID_MODULEDOCSTRING: 90 maDocString = aRecStrm.readCharArrayUC( nRecSize, meTextEnc ); 91 break; 92 case VBA_ID_MODULEDOCSTRINGUNICODE: 93 break; 94 case VBA_ID_MODULEOFFSET: 95 OOX_ENSURE_RECORDSIZE( nRecSize == 4 ); 96 aRecStrm >> mnOffset; 97 break; 98 case VBA_ID_MODULEHELPCONTEXT: 99 OOX_ENSURE_RECORDSIZE( nRecSize == 4 ); 100 break; 101 case VBA_ID_MODULECOOKIE: 102 OOX_ENSURE_RECORDSIZE( nRecSize == 2 ); 103 break; 104 case VBA_ID_MODULETYPEPROCEDURAL: 105 OOX_ENSURE_RECORDSIZE( nRecSize == 0 ); 106 OSL_ENSURE( mnType == ModuleType::UNKNOWN, "VbaModule::importDirRecords - multiple module type records" ); 107 mnType = ModuleType::NORMAL; 108 break; 109 case VBA_ID_MODULETYPEDOCUMENT: 110 OOX_ENSURE_RECORDSIZE( nRecSize == 0 ); 111 OSL_ENSURE( mnType == ModuleType::UNKNOWN, "VbaModule::importDirRecords - multiple module type records" ); 112 mnType = ModuleType::DOCUMENT; 113 break; 114 case VBA_ID_MODULEREADONLY: 115 OOX_ENSURE_RECORDSIZE( nRecSize == 0 ); 116 mbReadOnly = true; 117 break; 118 case VBA_ID_MODULEPRIVATE: 119 OOX_ENSURE_RECORDSIZE( nRecSize == 0 ); 120 mbPrivate = true; 121 break; 122 default: 123 OSL_ENSURE( false, "VbaModule::importDirRecords - unknown module record" ); 124 #undef OOX_ENSURE_RECORDSIZE 125 } 126 } 127 OSL_ENSURE( maName.getLength() > 0, "VbaModule::importDirRecords - missing module name" ); 128 OSL_ENSURE( maStreamName.getLength() > 0, "VbaModule::importDirRecords - missing module stream name" ); 129 OSL_ENSURE( mnType != ModuleType::UNKNOWN, "VbaModule::importDirRecords - missing module type" ); 130 OSL_ENSURE( mnOffset < SAL_MAX_UINT32, "VbaModule::importDirRecords - missing module stream offset" ); 131 } 132 133 void VbaModule::createAndImportModule( StorageBase& rVbaStrg, const Reference< XNameContainer >& rxBasicLib, 134 const Reference< XNameAccess >& rxDocObjectNA ) const 135 { 136 OUString aVBASourceCode = readSourceCode( rVbaStrg ); 137 createModule( aVBASourceCode, rxBasicLib, rxDocObjectNA ); 138 } 139 140 void VbaModule::createEmptyModule( const Reference< XNameContainer >& rxBasicLib, const Reference< XNameAccess >& rxDocObjectNA ) const 141 { 142 createModule( OUString(), rxBasicLib, rxDocObjectNA ); 143 } 144 145 // private -------------------------------------------------------------------- 146 147 OUString VbaModule::readSourceCode( StorageBase& rVbaStrg ) const 148 { 149 OUStringBuffer aSourceCode; 150 if( (maStreamName.getLength() > 0) && (mnOffset != SAL_MAX_UINT32) ) 151 { 152 BinaryXInputStream aInStrm( rVbaStrg.openInputStream( maStreamName ), true ); 153 OSL_ENSURE( !aInStrm.isEof(), "VbaModule::readSourceCode - cannot open module stream" ); 154 // skip the 'performance cache' stored before the actual source code 155 aInStrm.seek( mnOffset ); 156 // if stream is still valid, load the source code 157 if( !aInStrm.isEof() ) 158 { 159 // decompression starts at current stream position of aInStrm 160 VbaInputStream aVbaStrm( aInStrm ); 161 // load the source code line-by-line, with some more processing 162 TextInputStream aVbaTextStrm( mxContext, aVbaStrm, meTextEnc ); 163 while( !aVbaTextStrm.isEof() ) 164 { 165 OUString aCodeLine = aVbaTextStrm.readLine(); 166 if( !aCodeLine.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "Attribute " ) ) ) 167 { 168 // normal source code line 169 if( !mbExecutable ) 170 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Rem " ) ); 171 aSourceCode.append( aCodeLine ).append( sal_Unicode( '\n' ) ); 172 } 173 } 174 } 175 } 176 return aSourceCode.makeStringAndClear(); 177 } 178 179 void VbaModule::createModule( const OUString& rVBASourceCode, 180 const Reference< XNameContainer >& rxBasicLib, const Reference< XNameAccess >& rxDocObjectNA ) const 181 { 182 if( maName.getLength() == 0 ) 183 return; 184 185 // prepare the Basic module 186 ModuleInfo aModuleInfo; 187 aModuleInfo.ModuleType = mnType; 188 OUStringBuffer aSourceCode; 189 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Rem Attribute VBA_ModuleType=" ) ); 190 switch( mnType ) 191 { 192 case ModuleType::NORMAL: 193 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAModule" ) ); 194 break; 195 case ModuleType::CLASS: 196 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAClassModule" ) ); 197 break; 198 case ModuleType::FORM: 199 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAFormModule" ) ); 200 // hack from old filter, document Basic should know the XModel, but it doesn't 201 aModuleInfo.ModuleObject.set( mxDocModel, UNO_QUERY ); 202 break; 203 case ModuleType::DOCUMENT: 204 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBADocumentModule" ) ); 205 // get the VBA implementation object associated to the document module 206 if( rxDocObjectNA.is() ) try 207 { 208 aModuleInfo.ModuleObject.set( rxDocObjectNA->getByName( maName ), UNO_QUERY ); 209 } 210 catch( Exception& ) 211 { 212 } 213 break; 214 default: 215 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAUnknown" ) ); 216 } 217 aSourceCode.append( sal_Unicode( '\n' ) ); 218 if( mbExecutable ) 219 { 220 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Option VBASupport 1\n" ) ); 221 if( mnType == ModuleType::CLASS ) 222 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Option ClassModule\n" ) ); 223 } 224 else 225 { 226 // add a subroutine named after the module itself 227 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Sub " ) ). 228 append( maName.replace( ' ', '_' ) ).append( sal_Unicode( '\n' ) ); 229 } 230 231 // append passed VBA source code 232 aSourceCode.append( rVBASourceCode ); 233 234 // close the subroutine named after the module 235 if( !mbExecutable ) 236 aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "End Sub\n" ) ); 237 238 // insert extended module info 239 try 240 { 241 Reference< XVBAModuleInfo > xVBAModuleInfo( rxBasicLib, UNO_QUERY_THROW ); 242 xVBAModuleInfo->insertModuleInfo( maName, aModuleInfo ); 243 } 244 catch( Exception& ) 245 { 246 } 247 248 // insert the module into the passed Basic library 249 try 250 { 251 rxBasicLib->insertByName( maName, Any( aSourceCode.makeStringAndClear() ) ); 252 } 253 catch( Exception& ) 254 { 255 OSL_ENSURE( false, "VbaModule::createModule - cannot insert module into library" ); 256 } 257 } 258 259 // ============================================================================ 260 261 } // namespace ole 262 } // namespace oox 263