blob: 90ad4d9280797752055dbb2af4035f88af5b00f7 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_desktop.hxx"
#include "app.hxx"
#include "officeipcthread.hxx"
#include "cmdlineargs.hxx"
#include "dispatchwatcher.hxx"
#include <memory>
#include <stdio.h>
#include <vos/process.hxx>
#include <unotools/bootstrap.hxx>
#include <vcl/svapp.hxx>
#include <vcl/help.hxx>
#include <unotools/configmgr.hxx>
#include <osl/thread.hxx>
#include <rtl/digest.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/instance.hxx>
#include <osl/conditn.hxx>
#include <unotools/moduleoptions.hxx>
#include <rtl/bootstrap.hxx>
#include <rtl/strbuf.hxx>
#include <comphelper/processfactory.hxx>
#include "osl/file.hxx"
#include "rtl/process.h"
#include "tools/getprocessworkingdir.hxx"
using namespace vos;
using namespace rtl;
using namespace desktop;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::frame;
const char *OfficeIPCThread::sc_aTerminationSequence = "InternalIPC::TerminateThread";
const int OfficeIPCThread::sc_nTSeqLength = 28;
const char *OfficeIPCThread::sc_aShowSequence = "-tofront";
const int OfficeIPCThread::sc_nShSeqLength = 5;
const char *OfficeIPCThread::sc_aConfirmationSequence = "InternalIPC::ProcessingDone";
const int OfficeIPCThread::sc_nCSeqLength = 27;
namespace { static char const ARGUMENT_PREFIX[] = "InternalIPC::Arguments"; }
// Type of pipe we use
enum PipeMode
{
PIPEMODE_DONTKNOW,
PIPEMODE_CREATED,
PIPEMODE_CONNECTED
};
namespace desktop
{
namespace {
class Parser: public CommandLineArgs::Supplier {
public:
explicit Parser(rtl::OString const & input): m_input(input) {
if (!m_input.match(ARGUMENT_PREFIX) ||
m_input.getLength() == RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX))
{
throw CommandLineArgs::Supplier::Exception();
}
m_index = RTL_CONSTASCII_LENGTH(ARGUMENT_PREFIX);
switch (m_input[m_index++]) {
case '0':
break;
case '1':
{
rtl::OUString url;
if (!next(&url, false)) {
throw CommandLineArgs::Supplier::Exception();
}
m_cwdUrl.reset(url);
break;
}
case '2':
{
rtl::OUString path;
if (!next(&path, false)) {
throw CommandLineArgs::Supplier::Exception();
}
rtl::OUString url;
if (osl::FileBase::getFileURLFromSystemPath(path, url) ==
osl::FileBase::E_None)
{
m_cwdUrl.reset(url);
}
break;
}
default:
throw CommandLineArgs::Supplier::Exception();
}
}
virtual ~Parser() {}
virtual boost::optional< rtl::OUString > getCwdUrl() { return m_cwdUrl; }
virtual bool next(rtl::OUString * argument) { return next(argument, true); }
private:
virtual bool next(rtl::OUString * argument, bool prefix) {
OSL_ASSERT(argument != NULL);
if (m_index < m_input.getLength()) {
if (prefix) {
if (m_input[m_index] != ',') {
throw CommandLineArgs::Supplier::Exception();
}
++m_index;
}
rtl::OStringBuffer b;
while (m_index < m_input.getLength()) {
char c = m_input[m_index];
if (c == ',') {
break;
}
++m_index;
if (c == '\\') {
if (m_index < m_input.getLength()) {
c = m_input[m_index++];
switch (c) {
case '0':
c = '\0';
break;
case ',':
case '\\':
break;
default:
throw CommandLineArgs::Supplier::Exception();
}
} else {
throw CommandLineArgs::Supplier::Exception();
}
}
b.append(c);
}
rtl::OString b2(b.makeStringAndClear());
if (!rtl_convertStringToUString(
&argument->pData, b2.getStr(), b2.getLength(),
RTL_TEXTENCODING_UTF8,
(RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR |
RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
{
throw CommandLineArgs::Supplier::Exception();
}
return true;
} else {
return false;
}
}
boost::optional< rtl::OUString > m_cwdUrl;
rtl::OString m_input;
sal_Int32 m_index;
};
bool addArgument(
ByteString * arguments, char prefix, rtl::OUString const & argument)
{
rtl::OString utf8;
if (!argument.convertToString(
&utf8, RTL_TEXTENCODING_UTF8,
(RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
{
return false;
}
*arguments += prefix;
for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
char c = utf8[i];
switch (c) {
case '\0':
*arguments += "\\0";
break;
case ',':
*arguments += "\\,";
break;
case '\\':
*arguments += "\\\\";
break;
default:
*arguments += c;
break;
}
}
return true;
}
}
OfficeIPCThread* OfficeIPCThread::pGlobalOfficeIPCThread = 0;
namespace { struct Security : public rtl::Static<OSecurity, Security> {}; }
::osl::Mutex* OfficeIPCThread::pOfficeIPCThreadMutex = 0;
String CreateMD5FromString( const OUString& aMsg )
{
// PRE: aStr "file"
// BACK: Str "ababab....0f" Hexcode String
rtlDigest handle = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
if ( handle > 0 )
{
const sal_uInt8* pData = (const sal_uInt8*)aMsg.getStr();
sal_uInt32 nSize = ( aMsg.getLength() * sizeof( sal_Unicode ));
sal_uInt32 nMD5KeyLen = rtl_digest_queryLength( handle );
sal_uInt8* pMD5KeyBuffer = new sal_uInt8[ nMD5KeyLen ];
rtl_digest_init( handle, pData, nSize );
rtl_digest_update( handle, pData, nSize );
rtl_digest_get( handle, pMD5KeyBuffer, nMD5KeyLen );
rtl_digest_destroy( handle );
// Create hex-value string from the MD5 value to keep the string size minimal
OUStringBuffer aBuffer( nMD5KeyLen * 2 + 1 );
for ( sal_uInt32 i = 0; i < nMD5KeyLen; i++ )
aBuffer.append( (sal_Int32)pMD5KeyBuffer[i], 16 );
delete [] pMD5KeyBuffer;
return aBuffer.makeStringAndClear();
}
return String();
}
class ProcessEventsClass_Impl
{
public:
DECL_STATIC_LINK( ProcessEventsClass_Impl, CallEvent, void* pEvent );
DECL_STATIC_LINK( ProcessEventsClass_Impl, ProcessDocumentsEvent, void* pEvent );
};
IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl, CallEvent, void*, pEvent )
{
// Application events are processed by the Desktop::HandleAppEvent implementation.
Desktop::HandleAppEvent( *((ApplicationEvent*)pEvent) );
delete (ApplicationEvent*)pEvent;
return 0;
}
IMPL_STATIC_LINK_NOINSTANCE( ProcessEventsClass_Impl, ProcessDocumentsEvent, void*, pEvent )
{
// Documents requests are processed by the OfficeIPCThread implementation
ProcessDocumentsRequest* pDocsRequest = (ProcessDocumentsRequest*)pEvent;
if ( pDocsRequest )
{
OfficeIPCThread::ExecuteCmdLineRequests( *pDocsRequest );
delete pDocsRequest;
}
return 0;
}
void ImplPostForeignAppEvent( ApplicationEvent* pEvent )
{
Application::PostUserEvent( STATIC_LINK( NULL, ProcessEventsClass_Impl, CallEvent ), pEvent );
}
void ImplPostProcessDocumentsEvent( ProcessDocumentsRequest* pEvent )
{
Application::PostUserEvent( STATIC_LINK( NULL, ProcessEventsClass_Impl, ProcessDocumentsEvent ), pEvent );
}
OSignalHandler::TSignalAction SAL_CALL SalMainPipeExchangeSignalHandler::signal(TSignalInfo *pInfo)
{
if( pInfo->Signal == osl_Signal_Terminate )
OfficeIPCThread::DisableOfficeIPCThread();
return (TAction_CallNextHandler);
}
// ----------------------------------------------------------------------------
// The OfficeIPCThreadController implementation is a bookkeeper for all pending requests
// that were created by the OfficeIPCThread. The requests are waiting to be processed by
// our framework loadComponentFromURL function (e.g. open/print request).
// During shutdown the framework is asking OfficeIPCThreadController about pending requests.
// If there are pending requests framework has to stop the shutdown process. It is waiting
// for these requests because framework is not able to handle shutdown and open a document
// concurrently.
// XServiceInfo
OUString SAL_CALL OfficeIPCThreadController::getImplementationName()
throw ( RuntimeException )
{
return OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.OfficeIPCThreadController" ));
}
sal_Bool SAL_CALL OfficeIPCThreadController::supportsService( const OUString& )
throw ( RuntimeException )
{
return sal_False;
}
Sequence< OUString > SAL_CALL OfficeIPCThreadController::getSupportedServiceNames()
throw ( RuntimeException )
{
Sequence< OUString > aSeq( 0 );
return aSeq;
}
// XEventListener
void SAL_CALL OfficeIPCThreadController::disposing( const EventObject& )
throw( RuntimeException )
{
}
// XTerminateListener
void SAL_CALL OfficeIPCThreadController::queryTermination( const EventObject& )
throw( TerminationVetoException, RuntimeException )
{
// Desktop ask about pending request through our office ipc pipe. We have to
// be sure that no pending request is waiting because framework is not able to
// handle shutdown and open a document concurrently.
if ( OfficeIPCThread::AreRequestsPending() )
throw TerminationVetoException();
else
OfficeIPCThread::SetDowning();
}
void SAL_CALL OfficeIPCThreadController::notifyTermination( const EventObject& )
throw( RuntimeException )
{
}
// ----------------------------------------------------------------------------
::osl::Mutex& OfficeIPCThread::GetMutex()
{
// Get or create our mutex for thread-saftey
if ( !pOfficeIPCThreadMutex )
{
::osl::MutexGuard aGuard( osl::Mutex::getGlobalMutex() );
if ( !pOfficeIPCThreadMutex )
pOfficeIPCThreadMutex = new osl::Mutex;
}
return *pOfficeIPCThreadMutex;
}
void OfficeIPCThread::SetDowning()
{
// We have the order to block all incoming requests. Framework
// wants to shutdown and we have to make sure that no loading/printing
// requests are executed anymore.
::osl::MutexGuard aGuard( GetMutex() );
if ( pGlobalOfficeIPCThread )
pGlobalOfficeIPCThread->mbDowning = true;
}
static bool s_bInEnableRequests = false;
void OfficeIPCThread::EnableRequests( bool i_bEnable )
{
// switch between just queueing the requests and executing them
::osl::MutexGuard aGuard( GetMutex() );
if ( pGlobalOfficeIPCThread )
{
s_bInEnableRequests = true;
pGlobalOfficeIPCThread->mbRequestsEnabled = i_bEnable;
if( i_bEnable )
{
// hit the compiler over the head
ProcessDocumentsRequest aEmptyReq = ProcessDocumentsRequest( boost::optional< rtl::OUString >() );
// trigger already queued requests
OfficeIPCThread::ExecuteCmdLineRequests( aEmptyReq );
}
s_bInEnableRequests = false;
}
}
sal_Bool OfficeIPCThread::AreRequestsPending()
{
// Give info about pending requests
::osl::MutexGuard aGuard( GetMutex() );
if ( pGlobalOfficeIPCThread )
return ( pGlobalOfficeIPCThread->mnPendingRequests > 0 );
else
return sal_False;
}
void OfficeIPCThread::RequestsCompleted( int nCount )
{
// Remove nCount pending requests from our internal counter
::osl::MutexGuard aGuard( GetMutex() );
if ( pGlobalOfficeIPCThread )
{
if ( pGlobalOfficeIPCThread->mnPendingRequests > 0 )
pGlobalOfficeIPCThread->mnPendingRequests -= nCount;
}
}
OfficeIPCThread::Status OfficeIPCThread::EnableOfficeIPCThread()
{
::osl::MutexGuard aGuard( GetMutex() );
if( pGlobalOfficeIPCThread )
return IPC_STATUS_OK;
::rtl::OUString aUserInstallPath;
::rtl::OUString aDummy;
::vos::OStartupInfo aInfo;
OfficeIPCThread* pThread = new OfficeIPCThread;
pThread->maPipeIdent = OUString( RTL_CONSTASCII_USTRINGPARAM( "SingleOfficeIPC_" ) );
// The name of the named pipe is created with the hashcode of the user installation directory (without /user). We have to retrieve
// this information from a unotools implementation.
::utl::Bootstrap::PathStatus aLocateResult = ::utl::Bootstrap::locateUserInstallation( aUserInstallPath );
if ( aLocateResult == ::utl::Bootstrap::PATH_EXISTS || aLocateResult == ::utl::Bootstrap::PATH_VALID)
aDummy = aUserInstallPath;
else
{
delete pThread;
return IPC_STATUS_BOOTSTRAP_ERROR;
}
// Try to determine if we are the first office or not! This should prevent multiple
// access to the user directory !
// First we try to create our pipe if this fails we try to connect. We have to do this
// in a loop because the the other office can crash or shutdown between createPipe
// and connectPipe!!
OUString aIniName;
aInfo.getExecutableFile( aIniName );
sal_uInt32 lastIndex = aIniName.lastIndexOf('/');
if ( lastIndex > 0 )
{
aIniName = aIniName.copy( 0, lastIndex+1 );
aIniName += OUString( RTL_CONSTASCII_USTRINGPARAM( "perftune" ));
#if defined(WNT) || defined(OS2)
aIniName += OUString( RTL_CONSTASCII_USTRINGPARAM( ".ini" ));
#else
aIniName += OUString( RTL_CONSTASCII_USTRINGPARAM( "rc" ));
#endif
}
::rtl::Bootstrap aPerfTuneIniFile( aIniName );
OUString aDefault( RTL_CONSTASCII_USTRINGPARAM( "0" ));
OUString aPreloadData;
aPerfTuneIniFile.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "FastPipeCommunication" )), aPreloadData, aDefault );
OUString aUserInstallPathHashCode;
if ( aPreloadData.equalsAscii( "1" ))
{
sal_Char szBuffer[32];
sprintf( szBuffer, "%d", SUPD );
aUserInstallPathHashCode = OUString( szBuffer, strlen(szBuffer), osl_getThreadTextEncoding() );
}
else
aUserInstallPathHashCode = CreateMD5FromString( aDummy );
// Check result to create a hash code from the user install path
if ( aUserInstallPathHashCode.getLength() == 0 )
return IPC_STATUS_BOOTSTRAP_ERROR; // Something completely broken, we cannot create a valid hash code!
pThread->maPipeIdent = pThread->maPipeIdent + aUserInstallPathHashCode;
PipeMode nPipeMode = PIPEMODE_DONTKNOW;
do
{
OSecurity &rSecurity = Security::get();
// #119950# Try to connect pipe first. If connected, means another instance already launched.
if( pThread->maPipe.create( pThread->maPipeIdent.getStr(), OPipe::TOption_Open, rSecurity ))
{
// #119950# Test if launched in a new terminal session for same user. On Windows platform, normally a user is resticted
// to have only one terminal session. But if mutiple terminal session for one user is allowed, crash will happen if launched
// OpenOffice from more than one terminal session. So need to detect and prevent this happen.
// Will try to create a same name pipe. If creation is successfully, means current instance is launched in a new session.
vos::OPipe maSessionPipe;
if ( maSessionPipe.create( pThread->maPipeIdent.getStr(), OPipe::TOption_Create, rSecurity )) {
// Can create a pipe with same name. This can only happen in multiple terminal session environment on Windows platform.
// Will display a warning dialog and exit.
return IPC_STATUS_MULTI_TS_ERROR;
} else {
// Pipe connected to first office
nPipeMode = PIPEMODE_CONNECTED;
}
}
else if ( pThread->maPipe.create( pThread->maPipeIdent.getStr(), OPipe::TOption_Create, rSecurity )) // Connection not successfull, now we try to create
{
// Pipe created
nPipeMode = PIPEMODE_CREATED;
}
else
{
OPipe::TPipeError eReason = pThread->maPipe.getError();
if ((eReason == OPipe::E_ConnectionRefused) || (eReason == OPipe::E_invalidError))
return IPC_STATUS_BOOTSTRAP_ERROR;
// Wait for second office to be ready
TimeValue aTimeValue;
aTimeValue.Seconds = 0;
aTimeValue.Nanosec = 10000000; // 10ms
osl::Thread::wait( aTimeValue );
}
} while ( nPipeMode == PIPEMODE_DONTKNOW );
if ( nPipeMode == PIPEMODE_CREATED )
{
// Seems we are the one and only, so start listening thread
pGlobalOfficeIPCThread = pThread;
pThread->create(); // starts thread
}
else
{
// Seems another office is running. Pipe arguments to it and self terminate
pThread->maStreamPipe = pThread->maPipe;
sal_Bool bWaitBeforeClose = sal_False;
ByteString aArguments(RTL_CONSTASCII_STRINGPARAM(ARGUMENT_PREFIX));
rtl::OUString cwdUrl;
if (!(tools::getProcessWorkingDir(&cwdUrl) &&
addArgument(&aArguments, '1', cwdUrl)))
{
aArguments += '0';
}
sal_uInt32 nCount = rtl_getAppCommandArgCount();
for( sal_uInt32 i=0; i < nCount; i++ )
{
rtl_getAppCommandArg( i, &aDummy.pData );
if( aDummy.indexOf('-',0) != 0 )
{
bWaitBeforeClose = sal_True;
}
if (!addArgument(&aArguments, ',', aDummy)) {
return IPC_STATUS_BOOTSTRAP_ERROR;
}
}
// finaly, write the string onto the pipe
pThread->maStreamPipe.write( aArguments.GetBuffer(), aArguments.Len() );
pThread->maStreamPipe.write( "\0", 1 );
// wait for confirmation #95361# #95425#
ByteString aToken(sc_aConfirmationSequence);
char *aReceiveBuffer = new char[aToken.Len()+1];
int n = pThread->maStreamPipe.read( aReceiveBuffer, aToken.Len() );
aReceiveBuffer[n]='\0';
delete pThread;
if (aToken.CompareTo(aReceiveBuffer)!= COMPARE_EQUAL) {
// something went wrong
delete[] aReceiveBuffer;
return IPC_STATUS_BOOTSTRAP_ERROR;
} else {
delete[] aReceiveBuffer;
return IPC_STATUS_2ND_OFFICE;
}
}
return IPC_STATUS_OK;
}
void OfficeIPCThread::DisableOfficeIPCThread()
{
osl::ClearableMutexGuard aMutex( GetMutex() );
if( pGlobalOfficeIPCThread )
{
OfficeIPCThread *pOfficeIPCThread = pGlobalOfficeIPCThread;
pGlobalOfficeIPCThread = 0;
// send thread a termination message
// this is done so the subsequent join will not hang
// because the thread hangs in accept of pipe
OPipe Pipe( pOfficeIPCThread->maPipeIdent, OPipe::TOption_Open, Security::get() );
//Pipe.send( TERMINATION_SEQUENCE, TERMINATION_LENGTH );
if (Pipe.isValid())
{
Pipe.send( sc_aTerminationSequence, sc_nTSeqLength+1 ); // also send 0-byte
// close the pipe so that the streampipe on the other
// side produces EOF
Pipe.close();
}
// release mutex to avoid deadlocks
aMutex.clear();
OfficeIPCThread::SetReady(pOfficeIPCThread);
// exit gracefully and join
pOfficeIPCThread->join();
delete pOfficeIPCThread;
}
}
OfficeIPCThread::OfficeIPCThread() :
mbDowning( false ),
mbRequestsEnabled( false ),
mnPendingRequests( 0 ),
mpDispatchWatcher( 0 )
{
}
OfficeIPCThread::~OfficeIPCThread()
{
::osl::ClearableMutexGuard aGuard( GetMutex() );
if ( mpDispatchWatcher )
mpDispatchWatcher->release();
maPipe.close();
maStreamPipe.close();
pGlobalOfficeIPCThread = 0;
}
static void AddURLToStringList( const rtl::OUString& aURL, rtl::OUString& aStringList )
{
if ( aStringList.getLength() )
aStringList += ::rtl::OUString::valueOf( (sal_Unicode)APPEVENT_PARAM_DELIMITER );
aStringList += aURL;
}
void OfficeIPCThread::SetReady(OfficeIPCThread* pThread)
{
if (pThread == NULL) pThread = pGlobalOfficeIPCThread;
if (pThread != NULL)
{
pThread->cReady.set();
}
}
void SAL_CALL OfficeIPCThread::run()
{
do
{
OPipe::TPipeError
nError = maPipe.accept( maStreamPipe );
if( nError == OStreamPipe::E_None )
{
// #111143# and others:
// if we receive a request while the office is displaying some dialog or error during
// bootstrap, that dialogs event loop might get events that are dispatched by this thread
// we have to wait for cReady to be set by the real main loop.
// only reqests that dont dispatch events may be processed before cReady is set.
cReady.wait();
// we might have decided to shutdown while we were sleeping
if (!pGlobalOfficeIPCThread) return;
// only lock the mutex when processing starts, othewise we deadlock when the office goes
// down during wait
osl::ClearableMutexGuard aGuard( GetMutex() );
ByteString aArguments;
// test byte by byte
const int nBufSz = 2048;
char pBuf[nBufSz];
int nBytes = 0;
int nResult = 0;
// read into pBuf until '\0' is read or read-error
while ((nResult=maStreamPipe.recv( pBuf+nBytes, nBufSz-nBytes))>0) {
nBytes += nResult;
if (pBuf[nBytes-1]=='\0') {
aArguments += pBuf;
break;
}
}
// don't close pipe ...
// #90717# Is this a lookup message from another application? if so, ignore
if ( aArguments.Len() == 0 )
continue;
// is this a termination message ? if so, terminate
if(( aArguments.CompareTo( sc_aTerminationSequence, sc_nTSeqLength ) == COMPARE_EQUAL ) ||
mbDowning ) return;
String aEmpty;
std::auto_ptr< CommandLineArgs > aCmdLineArgs;
try
{
Parser p( aArguments );
aCmdLineArgs.reset( new CommandLineArgs( p ) );
}
catch ( CommandLineArgs::Supplier::Exception & )
{
#if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
fprintf( stderr, "Error in received command line arguments\n" );
#endif
continue;
}
CommandLineArgs *pCurrentCmdLineArgs = Desktop::GetCommandLineArgs();
if ( aCmdLineArgs->IsQuickstart() )
{
// we have to use application event, because we have to start quickstart service in main thread!!
ApplicationEvent* pAppEvent =
new ApplicationEvent( aEmpty, aEmpty,
"QUICKSTART", aEmpty );
ImplPostForeignAppEvent( pAppEvent );
}
// handle request for acceptor
sal_Bool bAcceptorRequest = sal_False;
OUString aAcceptString;
if ( aCmdLineArgs->GetAcceptString(aAcceptString) && Desktop::CheckOEM()) {
ApplicationEvent* pAppEvent =
new ApplicationEvent( aEmpty, aEmpty,
"ACCEPT", aAcceptString );
ImplPostForeignAppEvent( pAppEvent );
bAcceptorRequest = sal_True;
}
// handle acceptor removal
OUString aUnAcceptString;
if ( aCmdLineArgs->GetUnAcceptString(aUnAcceptString) ) {
ApplicationEvent* pAppEvent =
new ApplicationEvent( aEmpty, aEmpty,
"UNACCEPT", aUnAcceptString );
ImplPostForeignAppEvent( pAppEvent );
bAcceptorRequest = sal_True;
}
#ifndef UNX
// only in non-unix version, we need to handle a -help request
// in a running instance in order to display the command line help
if ( aCmdLineArgs->IsHelp() ) {
ApplicationEvent* pAppEvent =
new ApplicationEvent( aEmpty, aEmpty, "HELP", aEmpty );
ImplPostForeignAppEvent( pAppEvent );
}
#endif
sal_Bool bDocRequestSent = sal_False;
ProcessDocumentsRequest* pRequest = new ProcessDocumentsRequest(
aCmdLineArgs->getCwdUrl());
cProcessed.reset();
pRequest->pcProcessed = &cProcessed;
// Print requests are not dependent on the -invisible cmdline argument as they are
// loaded with the "hidden" flag! So they are always checked.
bDocRequestSent |= aCmdLineArgs->GetPrintList( pRequest->aPrintList );
bDocRequestSent |= ( aCmdLineArgs->GetPrintToList( pRequest->aPrintToList ) &&
aCmdLineArgs->GetPrinterName( pRequest->aPrinterName ) );
if ( !pCurrentCmdLineArgs->IsInvisible() )
{
// Read cmdline args that can open/create documents. As they would open a window
// they are only allowed if the "-invisible" is currently not used!
bDocRequestSent |= aCmdLineArgs->GetOpenList( pRequest->aOpenList );
bDocRequestSent |= aCmdLineArgs->GetViewList( pRequest->aViewList );
bDocRequestSent |= aCmdLineArgs->GetStartList( pRequest->aStartList );
bDocRequestSent |= aCmdLineArgs->GetForceOpenList( pRequest->aForceOpenList );
bDocRequestSent |= aCmdLineArgs->GetForceNewList( pRequest->aForceNewList );
// Special command line args to create an empty document for a given module
// #i18338# (lo)
// we only do this if no document was specified on the command line,
// since this would be inconsistent with the the behaviour of
// the first process, see OpenClients() (call to OpenDefault()) in app.cxx
if ( aCmdLineArgs->HasModuleParam() && Desktop::CheckOEM() && (!bDocRequestSent))
{
SvtModuleOptions aOpt;
SvtModuleOptions::EFactory eFactory = SvtModuleOptions::E_WRITER;
if ( aCmdLineArgs->IsWriter() )
eFactory = SvtModuleOptions::E_WRITER;
else if ( aCmdLineArgs->IsCalc() )
eFactory = SvtModuleOptions::E_CALC;
else if ( aCmdLineArgs->IsDraw() )
eFactory = SvtModuleOptions::E_DRAW;
else if ( aCmdLineArgs->IsImpress() )
eFactory = SvtModuleOptions::E_IMPRESS;
else if ( aCmdLineArgs->IsBase() )
eFactory = SvtModuleOptions::E_DATABASE;
else if ( aCmdLineArgs->IsMath() )
eFactory = SvtModuleOptions::E_MATH;
else if ( aCmdLineArgs->IsGlobal() )
eFactory = SvtModuleOptions::E_WRITERGLOBAL;
else if ( aCmdLineArgs->IsWeb() )
eFactory = SvtModuleOptions::E_WRITERWEB;
if ( pRequest->aOpenList.getLength() )
pRequest->aModule = aOpt.GetFactoryName( eFactory );
else
AddURLToStringList( aOpt.GetFactoryEmptyDocumentURL( eFactory ), pRequest->aOpenList );
bDocRequestSent = sal_True;
}
}
if (!aCmdLineArgs->IsQuickstart() && Desktop::CheckOEM()) {
sal_Bool bShowHelp = sal_False;
rtl::OUStringBuffer aHelpURLBuffer;
if (aCmdLineArgs->IsHelpWriter()) {
bShowHelp = sal_True;
aHelpURLBuffer.appendAscii("vnd.sun.star.help://swriter/start");
} else if (aCmdLineArgs->IsHelpCalc()) {
bShowHelp = sal_True;
aHelpURLBuffer.appendAscii("vnd.sun.star.help://scalc/start");
} else if (aCmdLineArgs->IsHelpDraw()) {
bShowHelp = sal_True;
aHelpURLBuffer.appendAscii("vnd.sun.star.help://sdraw/start");
} else if (aCmdLineArgs->IsHelpImpress()) {
bShowHelp = sal_True;
aHelpURLBuffer.appendAscii("vnd.sun.star.help://simpress/start");
} else if (aCmdLineArgs->IsHelpBase()) {
bShowHelp = sal_True;
aHelpURLBuffer.appendAscii("vnd.sun.star.help://sdatabase/start");
} else if (aCmdLineArgs->IsHelpBasic()) {
bShowHelp = sal_True;
aHelpURLBuffer.appendAscii("vnd.sun.star.help://sbasic/start");
} else if (aCmdLineArgs->IsHelpMath()) {
bShowHelp = sal_True;
aHelpURLBuffer.appendAscii("vnd.sun.star.help://smath/start");
}
if (bShowHelp) {
Any aRet = ::utl::ConfigManager::GetDirectConfigProperty( ::utl::ConfigManager::LOCALE );
rtl::OUString aTmp;
aRet >>= aTmp;
aHelpURLBuffer.appendAscii("?Language=");
aHelpURLBuffer.append(aTmp);
#if defined UNX
aHelpURLBuffer.appendAscii("&System=UNX");
#elif defined WNT
aHelpURLBuffer.appendAscii("&System=WIN");
#elif defined OS2
aHelpURLBuffer.appendAscii("&System=OS2");
#endif
ApplicationEvent* pAppEvent =
new ApplicationEvent( aEmpty, aEmpty,
"OPENHELPURL", aHelpURLBuffer.makeStringAndClear());
ImplPostForeignAppEvent( pAppEvent );
}
}
if ( bDocRequestSent && Desktop::CheckOEM())
{
// Send requests to dispatch watcher if we have at least one. The receiver
// is responsible to delete the request after processing it.
if ( aCmdLineArgs->HasModuleParam() )
{
SvtModuleOptions aOpt;
// Support command line parameters to start a module (as preselection)
if ( aCmdLineArgs->IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SWRITER ) )
pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::E_WRITER );
else if ( aCmdLineArgs->IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SCALC ) )
pRequest->aModule = aOpt.GetFactoryName( SvtModuleOptions::E_CALC );
else if ( aCmdLineArgs->IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SIMPRESS ) )
pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::E_IMPRESS );
else if ( aCmdLineArgs->IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::E_SDRAW ) )
pRequest->aModule= aOpt.GetFactoryName( SvtModuleOptions::E_DRAW );
}
ImplPostProcessDocumentsEvent( pRequest );
}
else
{
// delete not used request again
delete pRequest;
pRequest = NULL;
}
if (( aArguments.CompareTo( sc_aShowSequence, sc_nShSeqLength ) == COMPARE_EQUAL ) ||
aCmdLineArgs->IsEmpty() )
{
// no document was sent, just bring Office to front
ApplicationEvent* pAppEvent =
new ApplicationEvent( aEmpty, aEmpty, "APPEAR", aEmpty );
ImplPostForeignAppEvent( pAppEvent );
}
// we don't need the mutex any longer...
aGuard.clear();
// wait for processing to finish
if (bDocRequestSent)
cProcessed.wait();
// processing finished, inform the requesting end
nBytes = 0;
while (
(nResult = maStreamPipe.send(sc_aConfirmationSequence+nBytes, sc_nCSeqLength-nBytes))>0 &&
((nBytes += nResult) < sc_nCSeqLength) ) ;
// now we can close, don't we?
// maStreamPipe.close();
}
else
{
#if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
fprintf( stderr, "Error on accept: %d\n", (int)nError );
#endif
TimeValue tval;
tval.Seconds = 1;
tval.Nanosec = 0;
sleep( tval );
}
} while( schedule() );
}
static void AddToDispatchList(
DispatchWatcher::DispatchList& rDispatchList,
boost::optional< rtl::OUString > const & cwdUrl,
const OUString& aRequestList,
DispatchWatcher::RequestType nType,
const OUString& aParam,
const OUString& aFactory )
{
if ( aRequestList.getLength() > 0 )
{
sal_Int32 nIndex = 0;
do
{
OUString aToken = aRequestList.getToken( 0, APPEVENT_PARAM_DELIMITER, nIndex );
if ( aToken.getLength() > 0 )
rDispatchList.push_back(
DispatchWatcher::DispatchRequest( nType, aToken, cwdUrl, aParam, aFactory ));
}
while ( nIndex >= 0 );
}
}
sal_Bool OfficeIPCThread::ExecuteCmdLineRequests( ProcessDocumentsRequest& aRequest )
{
// protect the dispatch list
osl::ClearableMutexGuard aGuard( GetMutex() );
static DispatchWatcher::DispatchList aDispatchList;
rtl::OUString aEmpty;
// Create dispatch list for dispatch watcher
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aOpenList, DispatchWatcher::REQUEST_OPEN, aEmpty, aRequest.aModule );
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aViewList, DispatchWatcher::REQUEST_VIEW, aEmpty, aRequest.aModule );
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aStartList, DispatchWatcher::REQUEST_START, aEmpty, aRequest.aModule );
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintList, DispatchWatcher::REQUEST_PRINT, aEmpty, aRequest.aModule );
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aPrintToList, DispatchWatcher::REQUEST_PRINTTO, aRequest.aPrinterName, aRequest.aModule );
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceOpenList, DispatchWatcher::REQUEST_FORCEOPEN, aEmpty, aRequest.aModule );
AddToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aForceNewList, DispatchWatcher::REQUEST_FORCENEW, aEmpty, aRequest.aModule );
sal_Bool bShutdown( sal_False );
if ( pGlobalOfficeIPCThread )
{
if( ! pGlobalOfficeIPCThread->AreRequestsEnabled() )
return bShutdown;
pGlobalOfficeIPCThread->mnPendingRequests += aDispatchList.size();
if ( !pGlobalOfficeIPCThread->mpDispatchWatcher )
{
pGlobalOfficeIPCThread->mpDispatchWatcher = DispatchWatcher::GetDispatchWatcher();
pGlobalOfficeIPCThread->mpDispatchWatcher->acquire();
}
// copy for execute
DispatchWatcher::DispatchList aTempList( aDispatchList );
aDispatchList.clear();
aGuard.clear();
// Execute dispatch requests
bShutdown = pGlobalOfficeIPCThread->mpDispatchWatcher->executeDispatchRequests( aTempList, s_bInEnableRequests );
// set processed flag
if (aRequest.pcProcessed != NULL)
aRequest.pcProcessed->set();
}
return bShutdown;
}
}