Avoid potential negative array index access to cached text.
[LibreOffice.git] / scripting / source / provider / MasterScriptProvider.cxx
blob66fc58db78bd9ccfaa4d4f2575d7f42974343748
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <comphelper/SetFlagContextHelper.hxx>
22 #include <comphelper/documentinfo.hxx>
24 #include <cppuhelper/exc_hlp.hxx>
25 #include <cppuhelper/supportsservice.hxx>
26 #include <comphelper/diagnose_ex.hxx>
27 #include <tools/urlobj.hxx>
29 #include <com/sun/star/frame/XModel.hpp>
30 #include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
31 #include <com/sun/star/uri/XUriReference.hpp>
32 #include <com/sun/star/uri/UriReferenceFactory.hpp>
33 #include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
35 #include <com/sun/star/deployment/XPackage.hpp>
36 #include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
37 #include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
38 #include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
40 #include <util/MiscUtils.hxx>
41 #include <sal/log.hxx>
43 #include "MasterScriptProvider.hxx"
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::uno;
47 using namespace ::com::sun::star::script;
48 using namespace ::com::sun::star::document;
49 using namespace ::sf_misc;
51 namespace func_provider
54 static bool endsWith( std::u16string_view target, std::u16string_view item )
56 size_t index = target.find( item );
57 return index != std::u16string_view::npos &&
58 index == ( target.size() - item.size() );
61 /* should be available in some central location. */
63 // XScriptProvider implementation
66 MasterScriptProvider::MasterScriptProvider( const Reference< XComponentContext > & xContext ):
67 m_xContext( xContext ), m_bIsValid( false ), m_bInitialised( false ),
68 m_bIsPkgMSP( false )
70 ENSURE_OR_THROW( m_xContext.is(), "MasterScriptProvider::MasterScriptProvider: No context available\n" );
71 m_xMgr = m_xContext->getServiceManager();
72 ENSURE_OR_THROW( m_xMgr.is(), "MasterScriptProvider::MasterScriptProvider: No service manager available\n" );
73 m_bIsValid = true;
77 MasterScriptProvider::~MasterScriptProvider()
82 void SAL_CALL MasterScriptProvider::initialize( const Sequence < Any >& args )
84 if ( m_bInitialised )
85 return;
87 m_bIsValid = false;
89 sal_Int32 len = args.getLength();
90 if ( len > 1 )
92 throw RuntimeException(
93 "MasterScriptProvider::initialize: invalid number of arguments" );
96 Sequence< Any > invokeArgs( len );
98 if ( len != 0 )
100 auto pinvokeArgs = invokeArgs.getArray();
101 // check if first parameter is a string
102 // if it is, this implies that this is a MSP created
103 // with a user or share ctx ( used for browse functionality )
105 if ( args[ 0 ] >>= m_sCtxString )
107 pinvokeArgs[ 0 ] = args[ 0 ];
108 if ( m_sCtxString.startsWith( "vnd.sun.star.tdoc" ) )
110 m_xModel = MiscUtils::tDocUrlToModel( m_sCtxString );
113 else if ( args[ 0 ] >>= m_xInvocationContext )
115 m_xModel.set( m_xInvocationContext->getScriptContainer(), UNO_QUERY_THROW );
117 else
119 args[ 0 ] >>= m_xModel;
122 if ( m_xModel.is() )
124 // from the arguments, we were able to deduce a model. That alone doesn't
125 // suffice, we also need an XEmbeddedScripts which actually indicates support
126 // for embedding scripts
127 Reference< XEmbeddedScripts > xScripts( m_xModel, UNO_QUERY );
128 if ( !xScripts.is() )
130 throw lang::IllegalArgumentException(
131 "The given document does not support embedding scripts into it, and cannot be associated with such a document.",
132 *this,
139 m_sCtxString = MiscUtils::xModelToTdocUrl( m_xModel, m_xContext );
141 catch ( const Exception& )
143 Any aError( ::cppu::getCaughtException() );
145 Exception aException;
146 aError >>= aException;
147 OUString buf =
148 "MasterScriptProvider::initialize: caught " +
149 aError.getValueTypeName() +
150 ":" +
151 aException.Message;
152 throw lang::WrappedTargetException( buf, *this, aError );
155 if ( m_xInvocationContext.is() && m_xInvocationContext != m_xModel )
156 pinvokeArgs[ 0 ] <<= m_xInvocationContext;
157 else
158 pinvokeArgs[ 0 ] <<= m_sCtxString;
161 OUString pkgSpec = "uno_packages";
162 sal_Int32 indexOfPkgSpec = m_sCtxString.lastIndexOf( pkgSpec );
164 // if context string ends with "uno_packages"
165 if ( indexOfPkgSpec > -1 && m_sCtxString.match( pkgSpec, indexOfPkgSpec ) )
167 m_bIsPkgMSP = true;
169 else
171 m_bIsPkgMSP = false;
174 else // no args
176 // use either scripting context or maybe zero args?
177 invokeArgs = Sequence< Any >( 0 ); // no arguments
179 m_sAargs = invokeArgs;
180 // don't create pkg mgr MSP for documents, not supported
181 if ( !m_bIsPkgMSP && !m_xModel.is() )
183 createPkgProvider();
186 m_bInitialised = true;
187 m_bIsValid = true;
191 void MasterScriptProvider::createPkgProvider()
195 Any location;
196 location <<= m_sCtxString + ":uno_packages";
198 Reference< provider::XScriptProviderFactory > xFac =
199 provider::theMasterScriptProviderFactory::get( m_xContext );
201 m_xMSPPkg.set(
202 xFac->createScriptProvider( location ), UNO_SET_THROW );
205 catch ( const Exception& )
207 TOOLS_WARN_EXCEPTION("scripting.provider", "Exception creating MasterScriptProvider for uno_packages in context "
208 << m_sCtxString );
213 Reference< provider::XScript >
214 MasterScriptProvider::getScript( const OUString& scriptURI )
216 if ( !m_bIsValid )
218 throw provider::ScriptFrameworkErrorException(
219 "MasterScriptProvider not initialised", Reference< XInterface >(),
220 scriptURI, "",
221 provider::ScriptFrameworkErrorType::UNKNOWN );
224 // need to get the language from the string
226 Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) );
228 Reference< uri::XUriReference > uriRef = xFac->parse( scriptURI );
230 Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY );
232 if ( !uriRef.is() || !sfUri.is() )
234 throw provider::ScriptFrameworkErrorException(
235 "Incorrect format for Script URI: " + scriptURI,
236 Reference< XInterface >(),
237 scriptURI, "",
238 provider::ScriptFrameworkErrorType::UNKNOWN );
241 OUString langKey("language");
242 OUString locKey("location");
244 if ( !sfUri->hasParameter( langKey ) ||
245 !sfUri->hasParameter( locKey ) ||
246 ( sfUri->getName().isEmpty() ) )
248 throw provider::ScriptFrameworkErrorException(
249 "Incorrect format for Script URI: " + scriptURI,
250 Reference< XInterface >(),
251 scriptURI, "",
252 provider::ScriptFrameworkErrorType::UNKNOWN );
255 OUString language = sfUri->getParameter( langKey );
256 OUString location = sfUri->getParameter( locKey );
258 // if script us located in uno pkg
259 sal_Int32 index = -1;
260 OUString pkgTag(":uno_packages");
261 // for languages other than basic, scripts located in uno packages
262 // are merged into the user/share location context.
263 // For other languages the location attribute in script url has the form
264 // location = [user|share]:uno_packages or location :uno_packages/xxxx.uno.pkg
265 // we need to extract the value of location part from the
266 // location attribute of the script, if the script is located in an
267 // uno package then that is the location part up to and including
268 // ":uno_packages", if the script is not in a uno package then the
269 // normal value is used e.g. user or share.
270 // The value extracted will be used to determine if the script is
271 // located in the same location context as this MSP.
272 // For Basic, the language script provider can handle the execution of a
273 // script in any location context
274 if ( ( index = location.indexOf( pkgTag ) ) > -1 )
276 location = location.copy( 0, index + pkgTag.getLength() );
279 Reference< provider::XScript > xScript;
281 // If the script location is in the same location context as this
282 // MSP then delete to the language provider controlled by this MSP
283 // ** Special case is BASIC, all calls to getScript will be handled
284 // by the language script provider in the current location context
285 // even if it's different
286 if ( ( location == "document"
287 && m_xModel.is()
289 || ( endsWith( m_sCtxString, location ) )
290 || ( language == "Basic" )
293 Reference< provider::XScriptProvider > xScriptProvider;
294 OUString serviceName = "com.sun.star.script.provider.ScriptProviderFor" + language;
295 if ( !providerCache() )
297 throw provider::ScriptFrameworkErrorException(
298 "No LanguageProviders detected",
299 Reference< XInterface >(),
300 sfUri->getName(), language,
301 provider::ScriptFrameworkErrorType::NOTSUPPORTED );
306 xScriptProvider.set(
307 providerCache()->getProvider( serviceName ),
308 UNO_SET_THROW );
310 catch( const Exception& e )
312 throw provider::ScriptFrameworkErrorException(
313 e.Message, Reference< XInterface >(),
314 sfUri->getName(), language,
315 provider::ScriptFrameworkErrorType::NOTSUPPORTED );
318 xScript=xScriptProvider->getScript( scriptURI );
320 else
322 Reference< provider::XScriptProviderFactory > xFac_ =
323 provider::theMasterScriptProviderFactory::get( m_xContext );
325 Reference< provider::XScriptProvider > xSP(
326 xFac_->createScriptProvider( Any( location ) ), UNO_SET_THROW );
327 xScript = xSP->getScript( scriptURI );
330 return xScript;
334 ProviderCache*
335 MasterScriptProvider::providerCache()
337 if ( !m_pPCache )
339 std::scoped_lock aGuard( m_mutex );
340 if ( !m_pPCache )
342 Sequence<OUString> denylist { "com.sun.star.script.provider.ScriptProviderForBasic" };
344 if ( !m_bIsPkgMSP )
346 m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs ) );
348 else
350 m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs, denylist ) );
354 return m_pPCache.get();
358 OUString SAL_CALL
359 MasterScriptProvider::getName()
361 if ( !m_bIsPkgMSP )
363 OUString sCtx = getContextString();
364 if ( sCtx.startsWith( "vnd.sun.star.tdoc" ) )
366 Reference< frame::XModel > xModel = m_xModel;
367 if ( !xModel.is() )
369 xModel = MiscUtils::tDocUrlToModel( sCtx );
372 m_sNodeName = ::comphelper::DocumentInfo::getDocumentTitle( xModel );
374 else
376 m_sNodeName = parseLocationName( getContextString() );
379 else
381 m_sNodeName = "uno_packages";
383 return m_sNodeName;
387 Sequence< Reference< browse::XBrowseNode > > SAL_CALL
388 MasterScriptProvider::getChildNodes()
390 Sequence< Reference< provider::XScriptProvider > > providers = providerCache()->getAllProviders();
392 sal_Int32 size = providers.getLength();
393 bool hasPkgs = m_xMSPPkg.is();
394 if ( hasPkgs )
396 size++;
398 Sequence< Reference< browse::XBrowseNode > > children( size );
399 auto childrenRange = asNonConstRange(children);
400 sal_Int32 provIndex = 0;
401 for ( ; provIndex < providers.getLength(); provIndex++ )
403 childrenRange[ provIndex ].set( providers[ provIndex ], UNO_QUERY );
406 if ( hasPkgs )
408 childrenRange[ provIndex ].set( m_xMSPPkg, UNO_QUERY );
412 return children;
416 sal_Bool SAL_CALL
417 MasterScriptProvider::hasChildNodes()
419 return true;
423 sal_Int16 SAL_CALL
424 MasterScriptProvider::getType()
426 return browse::BrowseNodeTypes::CONTAINER;
430 OUString
431 MasterScriptProvider::parseLocationName( const OUString& location )
433 // strip out the last leaf of location name
434 // e.g. file://dir1/dir2/Blah.sxw - > Blah.sxw
435 OUString temp = location;
436 INetURLObject aURLObj( temp );
437 if ( !aURLObj.HasError() )
438 temp = aURLObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
439 return temp;
442 namespace
444 template <typename Proc> bool FindProviderAndApply(ProviderCache& rCache, Proc p)
446 auto pass = [&rCache, &p]() -> bool
448 bool bResult = false;
449 const Sequence<Reference<provider::XScriptProvider>> aAllProviders = rCache.getAllProviders();
450 for (const auto& rProv : aAllProviders)
452 Reference<container::XNameContainer> xCont(rProv, UNO_QUERY);
453 if (!xCont.is())
455 continue;
459 bResult = p(xCont);
460 if (bResult)
461 break;
463 catch (const Exception&)
465 TOOLS_INFO_EXCEPTION("scripting.provider", "ignoring");
468 return bResult;
470 bool bSuccess = false;
471 // 1. Try to perform the operation without trying to enable JVM (if disabled)
472 // This allows us to avoid useless user interaction in case when other provider
473 // (not JVM) actually handles the operation.
475 css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext());
476 bSuccess = pass();
478 // 2. Now retry asking to enable JVM in case we didn't succeed first time
479 if (!bSuccess)
481 bSuccess = pass();
483 return bSuccess;
485 } // namespace
487 // Register Package
488 void SAL_CALL
489 MasterScriptProvider::insertByName( const OUString& aName, const Any& aElement )
491 if ( !m_bIsPkgMSP )
493 if ( !m_xMSPPkg.is() )
495 throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
498 Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
499 xCont->insertByName( aName, aElement );
501 else
503 Reference< deployment::XPackage > xPkg( aElement, UNO_QUERY );
504 if ( !xPkg.is() )
506 throw lang::IllegalArgumentException( "Couldn't convert to XPackage",
507 Reference < XInterface > (), 2 );
509 if ( aName.isEmpty() )
511 throw lang::IllegalArgumentException( "Name not set!!",
512 Reference < XInterface > (), 1 );
514 // TODO for library package parse the language, for the moment will try
515 // to get each provider to process the new Package, the first one the succeeds
516 // will terminate processing
517 const bool bSuccess = FindProviderAndApply(
518 *providerCache(), [&aName, &aElement](Reference<container::XNameContainer>& xCont) {
519 xCont->insertByName(aName, aElement);
520 return true;
522 if (!bSuccess)
524 // No script providers could process the package
525 throw lang::IllegalArgumentException( "Failed to register package for " + aName,
526 Reference < XInterface > (), 2 );
532 // Revoke Package
533 void SAL_CALL
534 MasterScriptProvider::removeByName( const OUString& Name )
536 if ( !m_bIsPkgMSP )
538 if ( !m_xMSPPkg.is() )
540 throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
543 Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
544 xCont->removeByName( Name );
546 else
548 if ( Name.isEmpty() )
550 throw lang::IllegalArgumentException( "Name not set!!",
551 Reference < XInterface > (), 1 );
553 // TODO for Script library package url parse the language,
554 // for the moment will just try to get each provider to process remove/revoke
555 // request, the first one the succeeds will terminate processing
556 const bool bSuccess = FindProviderAndApply(
557 *providerCache(), [&Name](Reference<container::XNameContainer>& xCont) {
558 xCont->removeByName(Name);
559 return true;
561 if (!bSuccess)
563 // No script providers could process the package
564 throw lang::IllegalArgumentException( "Failed to revoke package for " + Name,
565 Reference < XInterface > (), 1 );
572 void SAL_CALL
573 MasterScriptProvider::replaceByName( const OUString& /*aName*/, const Any& /*aElement*/ )
575 // TODO needs implementing
576 throw RuntimeException( "replaceByName not implemented!!!!" );
579 Any SAL_CALL
580 MasterScriptProvider::getByName( const OUString& /*aName*/ )
582 // TODO needs to be implemented
583 throw RuntimeException( "getByName not implemented!!!!" );
586 sal_Bool SAL_CALL
587 MasterScriptProvider::hasByName( const OUString& aName )
589 bool result = false;
590 if ( !m_bIsPkgMSP )
592 if ( m_xMSPPkg.is() )
594 Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
595 result = xCont->hasByName( aName );
597 // If this is a document provider then we shouldn't
598 // have a PackageProvider
599 else if (!m_xModel.is())
601 throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
605 else
607 if ( aName.isEmpty() )
609 throw lang::IllegalArgumentException( "Name not set!!",
610 Reference < XInterface > (), 1 );
612 // TODO for Script library package url parse the language,
613 // for the moment will just try to get each provider to see if the
614 // package exists in any provider, first one that succeed will
615 // terminate the loop
616 result = FindProviderAndApply(
617 *providerCache(), [&aName](Reference<container::XNameContainer>& xCont) {
618 return xCont->hasByName(aName);
621 return result;
625 Sequence< OUString > SAL_CALL
626 MasterScriptProvider::getElementNames( )
628 // TODO needs implementing
629 throw RuntimeException( "getElementNames not implemented!!!!" );
632 Type SAL_CALL
633 MasterScriptProvider::getElementType( )
635 // TODO needs implementing
636 Type t;
637 return t;
640 sal_Bool SAL_CALL MasterScriptProvider::hasElements( )
642 // TODO needs implementing
643 throw RuntimeException( "hasElements not implemented!!!!" );
647 OUString SAL_CALL MasterScriptProvider::getImplementationName( )
649 return "com.sun.star.script.provider.MasterScriptProvider";
652 sal_Bool SAL_CALL MasterScriptProvider::supportsService( const OUString& serviceName )
654 return cppu::supportsService(this, serviceName);
658 Sequence< OUString > SAL_CALL MasterScriptProvider::getSupportedServiceNames( )
660 return {
661 "com.sun.star.script.provider.MasterScriptProvider",
662 "com.sun.star.script.browse.BrowseNode",
663 "com.sun.star.script.provider.ScriptProvider" };
666 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
667 scripting_MasterScriptProvider_get_implementation(
668 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
670 return cppu::acquire(new MasterScriptProvider(context));
673 } // namespace func_provider
676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */