1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
23 #include "officeipcthread.hxx"
24 #include "cmdlineargs.hxx"
25 #include "dispatchwatcher.hxx"
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;
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
69 OString
readStringFromPipe(osl::StreamPipe
& pipe
) {
70 for (OStringBuffer str
;;) {
72 sal_Int32 n
= pipe
.recv(buf
, SAL_N_ELEMENTS(buf
));
77 if (buf
[n
- 1] == '\0') {
82 //TODO: how does OStringBuffer.append handle overflow?
84 return str
.makeStringAndClear();
93 // Type of pipe we use
106 #if HAVE_FEATURE_DESKTOP
108 class Parser
: public CommandLineArgs::Supplier
{
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
++]) {
123 if (!next(&url
, false)) {
124 throw CommandLineArgs::Supplier::Exception();
132 if (!next(&path
, false)) {
133 throw CommandLineArgs::Supplier::Exception();
136 if (osl::FileBase::getFileURLFromSystemPath(path
, url
) ==
137 osl::FileBase::E_None
)
144 throw CommandLineArgs::Supplier::Exception();
150 virtual boost::optional
< OUString
> getCwdUrl() { return m_cwdUrl
; }
152 virtual bool next(OUString
* argument
) { return next(argument
, true); }
155 virtual bool next(OUString
* argument
, bool prefix
) {
156 OSL_ASSERT(argument
!= NULL
);
157 if (m_index
< m_input
.getLength()) {
159 if (m_input
[m_index
] != ',') {
160 throw CommandLineArgs::Supplier::Exception();
165 while (m_index
< m_input
.getLength()) {
166 char c
= m_input
[m_index
];
172 if (m_index
< m_input
.getLength()) {
173 c
= m_input
[m_index
++];
182 throw CommandLineArgs::Supplier::Exception();
185 throw CommandLineArgs::Supplier::Exception();
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();
206 boost::optional
< OUString
> m_cwdUrl
;
211 bool addArgument(OStringBuffer
&rArguments
, char prefix
,
212 const OUString
&rArgument
)
215 if (!rArgument
.convertToString(
216 &utf8
, RTL_TEXTENCODING_UTF8
,
217 (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
|
218 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
)))
222 rArguments
.append(prefix
);
223 for (sal_Int32 i
= 0; i
< utf8
.getLength(); ++i
) {
227 rArguments
.append("\\0");
230 rArguments
.append("\\,");
233 rArguments
.append("\\\\");
236 rArguments
.append(c
);
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 OUString
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() );
259 rtlDigest handle
= rtl_digest_create( rtl_Digest_AlgorithmMD5
);
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();
284 class ProcessEventsClass_Impl
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
;
299 IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl
, ProcessDocumentsEvent
, void*, pEvent
)
301 // Documents requests are processed by the OfficeIPCThread implementation
302 ProcessDocumentsRequest
* pDocsRequest
= (ProcessDocumentsRequest
*)pEvent
;
306 OfficeIPCThread::ExecuteCmdLineRequests( *pDocsRequest
);
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
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 );
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();
377 OfficeIPCThread::SetDowning();
380 void SAL_CALL
OfficeIPCThreadController::notifyTermination( const EventObject
& )
381 throw( RuntimeException
)
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
;
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 );
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
;
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
;
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
481 osl_getExecutableFile( &aIniName
.pData
);
483 sal_uInt32 lastIndex
= aIniName
.lastIndexOf('/');
486 aIniName
= aIniName
.copy( 0, lastIndex
+1 );
487 aIniName
+= "perftune";
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() );
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();
526 // Try to create pipe
527 if ( pThread
->maPipe
.create( aPipeIdent
.getStr(), osl_Pipe_CREATE
, rSecurity
))
530 nPipeMode
= PIPEMODE_CREATED
;
532 else if( pThread
->maPipe
.create( aPipeIdent
.getStr(), osl_Pipe_OPEN
, rSecurity
)) // Creation not successful, now we try to connect
534 osl::StreamPipe
aStreamPipe(pThread
->maPipe
.getHandle());
535 if (readStringFromPipe(aStreamPipe
) == SEND_ARGUMENTS
)
537 // Pipe connected to first office
538 nPipeMode
= PIPEMODE_CONNECTED
;
542 // Pipe connection failed (other office exited or crashed)
545 tval
.Nanosec
= 500000000;
546 salhelper::Thread::wait( tval
);
551 oslPipeError eReason
= pThread
->maPipe
.getError();
552 if ((eReason
== osl_Pipe_E_ConnectionRefused
) || (eReason
== osl_Pipe_E_invalidError
))
553 return IPC_STATUS_PIPE_ERROR
;
555 // Wait for second office to be ready
556 TimeValue aTimeValue
;
557 aTimeValue
.Seconds
= 0;
558 aTimeValue
.Nanosec
= 10000000; // 10ms
559 salhelper::Thread::wait( aTimeValue
);
562 } while ( nPipeMode
== PIPEMODE_DONTKNOW
);
564 if ( nPipeMode
== PIPEMODE_CREATED
)
566 // Seems we are the one and only, so start listening thread
567 pGlobalOfficeIPCThread
= pThread
;
572 // Seems another office is running. Pipe arguments to it and self terminate
573 osl::StreamPipe
aStreamPipe(pThread
->maPipe
.getHandle());
575 OStringBuffer
aArguments(ARGUMENT_PREFIX
);
577 if (!(tools::getProcessWorkingDir(cwdUrl
) &&
578 addArgument(aArguments
, '1', cwdUrl
)))
580 aArguments
.append('0');
582 sal_uInt32 nCount
= rtl_getAppCommandArgCount();
583 for( sal_uInt32 i
=0; i
< nCount
; i
++ )
585 rtl_getAppCommandArg( i
, &aDummy
.pData
);
586 if (!addArgument(aArguments
, ',', aDummy
)) {
587 return IPC_STATUS_BOOTSTRAP_ERROR
;
590 aArguments
.append('\0');
591 // finally, write the string onto the pipe
592 sal_Int32 n
= aStreamPipe
.write(
593 aArguments
.getStr(), aArguments
.getLength());
594 if (n
!= aArguments
.getLength()) {
595 SAL_INFO("desktop", "short write: " << n
);
596 return IPC_STATUS_BOOTSTRAP_ERROR
;
599 if (readStringFromPipe(aStreamPipe
) != PROCESSING_DONE
)
601 // something went wrong
602 return IPC_STATUS_BOOTSTRAP_ERROR
;
605 return IPC_STATUS_2ND_OFFICE
;
608 pGlobalOfficeIPCThread
= rtl::Reference
< OfficeIPCThread
>(new OfficeIPCThread
);
610 return IPC_STATUS_OK
;
613 void OfficeIPCThread::DisableOfficeIPCThread(bool join
)
615 #if HAVE_FEATURE_DESKTOP
616 osl::ClearableMutexGuard
aMutex( GetMutex() );
618 if( pGlobalOfficeIPCThread
.is() )
620 rtl::Reference
< OfficeIPCThread
> pOfficeIPCThread(
621 pGlobalOfficeIPCThread
);
622 pGlobalOfficeIPCThread
.clear();
624 pOfficeIPCThread
->mbDowning
= true;
625 pOfficeIPCThread
->maPipe
.close();
627 // release mutex to avoid deadlocks
630 OfficeIPCThread::SetReady(pOfficeIPCThread
);
632 // exit gracefully and join
635 pOfficeIPCThread
->join();
643 OfficeIPCThread::OfficeIPCThread() :
644 Thread( "OfficeIPCThread" ),
646 mbRequestsEnabled( false ),
647 mnPendingRequests( 0 ),
648 mpDispatchWatcher( 0 )
652 OfficeIPCThread::~OfficeIPCThread()
654 ::osl::ClearableMutexGuard
aGuard( GetMutex() );
656 if ( mpDispatchWatcher
)
657 mpDispatchWatcher
->release();
659 pGlobalOfficeIPCThread
.clear();
662 void OfficeIPCThread::SetReady(
663 rtl::Reference
< OfficeIPCThread
> const & pThread
)
665 rtl::Reference
< OfficeIPCThread
> const & t(
666 pThread
.is() ? pThread
: pGlobalOfficeIPCThread
);
673 void OfficeIPCThread::execute()
675 #if HAVE_FEATURE_DESKTOP
678 osl::StreamPipe aStreamPipe
;
679 oslPipeError nError
= maPipe
.accept( aStreamPipe
);
682 if( nError
== osl_Pipe_E_None
)
684 // if we receive a request while the office is displaying some dialog or error during
685 // bootstrap, that dialogs event loop might get events that are dispatched by this thread
686 // we have to wait for cReady to be set by the real main loop.
687 // only reqests that dont dispatch events may be processed before cReady is set.
690 // we might have decided to shutdown while we were sleeping
691 if (!pGlobalOfficeIPCThread
.is()) return;
693 // only lock the mutex when processing starts, othewise we deadlock when the office goes
695 osl::ClearableMutexGuard
aGuard( GetMutex() );
702 // notify client we're ready to process its args:
703 sal_Int32 n
= aStreamPipe
.write(
704 SEND_ARGUMENTS
, SAL_N_ELEMENTS(SEND_ARGUMENTS
));
705 // incl. terminating NUL
706 if (n
!= SAL_N_ELEMENTS(SEND_ARGUMENTS
)) {
707 SAL_WARN("desktop", "short write: " << n
);
711 OString aArguments
= readStringFromPipe(aStreamPipe
);
713 // Is this a lookup message from another application? if so, ignore
714 if (aArguments
.isEmpty())
717 std::auto_ptr
< CommandLineArgs
> aCmdLineArgs
;
720 Parser
p(aArguments
);
721 aCmdLineArgs
.reset( new CommandLineArgs( p
) );
723 catch ( const CommandLineArgs::Supplier::Exception
& )
725 #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
726 fprintf( stderr
, "Error in received command line arguments\n" );
731 sal_Bool bDocRequestSent
= sal_False
;
733 OUString
aUnknown( aCmdLineArgs
->GetUnknown() );
734 if ( !aUnknown
.isEmpty() || aCmdLineArgs
->IsHelp() )
736 ApplicationEvent
* pAppEvent
=
737 new ApplicationEvent(ApplicationEvent::TYPE_HELP
, aUnknown
);
738 ImplPostForeignAppEvent( pAppEvent
);
740 else if ( aCmdLineArgs
->IsVersion() )
742 ApplicationEvent
* pAppEvent
=
743 new ApplicationEvent(ApplicationEvent::TYPE_VERSION
);
744 ImplPostForeignAppEvent( pAppEvent
);
748 const CommandLineArgs
&rCurrentCmdLineArgs
= Desktop::GetCommandLineArgs();
750 if ( aCmdLineArgs
->IsQuickstart() )
752 // we have to use application event, because we have to start quickstart service in main thread!!
753 ApplicationEvent
* pAppEvent
=
754 new ApplicationEvent(ApplicationEvent::TYPE_QUICKSTART
);
755 ImplPostForeignAppEvent( pAppEvent
);
758 // handle request for acceptor
759 std::vector
< OUString
> const & accept
= aCmdLineArgs
->
761 for (std::vector
< OUString
>::const_iterator
i(accept
.begin());
762 i
!= accept
.end(); ++i
)
764 ApplicationEvent
* pAppEvent
= new ApplicationEvent(
765 ApplicationEvent::TYPE_ACCEPT
, *i
);
766 ImplPostForeignAppEvent( pAppEvent
);
768 // handle acceptor removal
769 std::vector
< OUString
> const & unaccept
= aCmdLineArgs
->
771 for (std::vector
< OUString
>::const_iterator
i(
773 i
!= unaccept
.end(); ++i
)
775 ApplicationEvent
* pAppEvent
= new ApplicationEvent(
776 ApplicationEvent::TYPE_UNACCEPT
, *i
);
777 ImplPostForeignAppEvent( pAppEvent
);
780 ProcessDocumentsRequest
* pRequest
= new ProcessDocumentsRequest(
781 aCmdLineArgs
->getCwdUrl());
783 pRequest
->pcProcessed
= &cProcessed
;
785 // Print requests are not dependent on the --invisible cmdline argument as they are
786 // loaded with the "hidden" flag! So they are always checked.
787 pRequest
->aPrintList
= aCmdLineArgs
->GetPrintList();
788 bDocRequestSent
|= !pRequest
->aPrintList
.empty();
789 pRequest
->aPrintToList
= aCmdLineArgs
->GetPrintToList();
790 pRequest
->aPrinterName
= aCmdLineArgs
->GetPrinterName();
791 bDocRequestSent
|= !( pRequest
->aPrintToList
.empty() || pRequest
->aPrinterName
.isEmpty() );
793 if ( !rCurrentCmdLineArgs
.IsInvisible() )
795 // Read cmdline args that can open/create documents. As they would open a window
796 // they are only allowed if the "--invisible" is currently not used!
797 pRequest
->aOpenList
= aCmdLineArgs
->GetOpenList();
798 bDocRequestSent
|= !pRequest
->aOpenList
.empty();
799 pRequest
->aViewList
= aCmdLineArgs
->GetViewList();
800 bDocRequestSent
|= !pRequest
->aViewList
.empty();
801 pRequest
->aStartList
= aCmdLineArgs
->GetStartList();
802 bDocRequestSent
|= !pRequest
->aStartList
.empty();
803 pRequest
->aForceOpenList
= aCmdLineArgs
->GetForceOpenList();
804 bDocRequestSent
|= !pRequest
->aForceOpenList
.empty();
805 pRequest
->aForceNewList
= aCmdLineArgs
->GetForceNewList();
806 bDocRequestSent
|= !pRequest
->aForceNewList
.empty();
808 // Special command line args to create an empty document for a given module
811 // we only do this if no document was specified on the command line,
812 // since this would be inconsistent with the behaviour of
813 // the first process, see OpenClients() (call to OpenDefault()) in app.cxx
814 if ( aCmdLineArgs
->HasModuleParam() && (!bDocRequestSent
) )
816 SvtModuleOptions aOpt
;
817 SvtModuleOptions::EFactory eFactory
= SvtModuleOptions::E_WRITER
;
818 if ( aCmdLineArgs
->IsWriter() )
819 eFactory
= SvtModuleOptions::E_WRITER
;
820 else if ( aCmdLineArgs
->IsCalc() )
821 eFactory
= SvtModuleOptions::E_CALC
;
822 else if ( aCmdLineArgs
->IsDraw() )
823 eFactory
= SvtModuleOptions::E_DRAW
;
824 else if ( aCmdLineArgs
->IsImpress() )
825 eFactory
= SvtModuleOptions::E_IMPRESS
;
826 else if ( aCmdLineArgs
->IsBase() )
827 eFactory
= SvtModuleOptions::E_DATABASE
;
828 else if ( aCmdLineArgs
->IsMath() )
829 eFactory
= SvtModuleOptions::E_MATH
;
830 else if ( aCmdLineArgs
->IsGlobal() )
831 eFactory
= SvtModuleOptions::E_WRITERGLOBAL
;
832 else if ( aCmdLineArgs
->IsWeb() )
833 eFactory
= SvtModuleOptions::E_WRITERWEB
;
835 if ( !pRequest
->aOpenList
.empty() )
836 pRequest
->aModule
= aOpt
.GetFactoryName( eFactory
);
838 pRequest
->aOpenList
.push_back( aOpt
.GetFactoryEmptyDocumentURL( eFactory
) );
839 bDocRequestSent
= sal_True
;
843 if ( !aCmdLineArgs
->IsQuickstart() ) {
844 sal_Bool bShowHelp
= sal_False
;
845 OUStringBuffer aHelpURLBuffer
;
846 if (aCmdLineArgs
->IsHelpWriter()) {
847 bShowHelp
= sal_True
;
848 aHelpURLBuffer
.appendAscii("vnd.sun.star.help://swriter/start");
849 } else if (aCmdLineArgs
->IsHelpCalc()) {
850 bShowHelp
= sal_True
;
851 aHelpURLBuffer
.appendAscii("vnd.sun.star.help://scalc/start");
852 } else if (aCmdLineArgs
->IsHelpDraw()) {
853 bShowHelp
= sal_True
;
854 aHelpURLBuffer
.appendAscii("vnd.sun.star.help://sdraw/start");
855 } else if (aCmdLineArgs
->IsHelpImpress()) {
856 bShowHelp
= sal_True
;
857 aHelpURLBuffer
.appendAscii("vnd.sun.star.help://simpress/start");
858 } else if (aCmdLineArgs
->IsHelpBase()) {
859 bShowHelp
= sal_True
;
860 aHelpURLBuffer
.appendAscii("vnd.sun.star.help://sdatabase/start");
861 } else if (aCmdLineArgs
->IsHelpBasic()) {
862 bShowHelp
= sal_True
;
863 aHelpURLBuffer
.appendAscii("vnd.sun.star.help://sbasic/start");
864 } else if (aCmdLineArgs
->IsHelpMath()) {
865 bShowHelp
= sal_True
;
866 aHelpURLBuffer
.appendAscii("vnd.sun.star.help://smath/start");
869 aHelpURLBuffer
.appendAscii("?Language=");
870 aHelpURLBuffer
.append(utl::ConfigManager::getLocale());
872 aHelpURLBuffer
.appendAscii("&System=UNX");
874 aHelpURLBuffer
.appendAscii("&System=WIN");
876 ApplicationEvent
* pAppEvent
= new ApplicationEvent(
877 ApplicationEvent::TYPE_OPENHELPURL
,
878 aHelpURLBuffer
.makeStringAndClear());
879 ImplPostForeignAppEvent( pAppEvent
);
883 if ( bDocRequestSent
)
885 // Send requests to dispatch watcher if we have at least one. The receiver
886 // is responsible to delete the request after processing it.
887 if ( aCmdLineArgs
->HasModuleParam() )
889 SvtModuleOptions aOpt
;
891 // Support command line parameters to start a module (as preselection)
892 if ( aCmdLineArgs
->IsWriter() && aOpt
.IsModuleInstalled( SvtModuleOptions::E_SWRITER
) )
893 pRequest
->aModule
= aOpt
.GetFactoryName( SvtModuleOptions::E_WRITER
);
894 else if ( aCmdLineArgs
->IsCalc() && aOpt
.IsModuleInstalled( SvtModuleOptions::E_SCALC
) )
895 pRequest
->aModule
= aOpt
.GetFactoryName( SvtModuleOptions::E_CALC
);
896 else if ( aCmdLineArgs
->IsImpress() && aOpt
.IsModuleInstalled( SvtModuleOptions::E_SIMPRESS
) )
897 pRequest
->aModule
= aOpt
.GetFactoryName( SvtModuleOptions::E_IMPRESS
);
898 else if ( aCmdLineArgs
->IsDraw() && aOpt
.IsModuleInstalled( SvtModuleOptions::E_SDRAW
) )
899 pRequest
->aModule
= aOpt
.GetFactoryName( SvtModuleOptions::E_DRAW
);
902 ImplPostProcessDocumentsEvent( pRequest
);
906 // delete not used request again
910 if (aArguments
.equalsL(sc_aShowSequence
, sc_nShSeqLength
) ||
911 aCmdLineArgs
->IsEmpty())
913 // no document was sent, just bring Office to front
914 ApplicationEvent
* pAppEvent
=
915 new ApplicationEvent(ApplicationEvent::TYPE_APPEAR
);
916 ImplPostForeignAppEvent( pAppEvent
);
920 // we don't need the mutex any longer...
922 // wait for processing to finish
925 // processing finished, inform the requesting end:
926 n
= aStreamPipe
.write(
927 PROCESSING_DONE
, SAL_N_ELEMENTS(PROCESSING_DONE
));
928 // incl. terminating NUL
929 if (n
!= SAL_N_ELEMENTS(PROCESSING_DONE
)) {
930 SAL_WARN("desktop", "short write: " << n
);
937 osl::MutexGuard
aGuard( GetMutex() );
944 #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
945 fprintf( stderr
, "Error on accept: %d\n", (int)nError
);
950 salhelper::Thread::wait( tval
);
952 } while( schedule() );
956 static void AddToDispatchList(
957 DispatchWatcher::DispatchList
& rDispatchList
,
958 boost::optional
< OUString
> const & cwdUrl
,
959 std::vector
< OUString
> const & aRequestList
,
960 DispatchWatcher::RequestType nType
,
961 const OUString
& aParam
,
962 const OUString
& aFactory
)
964 for (std::vector
< OUString
>::const_iterator
i(aRequestList
.begin());
965 i
!= aRequestList
.end(); ++i
)
967 rDispatchList
.push_back(
968 DispatchWatcher::DispatchRequest( nType
, *i
, cwdUrl
, aParam
, aFactory
));
972 static void AddConversionsToDispatchList(
973 DispatchWatcher::DispatchList
& rDispatchList
,
974 boost::optional
< OUString
> const & cwdUrl
,
975 std::vector
< OUString
> const & rRequestList
,
976 const OUString
& rParam
,
977 const OUString
& rPrinterName
,
978 const OUString
& rFactory
,
979 const OUString
& rParamOut
)
981 DispatchWatcher::RequestType nType
;
982 OUString
aParam( rParam
);
984 if( !rParam
.isEmpty() )
986 nType
= DispatchWatcher::REQUEST_CONVERSION
;
991 nType
= DispatchWatcher::REQUEST_BATCHPRINT
;
992 aParam
= rPrinterName
;
995 OUString
aOutDir( rParamOut
.trim() );
997 ::tools::getProcessWorkingDir( aPWD
);
999 if( !::osl::FileBase::getAbsoluteFileURL( aPWD
, rParamOut
, aOutDir
) )
1000 ::osl::FileBase::getSystemPathFromFileURL( aOutDir
, aOutDir
);
1002 if( !rParamOut
.trim().isEmpty() )
1009 ::osl::FileBase::getSystemPathFromFileURL( aPWD
, aPWD
);
1010 aParam
+= ";" + aPWD
;
1013 for (std::vector
< OUString
>::const_iterator
i(rRequestList
.begin());
1014 i
!= rRequestList
.end(); ++i
)
1016 rDispatchList
.push_back(
1017 DispatchWatcher::DispatchRequest( nType
, *i
, cwdUrl
, aParam
, rFactory
));
1022 sal_Bool
OfficeIPCThread::ExecuteCmdLineRequests( ProcessDocumentsRequest
& aRequest
)
1024 // protect the dispatch list
1025 osl::ClearableMutexGuard
aGuard( GetMutex() );
1027 static DispatchWatcher::DispatchList aDispatchList
;
1030 // Create dispatch list for dispatch watcher
1031 AddToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aInFilter
, DispatchWatcher::REQUEST_INFILTER
, aEmpty
, aRequest
.aModule
);
1032 AddToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aOpenList
, DispatchWatcher::REQUEST_OPEN
, aEmpty
, aRequest
.aModule
);
1033 AddToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aViewList
, DispatchWatcher::REQUEST_VIEW
, aEmpty
, aRequest
.aModule
);
1034 AddToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aStartList
, DispatchWatcher::REQUEST_START
, aEmpty
, aRequest
.aModule
);
1035 AddToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aPrintList
, DispatchWatcher::REQUEST_PRINT
, aEmpty
, aRequest
.aModule
);
1036 AddToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aPrintToList
, DispatchWatcher::REQUEST_PRINTTO
, aRequest
.aPrinterName
, aRequest
.aModule
);
1037 AddToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aForceOpenList
, DispatchWatcher::REQUEST_FORCEOPEN
, aEmpty
, aRequest
.aModule
);
1038 AddToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aForceNewList
, DispatchWatcher::REQUEST_FORCENEW
, aEmpty
, aRequest
.aModule
);
1039 AddConversionsToDispatchList( aDispatchList
, aRequest
.aCwdUrl
, aRequest
.aConversionList
, aRequest
.aConversionParams
, aRequest
.aPrinterName
, aRequest
.aModule
, aRequest
.aConversionOut
);
1040 sal_Bool
bShutdown( sal_False
);
1042 if ( pGlobalOfficeIPCThread
.is() )
1044 if( ! pGlobalOfficeIPCThread
->AreRequestsEnabled() )
1047 pGlobalOfficeIPCThread
->mnPendingRequests
+= aDispatchList
.size();
1048 if ( !pGlobalOfficeIPCThread
->mpDispatchWatcher
)
1050 pGlobalOfficeIPCThread
->mpDispatchWatcher
= DispatchWatcher::GetDispatchWatcher();
1051 pGlobalOfficeIPCThread
->mpDispatchWatcher
->acquire();
1055 DispatchWatcher::DispatchList
aTempList( aDispatchList
);
1056 aDispatchList
.clear();
1060 // Execute dispatch requests
1061 bShutdown
= pGlobalOfficeIPCThread
->mpDispatchWatcher
->executeDispatchRequests( aTempList
, s_bInEnableRequests
);
1063 // set processed flag
1064 if (aRequest
.pcProcessed
!= NULL
)
1065 aRequest
.pcProcessed
->set();
1073 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */