diff options
Diffstat (limited to 'desktop/source/app/officeipcthread.cxx')
-rw-r--r-- | desktop/source/app/officeipcthread.cxx | 1043 |
1 files changed, 1043 insertions, 0 deletions
diff --git a/desktop/source/app/officeipcthread.cxx b/desktop/source/app/officeipcthread.cxx new file mode 100644 index 000000000000..652d4c8dd416 --- /dev/null +++ b/desktop/source/app/officeipcthread.cxx @@ -0,0 +1,1043 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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 <osl/process.h> +#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 desktop; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; + +using ::rtl::OString; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +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<osl::Security, Security> {}; } +::osl::Mutex* OfficeIPCThread::pOfficeIPCThreadMutex = 0; + +// Turns a string in aMsg such as file:///home/foo/.libreoffice/3 +// Into a hex string of well known length ff132a86... +String CreateMD5FromString( const OUString& aMsg ) +{ +#if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL + fprintf (stderr, "create md5 frim '%s'\n", + (const sal_Char *)rtl::OUStringToOString (aMsg, RTL_TEXTENCODING_UTF8)); +#endif + + 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 ); +} + +oslSignalAction SAL_CALL SalMainPipeExchangeSignal_impl(void* /*pData*/, oslSignalInfo* pInfo) +{ + if( pInfo->Signal == osl_Signal_Terminate ) + OfficeIPCThread::DisableOfficeIPCThread(); + return osl_Signal_ActCallNextHdl; +} + +// ---------------------------------------------------------------------------- + +// 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; + + 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; + + osl_getExecutableFile( &aIniName.pData ); + + 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.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "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 + { + osl::Security &rSecurity = Security::get(); + // Try to create pipe + if ( pThread->maPipe.create( pThread->maPipeIdent.getStr(), osl_Pipe_CREATE, rSecurity )) + { + // Pipe created + nPipeMode = PIPEMODE_CREATED; + } + else if( pThread->maPipe.create( pThread->maPipeIdent.getStr(), osl_Pipe_OPEN, rSecurity )) // Creation not successfull, now we try to connect + { + // Pipe connected to first office + nPipeMode = PIPEMODE_CONNECTED; + } + else + { + oslPipeError eReason = pThread->maPipe.getError(); + if ((eReason == osl_Pipe_E_ConnectionRefused) || (eReason == osl_Pipe_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 + osl::StreamPipe aStreamPipe(pThread->maPipe.getHandle()); + + 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 (!addArgument(&aArguments, ',', aDummy)) { + return IPC_STATUS_BOOTSTRAP_ERROR; + } + } + // finally, write the string onto the pipe + aStreamPipe.write( aArguments.GetBuffer(), aArguments.Len() ); + aStreamPipe.write( "\0", 1 ); + + ByteString aToken(sc_aConfirmationSequence); + char *aReceiveBuffer = new char[aToken.Len()+1]; + int n = aStreamPipe.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 + osl::StreamPipe aPipe ( pOfficeIPCThread->maPipeIdent, osl_Pipe_OPEN, Security::get() ); + if (aPipe.is()) + { + aPipe.send( sc_aTerminationSequence, sc_nTSeqLength+1 ); // also send 0-byte + + // close the pipe so that the streampipe on the other + // side produces EOF + aPipe.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 + { + oslPipeError nError = maPipe.accept( maStreamPipe ); + + + if( nError == osl_Pipe_E_None ) + { + // 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 ... + + // 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 + OUString aAcceptString; + if ( aCmdLineArgs->GetAcceptString(aAcceptString) && Desktop::CheckOEM()) { + ApplicationEvent* pAppEvent = + new ApplicationEvent( aEmpty, aEmpty, + "ACCEPT", aAcceptString ); + ImplPostForeignAppEvent( pAppEvent ); + } + // handle acceptor removal + OUString aUnAcceptString; + if ( aCmdLineArgs->GetUnAcceptString(aUnAcceptString) ) { + ApplicationEvent* pAppEvent = + new ApplicationEvent( aEmpty, aEmpty, + "UNACCEPT", aUnAcceptString ); + ImplPostForeignAppEvent( pAppEvent ); + } + +#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) ) ; + } + 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; + wait( 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 ); + } +} + +static void AddConversionsToDispatchList( + DispatchWatcher::DispatchList& rDispatchList, + boost::optional< rtl::OUString > const & cwdUrl, + const OUString& rRequestList, + const OUString& rParam, + const OUString& rPrinterName, + const OUString& rFactory, + const OUString& rParamOut ) +{ + DispatchWatcher::RequestType nType; + OUString aParam( rParam ); + + if( rParam.getLength() ) + { + nType = DispatchWatcher::REQUEST_CONVERSION; + aParam = rParam; + } + else + { + nType = DispatchWatcher::REQUEST_BATCHPRINT; + aParam = rPrinterName; + } + + OUString aOutDir( rParamOut.trim() ); + ::rtl::OUString aPWD; + ::tools::getProcessWorkingDir( aPWD ); + + if( !::osl::FileBase::getAbsoluteFileURL( aPWD, rParamOut, aOutDir ) ) + ::osl::FileBase::getSystemPathFromFileURL( aOutDir, aOutDir ); + + if( rParamOut.trim().getLength() ) + { + aParam += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(";")); + aParam += aOutDir; + } + else + { + ::osl::FileBase::getSystemPathFromFileURL( aPWD, aPWD ); + aParam += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( ";" )) + aPWD; + } + + if ( rRequestList.getLength() > 0 ) + { + sal_Int32 nIndex = 0; + do + { + OUString aToken = rRequestList.getToken( 0, APPEVENT_PARAM_DELIMITER, nIndex ); + if ( aToken.getLength() > 0 ) + rDispatchList.push_back( + DispatchWatcher::DispatchRequest( nType, aToken, cwdUrl, aParam, rFactory )); + } + 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.aInFilter, DispatchWatcher::REQUEST_INFILTER, aEmpty, aRequest.aModule ); + 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 ); + AddConversionsToDispatchList( aDispatchList, aRequest.aCwdUrl, aRequest.aConversionList, aRequest.aConversionParams, aRequest.aPrinterName, aRequest.aModule, aRequest.aConversionOut ); + 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; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |