diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /scripting/source/protocolhandler | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scripting/source/protocolhandler')
-rw-r--r-- | scripting/source/protocolhandler/protocolhandler.component | 26 | ||||
-rw-r--r-- | scripting/source/protocolhandler/scripthandler.cxx | 436 | ||||
-rw-r--r-- | scripting/source/protocolhandler/scripthandler.hxx | 114 |
3 files changed, 576 insertions, 0 deletions
diff --git a/scripting/source/protocolhandler/protocolhandler.component b/scripting/source/protocolhandler/protocolhandler.component new file mode 100644 index 000000000..959c332c0 --- /dev/null +++ b/scripting/source/protocolhandler/protocolhandler.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.ScriptProtocolHandler" + constructor="scripting_ScriptProtocolHandler_get_implementation"> + <service name="com.sun.star.frame.ProtocolHandler"/> + </implementation> +</component> diff --git a/scripting/source/protocolhandler/scripthandler.cxx b/scripting/source/protocolhandler/scripthandler.cxx new file mode 100644 index 000000000..08ec8d302 --- /dev/null +++ b/scripting/source/protocolhandler/scripthandler.cxx @@ -0,0 +1,436 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "scripthandler.hxx" + +#include <com/sun/star/frame/DispatchResultEvent.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XModel.hpp> + +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/document/XScriptInvocationContext.hpp> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp> +#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp> +#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp> + +#include <sfx2/objsh.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/sfxdlg.hxx> +#include <tools/diagnose_ex.h> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <framework/documentundoguard.hxx> +#include <officecfg/Office/Common.hxx> + +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::script::provider; +using namespace ::com::sun::star::document; + +namespace scripting_protocolhandler +{ + +void SAL_CALL ScriptProtocolHandler::initialize( + const css::uno::Sequence < css::uno::Any >& aArguments ) +{ + if ( m_bInitialised ) + { + return ; + } + + // first argument contains a reference to the frame (may be empty or the desktop, + // but usually it's a "real" frame) + if ( aArguments.hasElements() && !( aArguments[ 0 ] >>= m_xFrame ) ) + { + throw RuntimeException( "ScriptProtocolHandler::initialize: could not extract reference to the frame" ); + } + + ENSURE_OR_THROW( m_xContext.is(), "ScriptProtocolHandler::initialize: No Service Manager available" ); + m_bInitialised = true; +} + +Reference< XDispatch > SAL_CALL ScriptProtocolHandler::queryDispatch( + const URL& aURL, const OUString&, sal_Int32 ) +{ + Reference< XDispatch > xDispatcher; + // get scheme of url + + Reference< uri::XUriReferenceFactory > xFac = uri::UriReferenceFactory::create( m_xContext ); + Reference< uri::XUriReference > uriRef = xFac->parse( aURL.Complete ); + if ( uriRef.is() ) + { + if ( uriRef->getScheme() == "vnd.sun.star.script" ) + { + xDispatcher = this; + } + } + + return xDispatcher; +} + +Sequence< Reference< XDispatch > > SAL_CALL +ScriptProtocolHandler::queryDispatches( +const Sequence < DispatchDescriptor >& seqDescriptor ) +{ + sal_Int32 nCount = seqDescriptor.getLength(); + Sequence< Reference< XDispatch > > lDispatcher( nCount ); + std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(), + [this](const DispatchDescriptor& rDescr) -> Reference<XDispatch> { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + return lDispatcher; +} + +void SAL_CALL ScriptProtocolHandler::dispatchWithNotification( + const URL& aURL, const Sequence < PropertyValue >& lArgs, + const Reference< XDispatchResultListener >& xListener ) +{ + if (officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get()) + return; + + bool bSuccess = false; + Any invokeResult; + bool bCaughtException = false; + Any aException; + + if ( m_bInitialised ) + { + try + { + css::uno::Reference<css::uri::XUriReferenceFactory> urifac( + css::uri::UriReferenceFactory::create(m_xContext)); + css::uno::Reference<css::uri::XVndSunStarScriptUrlReference> uri( + urifac->parse(aURL.Complete), css::uno::UNO_QUERY_THROW); + auto const loc = uri->getParameter("location"); + bool bIsDocumentScript = loc == "document"; + + if ( bIsDocumentScript ) + { + // obtain the component for our security check + Reference< XEmbeddedScripts > xDocumentScripts; + if ( getScriptInvocation() ) + xDocumentScripts.set( m_xScriptInvocation->getScriptContainer(), UNO_SET_THROW ); + + OSL_ENSURE( xDocumentScripts.is(), "ScriptProtocolHandler::dispatchWithNotification: can't do the security check!" ); + if ( !xDocumentScripts.is() || !xDocumentScripts->getAllowMacroExecution() ) + { + if ( xListener.is() ) + { + css::frame::DispatchResultEvent aEvent( + static_cast< ::cppu::OWeakObject* >( this ), + css::frame::DispatchResultState::FAILURE, + invokeResult ); + try + { + xListener->dispatchFinished( aEvent ) ; + } + catch(const RuntimeException &) + { + TOOLS_WARN_EXCEPTION("scripting", + "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException" + "while dispatchFinished with failure of the execution"); + } + } + return; + } + } + + // Creates a ScriptProvider ( if one is not created already ) + createScriptProvider(); + + Reference< provider::XScript > xFunc = + m_xScriptProvider->getScript( aURL.Complete ); + ENSURE_OR_THROW( xFunc.is(), + "ScriptProtocolHandler::dispatchWithNotification: validate xFunc - unable to obtain XScript interface" ); + + + Sequence< Any > inArgs; + Sequence< Any > outArgs; + Sequence< sal_Int16 > outIndex; + + if ( lArgs.hasElements() ) + { + int argCount = 0; + for ( const auto& rArg : lArgs ) + { + // Sometimes we get a propertyval with name = "Referer" or "SynchronMode". These + // are not actual arguments to be passed to script, but flags describing the + // call, so ignore. Who thought that passing such "meta-arguments" mixed in with + // real arguments was a good idea? + if ( (rArg.Name != "Referer" && + rArg.Name != "SynchronMode") || + rArg.Name.isEmpty() ) //TODO:??? + { + inArgs.realloc( ++argCount ); + inArgs.getArray()[ argCount - 1 ] = rArg.Value; + } + } + } + + // attempt to protect the document against the script tampering with its Undo Context + std::unique_ptr< ::framework::DocumentUndoGuard > pUndoGuard; + if ( bIsDocumentScript ) + pUndoGuard.reset( new ::framework::DocumentUndoGuard( m_xScriptInvocation ) ); + + bSuccess = false; + while ( !bSuccess ) + { + std::exception_ptr aFirstCaughtException; + try + { + invokeResult = xFunc->invoke( inArgs, outIndex, outArgs ); + bSuccess = true; + } + catch( const provider::ScriptFrameworkErrorException& se ) + { + if (!aFirstCaughtException) + aFirstCaughtException = std::current_exception(); + + if ( se.errorType != provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT ) + // the only condition which allows us to retry is if there is no method with the + // given name/signature + std::rethrow_exception(aFirstCaughtException); + + if ( !inArgs.hasElements() ) + // no chance to retry if we can't strip more in-args + std::rethrow_exception(aFirstCaughtException); + + // strip one argument, then retry + inArgs.realloc( inArgs.getLength() - 1 ); + } + } + } + // Office doesn't handle exceptions rethrown here very well, it cores, + // all we can is log them and then set fail for the dispatch event! + // (if there is a listener of course) + catch ( const Exception & e ) + { + aException = ::cppu::getCaughtException(); + + invokeResult <<= "ScriptProtocolHandler::dispatch: caught " + + aException.getValueTypeName() + ": " + e.Message; + + bCaughtException = true; + } + } + else + { + invokeResult <<= OUString( + "ScriptProtocolHandler::dispatchWithNotification failed, ScriptProtocolHandler not initialised" + ); + } + + if ( bCaughtException ) + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + pFact->ShowAsyncScriptErrorDialog( nullptr, aException ); + } + + if ( !xListener.is() ) + return; + + // always call dispatchFinished(), because we didn't load a document but + // executed a macro instead! + css::frame::DispatchResultEvent aEvent; + + aEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + aEvent.Result = invokeResult; + if ( bSuccess ) + { + aEvent.State = css::frame::DispatchResultState::SUCCESS; + } + else + { + aEvent.State = css::frame::DispatchResultState::FAILURE; + } + + try + { + xListener->dispatchFinished( aEvent ) ; + } + catch(const RuntimeException &) + { + TOOLS_WARN_EXCEPTION("scripting", + "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException" + "while dispatchFinished" ); + } +} + +void SAL_CALL ScriptProtocolHandler::dispatch( +const URL& aURL, const Sequence< PropertyValue >& lArgs ) +{ + dispatchWithNotification( aURL, lArgs, Reference< XDispatchResultListener >() ); +} + +void SAL_CALL ScriptProtocolHandler::addStatusListener( +const Reference< XStatusListener >&, const URL& ) +{ + // implement if status is supported +} + +void SAL_CALL ScriptProtocolHandler::removeStatusListener( +const Reference< XStatusListener >&, const URL& ) +{} + +bool +ScriptProtocolHandler::getScriptInvocation() +{ + if ( !m_xScriptInvocation.is() && m_xFrame.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + if ( xController .is() ) + { + // try to obtain an XScriptInvocationContext interface, preferred from the + // mode, then from the controller + if ( !m_xScriptInvocation.set( xController->getModel(), UNO_QUERY ) ) + m_xScriptInvocation.set( xController, UNO_QUERY ); + } + else + { + if ( m_xFrame.is() ) + { + SfxFrame* pFrame = nullptr; + for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) ) + { + if ( pFrame->GetFrameInterface() == m_xFrame ) + break; + } + if (SfxObjectShell* pDocShell = pFrame ? pFrame->GetCurrentDocument() : SfxObjectShell::Current()) + { + Reference< XModel > xModel( pDocShell->GetModel() ); + m_xScriptInvocation.set( xModel, UNO_QUERY ); + } + } + } + } + return m_xScriptInvocation.is(); +} + +void ScriptProtocolHandler::createScriptProvider() +{ + if ( m_xScriptProvider.is() ) + return; + + try + { + // first, ask the component supporting the XScriptInvocationContext interface + // (if there is one) for a script provider + if ( getScriptInvocation() ) + { + Reference< XScriptProviderSupplier > xSPS( m_xScriptInvocation, UNO_QUERY ); + if ( xSPS.is() ) + m_xScriptProvider = xSPS->getScriptProvider(); + } + + // second, ask the model in our frame + if ( !m_xScriptProvider.is() && m_xFrame.is() ) + { + Reference< XController > xController = m_xFrame->getController(); + if ( xController .is() ) + { + Reference< XScriptProviderSupplier > xSPS( xController->getModel(), UNO_QUERY ); + if ( xSPS.is() ) + m_xScriptProvider = xSPS->getScriptProvider(); + } + } + + + // as a fallback, ask the controller + if ( !m_xScriptProvider.is() && m_xFrame.is() ) + { + Reference< XScriptProviderSupplier > xSPS( m_xFrame->getController(), UNO_QUERY ); + if ( xSPS.is() ) + m_xScriptProvider = xSPS->getScriptProvider(); + } + + // if nothing of this is successful, use the master script provider + if ( !m_xScriptProvider.is() ) + { + Reference< provider::XScriptProviderFactory > xFac = + provider::theMasterScriptProviderFactory::get( m_xContext ); + + Any aContext; + if ( getScriptInvocation() ) + aContext <<= m_xScriptInvocation; + m_xScriptProvider.set( xFac->createScriptProvider( aContext ), UNO_SET_THROW ); + } + } + catch ( const Exception & e ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "ScriptProtocolHandler::createScriptProvider: " + e.Message, + nullptr, anyEx ); + } +} + +ScriptProtocolHandler::ScriptProtocolHandler( const Reference< css::uno::XComponentContext > & xContext ) + : m_bInitialised( false ), m_xContext( xContext ) +{ +} + +ScriptProtocolHandler::~ScriptProtocolHandler() +{ +} + +/* XServiceInfo */ +OUString SAL_CALL ScriptProtocolHandler::getImplementationName( ) +{ + return "com.sun.star.comp.ScriptProtocolHandler"; +} + +/* XServiceInfo */ +sal_Bool SAL_CALL ScriptProtocolHandler::supportsService(const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +/* XServiceInfo */ +Sequence< OUString > SAL_CALL ScriptProtocolHandler::getSupportedServiceNames() +{ + return {"com.sun.star.frame.ProtocolHandler"}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_ScriptProtocolHandler_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new ScriptProtocolHandler(context)); +} + +} // namespace scripting_protocolhandler + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/protocolhandler/scripthandler.hxx b/scripting/source/protocolhandler/scripthandler.hxx new file mode 100644 index 000000000..74667b5a4 --- /dev/null +++ b/scripting/source/protocolhandler/scripthandler.hxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/script/provider/XScriptProvider.hpp> + + +namespace com::sun::star { + + namespace document { + class XScriptInvocationContext; + } + namespace uno { + class Any; + class XComponentContext; + } + namespace lang { + class XMultiServiceFactory; + class XSingleServiceFactory; + } + namespace frame { + class XFrame; + class XDispatch; + class XNotifyingDispatch; + class XDispatchResultListener; + struct DispatchDescriptor; + } + namespace beans { + struct PropertyValue; + } + namespace util { + struct URL; + } +} + +namespace scripting_protocolhandler +{ + +class ScriptProtocolHandler : +public ::cppu::WeakImplHelper< css::frame::XDispatchProvider, + css::frame::XNotifyingDispatch, css::lang::XServiceInfo, css::lang::XInitialization > +{ +private: + bool m_bInitialised; + css::uno::Reference < css::uno::XComponentContext > m_xContext; + css::uno::Reference < css::frame::XFrame > m_xFrame; + css::uno::Reference < css::script::provider::XScriptProvider > m_xScriptProvider; + css::uno::Reference< css::document::XScriptInvocationContext > m_xScriptInvocation; + + void createScriptProvider(); + bool getScriptInvocation(); + +public: + explicit ScriptProtocolHandler( const css::uno::Reference < css::uno::XComponentContext >& xContext ); + virtual ~ScriptProtocolHandler() override; + + /* XServiceInfo */ + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override; + virtual css::uno::Sequence < OUString > SAL_CALL getSupportedServiceNames() override; + + /* Implementation for XDispatchProvider */ + virtual css::uno::Reference < css::frame::XDispatch > SAL_CALL + queryDispatch( const css::util::URL& aURL, const OUString& sTargetFrameName, + sal_Int32 eSearchFlags ) override ; + virtual css::uno::Sequence< css::uno::Reference < css::frame::XDispatch > > SAL_CALL + queryDispatches( + const css::uno::Sequence < css::frame::DispatchDescriptor >& seqDescriptor ) override; + + /* Implementation for X(Notifying)Dispatch */ + virtual void SAL_CALL dispatchWithNotification( + const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArgs, + const css::uno::Reference< css::frame::XDispatchResultListener >& Listener ) override; + virtual void SAL_CALL dispatch( + const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) override; + virtual void SAL_CALL addStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& xControl, + const css::util::URL& aURL ) override; + virtual void SAL_CALL removeStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& xControl, + const css::util::URL& aURL ) override; + + /* Implementation for XInitialization */ + virtual void SAL_CALL initialize( + const css::uno::Sequence < css::uno::Any >& aArguments ) override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |