blob: 3da6ad2a2c55a991e76b9e62cb318f0c9edff7ff [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_automation.hxx"
#define ENABLE_BYTESTRING_STREAM_OPERATORS
#include <tools/solar.h>
#include <automation/simplecm.hxx>
#include <automation/commdefines.hxx>
#include "packethandler.hxx"
#include "tcpio.hxx"
#if OSL_DEBUG_LEVEL > 1
#include <stdio.h>
void debug_printf( const char *chars )
{
static sal_Bool bPrint = (getenv("DEBUG") != NULL);
if ( bPrint )
{
printf( chars );
fflush( stdout );
}
}
#endif
CommunicationLink::CommunicationLink( CommunicationManager *pMan )
: pMyManager(pMan)
, pServiceData(NULL)
, nServiceProtocol( 0 )
, bIsInsideCallback( sal_False )
, nTotalBytes( 0 )
, maApplication("Undefined")
#if OSL_DEBUG_LEVEL > 1
, bFlag( sal_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 = sal_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;
}
sal_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 );
sal_Bool bWasError = sal_False;
sal_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;
}
sal_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, vos::OStreamSocket *pSocket )
: CommunicationLink( pMan )
, aCommunicationPartner()
, aMyName()
, pStreamSocket( pSocket )
, pReceiveStream( NULL )
, bIsRequestShutdownPending( sal_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( vos::OStreamSocket* pSocket )
{
if ( pTCPIO )
pTCPIO->SetStreamSocket( pSocket );
pStreamSocket = pSocket;
}
sal_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 sal_True;
}
void SimpleCommunicationLinkViaSocket::SetFinalRecieveTimeout()
{
if ( !IsCommunicationError() )
{
TimeValue aTime = {30, 0}; // 30 seconds
pStreamSocket->setRecvTimeout( &aTime );
}
}
sal_Bool SimpleCommunicationLinkViaSocket::IsCommunicationError()
{
return !pStreamSocket;
}
ByteString SimpleCommunicationLinkViaSocket::GetCommunicationPartner( CM_NameType eType )
{
if ( pStreamSocket )
{
switch ( eType )
{
case CM_DOTTED:
{
rtl::OUString aDotted;
vos::OSocketAddr *pPeerAdr = new vos::OSocketAddr;
pStreamSocket->getPeerAddr( *pPeerAdr );
((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;
vos::OSocketAddr *pPeerAdr = new vos::OSocketAddr;
pStreamSocket->getLocalAddr( *pPeerAdr );
((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;}
sal_Bool SimpleCommunicationLinkViaSocket::DoReceiveDataStream()
{
sal_Bool bWasError = sal_False;
void* pBuffer = NULL;
comm_UINT32 nLen;
bWasError = pPacketHandler->ReceiveData( 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, sal_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();
}
sal_Bool SimpleCommunicationLinkViaSocket::SendHandshake( HandshakeType aHandshakeType, SvStream* pData )
{
sal_Bool bWasError;
if ( pData )
{
sal_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 = sal_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, 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();
}
sal_Bool SimpleCommunicationLinkViaSocketWithReceiveCallbacks::ReceiveDataStream()
{
if ( DoReceiveDataStream() )
{
SetNewPacketAsCurrent();
StartCallback();
DataReceived();
return sal_True;
}
else
{
StartCallback();
ShutdownCommunication();
return sal_False;
}
}
sal_Bool SimpleCommunicationLinkViaSocketWithReceiveCallbacks::ShutdownCommunication()
{
if ( GetStreamSocket() )
GetStreamSocket()->shutdown();
if ( GetStreamSocket() )
GetStreamSocket()->close();
vos::OStreamSocket *pTempSocket = GetStreamSocket();
SetStreamSocket( NULL );
delete pTempSocket;
ConnectionClosed();
return sal_True;
}
CommunicationManager::CommunicationManager( sal_Bool bUseMultiChannel )
: nInfoType( CM_NONE )
, bIsCommunicationRunning( sal_False )
, maApplication("Unknown")
, bIsMultiChannel( bUseMultiChannel )
{
}
CommunicationManager::~CommunicationManager()
{
xLastNewLink.Clear();
}
sal_Bool CommunicationManager::StartCommunication( String aApp, String aParams )
{
(void) aApp; /* avoid warning about unused parameter */
(void) aParams; /* avoid warning about unused parameter */
return sal_False;
}
sal_Bool CommunicationManager::StartCommunication( ByteString aHost, sal_uLong nPort )
{
(void) aHost; /* avoid warning about unused parameter */
(void) nPort; /* avoid warning about unused parameter */
return sal_False;
}
ByteString CommunicationManager::GetMyName( CM_NameType )
{
rtl::OUString aHostname;
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 = sal_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();
sal_uInt16 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, sal_Bool bRunningLinks )
{
maApplication = aApp;
if ( bRunningLinks )
{
sal_uInt16 i;
for ( i = 0 ; i < GetCommunicationLinkCount() ; i++ )
GetCommunicationLink( i )->SetApplication( aApp );
}
}
SingleCommunicationManager::SingleCommunicationManager( sal_Bool bUseMultiChannel )
: CommunicationManager( bUseMultiChannel )
{
xActiveLink = NULL;
pInactiveLink = NULL;
}
SingleCommunicationManager::~SingleCommunicationManager()
{
StopCommunication();
if ( pInactiveLink )
pInactiveLink->InvalidateManager();
}
sal_Bool SingleCommunicationManager::StopCommunication()
{
if ( xActiveLink.Is() )
{
sal_Bool bSuccess = xActiveLink->StopCommunication();
if ( pInactiveLink )
pInactiveLink->InvalidateManager();
pInactiveLink = xActiveLink;
xActiveLink.Clear();
return bSuccess;
}
return sal_True;
}
sal_Bool SingleCommunicationManager::IsLinkValid( CommunicationLink* pCL )
{
return &xActiveLink == pCL;
}
sal_uInt16 SingleCommunicationManager::GetCommunicationLinkCount()
{
return IsCommunicationRunning()?1:0;
}
CommunicationLinkRef SingleCommunicationManager::GetCommunicationLink( sal_uInt16 )
{
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 = sal_False;
}
void SingleCommunicationManager::DestroyingLink( CommunicationLink *pCL )
{
pInactiveLink = NULL;
pCL->InvalidateManager();
}
SingleCommunicationManagerClientViaSocket::SingleCommunicationManagerClientViaSocket( ByteString aHost, sal_uLong nPort, sal_Bool bUseMultiChannel )
: SingleCommunicationManager( bUseMultiChannel )
, aHostToTalk( aHost )
, nPortToTalk( nPort )
{
}
SingleCommunicationManagerClientViaSocket::SingleCommunicationManagerClientViaSocket( sal_Bool bUseMultiChannel )
: SingleCommunicationManager( bUseMultiChannel )
, aHostToTalk()
, nPortToTalk( 0 )
{
}
sal_Bool CommonSocketFunctions::DoStartCommunication( CommunicationManager *pCM, ICommunicationManagerClient *pCMC, ByteString aHost, sal_uLong nPort )
{
vos::OInetSocketAddr Addr;
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 vos::OConnectorSocket();
pConnSocket->setTcpNoDelay( 1 );
if ( pConnSocket->connect( Addr, &aTV ) == vos::ISocketTypes::TResult_Ok )
{
pConnSocket->setTcpNoDelay( 1 );
pCM->CallConnectionOpened( CreateCommunicationLink( pCM, pConnSocket ) );
return sal_True;
}
else
delete pConnSocket;
} while ( pCMC->RetryConnect() );
return sal_False;
}