Line data Source code
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 "scripthandler.hxx"
21 :
22 : #include <osl/mutex.hxx>
23 :
24 : #include <com/sun/star/frame/DispatchResultEvent.hpp>
25 : #include <com/sun/star/frame/DispatchResultState.hpp>
26 : #include <com/sun/star/frame/XController.hpp>
27 : #include <com/sun/star/frame/XModel.hpp>
28 :
29 : #include <com/sun/star/document/XEmbeddedScripts.hpp>
30 : #include <com/sun/star/document/XScriptInvocationContext.hpp>
31 :
32 : #include <com/sun/star/lang/XSingleServiceFactory.hpp>
33 :
34 : #include <com/sun/star/script/provider/XScriptProviderSupplier.hpp>
35 : #include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
36 : #include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
37 :
38 : #include <sfx2/objsh.hxx>
39 : #include <sfx2/frame.hxx>
40 : #include <sfx2/sfxdlg.hxx>
41 : #include <vcl/abstdlg.hxx>
42 : #include <tools/diagnose_ex.h>
43 :
44 : #include <comphelper/processfactory.hxx>
45 : #include <cppuhelper/factory.hxx>
46 : #include <cppuhelper/exc_hlp.hxx>
47 : #include <cppuhelper/supportsservice.hxx>
48 : #include <framework/documentundoguard.hxx>
49 :
50 : #include "com/sun/star/uno/XComponentContext.hpp"
51 : #include "com/sun/star/uri/XUriReference.hpp"
52 : #include "com/sun/star/uri/UriReferenceFactory.hpp"
53 : #include "com/sun/star/uri/XVndSunStarScriptUrl.hpp"
54 :
55 : #include <boost/scoped_ptr.hpp>
56 :
57 : using namespace ::com::sun::star;
58 : using namespace ::com::sun::star::uno;
59 : using namespace ::com::sun::star::frame;
60 : using namespace ::com::sun::star::util;
61 : using namespace ::com::sun::star::beans;
62 : using namespace ::com::sun::star::lang;
63 : using namespace ::com::sun::star::script;
64 : using namespace ::com::sun::star::script::provider;
65 : using namespace ::com::sun::star::document;
66 :
67 : namespace scripting_protocolhandler
68 : {
69 :
70 : const sal_Char * const MYSERVICENAME = "com.sun.star.frame.ProtocolHandler";
71 : const sal_Char * const MYIMPLNAME = "com.sun.star.comp.ScriptProtocolHandler";
72 :
73 8848 : void SAL_CALL ScriptProtocolHandler::initialize(
74 : const css::uno::Sequence < css::uno::Any >& aArguments )
75 : throw ( css::uno::Exception, std::exception )
76 : {
77 8848 : if ( m_bInitialised )
78 : {
79 8848 : return ;
80 : }
81 :
82 : // first argument contains a reference to the frame (may be empty or the desktop,
83 : // but usually it's a "real" frame)
84 8848 : if ( aArguments.getLength() && !( aArguments[ 0 ] >>= m_xFrame ) )
85 : {
86 0 : OUString temp = "ScriptProtocolHandler::initialize: could not extract reference to the frame";
87 0 : throw RuntimeException( temp );
88 : }
89 :
90 8848 : ENSURE_OR_THROW( m_xContext.is(), "ScriptProtocolHandler::initialize: No Service Manager available" );
91 8848 : m_bInitialised = true;
92 : }
93 :
94 8848 : Reference< XDispatch > SAL_CALL ScriptProtocolHandler::queryDispatch(
95 : const URL& aURL, const OUString& sTargetFrameName, sal_Int32 nSearchFlags )
96 : throw( ::com::sun::star::uno::RuntimeException, std::exception )
97 : {
98 : (void)sTargetFrameName;
99 : (void)nSearchFlags;
100 :
101 8848 : Reference< XDispatch > xDispatcher;
102 : // get scheme of url
103 :
104 17696 : Reference< uri::XUriReferenceFactory > xFac = uri::UriReferenceFactory::create( m_xContext );
105 : Reference< uri::XUriReference > uriRef(
106 17696 : xFac->parse( aURL.Complete ), UNO_QUERY );
107 8848 : if ( uriRef.is() )
108 : {
109 8848 : if ( uriRef->getScheme() == "vnd.sun.star.script" )
110 : {
111 8848 : xDispatcher = this;
112 : }
113 : }
114 :
115 17696 : return xDispatcher;
116 : }
117 :
118 : Sequence< Reference< XDispatch > > SAL_CALL
119 0 : ScriptProtocolHandler::queryDispatches(
120 : const Sequence < DispatchDescriptor >& seqDescriptor )
121 : throw( RuntimeException, std::exception )
122 : {
123 0 : sal_Int32 nCount = seqDescriptor.getLength();
124 0 : Sequence< Reference< XDispatch > > lDispatcher( nCount );
125 0 : for ( sal_Int32 i = 0; i < nCount; ++i )
126 : {
127 0 : lDispatcher[ i ] = this->queryDispatch( seqDescriptor[ i ].FeatureURL,
128 0 : seqDescriptor[ i ].FrameName,
129 0 : seqDescriptor[ i ].SearchFlags );
130 : }
131 0 : return lDispatcher;
132 : }
133 :
134 8 : void SAL_CALL ScriptProtocolHandler::dispatchWithNotification(
135 : const URL& aURL, const Sequence < PropertyValue >& lArgs,
136 : const Reference< XDispatchResultListener >& xListener )
137 : throw ( RuntimeException, std::exception )
138 : {
139 :
140 8 : bool bSuccess = false;
141 8 : Any invokeResult;
142 8 : bool bCaughtException = false;
143 16 : Any aException;
144 :
145 8 : if ( m_bInitialised )
146 : {
147 : try
148 : {
149 8 : bool bIsDocumentScript = ( aURL.Complete.indexOf( "document" ) !=-1 );
150 : // TODO: isn't this somewhat strange? This should be a test for a location=document parameter, shouldn't it?
151 :
152 8 : if ( bIsDocumentScript )
153 : {
154 : // obtain the component for our security check
155 8 : Reference< XEmbeddedScripts > xDocumentScripts;
156 8 : if ( getScriptInvocation() )
157 8 : xDocumentScripts.set( m_xScriptInvocation->getScriptContainer(), UNO_SET_THROW );
158 :
159 : OSL_ENSURE( xDocumentScripts.is(), "ScriptProtocolHandler::dispatchWithNotification: can't do the security check!" );
160 8 : if ( !xDocumentScripts.is() || !xDocumentScripts->getAllowMacroExecution() )
161 : {
162 0 : if ( xListener.is() )
163 : {
164 : ::com::sun::star::frame::DispatchResultEvent aEvent(
165 : static_cast< ::cppu::OWeakObject* >( this ),
166 : ::com::sun::star::frame::DispatchResultState::FAILURE,
167 0 : invokeResult );
168 : try
169 : {
170 0 : xListener->dispatchFinished( aEvent ) ;
171 : }
172 0 : catch(RuntimeException & e)
173 : {
174 : OSL_TRACE(
175 : "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException"
176 : "while dispatchFinished with failture of the execution %s",
177 : ::rtl::OUStringToOString( e.Message,
178 : RTL_TEXTENCODING_ASCII_US ).pData->buffer );
179 0 : }
180 : }
181 8 : return;
182 8 : }
183 : }
184 :
185 : // Creates a ScriptProvider ( if one is not created already )
186 8 : createScriptProvider();
187 :
188 : Reference< provider::XScript > xFunc =
189 8 : m_xScriptProvider->getScript( aURL.Complete );
190 8 : ENSURE_OR_THROW( xFunc.is(),
191 : "ScriptProtocolHandler::dispatchWithNotification: validate xFunc - unable to obtain XScript interface" );
192 :
193 :
194 16 : Sequence< Any > inArgs( 0 );
195 16 : Sequence< Any > outArgs( 0 );
196 16 : Sequence< sal_Int16 > outIndex;
197 :
198 8 : if ( lArgs.getLength() > 0 )
199 : {
200 2 : int argCount = 0;
201 4 : for ( int index = 0; index < lArgs.getLength(); index++ )
202 : {
203 : // Sometimes we get a propertyval with name = "Referer"
204 : // this is not an argument to be passed to script, so
205 : // ignore.
206 2 : if ( !lArgs[ index ].Name.equalsAscii("Referer") ||
207 0 : lArgs[ index ].Name.isEmpty() )
208 : {
209 2 : inArgs.realloc( ++argCount );
210 2 : inArgs[ argCount - 1 ] = lArgs[ index ].Value;
211 : }
212 : }
213 : }
214 :
215 : // attempt to protect the document against the script tampering with its Undo Context
216 16 : boost::scoped_ptr< ::framework::DocumentUndoGuard > pUndoGuard;
217 8 : if ( bIsDocumentScript )
218 8 : pUndoGuard.reset( new ::framework::DocumentUndoGuard( m_xScriptInvocation ) );
219 :
220 8 : bSuccess = false;
221 24 : while ( !bSuccess )
222 : {
223 8 : Any aFirstCaughtException;
224 : try
225 : {
226 8 : invokeResult = xFunc->invoke( inArgs, outIndex, outArgs );
227 8 : bSuccess = true;
228 : }
229 0 : catch( const provider::ScriptFrameworkErrorException& se )
230 : {
231 0 : if ( !aFirstCaughtException.hasValue() )
232 0 : aFirstCaughtException = ::cppu::getCaughtException();
233 :
234 0 : if ( se.errorType != provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT )
235 : // the only condition which allows us to retry is if there is no method with the
236 : // given name/signature
237 0 : ::cppu::throwException( aFirstCaughtException );
238 :
239 0 : if ( inArgs.getLength() == 0 )
240 : // no chance to retry if we can't strip more in-args
241 0 : ::cppu::throwException( aFirstCaughtException );
242 :
243 : // strip one argument, then retry
244 0 : inArgs.realloc( inArgs.getLength() - 1 );
245 : }
246 16 : }
247 : }
248 : // Office doesn't handle exceptions rethrown here very well, it cores,
249 : // all we can is log them and then set fail for the dispatch event!
250 : // (if there is a listener of course)
251 0 : catch ( const Exception & e )
252 : {
253 0 : aException = ::cppu::getCaughtException();
254 :
255 0 : OUString reason = "ScriptProtocolHandler::dispatch: caught ";
256 :
257 0 : invokeResult <<= reason.concat( aException.getValueTypeName() ).concat( e.Message );
258 :
259 0 : bCaughtException = true;
260 : }
261 : }
262 : else
263 : {
264 : OUString reason(
265 : "ScriptProtocolHandler::dispatchWithNotification failed, ScriptProtocolHandler not initialised"
266 0 : );
267 0 : invokeResult <<= reason;
268 : }
269 :
270 8 : if ( bCaughtException )
271 : {
272 0 : SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
273 :
274 0 : if ( pFact != NULL )
275 : {
276 : boost::scoped_ptr<VclAbstractDialog> pDlg(
277 0 : pFact->CreateScriptErrorDialog( NULL, aException ));
278 :
279 0 : if ( pDlg )
280 0 : pDlg->Execute();
281 : }
282 : }
283 :
284 8 : if ( xListener.is() )
285 : {
286 : // always call dispatchFinished(), because we didn't load a document but
287 : // executed a macro instead!
288 2 : ::com::sun::star::frame::DispatchResultEvent aEvent;
289 :
290 2 : aEvent.Source = static_cast< ::cppu::OWeakObject* >( this );
291 2 : aEvent.Result = invokeResult;
292 2 : if ( bSuccess )
293 : {
294 2 : aEvent.State = ::com::sun::star::frame::DispatchResultState::SUCCESS;
295 : }
296 : else
297 : {
298 0 : aEvent.State = ::com::sun::star::frame::DispatchResultState::FAILURE;
299 : }
300 :
301 : try
302 : {
303 2 : xListener->dispatchFinished( aEvent ) ;
304 : }
305 0 : catch(const RuntimeException & e)
306 : {
307 : OSL_TRACE(
308 : "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException"
309 : "while dispatchFinished %s",
310 : OUStringToOString( e.Message,
311 : RTL_TEXTENCODING_ASCII_US ).pData->buffer );
312 2 : }
313 8 : }
314 : }
315 :
316 6 : void SAL_CALL ScriptProtocolHandler::dispatch(
317 : const URL& aURL, const Sequence< PropertyValue >& lArgs )
318 : throw ( RuntimeException, std::exception )
319 : {
320 6 : dispatchWithNotification( aURL, lArgs, Reference< XDispatchResultListener >() );
321 6 : }
322 :
323 8840 : void SAL_CALL ScriptProtocolHandler::addStatusListener(
324 : const Reference< XStatusListener >& xControl, const URL& aURL )
325 : throw ( RuntimeException, std::exception )
326 : {
327 : (void)xControl;
328 : (void)aURL;
329 :
330 : // implement if status is supported
331 8840 : }
332 :
333 8820 : void SAL_CALL ScriptProtocolHandler::removeStatusListener(
334 : const Reference< XStatusListener >& xControl, const URL& aURL )
335 : throw ( RuntimeException, std::exception )
336 : {
337 : (void)xControl;
338 : (void)aURL;
339 8820 : }
340 :
341 : bool
342 16 : ScriptProtocolHandler::getScriptInvocation()
343 : {
344 16 : if ( !m_xScriptInvocation.is() && m_xFrame.is() )
345 : {
346 8 : Reference< XController > xController = m_xFrame->getController();
347 8 : if ( xController .is() )
348 : {
349 : // try to obtain an XScriptInvocationContext interface, preferred from the
350 : // mode, then from the controller
351 8 : if ( !m_xScriptInvocation.set( xController->getModel(), UNO_QUERY ) )
352 0 : m_xScriptInvocation.set( xController, UNO_QUERY );
353 : }
354 : else
355 : {
356 0 : Reference< XFrame > xFrame( m_xFrame.get(), UNO_QUERY );
357 0 : if ( xFrame.is() )
358 : {
359 0 : SfxFrame* pFrame = NULL;
360 0 : for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) )
361 : {
362 0 : if ( pFrame->GetFrameInterface() == xFrame )
363 0 : break;
364 : }
365 0 : SfxObjectShell* pDocShell = pFrame ? pFrame->GetCurrentDocument() : SfxObjectShell::Current();
366 0 : if ( pDocShell )
367 : {
368 0 : Reference< XModel > xModel( pDocShell->GetModel() );
369 0 : m_xScriptInvocation.set( xModel, UNO_QUERY );
370 : }
371 0 : }
372 8 : }
373 : }
374 16 : return m_xScriptInvocation.is();
375 : }
376 :
377 8 : void ScriptProtocolHandler::createScriptProvider()
378 : {
379 8 : if ( m_xScriptProvider.is() )
380 8 : return;
381 :
382 : try
383 : {
384 : // first, ask the component supporting the XScriptInvocationContext interface
385 : // (if there is one) for a script provider
386 8 : if ( getScriptInvocation() )
387 : {
388 8 : Reference< XScriptProviderSupplier > xSPS( m_xScriptInvocation, UNO_QUERY );
389 8 : if ( xSPS.is() )
390 8 : m_xScriptProvider = xSPS->getScriptProvider();
391 : }
392 :
393 : // second, ask the model in our frame
394 8 : if ( !m_xScriptProvider.is() && m_xFrame.is() )
395 : {
396 0 : Reference< XController > xController = m_xFrame->getController();
397 0 : if ( xController .is() )
398 : {
399 0 : Reference< XScriptProviderSupplier > xSPS( xController->getModel(), UNO_QUERY );
400 0 : if ( xSPS.is() )
401 0 : m_xScriptProvider = xSPS->getScriptProvider();
402 0 : }
403 : }
404 :
405 :
406 : // as a fallback, ask the controller
407 8 : if ( !m_xScriptProvider.is() && m_xFrame.is() )
408 : {
409 0 : Reference< XScriptProviderSupplier > xSPS( m_xFrame->getController(), UNO_QUERY );
410 0 : if ( xSPS.is() )
411 0 : m_xScriptProvider = xSPS->getScriptProvider();
412 : }
413 :
414 : // if nothing of this is successful, use the master script provider
415 8 : if ( !m_xScriptProvider.is() )
416 : {
417 : Reference< provider::XScriptProviderFactory > xFac =
418 0 : provider::theMasterScriptProviderFactory::get( m_xContext );
419 :
420 0 : Any aContext;
421 0 : if ( getScriptInvocation() )
422 0 : aContext = makeAny( m_xScriptInvocation );
423 0 : m_xScriptProvider = Reference< provider::XScriptProvider > (
424 0 : xFac->createScriptProvider( aContext ), UNO_QUERY_THROW );
425 : }
426 : }
427 0 : catch ( const RuntimeException & e )
428 : {
429 0 : OUString temp = "ScriptProtocolHandler::createScriptProvider(), ";
430 0 : throw RuntimeException( temp.concat( e.Message ) );
431 : }
432 0 : catch ( const Exception & e )
433 : {
434 0 : OUString temp = "ScriptProtocolHandler::createScriptProvider: ";
435 0 : throw RuntimeException( temp.concat( e.Message ) );
436 : }
437 : }
438 :
439 8848 : ScriptProtocolHandler::ScriptProtocolHandler( const Reference< css::uno::XComponentContext > & xContext )
440 8848 : : m_bInitialised( false ), m_xContext( xContext )
441 : {
442 8848 : }
443 :
444 17656 : ScriptProtocolHandler::~ScriptProtocolHandler()
445 : {
446 17656 : }
447 :
448 : /* XServiceInfo */
449 0 : OUString SAL_CALL ScriptProtocolHandler::getImplementationName( )
450 : throw( RuntimeException, std::exception )
451 : {
452 0 : return impl_getStaticImplementationName();
453 : }
454 :
455 : /* XServiceInfo */
456 0 : sal_Bool SAL_CALL ScriptProtocolHandler::supportsService(const OUString& sServiceName )
457 : throw( RuntimeException, std::exception )
458 : {
459 0 : return cppu::supportsService(this, sServiceName);
460 : }
461 :
462 : /* XServiceInfo */
463 0 : Sequence< OUString > SAL_CALL ScriptProtocolHandler::getSupportedServiceNames()
464 : throw( RuntimeException, std::exception )
465 : {
466 0 : return impl_getStaticSupportedServiceNames();
467 : }
468 :
469 : /* Helper for XServiceInfo */
470 40 : Sequence< OUString > ScriptProtocolHandler::impl_getStaticSupportedServiceNames()
471 : {
472 40 : ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
473 40 : Sequence< OUString > seqServiceNames( 1 );
474 80 : seqServiceNames.getArray() [ 0 ] =
475 40 : OUString::createFromAscii( ::scripting_protocolhandler::MYSERVICENAME );
476 40 : return seqServiceNames ;
477 : }
478 :
479 : /* Helper for XServiceInfo */
480 80 : OUString ScriptProtocolHandler::impl_getStaticImplementationName()
481 : {
482 80 : return OUString::createFromAscii( ::scripting_protocolhandler::MYIMPLNAME );
483 : }
484 :
485 : /* Helper for registry */
486 8848 : Reference< XInterface > SAL_CALL ScriptProtocolHandler::impl_createInstance(
487 : const Reference< css::lang::XMultiServiceFactory >& xServiceManager )
488 : throw( RuntimeException )
489 : {
490 8848 : return Reference< XInterface > ( *new ScriptProtocolHandler( comphelper::getComponentContext(xServiceManager) ) );
491 : }
492 :
493 : /* Factory for registration */
494 40 : Reference< XSingleServiceFactory > ScriptProtocolHandler::impl_createFactory(
495 : const Reference< XMultiServiceFactory >& xServiceManager )
496 : {
497 : Reference< XSingleServiceFactory > xReturn (
498 : cppu::createSingleFactory( xServiceManager,
499 : ScriptProtocolHandler::impl_getStaticImplementationName(),
500 : ScriptProtocolHandler::impl_createInstance,
501 : ScriptProtocolHandler::impl_getStaticSupportedServiceNames() )
502 40 : );
503 40 : return xReturn;
504 : }
505 :
506 : } // namespace scripting_protocolhandler
507 :
508 : extern "C"
509 : {
510 40 : SAL_DLLPUBLIC_EXPORT void* SAL_CALL protocolhandler_component_getFactory( const sal_Char * pImplementationName ,
511 : void * pServiceManager ,
512 : void * pRegistryKey )
513 : {
514 : (void)pRegistryKey;
515 :
516 : // Set default return value for this operation - if it failed.
517 40 : void * pReturn = NULL ;
518 :
519 40 : if (
520 40 : ( pImplementationName != NULL ) &&
521 : ( pServiceManager != NULL )
522 : )
523 : {
524 : // Define variables which are used in following macros.
525 : ::com::sun::star::uno::Reference<
526 40 : ::com::sun::star::lang::XSingleServiceFactory > xFactory ;
527 : ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >
528 : xServiceManager( reinterpret_cast<
529 80 : ::com::sun::star::lang::XMultiServiceFactory* >( pServiceManager ) ) ;
530 :
531 80 : if ( ::scripting_protocolhandler::ScriptProtocolHandler::impl_getStaticImplementationName().equals(
532 80 : OUString::createFromAscii( pImplementationName ) ) )
533 : {
534 40 : xFactory = ::scripting_protocolhandler::ScriptProtocolHandler::impl_createFactory( xServiceManager );
535 : }
536 :
537 : // Factory is valid - service was found.
538 40 : if ( xFactory.is() )
539 : {
540 40 : xFactory->acquire();
541 40 : pReturn = xFactory.get();
542 40 : }
543 : }
544 :
545 : // Return with result of this operation.
546 40 : return pReturn ;
547 : }
548 120 : } // extern "C"
549 :
550 :
551 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|