/**************************************************************
 * 
 * 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();
		// Try to create pipe
		if ( pThread->maPipe.create( pThread->maPipeIdent.getStr(), OPipe::TOption_Create, rSecurity ))
		{
			// Pipe created
			nPipeMode = PIPEMODE_CREATED;
		}
		else if( pThread->maPipe.create( pThread->maPipeIdent.getStr(), OPipe::TOption_Open, rSecurity )) // Creation not successfull, now we try to connect
		{
			// Pipe connected to first office
			nPipeMode = PIPEMODE_CONNECTED;
		}
		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;
}

}
