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