1031 lines
37 KiB
C++
1031 lines
37 KiB
C++
/* -*- 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.
|
|
*
|
|
************************************************************************/
|
|
|
|
|
|
#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 <rtl/instance.hxx>
|
|
#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(rtl::OStringBuffer &rArguments, char prefix,
|
|
const rtl::OUString &rArgument)
|
|
{
|
|
rtl::OString utf8;
|
|
if (!rArgument.convertToString(
|
|
&utf8, RTL_TEXTENCODING_UTF8,
|
|
(RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
|
|
RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
|
|
{
|
|
return false;
|
|
}
|
|
rArguments.append(prefix);
|
|
for (sal_Int32 i = 0; i < utf8.getLength(); ++i) {
|
|
char c = utf8[i];
|
|
switch (c) {
|
|
case '\0':
|
|
rArguments.append("\\0");
|
|
break;
|
|
case ',':
|
|
rArguments.append("\\,");
|
|
break;
|
|
case '\\':
|
|
rArguments.append("\\\\");
|
|
break;
|
|
default:
|
|
rArguments.append(c);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
OfficeIPCThread* OfficeIPCThread::pGlobalOfficeIPCThread = 0;
|
|
namespace { struct Security : public rtl::Static<osl::Security, Security> {}; }
|
|
|
|
// 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 > 2)
|
|
fprintf( stderr, "create md5 from '%s'\n",
|
|
rtl::OUStringToOString (aMsg, RTL_TEXTENCODING_UTF8).getStr() );
|
|
#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 )
|
|
{
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class theOfficeIPCThreadMutex
|
|
: public rtl::Static<osl::Mutex, theOfficeIPCThreadMutex> {};
|
|
}
|
|
|
|
::osl::Mutex& OfficeIPCThread::GetMutex()
|
|
{
|
|
return theOfficeIPCThreadMutex::get();
|
|
}
|
|
|
|
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)
|
|
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());
|
|
|
|
rtl::OStringBuffer aArguments(RTL_CONSTASCII_STRINGPARAM(
|
|
ARGUMENT_PREFIX));
|
|
rtl::OUString cwdUrl;
|
|
if (!(tools::getProcessWorkingDir(cwdUrl) &&
|
|
addArgument(aArguments, '1', cwdUrl)))
|
|
{
|
|
aArguments.append('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.getStr(), aArguments.getLength());
|
|
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 )
|
|
{
|
|
::rtl::OUStringBuffer aStringListBuf(aStringList);
|
|
if ( aStringListBuf.getLength() )
|
|
aStringListBuf.append('\n');
|
|
aStringListBuf.append(aURL);
|
|
aStringList = aStringListBuf.makeStringAndClear();
|
|
}
|
|
|
|
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;
|
|
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;
|
|
}
|
|
const CommandLineArgs &rCurrentCmdLineArgs = 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(ApplicationEvent::TYPE_QUICKSTART);
|
|
ImplPostForeignAppEvent( pAppEvent );
|
|
}
|
|
|
|
// handle request for acceptor
|
|
OUString aAcceptString;
|
|
if ( aCmdLineArgs->GetAcceptString(aAcceptString) ) {
|
|
ApplicationEvent* pAppEvent = new ApplicationEvent(
|
|
ApplicationEvent::TYPE_ACCEPT, aAcceptString);
|
|
ImplPostForeignAppEvent( pAppEvent );
|
|
}
|
|
// handle acceptor removal
|
|
OUString aUnAcceptString;
|
|
if ( aCmdLineArgs->GetUnAcceptString(aUnAcceptString) ) {
|
|
ApplicationEvent* pAppEvent = new ApplicationEvent(
|
|
ApplicationEvent::TYPE_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(ApplicationEvent::TYPE_HELP);
|
|
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 ( !rCurrentCmdLineArgs.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() && (!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() ) {
|
|
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) {
|
|
aHelpURLBuffer.appendAscii("?Language=");
|
|
aHelpURLBuffer.append(utl::ConfigManager::getLocale());
|
|
#if defined UNX
|
|
aHelpURLBuffer.appendAscii("&System=UNX");
|
|
#elif defined WNT
|
|
aHelpURLBuffer.appendAscii("&System=WIN");
|
|
#endif
|
|
ApplicationEvent* pAppEvent = new ApplicationEvent(
|
|
ApplicationEvent::TYPE_OPENHELPURL,
|
|
aHelpURLBuffer.makeStringAndClear());
|
|
ImplPostForeignAppEvent( pAppEvent );
|
|
}
|
|
}
|
|
|
|
if ( bDocRequestSent )
|
|
{
|
|
// 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(ApplicationEvent::TYPE_APPEAR);
|
|
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, '\n', 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, '\n', 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: */
|