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