/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: simplecm.cxx,v $ * * $Revision: 1.7 $ * * last change: $Author: hr $ $Date: 2007-06-27 21:01:46 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_automation.hxx" #define ENABLE_BYTESTRING_STREAM_OPERATORS #include #ifndef _SIMPLECM_HXX #include #endif #include #include "packethandler.hxx" #include "tcpio.hxx" #if OSL_DEBUG_LEVEL > 1 #include void debug_printf( const char *chars ) { static BOOL bPrint = (getenv("DEBUG") != NULL); if ( bPrint ) { printf( chars ); fflush( stdout ); } } #endif CommunicationLink::CommunicationLink( CommunicationManager *pMan ) : pMyManager(pMan) , pServiceData(NULL) , nServiceProtocol( 0 ) , bIsInsideCallback( FALSE ) , nTotalBytes( 0 ) , maApplication("Undefined") #if OSL_DEBUG_LEVEL > 1 , bFlag( FALSE ) , nSomething( 0 ) #endif { } CommunicationLink::~CommunicationLink() { #if OSL_DEBUG_LEVEL > 1 if ( !bFlag ) // bFlag will be set if deletion is expected else we can set a breakpoint bFlag = FALSE; #endif if ( pMyManager ) pMyManager->DestroyingLink( this ); } void CommunicationLink::CallInfoMsg( InfoString aMsg ) { if ( pMyManager ) pMyManager->InfoMsg( aMsg ); }; CM_InfoType CommunicationLink::GetInfoType() { if ( pMyManager ) return pMyManager->GetInfoType(); else return CM_NO_TEXT; } IMPL_LINK( CommunicationLink, ConnectionClosed, void*, EMPTYARG ) { if ( pMyManager ) pMyManager->CallConnectionClosed( this ); return 1; } IMPL_LINK( CommunicationLink, DataReceived, void*, EMPTYARG ) { if ( pMyManager ) pMyManager->CallDataReceived( this ); return 1; } BOOL CommunicationLink::DoTransferDataStream( SvStream *pDataStream, CMProtocol nProtocol ) { INFO_MSG( CByteString("S :").Append( GetCommunicationPartner( CM_FQDN ) ), CByteString("Daten Senden:").Append( GetCommunicationPartner( CM_FQDN ) ), CM_SEND, this ); BOOL bWasError = FALSE; UINT32 nBuffer; nBuffer = pDataStream->SeekRel(0) +1; bWasError = pPacketHandler->TransferData( ((SvMemoryStream*)pDataStream)->GetData(), nBuffer, nProtocol ) != C_ERROR_NONE; if ( bWasError ) { INFO_MSG( CByteString("Send Failed:").Append( GetCommunicationPartner( CM_FQDN ) ), CByteString( "Socket wird wegen Fehlers beim Senden geschlossen: ").Append( GetCommunicationPartner( CM_FQDN ) ), CM_ERROR, this ); ShutdownCommunication(); } return !bWasError; } BOOL CommunicationLink::TransferDataStream( SvStream *pDataStream, CMProtocol nProtocol ) { aLastAccess = DateTime(); nTotalBytes += pDataStream->Seek( STREAM_SEEK_TO_END ); return DoTransferDataStream( pDataStream, nProtocol ); } void CommunicationLink::SetApplication( const ByteString& aApp ) { maApplication = aApp; } SimpleCommunicationLinkViaSocket::SimpleCommunicationLinkViaSocket( CommunicationManager *pMan, NAMESPACE_VOS(OStreamSocket) *pSocket ) : CommunicationLink( pMan ) , aCommunicationPartner() , aMyName() , pStreamSocket( pSocket ) , pReceiveStream( NULL ) , bIsRequestShutdownPending( FALSE ) { pTCPIO = new TCPIO( pStreamSocket ); pPacketHandler = new PacketHandler( (ITransmiter*) pTCPIO, pTCPIO, pMyManager->IsMultiChannel() ); } SimpleCommunicationLinkViaSocket::~SimpleCommunicationLinkViaSocket() { delete pPacketHandler; pPacketHandler = NULL; delete pTCPIO; pTCPIO = NULL; delete pStreamSocket; pStreamSocket = NULL; } void SimpleCommunicationLinkViaSocket::SetStreamSocket( NAMESPACE_VOS(OStreamSocket)* pSocket ) { if ( pTCPIO ) pTCPIO->SetStreamSocket( pSocket ); pStreamSocket = pSocket; } BOOL SimpleCommunicationLinkViaSocket::StopCommunication() { CommunicationLinkRef rHold(this); // avoid deleting this link before the end of the method if ( !IsCommunicationError() ) // Meaning that the Communication is still runnung { #if OSL_DEBUG_LEVEL > 1 debug_printf("Sending REQUEST_ShutdownLink\n"); #endif SendHandshake( CH_REQUEST_ShutdownLink ); } WaitForShutdown(); return TRUE; } void SimpleCommunicationLinkViaSocket::SetFinalRecieveTimeout() { if ( !IsCommunicationError() ) { TimeValue aTime = {30, 0}; // 30 seconds pStreamSocket->setRecvTimeout( &aTime ); } } BOOL SimpleCommunicationLinkViaSocket::IsCommunicationError() { return !pStreamSocket; } ByteString SimpleCommunicationLinkViaSocket::GetCommunicationPartner( CM_NameType eType ) { if ( pStreamSocket ) { switch ( eType ) { case CM_DOTTED: { rtl::OUString aDotted; NAMESPACE_VOS(OSocketAddr) *pPeerAdr = new NAMESPACE_VOS(OSocketAddr); pStreamSocket->getPeerAddr( *pPeerAdr ); ((NAMESPACE_VOS(OInetSocketAddr*))pPeerAdr)->getDottedAddr( aDotted ); delete pPeerAdr; return ByteString( UniString(aDotted), RTL_TEXTENCODING_UTF8 ); } //break; case CM_FQDN: { if ( !aCommunicationPartner.Len() ) { rtl::OUString aFQDN; pStreamSocket->getPeerHost( aFQDN ); aCommunicationPartner = ByteString( UniString(aFQDN), RTL_TEXTENCODING_UTF8 ); } return aCommunicationPartner; } //break; } } return CByteString( "Unknown" ); } ByteString SimpleCommunicationLinkViaSocket::GetMyName( CM_NameType eType ) { if ( pStreamSocket ) { switch ( eType ) { case CM_DOTTED: { rtl::OUString aDotted; NAMESPACE_VOS(OSocketAddr) *pPeerAdr = new NAMESPACE_VOS(OSocketAddr); pStreamSocket->getLocalAddr( *pPeerAdr ); ((NAMESPACE_VOS(OInetSocketAddr*))pPeerAdr)->getDottedAddr( aDotted ); delete pPeerAdr; return ByteString( UniString(aDotted), RTL_TEXTENCODING_UTF8 ); } //break; case CM_FQDN: { if ( !aMyName.Len() ) { rtl::OUString aFQDN; pStreamSocket->getLocalHost( aFQDN ); aMyName = ByteString( UniString(aFQDN), RTL_TEXTENCODING_UTF8 ); } return aMyName; } //break; } } return CByteString( "Error" ); } SvStream* SimpleCommunicationLinkViaSocket::GetBestCommunicationStream() { SvStream* pStream = new SvMemoryStream; // pStream->SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN ); return pStream; } #define READ_SOCKET( pBuffer, nLength )\ if ( !bWasError )\ {bWasError |= pTCPIO->ReceiveBytes( pBuffer, nLength ) != C_ERROR_NONE;} #define READ_SOCKET_LEN( pBuffer, nLength, nTotal )\ READ_SOCKET( pBuffer, nLength );\ if ( !bWasError )\ {nTotal += nLength;} BOOL SimpleCommunicationLinkViaSocket::DoReceiveDataStream() { BOOL bWasError = FALSE; char* pBuffer = NULL; comm_UINT32 nLen; bWasError = pPacketHandler->ReceiveData( (void* &)pBuffer, nLen ) != C_ERROR_NONE; if ( !bWasError ) { pReceiveStream = GetBestCommunicationStream(); DBG_ASSERT( pReceiveStream->IsA() == ID_MEMORYSTREAM, "CommunicationStream is not an SvMemoryStream. Communication has to be reimplemented here!") if ( pReceiveStream->IsA() == ID_MEMORYSTREAM ) ((SvMemoryStream*)pReceiveStream)->SetBuffer( pBuffer, nLen, TRUE, nLen ); DBG_ASSERT( pReceiveStream, "Datastream is NULL") } return !bWasError; } void SimpleCommunicationLinkViaSocket::SetApplication( const ByteString& aApp ) { CommunicationLink::SetApplication( aApp ); SvStream* pData = GetBestCommunicationStream(); *pData << aApp; SendHandshake( CH_SetApplication, pData ); delete pData; } void SimpleCommunicationLinkViaSocket::SetNewPacketAsCurrent() { pServiceData = pReceiveStream; nServiceProtocol = pPacketHandler->GetReceiveProtocol(); nServiceHeaderType = pPacketHandler->GetReceiveHeaderType(); } BOOL SimpleCommunicationLinkViaSocket::SendHandshake( HandshakeType aHandshakeType, SvStream* pData ) { BOOL bWasError; if ( pData ) { UINT32 nBuffer; nBuffer = pData->Seek( STREAM_SEEK_TO_END ); bWasError = !pPacketHandler->SendHandshake( aHandshakeType, ((SvMemoryStream*)pData)->GetData(), nBuffer ); } else bWasError = !pPacketHandler->SendHandshake( aHandshakeType ); if ( bWasError ) { INFO_MSG( CByteString("Send Failed:").Append( GetCommunicationPartner( CM_FQDN ) ), CByteString( "Socket wird wegen Fehlers beim Senden geschlossen: ").Append( GetCommunicationPartner( CM_FQDN ) ), CM_ERROR, this ); ShutdownCommunication(); } else { // set new status switch ( aHandshakeType ) { case CH_REQUEST_HandshakeAlive: break; case CH_RESPONSE_HandshakeAlive: break; case CH_REQUEST_ShutdownLink: bIsRequestShutdownPending = TRUE; break; case CH_ShutdownLink: break; case CH_SUPPORT_OPTIONS: break; case CH_SetApplication: break; default: DBG_ERROR("Unknown HandshakeType"); } } return !bWasError; } SimpleCommunicationLinkViaSocketWithReceiveCallbacks::SimpleCommunicationLinkViaSocketWithReceiveCallbacks( CommunicationManager *pMan, NAMESPACE_VOS(OStreamSocket) *pSocket ) : SimpleCommunicationLinkViaSocket( pMan, pSocket ) { } SimpleCommunicationLinkViaSocketWithReceiveCallbacks::~SimpleCommunicationLinkViaSocketWithReceiveCallbacks() { if ( pMyManager && pMyManager->IsLinkValid( this ) && !bIsRequestShutdownPending ) StopCommunication(); } void SimpleCommunicationLinkViaSocketWithReceiveCallbacks::WaitForShutdown() { CommunicationLinkRef rHold(this); // avoid deleting this link before the end of the method SetFinalRecieveTimeout(); while ( pMyManager && !IsCommunicationError() ) ReceiveDataStream(); } BOOL SimpleCommunicationLinkViaSocketWithReceiveCallbacks::ReceiveDataStream() { if ( DoReceiveDataStream() ) { SetNewPacketAsCurrent(); StartCallback(); DataReceived(); return TRUE; } else { StartCallback(); ShutdownCommunication(); return FALSE; } } BOOL SimpleCommunicationLinkViaSocketWithReceiveCallbacks::ShutdownCommunication() { if ( GetStreamSocket() ) GetStreamSocket()->shutdown(); if ( GetStreamSocket() ) GetStreamSocket()->close(); NAMESPACE_VOS(OStreamSocket) *pTempSocket = GetStreamSocket(); SetStreamSocket( NULL ); delete pTempSocket; ConnectionClosed(); return TRUE; } CommunicationManager::CommunicationManager( BOOL bUseMultiChannel ) : nInfoType( CM_NONE ) , bIsCommunicationRunning( FALSE ) , maApplication("Unknown") , bIsMultiChannel( bUseMultiChannel ) { } CommunicationManager::~CommunicationManager() { xLastNewLink.Clear(); } BOOL CommunicationManager::StartCommunication( String aApp, String aParams ) { (void) aApp; /* avoid warning about unused parameter */ (void) aParams; /* avoid warning about unused parameter */ return FALSE; } BOOL CommunicationManager::StartCommunication( ByteString aHost, ULONG nPort ) { (void) aHost; /* avoid warning about unused parameter */ (void) nPort; /* avoid warning about unused parameter */ return FALSE; } ByteString CommunicationManager::GetMyName( CM_NameType ) { rtl::OUString aHostname; NAMESPACE_VOS(OSocketAddr)::getLocalHostname( aHostname ); return ByteString( UniString(aHostname), RTL_TEXTENCODING_UTF8 ); } void CommunicationManager::CallConnectionOpened( CommunicationLink* pCL ) { pCL->StartCallback(); // Sollte bereits vor dem Aufruf gerufen werden pCL->aStart = DateTime(); pCL->aLastAccess = pCL->aStart; bIsCommunicationRunning = TRUE; pCL->SetApplication( GetApplication() ); xLastNewLink = pCL; INFO_MSG( CByteString("C+:").Append( pCL->GetCommunicationPartner( CM_FQDN ) ), CByteString("Verbindung aufgebaut: ").Append( pCL->GetCommunicationPartner( CM_FQDN ) ), CM_OPEN, pCL ); ConnectionOpened( pCL ); pCL->FinishCallback(); } void CommunicationManager::CallConnectionClosed( CommunicationLink* pCL ) { pCL->StartCallback(); // Sollte bereits vor dem Aufruf gerufen werden pCL->aLastAccess = DateTime(); INFO_MSG( CByteString("C-:").Append( pCL->GetCommunicationPartner( CM_FQDN ) ), CByteString("Verbindung abgebrochen: ").Append( pCL->GetCommunicationPartner( CM_FQDN ) ), CM_CLOSE, pCL ); ConnectionClosed( pCL ); if ( xLastNewLink == pCL ) xLastNewLink.Clear(); pCL->FinishCallback(); // delete pCL; } void CommunicationManager::CallDataReceived( CommunicationLink* pCL ) { pCL->StartCallback(); // Sollte bereits vor dem Aufruf gerufen werden pCL->aLastAccess = DateTime(); CommunicationLinkRef rHold(pCL); // Hält den Zeiger bis zum Ende des calls // should be impossible but happens for mysterious reasons if ( !pCL->pServiceData ) { DBG_ERROR( "Datastream is NULL" ) pCL->FinishCallback(); return; } if ( CH_Handshake == pCL->nServiceHeaderType ) { SvStream *pData = pCL->GetServiceData(); USHORT nType; pData->SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN ); // Unfortulately it is written this way :(( *pData >> nType; pData->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); switch ( nType ) { case CH_REQUEST_HandshakeAlive: { pCL->SendHandshake( CH_RESPONSE_HandshakeAlive ); } break; case CH_REQUEST_ShutdownLink: { #if OSL_DEBUG_LEVEL > 1 debug_printf("Sending ShutdownLink\n"); #endif pCL->SendHandshake( CH_ShutdownLink ); } break; case CH_ShutdownLink: { #if OSL_DEBUG_LEVEL > 1 debug_printf("Executing ShutdownLink\n"); #endif pCL->ShutdownCommunication(); } break; case CH_SetApplication: { ByteString aApplication; *pData >> aApplication; pCL->CommunicationLink::SetApplication( aApplication ); #if OSL_DEBUG_LEVEL > 1 debug_printf( "Setting Application to " ); debug_printf( aApplication.GetBuffer() ); debug_printf( "\n" ); #endif } break; #if OSL_DEBUG_LEVEL > 1 default: { debug_printf("Unknown Handshake received\n"); } #endif } delete pData; } else { if ( pCL->pServiceData ) { pCL->nTotalBytes += pCL->pServiceData->Seek( STREAM_SEEK_TO_END ); pCL->pServiceData->Seek( STREAM_SEEK_TO_BEGIN ); } INFO_MSG( CByteString("D :").Append( pCL->GetCommunicationPartner( CM_FQDN ) ), CByteString("Daten Empfangen:").Append( pCL->GetCommunicationPartner( CM_FQDN ) ), CM_RECEIVE, pCL ); DataReceived( pCL ); } delete pCL->GetServiceData(); pCL->FinishCallback(); } void CommunicationManager::CallInfoMsg( InfoString aMsg ) { // Hier wird es wohl kein Housekeeping geben InfoMsg( aMsg ); } void CommunicationManager::SetApplication( const ByteString& aApp, BOOL bRunningLinks ) { maApplication = aApp; if ( bRunningLinks ) { USHORT i; for ( i = 0 ; i < GetCommunicationLinkCount() ; i++ ) GetCommunicationLink( i )->SetApplication( aApp ); } } SingleCommunicationManager::SingleCommunicationManager( BOOL bUseMultiChannel ) : CommunicationManager( bUseMultiChannel ) { xActiveLink = NULL; pInactiveLink = NULL; } SingleCommunicationManager::~SingleCommunicationManager() { StopCommunication(); if ( pInactiveLink ) pInactiveLink->InvalidateManager(); } BOOL SingleCommunicationManager::StopCommunication() { if ( xActiveLink.Is() ) { BOOL bSuccess = xActiveLink->StopCommunication(); if ( pInactiveLink ) pInactiveLink->InvalidateManager(); pInactiveLink = xActiveLink; xActiveLink.Clear(); return bSuccess; } return TRUE; } BOOL SingleCommunicationManager::IsLinkValid( CommunicationLink* pCL ) { return &xActiveLink == pCL; } USHORT SingleCommunicationManager::GetCommunicationLinkCount() { return IsCommunicationRunning()?1:0; } CommunicationLinkRef SingleCommunicationManager::GetCommunicationLink( USHORT ) { return xActiveLink; } void SingleCommunicationManager::CallConnectionOpened( CommunicationLink* pCL ) { DBG_ASSERT( !xActiveLink.Is(), "Es ist bereits ein CommunicationLink aktiv"); if ( xActiveLink.Is() ) { if ( pInactiveLink ) pInactiveLink->InvalidateManager(); pInactiveLink = xActiveLink; xActiveLink->StopCommunication(); // Den alten Link brutal abwürgen } xActiveLink = pCL; CommunicationManager::CallConnectionOpened( pCL ); } void SingleCommunicationManager::CallConnectionClosed( CommunicationLink* pCL ) { CommunicationManager::CallConnectionClosed( pCL ); DBG_ASSERT( pCL == xActiveLink, "SingleCommunicationManager::CallConnectionClosed mit fremdem Link"); if ( pInactiveLink ) pInactiveLink->InvalidateManager(); pInactiveLink = xActiveLink; xActiveLink.Clear(); bIsCommunicationRunning = FALSE; } void SingleCommunicationManager::DestroyingLink( CommunicationLink *pCL ) { pInactiveLink = NULL; pCL->InvalidateManager(); } SingleCommunicationManagerClientViaSocket::SingleCommunicationManagerClientViaSocket( ByteString aHost, ULONG nPort, BOOL bUseMultiChannel ) : SingleCommunicationManager( bUseMultiChannel ) , aHostToTalk( aHost ) , nPortToTalk( nPort ) { } SingleCommunicationManagerClientViaSocket::SingleCommunicationManagerClientViaSocket( BOOL bUseMultiChannel ) : SingleCommunicationManager( bUseMultiChannel ) , aHostToTalk() , nPortToTalk( 0 ) { } BOOL CommonSocketFunctions::DoStartCommunication( CommunicationManager *pCM, ICommunicationManagerClient *pCMC, ByteString aHost, ULONG nPort ) { NAMESPACE_VOS(OInetSocketAddr) Addr; NAMESPACE_VOS(OConnectorSocket) *pConnSocket; Addr.setAddr( rtl::OUString( UniString( aHost, RTL_TEXTENCODING_UTF8 ) ) ); Addr.setPort( nPort ); TimeValue aTV; aTV.Seconds = 10; // Warte 10 Sekunden aTV.Nanosec = 0; do { pConnSocket = new NAMESPACE_VOS(OConnectorSocket)(); pConnSocket->setTcpNoDelay( 1 ); if ( pConnSocket->connect( Addr, &aTV ) == NAMESPACE_VOS(ISocketTypes::TResult_Ok) ) { pConnSocket->setTcpNoDelay( 1 ); pCM->CallConnectionOpened( CreateCommunicationLink( pCM, pConnSocket ) ); return TRUE; } else delete pConnSocket; } while ( pCMC->RetryConnect() ); return FALSE; }