xref: /core/scripting/source/basprov/basprov.cxx (revision ba796f2c)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "basprov.hxx"
21 #include "basscript.hxx"
22 #include "baslibnode.hxx"
23 #include <com/sun/star/frame/XModel.hpp>
24 #include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
25 #include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
26 #include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
27 #include <com/sun/star/document/XEmbeddedScripts.hpp>
28 #include <com/sun/star/uri/UriReferenceFactory.hpp>
29 
30 #include <cppuhelper/implementationentry.hxx>
31 #include <cppuhelper/supportsservice.hxx>
32 #include <rtl/uri.hxx>
33 #include <sal/log.hxx>
34 #include <osl/process.h>
35 #include <osl/file.hxx>
36 #include <osl/mutex.hxx>
37 #include <vcl/svapp.hxx>
38 #include <basic/sbx.hxx>
39 #include <basic/basmgr.hxx>
40 #include <basic/basicmanagerrepository.hxx>
41 #include <basic/sbstar.hxx>
42 #include <basic/sbmod.hxx>
43 #include <basic/sbmeth.hxx>
44 #include <sfx2/app.hxx>
45 #include <sfx2/objsh.hxx>
46 
47 #include <com/sun/star/util/theMacroExpander.hpp>
48 #include <com/sun/star/script/XLibraryContainer2.hpp>
49 #include <com/sun/star/uri/XUriReference.hpp>
50 #include <com/sun/star/uri/XUriReferenceFactory.hpp>
51 #include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
52 
53 #include <util/MiscUtils.hxx>
54 
55 
56 using namespace ::com::sun::star;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::uno;
59 using namespace ::com::sun::star::script;
60 using namespace ::com::sun::star::document;
61 using namespace ::sf_misc;
62 
63 
64 namespace basprov
65 {
66 
67 
68     // component operations
69 
70 
71     static OUString getImplementationName_BasicProviderImpl()
72     {
73         return OUString( "com.sun.star.comp.scripting.ScriptProviderForBasic"  );
74     }
75 
76 
77     static Sequence< OUString > getSupportedServiceNames_BasicProviderImpl()
78     {
79         static Sequence< OUString > s_Names{
80             "com.sun.star.script.provider.ScriptProviderForBasic",
81             "com.sun.star.script.provider.LanguageScriptProvider",
82             "com.sun.star.script.provider.ScriptProvider",
83             "com.sun.star.script.browse.BrowseNode"};
84 
85         return s_Names;
86     }
87 
88 
89     // BasicProviderImpl
90 
91 
92     BasicProviderImpl::BasicProviderImpl( const Reference< XComponentContext >& xContext )
93         :m_pAppBasicManager( nullptr )
94         ,m_pDocBasicManager( nullptr )
95         ,m_xContext( xContext )
96         ,m_bIsAppScriptCtx( true )
97         ,m_bIsUserCtx(true)
98     {
99     }
100 
101 
102     BasicProviderImpl::~BasicProviderImpl()
103     {
104     }
105 
106 
107     bool BasicProviderImpl::isLibraryShared( const Reference< script::XLibraryContainer >& rxLibContainer, const OUString& rLibName )
108     {
109         bool bIsShared = false;
110 
111         Reference< script::XLibraryContainer2 > xLibContainer( rxLibContainer, UNO_QUERY );
112         if ( xLibContainer.is() && xLibContainer->hasByName( rLibName ) && xLibContainer->isLibraryLink( rLibName ) )
113         {
114             OUString aFileURL;
115             if ( m_xContext.is() )
116             {
117                 Reference< uri::XUriReferenceFactory > xUriFac( uri::UriReferenceFactory::create( m_xContext ) );
118 
119                 OUString aLinkURL( xLibContainer->getLibraryLinkURL( rLibName ) );
120                 Reference<  uri::XUriReference > xUriRef( xUriFac->parse( aLinkURL ), UNO_QUERY );
121 
122                 if ( xUriRef.is() )
123                 {
124                     OUString aScheme = xUriRef->getScheme();
125                     if ( aScheme.equalsIgnoreAsciiCase("file") )
126                     {
127                         aFileURL = aLinkURL;
128                     }
129                     else if ( aScheme.equalsIgnoreAsciiCase("vnd.sun.star.pkg") )
130                     {
131                         OUString aAuthority = xUriRef->getAuthority();
132                         if ( aAuthority.matchIgnoreAsciiCase( "vnd.sun.star.expand:" ) )
133                         {
134                             OUString aDecodedURL( aAuthority.copy( sizeof ( "vnd.sun.star.expand:" ) - 1 ) );
135                             aDecodedURL = ::rtl::Uri::decode( aDecodedURL, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
136                             Reference<util::XMacroExpander> xMacroExpander =
137                                 util::theMacroExpander::get(m_xContext);
138                             aFileURL = xMacroExpander->expandMacros( aDecodedURL );
139                         }
140                     }
141                 }
142             }
143 
144             if ( !aFileURL.isEmpty() )
145             {
146                 osl::DirectoryItem aFileItem;
147                 osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileURL );
148                 OSL_VERIFY( osl::DirectoryItem::get( aFileURL, aFileItem ) == osl::FileBase::E_None );
149                 OSL_VERIFY( aFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None );
150                 OUString aCanonicalFileURL( aFileStatus.getFileURL() );
151 
152                 if( aCanonicalFileURL.indexOf( "share/basic" ) != -1
153                     || aCanonicalFileURL.indexOf( "share/uno_packages" ) != -1 )
154                     bIsShared = true;
155             }
156         }
157 
158         return bIsShared;
159     }
160 
161     // SfxListener
162     void BasicProviderImpl::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
163     {
164         if (auto pManager = dynamic_cast<const BasicManager*>(&rBC))
165             if (pManager == m_pAppBasicManager && rHint.GetId() == SfxHintId::Dying)
166             {
167                 EndListening(*m_pAppBasicManager);
168                 m_pAppBasicManager = nullptr;
169             }
170     }
171 
172     // XServiceInfo
173     OUString BasicProviderImpl::getImplementationName(  )
174     {
175         return getImplementationName_BasicProviderImpl();
176     }
177 
178     sal_Bool BasicProviderImpl::supportsService( const OUString& rServiceName )
179     {
180         return cppu::supportsService(this, rServiceName);
181     }
182 
183     Sequence< OUString > BasicProviderImpl::getSupportedServiceNames(  )
184     {
185         return getSupportedServiceNames_BasicProviderImpl();
186     }
187 
188 
189     // XInitialization
190 
191 
192     void BasicProviderImpl::initialize( const Sequence< Any >& aArguments )
193     {
194         // TODO
195 
196         SolarMutexGuard aGuard;
197 
198         if ( aArguments.getLength() != 1 )
199         {
200             throw IllegalArgumentException(
201                 "BasicProviderImpl::initialize: incorrect argument count.",
202                 *this,
203                 1
204             );
205         }
206 
207         Reference< frame::XModel > xModel;
208 
209         m_xInvocationContext.set( aArguments[0], UNO_QUERY );
210         if ( m_xInvocationContext.is() )
211         {
212             xModel.set( m_xInvocationContext->getScriptContainer(), UNO_QUERY );
213             if ( !xModel.is() )
214             {
215                 throw IllegalArgumentException(
216                     "BasicProviderImpl::initialize: unable to determine the document model from the script invocation context.",
217                     *this,
218                     1
219                 );
220             }
221         }
222         else
223         {
224             if ( !( aArguments[0] >>= m_sScriptingContext ) )
225             {
226                 throw IllegalArgumentException(
227                     "BasicProviderImpl::initialize: incorrect argument type " + aArguments[0].getValueTypeName(),
228                     *this,
229                     1
230                 );
231             }
232 
233             OUString sDoc = "vnd.sun.star.tdoc";
234             if ( m_sScriptingContext.startsWith( sDoc  ) )
235             {
236                 xModel = MiscUtils::tDocUrlToModel(  m_sScriptingContext );
237                 // TODO: use ScriptingContantsPool for SCRIPTING_DOC_REF
238             }
239         }
240 
241         if ( xModel.is() )
242         {
243             Reference< XEmbeddedScripts > xDocumentScripts( xModel, UNO_QUERY );
244             if ( xDocumentScripts.is() )
245             {
246                 m_pDocBasicManager = ::basic::BasicManagerRepository::getDocumentBasicManager( xModel );
247                 m_xLibContainerDoc.set( xDocumentScripts->getBasicLibraries(), UNO_QUERY );
248                 OSL_ENSURE( m_pDocBasicManager && m_xLibContainerDoc.is(),
249                     "BasicProviderImpl::initialize: invalid BasicManager, or invalid script container!" );
250             }
251             m_bIsAppScriptCtx = false;
252         }
253         else
254         {
255             // Provider has been created with application context for user
256             // or share
257             if ( m_sScriptingContext != "user" )
258             {
259                 m_bIsUserCtx = false;
260             }
261             else
262             {
263                 /*
264                 throw RuntimeException(
265                     "BasicProviderImpl::initialize: no scripting context!" );
266                 */
267             }
268         }
269 
270         // TODO
271         if ( !m_pAppBasicManager )
272         {
273             m_pAppBasicManager = SfxApplication::GetBasicManager();
274             if (m_pAppBasicManager)
275                 StartListening(*m_pAppBasicManager);
276         }
277 
278         if ( !m_xLibContainerApp.is() )
279             m_xLibContainerApp.set( SfxGetpApp()->GetBasicContainer(), UNO_QUERY );
280     }
281 
282 
283     // XScriptProvider
284 
285 
286     Reference < provider::XScript > BasicProviderImpl::getScript( const OUString& scriptURI )
287     {
288         // TODO
289 
290         SolarMutexGuard aGuard;
291 
292         Reference< provider::XScript > xScript;
293         Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext )  );
294 
295         Reference<  uri::XUriReference > uriRef(
296             xFac->parse( scriptURI ), UNO_QUERY );
297 
298         Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY );
299 
300         if ( !uriRef.is() || !sfUri.is() )
301         {
302             throw provider::ScriptFrameworkErrorException(
303                 "BasicProviderImpl::getScript: failed to parse URI: " + scriptURI,
304                 Reference< XInterface >(),
305                 scriptURI, "Basic",
306                 provider::ScriptFrameworkErrorType::MALFORMED_URL );
307         }
308 
309 
310         OUString aDescription = sfUri->getName();
311         OUString aLocation = sfUri->getParameter( "location" );
312 
313         sal_Int32 nIndex = 0;
314         // In some strange circumstances the Library name can have an
315         // apparently illegal '.' in it ( in imported VBA )
316 
317         BasicManager* pBasicMgr =  nullptr;
318         if ( aLocation == "document" )
319         {
320             pBasicMgr = m_pDocBasicManager;
321         }
322         else if ( aLocation == "application" )
323         {
324             pBasicMgr = m_pAppBasicManager;
325         }
326         OUString sProjectName;
327         if (  pBasicMgr )
328             sProjectName = pBasicMgr->GetName();
329 
330         OUString aLibrary;
331         if ( !sProjectName.isEmpty() && aDescription.match( sProjectName ) )
332         {
333             SAL_WARN("scripting", "LibraryName " << sProjectName << " is part of the url " << aDescription );
334             aLibrary = sProjectName;
335             nIndex = sProjectName.getLength() + 1;
336         }
337         else
338             aLibrary = aDescription.getToken( 0, '.', nIndex );
339         OUString aModule;
340         if ( nIndex != -1 )
341             aModule = aDescription.getToken( 0, '.', nIndex );
342         OUString aMethod;
343         if ( nIndex != -1 )
344             aMethod = aDescription.getToken( 0, '.', nIndex );
345 
346         if ( !aLibrary.isEmpty() && !aModule.isEmpty() && !aMethod.isEmpty() && !aLocation.isEmpty() )
347         {
348 
349             if ( pBasicMgr )
350             {
351                 StarBASIC* pBasic = pBasicMgr->GetLib( aLibrary );
352                 if ( !pBasic )
353                 {
354                     sal_uInt16 nId = pBasicMgr->GetLibId( aLibrary );
355                     if ( nId != LIB_NOTFOUND )
356                     {
357                         pBasicMgr->LoadLib( nId );
358                         pBasic = pBasicMgr->GetLib( aLibrary );
359                     }
360                 }
361                 if ( pBasic )
362                 {
363                     SbModule* pModule = pBasic->FindModule( aModule );
364                     if ( pModule )
365                     {
366                         SbMethod* pMethod = pModule->FindMethod( aMethod, SbxClassType::Method );
367                         if ( pMethod && !pMethod->IsHidden() )
368                         {
369                             if ( m_pDocBasicManager == pBasicMgr )
370                                 xScript = new BasicScriptImpl( aDescription, pMethod, *m_pDocBasicManager, m_xInvocationContext );
371                             else
372                                 xScript = new BasicScriptImpl( aDescription, pMethod );
373                         }
374                     }
375                 }
376             }
377         }
378 
379         if ( !xScript.is() )
380         {
381             throw provider::ScriptFrameworkErrorException(
382                 "The following Basic script could not be found:\n"
383                 "library: '" + aLibrary + "'\n"
384                 "module: '" + aModule + "'\n"
385                 "method: '" + aMethod + "'\n"
386                 "location: '" + aLocation + "'\n",
387                 Reference< XInterface >(),
388                 scriptURI, "Basic",
389                 provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT );
390         }
391 
392         return xScript;
393     }
394 
395 
396     // XBrowseNode
397 
398 
399     OUString BasicProviderImpl::getName(  )
400     {
401         return OUString("Basic");
402     }
403 
404 
405     Sequence< Reference< browse::XBrowseNode > > BasicProviderImpl::getChildNodes(  )
406     {
407         SolarMutexGuard aGuard;
408 
409         Reference< script::XLibraryContainer > xLibContainer;
410         BasicManager* pBasicManager = nullptr;
411 
412         if ( m_bIsAppScriptCtx )
413         {
414             xLibContainer = m_xLibContainerApp;
415             pBasicManager = m_pAppBasicManager;
416         }
417         else
418         {
419             xLibContainer = m_xLibContainerDoc;
420             pBasicManager = m_pDocBasicManager;
421         }
422 
423         Sequence< Reference< browse::XBrowseNode > > aChildNodes;
424 
425         if ( pBasicManager && xLibContainer.is() )
426         {
427             Sequence< OUString > aLibNames = xLibContainer->getElementNames();
428             sal_Int32 nLibCount = aLibNames.getLength();
429             const OUString* pLibNames = aLibNames.getConstArray();
430             aChildNodes.realloc( nLibCount );
431             Reference< browse::XBrowseNode >* pChildNodes = aChildNodes.getArray();
432             sal_Int32 childrenFound = 0;
433 
434             for ( sal_Int32 i = 0 ; i < nLibCount ; ++i )
435             {
436                 bool bCreate = false;
437                 if ( m_bIsAppScriptCtx )
438                 {
439                     const bool bShared = isLibraryShared( xLibContainer, pLibNames[i] );
440                     if (m_bIsUserCtx != bShared)
441                         bCreate = true;
442                 }
443                 else
444                 {
445                     bCreate = true;
446                 }
447                 if ( bCreate )
448                 {
449                     pChildNodes[childrenFound++] = static_cast< browse::XBrowseNode* >( new BasicLibraryNodeImpl(
450                         m_xContext, m_sScriptingContext, pBasicManager, xLibContainer, pLibNames[i], m_bIsAppScriptCtx ) );
451                 }
452             }
453 
454             if ( childrenFound != nLibCount )
455                 aChildNodes.realloc( childrenFound );
456         }
457 
458         return aChildNodes;
459     }
460 
461 
462     sal_Bool BasicProviderImpl::hasChildNodes(  )
463     {
464         SolarMutexGuard aGuard;
465 
466         bool bReturn = false;
467         Reference< script::XLibraryContainer > xLibContainer;
468         if ( m_bIsAppScriptCtx )
469         {
470             xLibContainer = m_xLibContainerApp;
471         }
472         else
473         {
474              xLibContainer = m_xLibContainerDoc;
475         }
476         if ( xLibContainer.is() )
477             bReturn = xLibContainer->hasElements();
478 
479         return bReturn;
480     }
481 
482 
483     sal_Int16 BasicProviderImpl::getType(  )
484     {
485         return browse::BrowseNodeTypes::CONTAINER;
486     }
487 
488 
489     // component operations
490 
491 
492     static Reference< XInterface > create_BasicProviderImpl(
493         Reference< XComponentContext > const & xContext )
494     {
495         return static_cast< lang::XTypeProvider * >( new BasicProviderImpl( xContext ) );
496     }
497 
498 
499     static struct ::cppu::ImplementationEntry const s_component_entries [] =
500     {
501         {
502             create_BasicProviderImpl, getImplementationName_BasicProviderImpl,
503             getSupportedServiceNames_BasicProviderImpl, ::cppu::createSingleComponentFactory,
504             nullptr, 0
505         },
506         { nullptr, nullptr, nullptr, nullptr, nullptr, 0 }
507     };
508 
509 
510 }   // namespace basprov
511 
512 
513 // component exports
514 
515 
516 extern "C"
517 {
518     SAL_DLLPUBLIC_EXPORT void * basprov_component_getFactory(
519         const sal_Char * pImplName, void * pServiceManager,
520         void * pRegistryKey )
521     {
522         return ::cppu::component_getFactoryHelper(
523             pImplName, pServiceManager, pRegistryKey, ::basprov::s_component_entries );
524     }
525 }
526 
527 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
528