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