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_desktop.hxx"
26 
27 #include "dispatchwatcher.hxx"
28 #include <rtl/ustring.hxx>
29 #include <tools/string.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/synchronousdispatch.hxx>
32 #include <com/sun/star/util/XCloseable.hpp>
33 #include <com/sun/star/util/CloseVetoException.hpp>
34 #include <com/sun/star/task/XInteractionHandler.hpp>
35 #include <com/sun/star/util/URL.hpp>
36 #include <com/sun/star/frame/XDesktop.hpp>
37 #include <com/sun/star/container/XEnumeration.hpp>
38 #include <com/sun/star/frame/XFramesSupplier.hpp>
39 #include <com/sun/star/frame/XDispatch.hpp>
40 #include <com/sun/star/frame/XComponentLoader.hpp>
41 #include <com/sun/star/beans/PropertyValue.hpp>
42 #include <com/sun/star/view/XPrintable.hpp>
43 #include <com/sun/star/frame/XDispatchProvider.hpp>
44 #include <com/sun/star/util/XURLTransformer.hpp>
45 #include <com/sun/star/document/MacroExecMode.hpp>
46 #include <com/sun/star/document/UpdateDocMode.hpp>
47 
48 #include <tools/urlobj.hxx>
49 #include <comphelper/mediadescriptor.hxx>
50 
51 #include <vector>
52 
53 using namespace ::rtl;
54 using namespace ::osl;
55 using namespace ::com::sun::star::uno;
56 using namespace ::com::sun::star::util;
57 using namespace ::com::sun::star::lang;
58 using namespace ::com::sun::star::frame;
59 using namespace ::com::sun::star::container;
60 using namespace ::com::sun::star::beans;
61 using namespace ::com::sun::star::view;
62 
63 namespace desktop
64 {
65 
66 String GetURL_Impl(
67     const String& rName, boost::optional< rtl::OUString > const & cwdUrl );
68 
69 struct DispatchHolder
70 {
71 	DispatchHolder( const URL& rURL, Reference< XDispatch >& rDispatch ) :
72 		aURL( rURL ), xDispatch( rDispatch ) {}
73 
74 	URL	aURL;
75     rtl::OUString cwdUrl;
76 	Reference< XDispatch > xDispatch;
77 };
78 
79 Mutex* DispatchWatcher::pWatcherMutex = NULL;
80 
81 Mutex& DispatchWatcher::GetMutex()
82 {
83 	if ( !pWatcherMutex )
84 	{
85 		::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
86 		if ( !pWatcherMutex )
87 			pWatcherMutex = new osl::Mutex();
88 	}
89 
90 	return *pWatcherMutex;
91 }
92 
93 // Create or get the dispatch watcher implementation. This implementation must be
94 // a singleton to prevent access to the framework after it wants to terminate.
95 DispatchWatcher* DispatchWatcher::GetDispatchWatcher()
96 {
97     static Reference< XInterface > xDispatchWatcher;
98     static DispatchWatcher*        pDispatchWatcher = NULL;
99 
100     if ( !xDispatchWatcher.is() )
101 	{
102 		::osl::MutexGuard aGuard( GetMutex() );
103 
104 		if ( !xDispatchWatcher.is() )
105         {
106 			pDispatchWatcher = new DispatchWatcher();
107 
108             // We have to hold a reference to ourself forever to prevent our own destruction.
109             xDispatchWatcher = static_cast< cppu::OWeakObject *>( pDispatchWatcher );
110         }
111 	}
112 
113     return pDispatchWatcher;
114 }
115 
116 
117 DispatchWatcher::DispatchWatcher()
118     : m_nRequestCount(0)
119 {
120 }
121 
122 
123 DispatchWatcher::~DispatchWatcher()
124 {
125 }
126 
127 
128 sal_Bool DispatchWatcher::executeDispatchRequests( const DispatchList& aDispatchRequestsList, bool bNoTerminate )
129 {
130     Reference< XComponentLoader > xDesktop( ::comphelper::getProcessServiceFactory()->createInstance(
131 												OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ),
132 											UNO_QUERY );
133 
134 	DispatchList::const_iterator	p;
135 	std::vector< DispatchHolder >	aDispatches;
136 	::rtl::OUString					aAsTemplateArg( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate"));
137 
138 	for ( p = aDispatchRequestsList.begin(); p != aDispatchRequestsList.end(); p++ )
139 	{
140         String					aPrinterName;
141 		const DispatchRequest&	aDispatchRequest = *p;
142 
143         // create parameter array
144         sal_Int32 nCount = 4;
145         if ( aDispatchRequest.aPreselectedFactory.getLength() )
146             nCount++;
147 
148 		// we need more properties for a print/print to request
149 		if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
150 			 aDispatchRequest.aRequestType == REQUEST_PRINTTO  )
151             nCount++;
152 
153         Sequence < PropertyValue > aArgs( nCount );
154 
155         // mark request as user interaction from outside
156         aArgs[0].Name = ::rtl::OUString::createFromAscii("Referer");
157         aArgs[0].Value <<= ::rtl::OUString::createFromAscii("private:OpenEvent");
158 
159         if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
160 			 aDispatchRequest.aRequestType == REQUEST_PRINTTO )
161         {
162             aArgs[1].Name = ::rtl::OUString::createFromAscii("ReadOnly");
163             aArgs[2].Name = ::rtl::OUString::createFromAscii("OpenNewView");
164             aArgs[3].Name = ::rtl::OUString::createFromAscii("Hidden");
165             aArgs[4].Name = ::rtl::OUString::createFromAscii("Silent");
166         }
167         else
168         {
169             Reference < com::sun::star::task::XInteractionHandler > xInteraction(
170                 ::comphelper::getProcessServiceFactory()->createInstance( OUString::createFromAscii("com.sun.star.task.InteractionHandler") ),
171                 com::sun::star::uno::UNO_QUERY );
172 
173             aArgs[1].Name = OUString::createFromAscii( "InteractionHandler" );
174             aArgs[1].Value <<= xInteraction;
175 
176 			sal_Int16 nMacroExecMode = ::com::sun::star::document::MacroExecMode::USE_CONFIG;
177             aArgs[2].Name = OUString::createFromAscii( "MacroExecutionMode" );
178             aArgs[2].Value <<= nMacroExecMode;
179 
180 			sal_Int16 nUpdateDoc = ::com::sun::star::document::UpdateDocMode::ACCORDING_TO_CONFIG;
181             aArgs[3].Name = OUString::createFromAscii( "UpdateDocMode" );
182             aArgs[3].Value <<= nUpdateDoc;
183         }
184 
185         if ( aDispatchRequest.aPreselectedFactory.getLength() )
186         {
187             aArgs[nCount-1].Name = ::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE();
188             aArgs[nCount-1].Value <<= aDispatchRequest.aPreselectedFactory;
189         }
190 
191         String aName( GetURL_Impl( aDispatchRequest.aURL, aDispatchRequest.aCwdUrl ) );
192         ::rtl::OUString aTarget( RTL_CONSTASCII_USTRINGPARAM("_default") );
193 
194         if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
195 			 aDispatchRequest.aRequestType == REQUEST_PRINTTO )
196         {
197             // documents opened for printing are opened readonly because they must be opened as a new document and this
198             // document could be open already
199             aArgs[1].Value <<= sal_True;
200 
201             // always open a new document for printing, because it must be disposed afterwards
202             aArgs[2].Value <<= sal_True;
203 
204             // printing is done in a hidden view
205             aArgs[3].Value <<= sal_True;
206 
207             // load document for printing without user interaction
208             aArgs[4].Value <<= sal_True;
209 
210             // hidden documents should never be put into open tasks
211             aTarget = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("_blank") );
212         }
213 
214         // load the document ... if they are loadable!
215         // Otherwise try to dispatch it ...
216         Reference < XPrintable > xDoc;
217         if(
218             ( aName.CompareToAscii( ".uno"  , 4 ) == COMPARE_EQUAL )  ||
219             ( aName.CompareToAscii( "slot:" , 5 ) == COMPARE_EQUAL )  ||
220             ( aName.CompareToAscii( "macro:", 6 ) == COMPARE_EQUAL )  ||
221             ( aName.CompareToAscii("vnd.sun.star.script", 19) == COMPARE_EQUAL)
222           )
223         {
224             // Attention: URL must be parsed full. Otherwise some detections on it will fail!
225             // It doesnt matter, if parser isn't available. Because; We try loading of URL then ...
226             URL             aURL ;
227             aURL.Complete = aName;
228 
229             Reference < XDispatch >         xDispatcher ;
230             Reference < XDispatchProvider > xProvider   ( xDesktop, UNO_QUERY );
231             Reference < XURLTransformer >   xParser     ( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer")) ), ::com::sun::star::uno::UNO_QUERY );
232 
233             if( xParser.is() == sal_True )
234                 xParser->parseStrict( aURL );
235 
236             if( xProvider.is() == sal_True )
237                 xDispatcher = xProvider->queryDispatch( aURL, ::rtl::OUString(), 0 );
238 
239             if( xDispatcher.is() == sal_True )
240 			{
241 				{
242 					::osl::ClearableMutexGuard aGuard( GetMutex() );
243 					// Remember request so we can find it in statusChanged!
244 					m_aRequestContainer.insert( DispatchWatcherHashMap::value_type( aURL.Complete, (sal_Int32)1 ) );
245                     m_nRequestCount++;
246 				}
247 
248 				// Use local vector to store dispatcher because we have to fill our request container before
249 				// we can dispatch. Otherwise it would be possible that statusChanged is called before we dispatched all requests!!
250 				aDispatches.push_back( DispatchHolder( aURL, xDispatcher ));
251 			}
252         }
253         else if ( ( aName.CompareToAscii( "service:"  , 8 ) == COMPARE_EQUAL ) )
254         {
255             // TODO: the dispatch has to be done for loadComponentFromURL as well. Please ask AS for more details.
256             URL             aURL ;
257             aURL.Complete = aName;
258 
259             Reference < XDispatch >         xDispatcher ;
260             Reference < XDispatchProvider > xProvider   ( xDesktop, UNO_QUERY );
261             Reference < XURLTransformer >   xParser     ( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer")) ), ::com::sun::star::uno::UNO_QUERY );
262 
263             if( xParser.is() == sal_True )
264                 xParser->parseStrict( aURL );
265 
266             if( xProvider.is() == sal_True )
267                 xDispatcher = xProvider->queryDispatch( aURL, ::rtl::OUString(), 0 );
268 
269             if( xDispatcher.is() == sal_True )
270             {
271 			    try
272 			    {
273 				    // We have to be listener to catch errors during dispatching URLs.
274 				    // Otherwise it would be possible to have an office running without an open
275 				    // window!!
276                     Sequence < PropertyValue > aArgs2(1);
277                     aArgs2[0].Name    = ::rtl::OUString::createFromAscii("SynchronMode");
278                     aArgs2[0].Value <<= sal_True;
279 				    Reference < XNotifyingDispatch > xDisp( xDispatcher, UNO_QUERY );
280 				    if ( xDisp.is() )
281 					    xDisp->dispatchWithNotification( aURL, aArgs2, DispatchWatcher::GetDispatchWatcher() );
282 				    else
283 					    xDispatcher->dispatch( aURL, aArgs2 );
284 			    }
285 			    catch ( ::com::sun::star::uno::Exception& )
286 			    {
287 				    OUString aMsg = OUString::createFromAscii(
288 					    "Desktop::OpenDefault() IllegalArgumentException while calling XNotifyingDispatch: ");
289 				    OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr());
290 			    }
291             }
292         }
293         else
294         {
295             INetURLObject aObj( aName );
296             if ( aObj.GetProtocol() == INET_PROT_PRIVATE )
297                 aTarget = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("_default") );
298 
299 			// Set "AsTemplate" argument according to request type
300             if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW ||
301 				 aDispatchRequest.aRequestType == REQUEST_FORCEOPEN		)
302 			{
303 				sal_Int32 nIndex = aArgs.getLength();
304 				aArgs.realloc( nIndex+1 );
305 				aArgs[nIndex].Name = aAsTemplateArg;
306 				if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW )
307 					aArgs[nIndex].Value <<= sal_True;
308 				else
309 					aArgs[nIndex].Value <<= sal_False;
310 			}
311 
312 			// if we are called in viewmode, open document read-only
313 			// #95425#
314 			if(aDispatchRequest.aRequestType == REQUEST_VIEW) {
315 				sal_Int32 nIndex = aArgs.getLength();
316 				aArgs.realloc(nIndex+1);
317 				aArgs[nIndex].Name = OUString::createFromAscii("ReadOnly");
318 				aArgs[nIndex].Value <<= sal_True;
319 			}
320 
321             // if we are called with -start set Start in mediadescriptor
322 			if(aDispatchRequest.aRequestType == REQUEST_START) {
323 				sal_Int32 nIndex = aArgs.getLength();
324 				aArgs.realloc(nIndex+1);
325 				aArgs[nIndex].Name = OUString::createFromAscii("StartPresentation");
326 				aArgs[nIndex].Value <<= sal_True;
327 			}
328 
329 			// This is a synchron loading of a component so we don't have to deal with our statusChanged listener mechanism.
330 
331             try
332             {
333                 xDoc = Reference < XPrintable >( ::comphelper::SynchronousDispatch::dispatch( xDesktop, aName, aTarget, 0, aArgs ), UNO_QUERY );
334                 //xDoc = Reference < XPrintable >( xDesktop->loadComponentFromURL( aName, aTarget, 0, aArgs ), UNO_QUERY );
335             }
336             catch ( ::com::sun::star::lang::IllegalArgumentException& iae)
337             {
338                 OUString aMsg = OUString::createFromAscii(
339                     "Dispatchwatcher IllegalArgumentException while calling loadComponentFromURL: ")
340                     + iae.Message;
341                 OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr());
342             }
343             catch (com::sun::star::io::IOException& ioe)
344             {
345                 OUString aMsg = OUString::createFromAscii(
346                     "Dispatchwatcher IOException while calling loadComponentFromURL: ")
347                     + ioe.Message;
348                 OSL_ENSURE( sal_False, OUStringToOString(aMsg, RTL_TEXTENCODING_ASCII_US).getStr());
349             }
350 			if ( aDispatchRequest.aRequestType == REQUEST_OPEN ||
351 				 aDispatchRequest.aRequestType == REQUEST_VIEW ||
352                  aDispatchRequest.aRequestType == REQUEST_START ||
353 				 aDispatchRequest.aRequestType == REQUEST_FORCEOPEN ||
354 				 aDispatchRequest.aRequestType == REQUEST_FORCENEW		)
355 			{
356 				// request is completed
357 				OfficeIPCThread::RequestsCompleted( 1 );
358 			}
359 			else if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
360 					  aDispatchRequest.aRequestType == REQUEST_PRINTTO )
361 			{
362 				if ( xDoc.is() )
363 				{
364 					if ( aDispatchRequest.aRequestType == REQUEST_PRINTTO )
365 					{
366 						// create the printer
367 						Sequence < PropertyValue > aPrinterArgs( 1 );
368 						aPrinterArgs[0].Name = ::rtl::OUString::createFromAscii("Name");
369 						aPrinterArgs[0].Value <<= ::rtl::OUString( aDispatchRequest.aPrinterName );
370 						xDoc->setPrinter( aPrinterArgs );
371 					}
372 
373 					// print ( also without user interaction )
374 					Sequence < PropertyValue > aPrinterArgs( 1 );
375 					aPrinterArgs[0].Name = ::rtl::OUString::createFromAscii("Wait");
376 					aPrinterArgs[0].Value <<= ( sal_Bool ) sal_True;
377 					xDoc->print( aPrinterArgs );
378 				}
379 				else
380 				{
381 					// place error message here ...
382 				}
383 
384 				// remove the document
385 				try
386 				{
387 					Reference < XCloseable > xClose( xDoc, UNO_QUERY );
388 					if ( xClose.is() )
389 						xClose->close( sal_True );
390 					else
391 					{
392 						Reference < XComponent > xComp( xDoc, UNO_QUERY );
393 						if ( xComp.is() )
394 							xComp->dispose();
395 					}
396 				}
397 				catch ( com::sun::star::util::CloseVetoException& )
398 				{
399 				}
400 
401 				// request is completed
402 				OfficeIPCThread::RequestsCompleted( 1 );
403 			}
404 		}
405 	}
406 
407     if ( aDispatches.size() > 0 )
408 	{
409 		// Execute all asynchronous dispatches now after we placed them into our request container!
410 		Sequence < PropertyValue > aArgs( 2 );
411 		aArgs[0].Name = ::rtl::OUString::createFromAscii("Referer");
412         aArgs[0].Value <<= ::rtl::OUString::createFromAscii("private:OpenEvent");
413 		aArgs[1].Name = ::rtl::OUString::createFromAscii("SynchronMode");
414         aArgs[1].Value <<= sal_True;
415 
416 		for ( sal_uInt32 n = 0; n < aDispatches.size(); n++ )
417 		{
418 			Reference< XDispatch > xDispatch = aDispatches[n].xDispatch;
419             Reference < XNotifyingDispatch > xDisp( xDispatch, UNO_QUERY );
420             if ( xDisp.is() )
421                 xDisp->dispatchWithNotification( aDispatches[n].aURL, aArgs, this );
422             else
423             {
424                 ::osl::ClearableMutexGuard aGuard( GetMutex() );
425                 m_nRequestCount--;
426                 aGuard.clear();
427                 xDispatch->dispatch( aDispatches[n].aURL, aArgs );
428             }
429 		}
430 	}
431 
432 	::osl::ClearableMutexGuard aGuard( GetMutex() );
433     bool bEmpty = (m_nRequestCount == 0);
434     aGuard.clear();
435 
436 	// No more asynchronous requests?
437 	// The requests are removed from the request container after they called back to this
438 	// implementation via statusChanged!!
439     if ( bEmpty && !bNoTerminate /*m_aRequestContainer.empty()*/ )
440 	{
441 		// We have to check if we have an open task otherwise we have to shutdown the office.
442         Reference< XFramesSupplier > xTasksSupplier( xDesktop, UNO_QUERY );
443         aGuard.clear();
444 
445         Reference< XElementAccess > xList( xTasksSupplier->getFrames(), UNO_QUERY );
446 
447         if ( !xList->hasElements() )
448 		{
449 			// We don't have any task open so we have to shutdown ourself!!
450 			Reference< XDesktop > xDesktop2( xTasksSupplier, UNO_QUERY );
451 			if ( xDesktop2.is() )
452 				return xDesktop2->terminate();
453 		}
454 	}
455 
456 	return sal_False;
457 }
458 
459 
460 void SAL_CALL DispatchWatcher::disposing( const ::com::sun::star::lang::EventObject& )
461 throw(::com::sun::star::uno::RuntimeException)
462 {
463 }
464 
465 
466 void SAL_CALL DispatchWatcher::dispatchFinished( const DispatchResultEvent& ) throw( RuntimeException )
467 {
468 	osl::ClearableMutexGuard aGuard( GetMutex() );
469     sal_Int16 nCount = --m_nRequestCount;
470     aGuard.clear();
471     OfficeIPCThread::RequestsCompleted( 1 );
472 /*
473 	// Find request in our hash map and remove it as a pending request
474     DispatchWatcherHashMap::iterator pDispatchEntry = m_aRequestContainer.find( rEvent.FeatureURL.Complete ) ;
475     if ( pDispatchEntry != m_aRequestContainer.end() )
476 	{
477         m_aRequestContainer.erase( pDispatchEntry );
478         aGuard.clear();
479 		OfficeIPCThread::RequestsCompleted( 1 );
480 	}
481 	else
482 		aGuard.clear();
483 */
484     if ( !nCount && !OfficeIPCThread::AreRequestsPending() )
485 	{
486 		// We have to check if we have an open task otherwise we have to shutdown the office.
487         Reference< XFramesSupplier > xTasksSupplier( ::comphelper::getProcessServiceFactory()->createInstance(
488 													OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ),
489 												UNO_QUERY );
490         Reference< XElementAccess > xList( xTasksSupplier->getFrames(), UNO_QUERY );
491 
492         if ( !xList->hasElements() )
493 		{
494 			// We don't have any task open so we have to shutdown ourself!!
495 			Reference< XDesktop > xDesktop( xTasksSupplier, UNO_QUERY );
496 			if ( xDesktop.is() )
497 				xDesktop->terminate();
498 		}
499 	}
500 }
501 
502 }
503 
504 
505 
506 
507 
508 
509 
510 
511