update credits
[LibreOffice.git] / desktop / source / app / officeipcthread.cxx
blobdb8a4079583d8862540e2cf01f4d2b563d152f73
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_features.h>
22 #include "app.hxx"
23 #include "officeipcthread.hxx"
24 #include "cmdlineargs.hxx"
25 #include "dispatchwatcher.hxx"
26 #include <memory>
27 #include <stdio.h>
28 #include <osl/process.h>
29 #include <unotools/bootstrap.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/help.hxx>
32 #include <unotools/configmgr.hxx>
33 #include <osl/thread.hxx>
34 #include <rtl/digest.h>
35 #include <rtl/ustrbuf.hxx>
36 #include <rtl/instance.hxx>
37 #include <osl/conditn.hxx>
38 #include <unotools/moduleoptions.hxx>
39 #include <rtl/bootstrap.hxx>
40 #include <rtl/strbuf.hxx>
41 #include <comphelper/processfactory.hxx>
42 #include <cppuhelper/supportsservice.hxx>
43 #include <osl/file.hxx>
44 #include <rtl/process.h>
45 #include "tools/getprocessworkingdir.hxx"
47 using namespace desktop;
48 using namespace ::com::sun::star::uno;
49 using namespace ::com::sun::star::lang;
50 using namespace ::com::sun::star::frame;
53 const char *OfficeIPCThread::sc_aShowSequence = "-tofront";
54 const int OfficeIPCThread::sc_nShSeqLength = 5;
56 namespace {
58 static char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments";
60 #if HAVE_FEATURE_DESKTOP
62 static char const SEND_ARGUMENTS[] = "InternalIPC::SendArguments";
63 static char const PROCESSING_DONE[] = "InternalIPC::ProcessingDone";
65 // Receives packets from the pipe until a packet ends in a NUL character (that
66 // will not be included in the returned string) or it cannot read anything (due
67 // to error or closed pipe, in which case an empty string will be returned to
68 // signal failure):
69 OString readStringFromPipe(osl::StreamPipe & pipe) {
70 for (OStringBuffer str;;) {
71 char buf[1024];
72 sal_Int32 n = pipe.recv(buf, SAL_N_ELEMENTS(buf));
73 if (n <= 0) {
74 return "";
76 bool end = false;
77 if (buf[n - 1] == '\0') {
78 end = true;
79 --n;
81 str.append(buf, n);
82 //TODO: how does OStringBuffer.append handle overflow?
83 if (end) {
84 return str.makeStringAndClear();
89 #endif
93 // Type of pipe we use
94 enum PipeMode
96 PIPEMODE_DONTKNOW,
97 PIPEMODE_CREATED,
98 PIPEMODE_CONNECTED
101 namespace desktop
104 namespace {
106 #if HAVE_FEATURE_DESKTOP
108 class Parser: public CommandLineArgs::Supplier {
109 public:
110 explicit Parser(OString const & input): m_input(input) {
111 if (!m_input.match(ARGUMENT_PREFIX) ||
112 m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
114 throw CommandLineArgs::Supplier::Exception();
116 m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
117 switch (m_input[m_index++]) {
118 case '0':
119 break;
120 case '1':
122 OUString url;
123 if (!next(&url, false)) {
124 throw CommandLineArgs::Supplier::Exception();
126 m_cwdUrl.reset(url);
127 break;
129 case '2':
131 OUString path;
132 if (!next(&path, false)) {
133 throw CommandLineArgs::Supplier::Exception();
135 OUString url;
136 if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
137 osl::FileBase::E_None)
139 m_cwdUrl.reset(url);
141 break;
143 default:
144 throw CommandLineArgs::Supplier::Exception();
148 virtual ~Parser() {}
150 virtual boost::optional< OUString > getCwdUrl() { return m_cwdUrl; }
152 virtual bool next(OUString * argument) { return next(argument, true); }
154 private:
155 virtual bool next(OUString * argument, bool prefix) {
156 OSL_ASSERT(argument != NULL);
157 if (m_index < m_input.getLength()) {
158 if (prefix) {
159 if (m_input[m_index] != ',') {
160 throw CommandLineArgs::Supplier::Exception();
162 ++m_index;
164 OStringBuffer b;
165 while (m_index < m_input.getLength()) {
166 char c = m_input[m_index];
167 if (c == ',') {
168 break;
170 ++m_index;
171 if (c == '\\') {
172 if (m_index < m_input.getLength()) {
173 c = m_input[m_index++];
174 switch (c) {
175 case '0':
176 c = '\0';
177 break;
178 case ',':
179 case '\\':
180 break;
181 default:
182 throw CommandLineArgs::Supplier::Exception();
184 } else {
185 throw CommandLineArgs::Supplier::Exception();
188 b.append(c);
190 OString b2(b.makeStringAndClear());
191 if (!rtl_convertStringToUString(
192 &argument->pData, b2.getStr(), b2.getLength(),
193 RTL_TEXTENCODING_UTF8,
194 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
195 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
196 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
198 throw CommandLineArgs::Supplier::Exception();
200 return true;
201 } else {
202 return false;
206 boost::optional< OUString > m_cwdUrl;
207 OString m_input;
208 sal_Int32 m_index;
211 bool addArgument(OStringBuffer &rArguments, char prefix,
212 const OUString &rArgument)
214 OString utf8;
215 if (!rArgument.convertToString(
216 &utf8, RTL_TEXTENCODING_UTF8,
217 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
218 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
220 return false;
222 rArguments.append(prefix);
223 for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
224 char c = utf8[i];
225 switch (c) {
226 case '\0':
227 rArguments.append("\\0");
228 break;
229 case ',':
230 rArguments.append("\\,");
231 break;
232 case '\\':
233 rArguments.append("\\\\");
234 break;
235 default:
236 rArguments.append(c);
237 break;
240 return true;
243 #endif
247 rtl::Reference< OfficeIPCThread > OfficeIPCThread::pGlobalOfficeIPCThread;
248 namespace { struct Security : public rtl::Static<osl::Security, Security> {}; }
250 // Turns a string in aMsg such as file:///home/foo/.libreoffice/3
251 // Into a hex string of well known length ff132a86...
252 String CreateMD5FromString( const OUString& aMsg )
254 #if (OSL_DEBUG_LEVEL > 2)
255 fprintf( stderr, "create md5 from '%s'\n",
256 OUStringToOString (aMsg, RTL_TEXTENCODING_UTF8).getStr() );
257 #endif
259 rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
260 if ( handle )
262 const sal_uInt8* pData = (const sal_uInt8*)aMsg.getStr();
263 sal_uInt32 nSize = ( aMsg.getLength() * sizeof( sal_Unicode ));
264 sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
265 sal_uInt8* pMD5KeyBuffer = new sal_uInt8[ nMD5KeyLen ];
267 rtl_digest_init( handle, pData, nSize );
268 rtl_digest_update( handle, pData, nSize );
269 rtl_digest_get( handle, pMD5KeyBuffer, nMD5KeyLen );
270 rtl_digest_destroy( handle );
272 // Create hex-value string from the MD5 value to keep the string size minimal
273 OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
274 for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
275 aBuffer.append( (sal_Int32)pMD5KeyBuffer[i], 16 );
277 delete [] pMD5KeyBuffer;
278 return aBuffer.makeStringAndClear();
281 return String();
284 class ProcessEventsClass_Impl
286 public:
287 DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void* pEvent );
288 DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void* pEvent );
291 IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl, CallEvent, void*, pEvent )
293 // Application events are processed by the Desktop::HandleAppEvent implementation.
294 Desktop::HandleAppEvent( *((ApplicationEvent*)pEvent) );
295 delete (ApplicationEvent*)pEvent;
296 return 0;
299 IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent )
301 // Documents requests are processed by the OfficeIPCThread implementation
302 ProcessDocumentsRequest* pDocsRequest = (ProcessDocumentsRequest*)pEvent;
304 if ( pDocsRequest )
306 OfficeIPCThread::ExecuteCmdLineRequests( *pDocsRequest );
307 delete pDocsRequest;
309 return 0;
312 void ImplPostForeignAppEvent( ApplicationEvent* pEvent )
314 Application::PostUserEvent( STATIC_LINK( NULL, ProcessEventsClass_Impl, CallEvent ), pEvent );
317 void ImplPostProcessDocumentsEvent( ProcessDocumentsRequest* pEvent )
319 Application::PostUserEvent( STATIC_LINK( NULL, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent );
322 oslSignalAction SAL_CALL SalMainPipeExchangeSignal_impl(void* /*pData*/, oslSignalInfo* pInfo)
324 if( pInfo->Signal == osl_Signal_Terminate )
325 OfficeIPCThread::DisableOfficeIPCThread(false);
326 return osl_Signal_ActCallNextHdl;
329 // ----------------------------------------------------------------------------
331 // The OfficeIPCThreadController implementation is a bookkeeper for all pending requests
332 // that were created by the OfficeIPCThread. The requests are waiting to be processed by
333 // our framework loadComponentFromURL function (e.g. open/print request).
334 // During shutdown the framework is asking OfficeIPCThreadController about pending requests.
335 // If there are pending requests framework has to stop the shutdown process. It is waiting
336 // for these requests because framework is not able to handle shutdown and open a document
337 // concurrently.
340 // XServiceInfo
341 OUString SAL_CALL OfficeIPCThreadController::getImplementationName()
342 throw ( RuntimeException )
344 return OUString( "com.sun.star.comp.OfficeIPCThreadController" );
347 sal_Bool OfficeIPCThreadController::supportsService(
348 OUString const & ServiceName) throw (css::uno::RuntimeException)
350 return cppu::supportsService(this, ServiceName);
353 Sequence< OUString > SAL_CALL OfficeIPCThreadController::getSupportedServiceNames()
354 throw ( RuntimeException )
356 Sequence< OUString > aSeq( 0 );
357 return aSeq;
360 // XEventListener
361 void SAL_CALL OfficeIPCThreadController::disposing( const EventObject& )
362 throw( RuntimeException )
366 // XTerminateListener
367 void SAL_CALL OfficeIPCThreadController::queryTermination( const EventObject& )
368 throw( TerminationVetoException, RuntimeException )
370 // Desktop ask about pending request through our office ipc pipe. We have to
371 // be sure that no pending request is waiting because framework is not able to
372 // handle shutdown and open a document concurrently.
374 if ( OfficeIPCThread::AreRequestsPending() )
375 throw TerminationVetoException();
376 else
377 OfficeIPCThread::SetDowning();
380 void SAL_CALL OfficeIPCThreadController::notifyTermination( const EventObject& )
381 throw( RuntimeException )
385 namespace
387 class theOfficeIPCThreadMutex
388 : public rtl::Static<osl::Mutex, theOfficeIPCThreadMutex> {};
391 ::osl::Mutex& OfficeIPCThread::GetMutex()
393 return theOfficeIPCThreadMutex::get();
396 void OfficeIPCThread::SetDowning()
398 // We have the order to block all incoming requests. Framework
399 // wants to shutdown and we have to make sure that no loading/printing
400 // requests are executed anymore.
401 ::osl::MutexGuard aGuard( GetMutex() );
403 if ( pGlobalOfficeIPCThread.is() )
404 pGlobalOfficeIPCThread->mbDowning = true;
407 static bool s_bInEnableRequests = false;
409 void OfficeIPCThread::EnableRequests( bool i_bEnable )
411 // switch between just queueing the requests and executing them
412 ::osl::MutexGuard aGuard( GetMutex() );
414 if ( pGlobalOfficeIPCThread.is() )
416 s_bInEnableRequests = true;
417 pGlobalOfficeIPCThread->mbRequestsEnabled = i_bEnable;
418 if( i_bEnable )
420 // hit the compiler over the head
421 ProcessDocumentsRequest aEmptyReq = ProcessDocumentsRequest( boost::optional< OUString >() );
422 // trigger already queued requests
423 OfficeIPCThread::ExecuteCmdLineRequests( aEmptyReq );
425 s_bInEnableRequests = false;
429 sal_Bool OfficeIPCThread::AreRequestsPending()
431 // Give info about pending requests
432 ::osl::MutexGuard aGuard( GetMutex() );
433 if ( pGlobalOfficeIPCThread.is() )
434 return ( pGlobalOfficeIPCThread->mnPendingRequests > 0 );
435 else
436 return sal_False;
439 void OfficeIPCThread::RequestsCompleted( int nCount )
441 // Remove nCount pending requests from our internal counter
442 ::osl::MutexGuard aGuard( GetMutex() );
443 if ( pGlobalOfficeIPCThread.is() )
445 if ( pGlobalOfficeIPCThread->mnPendingRequests > 0 )
446 pGlobalOfficeIPCThread->mnPendingRequests -= nCount;
450 OfficeIPCThread::Status OfficeIPCThread::EnableOfficeIPCThread()
452 #if HAVE_FEATURE_DESKTOP
453 ::osl::MutexGuard aGuard( GetMutex() );
455 if( pGlobalOfficeIPCThread.is() )
456 return IPC_STATUS_OK;
458 OUString aUserInstallPath;
459 OUString aDummy;
461 rtl::Reference< OfficeIPCThread > pThread(new OfficeIPCThread);
463 // The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
464 // this information from a unotools implementation.
465 ::utl::Bootstrap::PathStatus aLocateResult = ::utl::Bootstrap::locateUserInstallation( aUserInstallPath );
466 if ( aLocateResult == ::utl::Bootstrap::PATH_EXISTS || aLocateResult == ::utl::Bootstrap::PATH_VALID)
467 aDummy = aUserInstallPath;
468 else
470 return IPC_STATUS_BOOTSTRAP_ERROR;
473 // Try to determine if we are the first office or not! This should prevent multiple
474 // access to the user directory !
475 // First we try to create our pipe if this fails we try to connect. We have to do this
476 // in a loop because the other office can crash or shutdown between createPipe
477 // and connectPipe!!
479 OUString aIniName;
481 osl_getExecutableFile( &aIniName.pData );
483 sal_uInt32 lastIndex = aIniName.lastIndexOf('/');
484 if ( lastIndex > 0 )
486 aIniName = aIniName.copy( 0, lastIndex+1 );
487 aIniName += "perftune";
488 #if defined(WNT)
489 aIniName += ".ini";
490 #else
491 aIniName += "rc";
492 #endif
495 ::rtl::Bootstrap aPerfTuneIniFile( aIniName );
497 OUString aDefault( "0" );
498 OUString aPreloadData;
500 aPerfTuneIniFile.getFrom( OUString( "FastPipeCommunication" ), aPreloadData, aDefault );
503 OUString aUserInstallPathHashCode;
505 if ( aPreloadData == "1" )
507 sal_Char szBuffer[32];
508 sprintf( szBuffer, "%d", SUPD );
509 aUserInstallPathHashCode = OUString( szBuffer, strlen(szBuffer), osl_getThreadTextEncoding() );
511 else
512 aUserInstallPathHashCode = CreateMD5FromString( aDummy );
515 // Check result to create a hash code from the user install path
516 if ( aUserInstallPathHashCode.isEmpty() )
517 return IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
519 OUString aPipeIdent( "SingleOfficeIPC_" + aUserInstallPathHashCode );
521 PipeMode nPipeMode = PIPEMODE_DONTKNOW;
524 osl::Security &rSecurity = Security::get();
525 // Try to create pipe
526 if ( pThread->maPipe.create( aPipeIdent.getStr(), osl_Pipe_CREATE, rSecurity ))
528 // Pipe created
529 nPipeMode = PIPEMODE_CREATED;
531 else if( pThread->maPipe.create( aPipeIdent.getStr(), osl_Pipe_OPEN, rSecurity )) // Creation not successful, now we try to connect
533 osl::StreamPipe aStreamPipe(pThread->maPipe.getHandle());
534 if (readStringFromPipe(aStreamPipe) == SEND_ARGUMENTS)
536 // Pipe connected to first office
537 nPipeMode = PIPEMODE_CONNECTED;
539 else
541 // Pipe connection failed (other office exited or crashed)
542 TimeValue tval;
543 tval.Seconds = 0;
544 tval.Nanosec = 500000000;
545 salhelper::Thread::wait( tval );
548 else
550 oslPipeError eReason = pThread->maPipe.getError();
551 if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_E_invalidError))
552 return IPC_STATUS_PIPE_ERROR;
554 // Wait for second office to be ready
555 TimeValue aTimeValue;
556 aTimeValue.Seconds = 0;
557 aTimeValue.Nanosec = 10000000; // 10ms
558 salhelper::Thread::wait( aTimeValue );
561 } while ( nPipeMode == PIPEMODE_DONTKNOW );
563 if ( nPipeMode == PIPEMODE_CREATED )
565 // Seems we are the one and only, so start listening thread
566 pGlobalOfficeIPCThread = pThread;
567 pThread->launch();
569 else
571 // Seems another office is running. Pipe arguments to it and self terminate
572 osl::StreamPipe aStreamPipe(pThread->maPipe.getHandle());
574 OStringBuffer aArguments(ARGUMENT_PREFIX);
575 OUString cwdUrl;
576 if (!(tools::getProcessWorkingDir(cwdUrl) &&
577 addArgument(aArguments, '1', cwdUrl)))
579 aArguments.append('0');
581 sal_uInt32 nCount = rtl_getAppCommandArgCount();
582 for( sal_uInt32 i=0; i < nCount; i++ )
584 rtl_getAppCommandArg( i, &aDummy.pData );
585 if (!addArgument(aArguments, ',', aDummy)) {
586 return IPC_STATUS_BOOTSTRAP_ERROR;
589 aArguments.append('\0');
590 // finally, write the string onto the pipe
591 sal_Int32 n = aStreamPipe.write(
592 aArguments.getStr(), aArguments.getLength());
593 if (n != aArguments.getLength()) {
594 SAL_INFO("desktop", "short write: " << n);
595 return IPC_STATUS_BOOTSTRAP_ERROR;
598 if (readStringFromPipe(aStreamPipe) != PROCESSING_DONE)
600 // something went wrong
601 return IPC_STATUS_BOOTSTRAP_ERROR;
604 return IPC_STATUS_2ND_OFFICE;
606 #else
607 pGlobalOfficeIPCThread = rtl::Reference< OfficeIPCThread >(new OfficeIPCThread);
608 #endif
609 return IPC_STATUS_OK;
612 void OfficeIPCThread::DisableOfficeIPCThread(bool join)
614 #if HAVE_FEATURE_DESKTOP
615 osl::ClearableMutexGuard aMutex( GetMutex() );
617 if( pGlobalOfficeIPCThread.is() )
619 rtl::Reference< OfficeIPCThread > pOfficeIPCThread(
620 pGlobalOfficeIPCThread);
621 pGlobalOfficeIPCThread.clear();
623 pOfficeIPCThread->mbDowning = true;
624 pOfficeIPCThread->maPipe.close();
626 // release mutex to avoid deadlocks
627 aMutex.clear();
629 OfficeIPCThread::SetReady(pOfficeIPCThread);
631 // exit gracefully and join
632 if (join)
634 pOfficeIPCThread->join();
637 #else
638 (void) join;
639 #endif
642 OfficeIPCThread::OfficeIPCThread() :
643 Thread( "OfficeIPCThread" ),
644 mbDowning( false ),
645 mbRequestsEnabled( false ),
646 mnPendingRequests( 0 ),
647 mpDispatchWatcher( 0 )
651 OfficeIPCThread::~OfficeIPCThread()
653 ::osl::ClearableMutexGuard aGuard( GetMutex() );
655 if ( mpDispatchWatcher )
656 mpDispatchWatcher->release();
657 maPipe.close();
658 pGlobalOfficeIPCThread.clear();
661 void OfficeIPCThread::SetReady(
662 rtl::Reference< OfficeIPCThread > const & pThread)
664 rtl::Reference< OfficeIPCThread > const & t(
665 pThread.is() ? pThread : pGlobalOfficeIPCThread);
666 if (t.is())
668 t->cReady.set();
672 void OfficeIPCThread::execute()
674 #if HAVE_FEATURE_DESKTOP
677 osl::StreamPipe aStreamPipe;
678 oslPipeError nError = maPipe.accept( aStreamPipe );
681 if( nError == osl_Pipe_E_None )
683 // if we receive a request while the office is displaying some dialog or error during
684 // bootstrap, that dialogs event loop might get events that are dispatched by this thread
685 // we have to wait for cReady to be set by the real main loop.
686 // only reqests that dont dispatch events may be processed before cReady is set.
687 cReady.wait();
689 // we might have decided to shutdown while we were sleeping
690 if (!pGlobalOfficeIPCThread.is()) return;
692 // only lock the mutex when processing starts, othewise we deadlock when the office goes
693 // down during wait
694 osl::ClearableMutexGuard aGuard( GetMutex() );
696 if ( mbDowning )
698 break;
701 // notify client we're ready to process its args:
702 sal_Int32 n = aStreamPipe.write(
703 SEND_ARGUMENTS, SAL_N_ELEMENTS(SEND_ARGUMENTS));
704 // incl. terminating NUL
705 if (n != SAL_N_ELEMENTS(SEND_ARGUMENTS)) {
706 SAL_WARN("desktop", "short write: " << n);
707 continue;
710 OString aArguments = readStringFromPipe(aStreamPipe);
712 // Is this a lookup message from another application? if so, ignore
713 if (aArguments.isEmpty())
714 continue;
716 std::auto_ptr< CommandLineArgs > aCmdLineArgs;
719 Parser p(aArguments);
720 aCmdLineArgs.reset( new CommandLineArgs( p ) );
722 catch ( const CommandLineArgs::Supplier::Exception & )
724 #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
725 fprintf( stderr, "Error in received command line arguments\n" );
726 #endif
727 continue;
730 sal_Bool bDocRequestSent = sal_False;
732 OUString aUnknown( aCmdLineArgs->GetUnknown() );
733 if ( !aUnknown.isEmpty() || aCmdLineArgs->IsHelp() )
735 ApplicationEvent* pAppEvent =
736 new ApplicationEvent(ApplicationEvent::TYPE_HELP, aUnknown);
737 ImplPostForeignAppEvent( pAppEvent );
739 else if ( aCmdLineArgs->IsVersion() )
741 ApplicationEvent* pAppEvent =
742 new ApplicationEvent(ApplicationEvent::TYPE_VERSION);
743 ImplPostForeignAppEvent( pAppEvent );
745 else
747 const CommandLineArgs &rCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
749 if ( aCmdLineArgs->IsQuickstart() )
751 // we have to use application event, because we have to start quickstart service in main thread!!
752 ApplicationEvent* pAppEvent =
753 new ApplicationEvent(ApplicationEvent::TYPE_QUICKSTART);
754 ImplPostForeignAppEvent( pAppEvent );
757 // handle request for acceptor
758 std::vector< OUString > const & accept = aCmdLineArgs->
759 GetAccept();
760 for (std::vector< OUString >::const_iterator i(accept.begin());
761 i != accept.end(); ++i)
763 ApplicationEvent* pAppEvent = new ApplicationEvent(
764 ApplicationEvent::TYPE_ACCEPT, *i);
765 ImplPostForeignAppEvent( pAppEvent );
767 // handle acceptor removal
768 std::vector< OUString > const & unaccept = aCmdLineArgs->
769 GetUnaccept();
770 for (std::vector< OUString >::const_iterator i(
771 unaccept.begin());
772 i != unaccept.end(); ++i)
774 ApplicationEvent* pAppEvent = new ApplicationEvent(
775 ApplicationEvent::TYPE_UNACCEPT, *i);
776 ImplPostForeignAppEvent( pAppEvent );
779 ProcessDocumentsRequest* pRequest = new ProcessDocumentsRequest(
780 aCmdLineArgs->getCwdUrl());
781 cProcessed.reset();
782 pRequest->pcProcessed = &cProcessed;
784 // Print requests are not dependent on the --invisible cmdline argument as they are
785 // loaded with the "hidden" flag! So they are always checked.
786 pRequest->aPrintList = aCmdLineArgs->GetPrintList();
787 bDocRequestSent |= !pRequest->aPrintList.empty();
788 pRequest->aPrintToList = aCmdLineArgs->GetPrintToList();
789 pRequest->aPrinterName = aCmdLineArgs->GetPrinterName();
790 bDocRequestSent |= !( pRequest->aPrintToList.empty() || pRequest->aPrinterName.isEmpty() );
792 if ( !rCurrentCmdLineArgs.IsInvisible() )
794 // Read cmdline args that can open/create documents. As they would open a window
795 // they are only allowed if the "--invisible" is currently not used!
796 pRequest->aOpenList = aCmdLineArgs->GetOpenList();
797 bDocRequestSent |= !pRequest->aOpenList.empty();
798 pRequest->aViewList = aCmdLineArgs->GetViewList();
799 bDocRequestSent |= !pRequest->aViewList.empty();
800 pRequest->aStartList = aCmdLineArgs->GetStartList();
801 bDocRequestSent |= !pRequest->aStartList.empty();
802 pRequest->aForceOpenList = aCmdLineArgs->GetForceOpenList();
803 bDocRequestSent |= !pRequest->aForceOpenList.empty();
804 pRequest->aForceNewList = aCmdLineArgs->GetForceNewList();
805 bDocRequestSent |= !pRequest->aForceNewList.empty();
807 // Special command line args to create an empty document for a given module
809 // #i18338# (lo)
810 // we only do this if no document was specified on the command line,
811 // since this would be inconsistent with the behaviour of
812 // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
813 if ( aCmdLineArgs->HasModuleParam() && (!bDocRequestSent) )
815 SvtModuleOptions aOpt;
816 SvtModuleOptions::EFactory eFactory = SvtModuleOptions::E_WRITER;
817 if ( aCmdLineArgs->IsWriter() )
818 eFactory = SvtModuleOptions::E_WRITER;
819 else if ( aCmdLineArgs->IsCalc() )
820 eFactory = SvtModuleOptions::E_CALC;
821 else if ( aCmdLineArgs->IsDraw() )
822 eFactory = SvtModuleOptions::E_DRAW;
823 else if ( aCmdLineArgs->IsImpress() )
824 eFactory = SvtModuleOptions::E_IMPRESS;
825 else if ( aCmdLineArgs->IsBase() )
826 eFactory = SvtModuleOptions::E_DATABASE;
827 else if ( aCmdLineArgs->IsMath() )
828 eFactory = SvtModuleOptions::E_MATH;
829 else if ( aCmdLineArgs->IsGlobal() )
830 eFactory = SvtModuleOptions::E_WRITERGLOBAL;
831 else if ( aCmdLineArgs->IsWeb() )
832 eFactory = SvtModuleOptions::E_WRITERWEB;
834 if ( !pRequest->aOpenList.empty() )
835 pRequest->aModule = aOpt.GetFactoryName( eFactory );
836 else
837 pRequest->aOpenList.push_back( aOpt.GetFactoryEmptyDocumentURL( eFactory ) );
838 bDocRequestSent = sal_True;
842 if ( !aCmdLineArgs->IsQuickstart() ) {
843 sal_Bool bShowHelp = sal_False;
844 OUStringBuffer aHelpURLBuffer;
845 if (aCmdLineArgs->IsHelpWriter()) {
846 bShowHelp = sal_True;
847 aHelpURLBuffer.appendAscii("vnd.sun.star.help://swriter/start");
848 } else if (aCmdLineArgs->IsHelpCalc()) {
849 bShowHelp = sal_True;
850 aHelpURLBuffer.appendAscii("vnd.sun.star.help://scalc/start");
851 } else if (aCmdLineArgs->IsHelpDraw()) {
852 bShowHelp = sal_True;
853 aHelpURLBuffer.appendAscii("vnd.sun.star.help://sdraw/start");
854 } else if (aCmdLineArgs->IsHelpImpress()) {
855 bShowHelp = sal_True;
856 aHelpURLBuffer.appendAscii("vnd.sun.star.help://simpress/start");
857 } else if (aCmdLineArgs->IsHelpBase()) {
858 bShowHelp = sal_True;
859 aHelpURLBuffer.appendAscii("vnd.sun.star.help://sdatabase/start");
860 } else if (aCmdLineArgs->IsHelpBasic()) {
861 bShowHelp = sal_True;
862 aHelpURLBuffer.appendAscii("vnd.sun.star.help://sbasic/start");
863 } else if (aCmdLineArgs->IsHelpMath()) {
864 bShowHelp = sal_True;
865 aHelpURLBuffer.appendAscii("vnd.sun.star.help://smath/start");
867 if (bShowHelp) {
868 aHelpURLBuffer.appendAscii("?Language=");
869 aHelpURLBuffer.append(utl::ConfigManager::getLocale());
870 #if defined UNX
871 aHelpURLBuffer.appendAscii("&System=UNX");
872 #elif defined WNT
873 aHelpURLBuffer.appendAscii("&System=WIN");
874 #endif
875 ApplicationEvent* pAppEvent = new ApplicationEvent(
876 ApplicationEvent::TYPE_OPENHELPURL,
877 aHelpURLBuffer.makeStringAndClear());
878 ImplPostForeignAppEvent( pAppEvent );
882 if ( bDocRequestSent )
884 // Send requests to dispatch watcher if we have at least one. The receiver
885 // is responsible to delete the request after processing it.
886 if ( aCmdLineArgs->HasModuleParam() )
888 SvtModuleOptions aOpt;
890 // Support command line parameters to start a module (as preselection)
891 if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SWRITER ) )
892 pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::E_WRITER );
893 else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SCALC ) )
894 pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::E_CALC );
895 else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SIMPRESS ) )
896 pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::E_IMPRESS );
897 else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SDRAW ) )
898 pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::E_DRAW );
901 ImplPostProcessDocumentsEvent( pRequest );
903 else
905 // delete not used request again
906 delete pRequest;
907 pRequest = NULL;
909 if (aArguments.equalsL(sc_aShowSequence, sc_nShSeqLength) ||
910 aCmdLineArgs->IsEmpty())
912 // no document was sent, just bring Office to front
913 ApplicationEvent* pAppEvent =
914 new ApplicationEvent(ApplicationEvent::TYPE_APPEAR);
915 ImplPostForeignAppEvent( pAppEvent );
919 // we don't need the mutex any longer...
920 aGuard.clear();
921 // wait for processing to finish
922 if (bDocRequestSent)
923 cProcessed.wait();
924 // processing finished, inform the requesting end:
925 n = aStreamPipe.write(
926 PROCESSING_DONE, SAL_N_ELEMENTS(PROCESSING_DONE));
927 // incl. terminating NUL
928 if (n != SAL_N_ELEMENTS(PROCESSING_DONE)) {
929 SAL_WARN("desktop", "short write: " << n);
930 continue;
933 else
936 osl::MutexGuard aGuard( GetMutex() );
937 if ( mbDowning )
939 break;
943 #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
944 fprintf( stderr, "Error on accept: %d\n", (int)nError );
945 #endif
946 TimeValue tval;
947 tval.Seconds = 1;
948 tval.Nanosec = 0;
949 salhelper::Thread::wait( tval );
951 } while( schedule() );
952 #endif
955 static void AddToDispatchList(
956 DispatchWatcher::DispatchList& rDispatchList,
957 boost::optional< OUString > const & cwdUrl,
958 std::vector< OUString > const & aRequestList,
959 DispatchWatcher::RequestType nType,
960 const OUString& aParam,
961 const OUString& aFactory )
963 for (std::vector< OUString >::const_iterator i(aRequestList.begin());
964 i != aRequestList.end(); ++i)
966 rDispatchList.push_back(
967 DispatchWatcher::DispatchRequest( nType, *i, cwdUrl, aParam, aFactory ));
971 static void AddConversionsToDispatchList(
972 DispatchWatcher::DispatchList& rDispatchList,
973 boost::optional< OUString > const & cwdUrl,
974 std::vector< OUString > const & rRequestList,
975 const OUString& rParam,
976 const OUString& rPrinterName,
977 const OUString& rFactory,
978 const OUString& rParamOut )
980 DispatchWatcher::RequestType nType;
981 OUString aParam( rParam );
983 if( !rParam.isEmpty() )
985 nType = DispatchWatcher::REQUEST_CONVERSION;
986 aParam = rParam;
988 else
990 nType = DispatchWatcher::REQUEST_BATCHPRINT;
991 aParam = rPrinterName;
994 OUString aOutDir( rParamOut.trim() );
995 OUString aPWD;
996 ::tools::getProcessWorkingDir( aPWD );
998 if( !::osl::FileBase::getAbsoluteFileURL( aPWD, rParamOut, aOutDir ) )
999 ::osl::FileBase::getSystemPathFromFileURL( aOutDir, aOutDir );
1001 if( !rParamOut.trim().isEmpty() )
1003 aParam += OUString(";");
1004 aParam += aOutDir;
1006 else
1008 ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD );
1009 aParam += OUString(";" ) + aPWD;
1012 for (std::vector< OUString >::const_iterator i(rRequestList.begin());
1013 i != rRequestList.end(); ++i)
1015 rDispatchList.push_back(
1016 DispatchWatcher::DispatchRequest( nType, *i, cwdUrl, aParam, rFactory ));
1021 sal_Bool OfficeIPCThread::ExecuteCmdLineRequests( ProcessDocumentsRequest& aRequest )
1023 // protect the dispatch list
1024 osl::ClearableMutexGuard aGuard( GetMutex() );
1026 static DispatchWatcher::DispatchList aDispatchList;
1028 OUString aEmpty;
1029 // Create dispatch list for dispatch watcher
1030 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aInFilter, DispatchWatcher::REQUEST_INFILTER, aEmpty, aRequest.aModule );
1031 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, aEmpty, aRequest.aModule );
1032 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, aEmpty, aRequest.aModule );
1033 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, aEmpty, aRequest.aModule );
1034 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, aEmpty, aRequest.aModule );
1035 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
1036 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, aEmpty, aRequest.aModule );
1037 AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, aEmpty, aRequest.aModule );
1038 AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut );
1039 sal_Bool bShutdown( sal_False );
1041 if ( pGlobalOfficeIPCThread.is() )
1043 if( ! pGlobalOfficeIPCThread->AreRequestsEnabled() )
1044 return bShutdown;
1046 pGlobalOfficeIPCThread->mnPendingRequests += aDispatchList.size();
1047 if ( !pGlobalOfficeIPCThread->mpDispatchWatcher )
1049 pGlobalOfficeIPCThread->mpDispatchWatcher = DispatchWatcher::GetDispatchWatcher();
1050 pGlobalOfficeIPCThread->mpDispatchWatcher->acquire();
1053 // copy for execute
1054 DispatchWatcher::DispatchList aTempList( aDispatchList );
1055 aDispatchList.clear();
1057 aGuard.clear();
1059 // Execute dispatch requests
1060 bShutdown = pGlobalOfficeIPCThread->mpDispatchWatcher->executeDispatchRequests( aTempList, s_bInEnableRequests );
1062 // set processed flag
1063 if (aRequest.pcProcessed != NULL)
1064 aRequest.pcProcessed->set();
1067 return bShutdown;
1072 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */