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 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_filter.hxx" 26 27 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ 28 #include <tools/debug.hxx> 29 #include <sfx2/objsh.hxx> 30 #include <sfx2/app.hxx> 31 #include <basic/basmgr.hxx> 32 #include <basic/sbmod.hxx> 33 #include <svx/svxerr.hxx> 34 #include <filter/msfilter/svxmsbas.hxx> 35 #include <msvbasic.hxx> 36 #include <filter/msfilter/msocximex.hxx> 37 #include <sot/storinfo.hxx> 38 #include <comphelper/processfactory.hxx> 39 #include <com/sun/star/beans/XPropertySet.hpp> 40 #include <com/sun/star/awt/Size.hpp> 41 #include <com/sun/star/awt/XControlModel.hpp> 42 using namespace com::sun::star::beans; 43 using namespace com::sun::star::io; 44 using namespace com::sun::star::awt; 45 #include <comphelper/storagehelper.hxx> 46 47 #include <com/sun/star/container/XNameContainer.hpp> 48 #include <com/sun/star/script/XLibraryContainer.hpp> 49 #include <com/sun/star/script/ModuleInfo.hpp> 50 #include <com/sun/star/script/ModuleType.hpp> 51 #include <com/sun/star/script/vba/XVBACompatibility.hpp> 52 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp> 53 54 using namespace com::sun::star::container; 55 using namespace com::sun::star::script; 56 using namespace com::sun::star::uno; 57 using namespace com::sun::star::lang; 58 using namespace com::sun::star; 59 60 using rtl::OUString; 61 62 static ::rtl::OUString sVBAOption( RTL_CONSTASCII_USTRINGPARAM( "Option VBASupport 1\n" ) ); 63 64 int SvxImportMSVBasic::Import( const String& rStorageName, 65 const String &rSubStorageName, 66 sal_Bool bAsComment, sal_Bool bStripped ) 67 { 68 std::vector< String > codeNames; 69 return Import( rStorageName, rSubStorageName, codeNames, bAsComment, bStripped ); 70 } 71 72 int SvxImportMSVBasic::Import( const String& rStorageName, 73 const String &rSubStorageName, 74 const std::vector< String >& codeNames, 75 sal_Bool bAsComment, sal_Bool bStripped ) 76 { 77 int nRet = 0; 78 if( bImport && ImportCode_Impl( rStorageName, rSubStorageName, codeNames, 79 bAsComment, bStripped )) 80 nRet |= 1; 81 82 if (bImport) 83 ImportForms_Impl(rStorageName, rSubStorageName); 84 85 if( bCopy && CopyStorage_Impl( rStorageName, rSubStorageName )) 86 nRet |= 2; 87 88 return nRet; 89 } 90 91 bool SvxImportMSVBasic::ImportForms_Impl(const String& rStorageName, 92 const String& rSubStorageName) 93 { 94 SvStorageRef xVBAStg(xRoot->OpenSotStorage(rStorageName, 95 STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYALL)); 96 if (!xVBAStg.Is() || xVBAStg->GetError()) 97 return false; 98 99 std::vector<String> aUserForms; 100 SvStorageInfoList aContents; 101 xVBAStg->FillInfoList(&aContents); 102 for (sal_uInt16 nI = 0; nI < aContents.Count(); ++nI) 103 { 104 SvStorageInfo& rInfo = aContents.GetObject(nI); 105 if (!rInfo.IsStream() && rInfo.GetName() != rSubStorageName) 106 aUserForms.push_back(rInfo.GetName()); 107 } 108 109 if (aUserForms.empty()) 110 return false; 111 112 bool bRet = true; 113 try 114 { 115 Reference<XMultiServiceFactory> xSF(comphelper::getProcessServiceFactory()); 116 117 Reference<XComponentContext> xContext; 118 Reference<XPropertySet> xProps(xSF, UNO_QUERY); 119 xProps->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("DefaultContext")) ) >>= xContext; 120 121 122 Reference<XLibraryContainer> xLibContainer = rDocSh.GetDialogContainer(); 123 DBG_ASSERT( xLibContainer.is(), "No BasicContainer!" ); 124 125 String aLibName( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) ); 126 Reference<XNameContainer> xLib; 127 if (xLibContainer.is()) 128 { 129 if( !xLibContainer->hasByName(aLibName)) 130 xLibContainer->createLibrary(aLibName); 131 132 Any aLibAny = xLibContainer->getByName( aLibName ); 133 aLibAny >>= xLib; 134 } 135 136 if(xLib.is()) 137 { 138 typedef std::vector<String>::iterator myIter; 139 myIter aEnd = aUserForms.end(); 140 for (myIter aIter = aUserForms.begin(); aIter != aEnd; ++aIter) 141 { 142 SvStorageRef xForm (xVBAStg->OpenSotStorage(*aIter, 143 STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYALL)); 144 145 if (!xForm.Is() || xForm->GetError()) 146 continue; 147 148 SvStorageStreamRef xFrame = xForm->OpenSotStream( 149 String( RTL_CONSTASCII_USTRINGPARAM( "\3VBFrame" ) ), 150 STREAM_STD_READ | STREAM_NOCREATE); 151 152 if (!xFrame.Is() || xFrame->GetError()) 153 continue; 154 155 SvStorageStreamRef xTypes = xForm->OpenSotStream( 156 String( 'f' ), STREAM_STD_READ | STREAM_NOCREATE); 157 158 if (!xTypes.Is() || xTypes->GetError()) 159 continue; 160 161 //<UserForm Name=""><VBFrame></VBFrame>" 162 String sData; 163 String sLine; 164 while(xFrame->ReadByteStringLine(sLine, RTL_TEXTENCODING_MS_1252)) 165 { 166 sData += sLine; 167 sData += '\n'; 168 } 169 sData.ConvertLineEnd(); 170 171 Reference<container::XNameContainer> xDialog( 172 xSF->createInstance( 173 OUString(RTL_CONSTASCII_USTRINGPARAM( 174 "com.sun.star.awt.UnoControlDialogModel"))), uno::UNO_QUERY); 175 176 OCX_UserForm aForm(xVBAStg, *aIter, *aIter, xDialog, xSF ); 177 aForm.pDocSh = &rDocSh; 178 sal_Bool bOk = aForm.Read(xTypes); 179 DBG_ASSERT(bOk, "Had unexpected content, not risking this module"); 180 if (bOk) 181 aForm.Import(xLib); 182 } 183 } 184 } 185 catch(...) 186 { 187 DBG_ERRORFILE( "SvxImportMSVBasic::ImportForms_Impl - any exception caught" ); 188 //bRet = false; 189 } 190 return bRet; 191 } 192 193 194 sal_Bool SvxImportMSVBasic::CopyStorage_Impl( const String& rStorageName, 195 const String& rSubStorageName) 196 { 197 sal_Bool bValidStg = sal_False; 198 { 199 SvStorageRef xVBAStg( xRoot->OpenSotStorage( rStorageName, 200 STREAM_READWRITE | STREAM_NOCREATE | 201 STREAM_SHARE_DENYALL )); 202 if( xVBAStg.Is() && !xVBAStg->GetError() ) 203 { 204 SvStorageRef xVBASubStg( xVBAStg->OpenSotStorage( rSubStorageName, 205 STREAM_READWRITE | STREAM_NOCREATE | 206 STREAM_SHARE_DENYALL )); 207 if( xVBASubStg.Is() && !xVBASubStg->GetError() ) 208 { 209 // then we will copy these storages into the (temporary) storage of the document 210 bValidStg = sal_True; 211 } 212 } 213 } 214 215 if( bValidStg ) 216 { 217 String aDstStgName( GetMSBasicStorageName() ); 218 SotStorageRef xDst = SotStorage::OpenOLEStorage( rDocSh.GetStorage(), aDstStgName, STREAM_READWRITE | STREAM_TRUNC ); 219 SotStorageRef xSrc = xRoot->OpenSotStorage( rStorageName, STREAM_STD_READ ); 220 221 // TODO/LATER: should we commit the storage? 222 xSrc->CopyTo( xDst ); 223 xDst->Commit(); 224 ErrCode nError = xDst->GetError(); 225 if ( nError == ERRCODE_NONE ) 226 nError = xSrc->GetError(); 227 if ( nError != ERRCODE_NONE ) 228 xRoot->SetError( nError ); 229 else 230 bValidStg = sal_True; 231 } 232 233 return bValidStg; 234 } 235 236 sal_Bool SvxImportMSVBasic::ImportCode_Impl( const String& rStorageName, 237 const String &rSubStorageName, 238 const std::vector< String >& codeNames, 239 sal_Bool bAsComment, sal_Bool bStripped ) 240 { 241 sal_Bool bRet = sal_False; 242 VBA_Impl aVBA( *xRoot, bAsComment ); 243 if( aVBA.Open(rStorageName,rSubStorageName) ) 244 { 245 Reference<XLibraryContainer> xLibContainer = rDocSh.GetBasicContainer(); 246 DBG_ASSERT( xLibContainer.is(), "No BasicContainer!" ); 247 248 /* Set library container to VBA compatibility mode. This will create 249 the VBA Globals object and store it in the Basic manager of the 250 document. */ 251 if( !bAsComment ) try 252 { 253 Reference< vba::XVBACompatibility >( xLibContainer, UNO_QUERY_THROW )->setVBACompatibilityMode( sal_True ); 254 } 255 catch( Exception& ) 256 { 257 } 258 259 sal_uInt16 nStreamCount = aVBA.GetNoStreams(); 260 Reference<XNameContainer> xLib; 261 String aLibName( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) ); 262 if( xLibContainer.is() && nStreamCount ) 263 { 264 if( !xLibContainer->hasByName( aLibName ) ) 265 xLibContainer->createLibrary( aLibName ); 266 267 Any aLibAny = xLibContainer->getByName( aLibName ); 268 aLibAny >>= xLib; 269 } 270 if( xLib.is() ) 271 { 272 Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, UNO_QUERY ); 273 Reference< container::XNameAccess > xVBACodeNamedObjectAccess; 274 if ( !bAsComment ) 275 { 276 Reference< XMultiServiceFactory> xSF(rDocSh.GetModel(), UNO_QUERY); 277 if ( xSF.is() ) 278 { 279 try 280 { 281 xVBACodeNamedObjectAccess.set( xSF->createInstance( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "ooo.vba.VBAObjectModuleObjectProvider"))), UNO_QUERY ); 282 } 283 catch( Exception& ) { } 284 } 285 } 286 typedef std::hash_map< rtl::OUString, uno::Any, ::rtl::OUStringHash, 287 ::std::equal_to< ::rtl::OUString > > NameModuleDataHash; 288 typedef std::hash_map< rtl::OUString, script::ModuleInfo, ::rtl::OUStringHash, 289 ::std::equal_to< ::rtl::OUString > > NameModuleInfoHash; 290 291 NameModuleDataHash moduleData; 292 NameModuleInfoHash moduleInfos; 293 294 for( sal_uInt16 i=0; i<nStreamCount;i++) 295 { 296 StringArray aDecompressed = aVBA.Decompress(i); 297 #if 0 298 /* DR 2005-08-11 #124850# Do not filter special characters from module name. 299 Just put the original module name and let the Basic interpreter deal with 300 it. Needed for roundtrip... 301 */ 302 ByteString sByteBasic(aVBA.GetStreamName(i), 303 RTL_TEXTENCODING_ASCII_US, 304 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_UNDERLINE| 305 RTL_UNICODETOTEXT_FLAGS_INVALID_UNDERLINE | 306 RTL_UNICODETOTEXT_FLAGS_PRIVATE_MAPTO0 | 307 RTL_UNICODETOTEXT_FLAGS_NOCOMPOSITE) 308 ); 309 310 const String sBasicModule(sByteBasic, 311 RTL_TEXTENCODING_ASCII_US); 312 #else 313 const String &sBasicModule = aVBA.GetStreamName( i); 314 #endif 315 /* #117718# expose information regarding type of Module 316 * Class, Form or plain 'ould VBA module with a REM statement 317 * at the top of the module. Mapping of Module Name 318 * to type is performed in VBA_Impl::Open() method, 319 * ( msvbasic.cxx ) by examining the PROJECT stream. 320 */ 321 322 // using name from aVBA.GetStreamName 323 // because the encoding of the same returned 324 // is the same as the encoding for the names 325 // that are keys in the map used by GetModuleType method 326 const String &sOrigVBAModName = aVBA.GetStreamName( i ); 327 ModType mType = aVBA.GetModuleType( sOrigVBAModName ); 328 329 rtl::OUString sClassRem( RTL_CONSTASCII_USTRINGPARAM( "Rem Attribute VBA_ModuleType=" ) ); 330 331 rtl::OUString modeTypeComment; 332 333 switch( mType ) 334 { 335 case ModuleType::CLASS: 336 modeTypeComment = sClassRem + 337 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAClassModule\n" ) ); 338 break; 339 case ModuleType::FORM: 340 modeTypeComment = sClassRem + 341 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAFormModule\n" ) ); 342 break; 343 case ModuleType::DOCUMENT: 344 modeTypeComment = sClassRem + 345 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBADocumentModule\n" ) ); 346 break; 347 case ModuleType::NORMAL: 348 modeTypeComment = sClassRem + 349 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAModule\n" ) ); 350 break; 351 case ModuleType::UNKNOWN: 352 modeTypeComment = sClassRem + 353 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAUnknown\n" ) ); 354 break; 355 default: 356 DBG_ERRORFILE( "SvxImportMSVBasic::ImportCode_Impl - unknown module type" ); 357 break; 358 } 359 static ::rtl::OUString sClassOption( RTL_CONSTASCII_USTRINGPARAM( "Option ClassModule\n" ) ); 360 if ( !bAsComment ) 361 { 362 modeTypeComment += sVBAOption; 363 if ( mType == ModuleType::CLASS ) 364 modeTypeComment += sClassOption; 365 } 366 367 String sModule(sBasicModule); //#i52606# no need to split Macros in 64KB blocks any more! 368 String sTemp; 369 if (bAsComment) 370 { 371 sTemp+=String(RTL_CONSTASCII_USTRINGPARAM( "Sub " )); 372 String sMunge(sModule); 373 //Streams can have spaces in them, but modulenames 374 //cannot ! 375 sMunge.SearchAndReplaceAll(' ','_'); 376 377 sTemp += sMunge; 378 sTemp.AppendAscii("\n"); 379 }; 380 ::rtl::OUString aSource(sTemp); 381 382 for(sal_uLong j=0;j<aDecompressed.GetSize();j++) 383 { 384 if (bStripped) 385 { 386 String *pStr = aDecompressed.Get(j); 387 bool bMac = true; 388 xub_StrLen nBegin = pStr->Search('\x0D'); 389 if ((STRING_NOTFOUND != nBegin) && (pStr->Len() > 1) && (pStr->GetChar(nBegin+1) == '\x0A')) 390 bMac = false; 391 392 const char cLineEnd = bMac ? '\x0D' : '\x0A'; 393 const String sAttribute(String::CreateFromAscii( 394 bAsComment ? "Rem Attribute" : "Attribute")); 395 nBegin = 0; 396 while (STRING_NOTFOUND != (nBegin = pStr->Search(sAttribute, nBegin))) 397 { 398 if ((nBegin) && pStr->GetChar(nBegin-1) != cLineEnd) 399 { 400 // npower #i63766# Need to skip instances of Attribute 401 // that are NOT Attribute statements 402 nBegin = nBegin + sAttribute.Len(); 403 continue; 404 } 405 xub_StrLen nEnd = pStr->Search(cLineEnd ,nBegin); 406 // DR #i26521# catch STRING_NOTFOUND, will loop endless otherwise 407 if( nEnd == STRING_NOTFOUND ) 408 pStr->Erase(); 409 else 410 pStr->Erase(nBegin, (nEnd-nBegin)+1); 411 } 412 } 413 if( aDecompressed.Get(j)->Len() ) 414 { 415 aSource+=::rtl::OUString( *aDecompressed.Get(j) ); 416 } 417 418 } 419 if (bAsComment) 420 { 421 aSource += rtl::OUString::createFromAscii("\nEnd Sub"); 422 } 423 ::rtl::OUString aModName( sModule ); 424 aSource = modeTypeComment + aSource; 425 426 Any aSourceAny; 427 OSL_TRACE("erm %d", mType ); 428 aSourceAny <<= aSource; 429 if ( !bAsComment ) 430 { 431 OSL_TRACE("vba processing %d", mType ); 432 script::ModuleInfo sModuleInfo; 433 sModuleInfo.ModuleType = mType; 434 moduleInfos[ aModName ] = sModuleInfo; 435 } 436 moduleData[ aModName ] = aSourceAny; 437 } 438 // Hack for missing codenames ( only know to happen in excel but... ) 439 // only makes sense to do this if we are importing non-commented basic 440 if ( !bAsComment ) 441 { 442 for ( std::vector< String >::const_iterator it = codeNames.begin(); it != codeNames.end(); ++it ) 443 { 444 script::ModuleInfo sModuleInfo; 445 sModuleInfo.ModuleType = ModuleType::DOCUMENT; 446 moduleInfos[ *it ] = sModuleInfo; 447 moduleData[ *it ] = uno::makeAny( sVBAOption ); 448 } 449 } 450 NameModuleDataHash::iterator it_end = moduleData.end(); 451 for ( NameModuleDataHash::iterator it = moduleData.begin(); it != it_end; ++it ) 452 { 453 NameModuleInfoHash::iterator it_info = moduleInfos.find( it->first ); 454 if ( it_info != moduleInfos.end() ) 455 { 456 ModuleInfo& sModuleInfo = it_info->second; 457 if ( sModuleInfo.ModuleType == ModuleType::FORM ) 458 // hack, the module ( imo document basic should... 459 // know the XModel... ) but it doesn't 460 sModuleInfo.ModuleObject.set( rDocSh.GetModel(), UNO_QUERY ); 461 // document modules, we should be able to access 462 // the api objects at this time 463 else if ( sModuleInfo.ModuleType == ModuleType::DOCUMENT ) 464 { 465 if ( xVBACodeNamedObjectAccess.is() ) 466 { 467 try 468 { 469 sModuleInfo.ModuleObject.set( xVBACodeNamedObjectAccess->getByName( it->first ), uno::UNO_QUERY ); 470 OSL_TRACE("** Straight up creation of Module"); 471 } 472 catch(uno::Exception& e) 473 { 474 OSL_TRACE("Failed to get documument object for %s", rtl::OUStringToOString( it->first, RTL_TEXTENCODING_UTF8 ).getStr() ); 475 } 476 } 477 } 478 xVBAModuleInfo->insertModuleInfo( it->first, sModuleInfo ); 479 } 480 481 if( xLib->hasByName( it->first ) ) 482 xLib->replaceByName( it->first, it->second ); 483 else 484 xLib->insertByName( it->first, it->second ); 485 } 486 bRet = true; 487 } 488 } 489 return bRet; 490 } 491 492 /* vi:set tabstop=4 shiftwidth=4 expandtab: */ 493