/*
* Copyright (c) 2006 - 2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:    Creates connection to Connection Multiplexer and TURN client.
*                Handles connection specific issues and receives notifications
*                through implemented observers. Sends notifications to NAT FW
*                Client through NAT Protocol Plug-in observer.
*
*/




#include "mncmconnectionmultiplexer.h"
#include <cnatfwsettingsapi.h>
#include <mnatfwserversettings.h>
#include "natfwcandidate.h"
#include "mnatfwpluginobserver.h"
#include "natfwstunrelaybinding.h"
#include "natfwstunclient.h"
#include <mnatfwturnsettings.h>

#include "natfwturnconnectionhandler.h"
#include "natfwturnpluginlogs.h"
#include "natfwturnrefreshtimer.h"
#include "natfwturnstreamdata.h"
#include "natfwturnactivedestinationtimer.h"
#include "cturnasynccallback.h"
#include "cnatfwturnserversettings.h"

#include "natfwunsafmessage.h"
#include "natfwunsafmessagefactory.h"
#include "natfwunsafremoteaddressattribute.h"
#include "natfwunsafdataattribute.h"
#include "natfwunsaftcprelaypacket.h"

const TUint KDefaultStunSrvPort = 3478;
const TUint KDefaultRefreshInterval = 28000000;
const TUint KMicrosecFactor = 1000000;
const TUint32 KWaitTimeAfterReset = 5000000;
// transitioning time changed from 3000000=>0 to get ICE work
const TUint32 KDefaultSrvTransitioningTime = 0;
const TUint32 KRetryTimeAfter439 = 5000;
const TUint KNoBinding = 437;
const TUint KServerTransitioning = 439;
const TUint KTryAlternate = 300;
const TInt KClientSpecificError = -11000;


// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------------------------
// C++ default constructor
// ---------------------------------------------------------------------------
//
CNATFWTurnConnectionHandler::CNATFWTurnConnectionHandler(
    const CNATFWPluginApi& aTurnPlugin )
    :
    iTurnPlugin( aTurnPlugin )
    {

    }


// ---------------------------------------------------------------------------
// Symbian constructor
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::ConstructL(
    MNATFWPluginObserver& aPluginObserver )
    {
    iTimerServ = CDeltaTimer::NewL( CActive::EPriorityStandard );
    iTurnRefreshTimer = CNATFWTurnRefreshTimer::NewL( *this );
    iActiveDestinationTimer = CNATFWTurnActiveDestinationTimer::NewL( *this );
    iAsyncCallback = CTurnAsyncCallback::NewL( iTurnPlugin, aPluginObserver );
    }


// ---------------------------------------------------------------------------
// Symbian constructor
// ---------------------------------------------------------------------------
//
CNATFWTurnConnectionHandler* CNATFWTurnConnectionHandler::NewL(
    const CNATFWPluginApi& aTurnPlugin,
    MNATFWPluginObserver& aPluginObserver )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::NewL" )
    CNATFWTurnConnectionHandler* self = CNATFWTurnConnectionHandler::NewLC(
        aTurnPlugin, aPluginObserver );
    CleanupStack::Pop( self );
    return self;
    }


// ---------------------------------------------------------------------------
// Symbian constructor
// ---------------------------------------------------------------------------
//
CNATFWTurnConnectionHandler* CNATFWTurnConnectionHandler::NewLC(
    const CNATFWPluginApi& aTurnPlugin,
    MNATFWPluginObserver& aPluginObserver )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::NewLC" )
    CNATFWTurnConnectionHandler* self =
        new( ELeave ) CNATFWTurnConnectionHandler( aTurnPlugin );
    CleanupStack::PushL( self );
    self->ConstructL( aPluginObserver );
    return self;
    }


// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CNATFWTurnConnectionHandler::~CNATFWTurnConnectionHandler()
    {
    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::~CNATFWTurnConnectionHandler start" )

    iServerList.ResetAndDestroy();

    delete iDomain;
    delete iNatSettings;
    delete iTurnRefreshTimer;
    delete iActiveDestinationTimer;

    TInt count( iStreamArray.Count() );
    
    for ( TInt index = count - 1; index >= 0; index-- )
        {
        DeleteStream( index, ETrue );
        }

    iStreamArray.Close();
    delete iStunClient;
    delete iTimerServ;
    iConnection.Close();
    iConnMux = NULL;
    delete iAsyncCallback;

    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::~CNATFWTurnConnectionHandler end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::PluginInitializeL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::PluginInitializeL(
    TUint32 aIapId,
    const TDesC8& aDomain,
    MNcmConnectionMultiplexer& aMultiplexer )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::PluginInitializeL start" )
    iConnMux = &aMultiplexer;
    delete iDomain;
    iDomain = NULL;
    iDomain = aDomain.AllocL();
    delete iNatSettings;
    iNatSettings = NULL;
    iNatSettings = CNATFWNatSettingsApi::NewL( *iDomain );        
    iNatSettings->RetrieveIapSettingsL( aIapId );
    iTurnSettings = &iNatSettings->TurnSettingsL();
    
    // Generates Server list
    this->GenerateServerListL();
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::PluginInitializeL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::ConnectServerL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::ConnectServerL(
    const RSocketServ& aSocketServ,
    const TName& aConnectionName )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::ConnectServerL start" )
    
    iSocketServ = aSocketServ;
    TName name( aConnectionName );
    User::LeaveIfError( iConnection.Open( iSocketServ, name ) );
    this->TryNextServerL();
    
    // wait MNATFWStunClientObserver::STUNClientInitCompleted
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::ConnectServerL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::FetchCandidateL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::FetchCandidateL( TUint aStreamId,
    TUint aRtoValue, TUint aAddrFamily, const TInetAddr& aBaseAddr )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::FetchCandidateL start" )
    
    __ASSERT_ALWAYS( iStunClient, User::Leave( KErrNotReady) );
    __ASSERT_ALWAYS( iStunClient->IsInitialized(), User::Leave(
        KErrNotReady) );
    
    TUint connectionId( aBaseAddr.IsUnspecified() ?
        iConnMux->CreateConnectionL( aStreamId, aAddrFamily ) :
        iConnMux->CreateConnectionL( aStreamId, aBaseAddr ) );
    
    TConnectionData connData;
    connData.iConnectionId = connectionId;
    connData.iServerAddr.SetAddress( iStunClient->STUNServerAddrL().
        Address() );
    connData.iServerAddr.SetPort( iStunClient->STUNServerAddrL().Port() );
    connData.iLocalAddr = iConnMux->LocalIPAddressL( aStreamId,
        connectionId );
    
    TInt index( IndexByStreamId( aStreamId ) );
    
    if ( KErrNotFound == index )
        {
        TUint32 iapID( 0 );
        TInt qos( 0 );
        
        // The stream is new for the plug-in
        iConnMux->RegisterConnectionObserverL( aStreamId, *this );
        iConnMux->RegisterIncomingConnectionObserverL( aStreamId, *this );
        iConnMux->RegisterOutgoingConnectionObserverL( aStreamId, *this );
        iConnMux->RegisterMessageObserverL( aStreamId, *this );
        
        TStreamData streamData;
        streamData.iStreamId = aStreamId;
        iConnMux->GetStreamInfoL( aStreamId, iapID, qos,
            streamData.iTransportProtocol );
        streamData.iRtoValue = aRtoValue;
        streamData.iConnArray.AppendL( connData );
        CleanupClosePushL( streamData.iConnArray );
        iStreamArray.AppendL( streamData );
        CleanupStack::Pop( &streamData.iConnArray );
        }
    else
        {
        // Store new connection for the existing stream in array
        iStreamArray[index].iConnArray.AppendL( connData );
        }
    
    // Accept data only from TURN server
    iConnMux->SetAcceptedFromAddressL( aStreamId, connectionId, 
        connData.iServerAddr );
    
    // Set receiving and sending state active for the created connection
    iConnMux->SetReceivingStateL( aStreamId, connectionId,
        EStreamingStateActive );
    
    iConnMux->SetSendingStateL( aStreamId, connectionId, connData.iServerAddr,
        EStreamingStateActive );
    // wait MNcmConnectionObserver::ConnectionNotify
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::FetchCandidateL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::GetConnectionIdL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::GetConnectionIdL(
    const CNATFWCandidate& aLocalCandidate,
    TUint aStreamId,
    TUint& aConnectionId )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::GetConnectionIdL start" )
    
    TInt streamInd( IndexByStreamId( aStreamId ) );
    TInt connInd( KErrNotFound );
    TBool connFound( EFalse );
    
    if ( KErrNotFound != streamInd )
        {
        connInd = iStreamArray[streamInd].iConnArray.Count() - 1;
        }
    
    while ( KErrNotFound != streamInd && !connFound && ( 0 <= connInd ) )
        {
        TConnectionData* connection = ConnectionByIndex( streamInd, connInd );
        
        if ( CNATFWCandidate::ERelay == aLocalCandidate.Type() &&
            MatchAddresses( aLocalCandidate.Base(), connection->
            iLocalCandidate->Base() ) && MatchAddresses( aLocalCandidate.
            TransportAddr(), connection->iLocalCandidate->TransportAddr() ) )
            {
            connFound = ETrue;
            aConnectionId = connection->iConnectionId;
            }
        
        connInd--;
        }
    
    __ASSERT_ALWAYS( connFound, User::Leave( KErrNotFound ) );
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::GetConnectionIdL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::StartTurnRefresh
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::StartTurnRefresh()
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::StartTurnRefresh start" )
    
    iTurnRefreshStarted = EFalse;
    iTurnRefreshTimer->StartTurnRefresh( iTurnRefreshInterval );
    
    if ( iTurnRefreshTimer->IsRunning() )
        {
        iTurnRefreshStarted = ETrue;
        }

    __TURNPLUGIN( "CNATFWTurnConnectionHandler::StartTurnRefresh end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::CreateTURNBindingL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::CreateTURNBindingL( TUint aStreamId,
                                                      TUint aConnectionId )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::CreateTURNBindingL" )
    
    TInt streamInd( IndexByStreamId( aStreamId ) );
    TConnectionData* connection( NULL );
    
    if ( KErrNotFound != streamInd )
        {
        connection = ConnectionById( streamInd, aConnectionId );
        }
    
    if ( connection )
        {
        CSTUNRelayBinding* turnBinding = CSTUNRelayBinding::NewL(
            *iStunClient, aStreamId, aConnectionId );
        
        CleanupStack::PushL( turnBinding );
        delete connection->iTurnBinding;
        connection->iTurnBinding = turnBinding;
        turnBinding->AllocateRequestL( iStreamArray[streamInd].iRtoValue );
        CleanupStack::Pop( turnBinding );
        // wait MStunClientObserver::STUNBindingEventOccurredL
        }
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::SetReceivingStateL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::SetReceivingStateL(
    const CNATFWCandidate& aLocalCandidate,
    TNATFWStreamingState aState )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::SetReceivingStateL start" )
    
    TUint streamId( aLocalCandidate.StreamId() );
    TUint connId( 0 );
    TConnectionData* connection = NULL;
    TRAPD( err, GetConnectionIdL( aLocalCandidate, streamId, connId ) );
    
    if ( !err )
        {
        TInt streamInd( IndexByStreamId( streamId ) );
        connection = ConnectionById( streamInd, connId );
        }
    
    if ( connection )
        {
        if ( EStreamingStateActive == aState )
            {
            if ( connection->iReceivingActivated  )
                {
                iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                    EActiveReceiving, streamId, KErrNone, NULL );
                }
            else
                {
                iConnMux->SetReceivingStateL( streamId, connId,
                    EStreamingStateActive );
                }
            }
        else
            {
            if ( !connection->iReceivingActivated )
                {
                iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                    EDeactiveReceiving, streamId, KErrNone, NULL );
                }
            else
                {
                iConnMux->SetReceivingStateL( streamId, connId,
                    EStreamingStatePassive );
                }
            }
        }
    else
        {
        if ( EStreamingStateActive == aState )
            {
            iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                EActiveReceiving, streamId, KErrNotFound, NULL );
            }
        else
            {
            iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                EDeactiveReceiving, streamId, KErrNotFound, NULL );
            }
        }
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::SetReceivingStateL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::SetSendingStateL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::SetSendingStateL(
    const CNATFWCandidate& aLocalCandidate,
    TNATFWStreamingState aState,
    const TInetAddr& aDestAddr )
    {
    __TURNPLUGIN_ADDRLOG(
    "CNATFWTurnConnectionHandler::SetSendingStateL start",
        aDestAddr )
    
    TUint streamId( aLocalCandidate.StreamId() );
    TUint connId( 0 );
    TConnectionData* connection = NULL;
    TInt streamInd( IndexByStreamId( streamId ) );
    TRAPD( err, GetConnectionIdL( aLocalCandidate, streamId, connId ) );
    
    if ( !err )
        {
        connection = ConnectionById( streamInd, connId );
        }
    
    if ( connection )
        {
        if ( EStreamingStateActive == aState && TConnectionData::
            EActDestTransitioning == connection->iActDestState )
            {
            // Only Active Destination reset (sending passivation)
            // can be done when in "Transitioning" state.
            iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                EActiveSending, streamId, KErrNotReady, NULL );
            }
        else
            {
            if ( EStreamingStateActive == aState )
                {
                // Do Set Active Destination Request if the remote
                // address differs from current active destination
                
                if ( !MatchAddresses( connection->iCurrentActDest,
                    aDestAddr ) )
                    {
                    connection->iPeerAddr.SetAddress( aDestAddr.Address() );
                    connection->iPeerAddr.SetPort( aDestAddr.Port() );
                    
                    if ( KProtocolInetTcp == iStreamArray[streamInd].
                        iTransportProtocol )
                        {
                        // We need to do the Connect Request first
                        // and have successfull response to it.
                        connection->iTurnBinding->ConnectRequestL(
                            connection->iPeerAddr );
                        }
                    else
                        {
                        connection->iTurnBinding
                            ->SetActiveDestinationRequestL(
                                connection->iPeerAddr, 
                                connection->iTimerValue );
                        }
                    }
                else
                    {
                    // No need to set this destination active again
                    iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                        EActiveSending, streamId, KErrNone, NULL );
                    }
                }
            else
                {
                // Plug-in's client wants to reset Active Destination.
                connection->iPeerAddr = KAFUnspec;
                
                connection->iTurnBinding->SetActiveDestinationRequestL(
                    connection->iPeerAddr, connection->iTimerValue );
                }
            }
        }
    else
        {
        if ( EStreamingStateActive == aState )
            {
            iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                EActiveSending, streamId, KErrNotFound, NULL );
            }
        else
            {
            iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                EDeactiveSending, streamId, KErrNotFound, NULL );
            }
        }
    
    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::SetSendingStateL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::IsRequestOrIndicationL
// ---------------------------------------------------------------------------
//
TBool CNATFWTurnConnectionHandler::IsRequestOrIndicationL(
    const TDesC8& aMessage ) const
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::IsRequestOrIndicationL" )

    CNATFWUNSAFMessageFactory* decoder = CNATFWUNSAFMessageFactory::NewLC();
    CNATFWUNSAFMessage* msg = decoder->DecodeL( aMessage );
    CleanupStack::PopAndDestroy( decoder );

    if ( CNATFWUNSAFMessage::EAllocateRequest == msg->Type() ||
         CNATFWUNSAFMessage::EConnectRequest == msg->Type() ||
         CNATFWUNSAFMessage::ESetActiveDestinationRequest == msg->Type() ||
         CNATFWUNSAFMessage::ESendIndication == msg->Type() )
        {
        delete msg;
        return ETrue;
        }
    else
        {
        delete msg;
        return EFalse;
        }
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::IsTurnResponseL
// ---------------------------------------------------------------------------
//
TBool CNATFWTurnConnectionHandler::IsTurnResponseL(
    const TDesC8& aMessage ) const
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::IsTurnResponseL" )

    CNATFWUNSAFMessageFactory* decoder = CNATFWUNSAFMessageFactory::NewLC();
    CNATFWUNSAFMessage* msg = decoder->DecodeL( aMessage );
    CleanupStack::PopAndDestroy( decoder );

    if ( CNATFWUNSAFMessage::EAllocateResponse == msg->Type() ||
         CNATFWUNSAFMessage::EAllocateErrorResponse == msg->Type() ||
         CNATFWUNSAFMessage::ESetActiveDestinationResponse == msg->Type() ||
         CNATFWUNSAFMessage::ESetActiveDestinationErrorResponse ==
            msg->Type() ||
         CNATFWUNSAFMessage::EConnectResponse == msg->Type() ||
         CNATFWUNSAFMessage::EConnectErrorResponse == msg->Type() )
        {
        delete msg;
        return ETrue;
        }
    else
        {
        delete msg;
        return EFalse;
        }
    }


// ---------------------------------------------------------------------------
// From base class MStunClientObserver.
// CNATFWTurnConnectionHandler::STUNClientInitCompleted
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::STUNClientInitCompleted(
    const CSTUNClient& /*aClient*/,
    TInt aCompletionCode )
    {
    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::STUNClientInitCompleted start" )
    
    if ( KErrNone == aCompletionCode )
        {
        TRAP_IGNORE( iAsyncCallback->MakeCallbackL(
            TTurnPluginCallbackInfo::EConnectServer, 0,
            aCompletionCode, NULL ) )
        }
    else
        {
        TInt error( KErrNone );
        TRAP( error, TryNextServerL() );
        
        if ( KErrNone != error )
            {
            delete iStunClient;
            iStunClient = NULL;
            
            TRAP_IGNORE( iAsyncCallback->MakeCallbackL(
                TTurnPluginCallbackInfo::EConnectServer, 0,
                ( KClientSpecificError + aCompletionCode), NULL ) )
            }
        }
    
    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::STUNClientInitCompleted end" )
    }


// ---------------------------------------------------------------------------
// From base class MStunClientObserver
// CNATFWTurnConnectionHandler::STUNBindingEventOccurredL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::STUNBindingEventOccurredL(
    TSTUNBindingEvent aEvent,
    const CBinding& aBinding )
    {
    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::STUNBindingEventOccurredL start" )
    
    const CSTUNRelayBinding& turnBinding =
        static_cast<const CSTUNRelayBinding&>( aBinding );
    TUint streamId( turnBinding.StreamId() );
    TUint connId( turnBinding.ConnectionId() );
    
    TInt streamInd( IndexByStreamId( streamId ) );
    TConnectionData* connection( NULL );
    
    if ( KErrNotFound != streamInd )
        {
        connection = ConnectionById( streamInd, connId );
        }
    
    if ( connection )
        {
        switch ( aEvent )
            {
            case EPublicAddressResolved:
                {
                if ( !connection->iLocalCandidate )
                    {
                    if ( iTurnRefreshTimer && !iTurnRefreshStarted )
                        {
                        StartTurnRefresh();
                        }
                    
                    TInetAddr relayAddr;
                    relayAddr.SetAddress( turnBinding.RelayAddr().Address() );
                    relayAddr.SetPort( turnBinding.RelayAddr().Port() );
                    
                    __TURNPLUGIN_ADDRLOG( "CNATFWTurnConnectionHandler::\
                    STUNBindingEventOccurredL  Adress Resolved ADDRESS: ",
                        relayAddr )
                    
                    CNATFWCandidate* newCandidate = CNATFWCandidate::NewL();
                    CleanupStack::PushL( newCandidate );
                    
                    // Set candidate parameters
                    newCandidate->SetStreamId( streamId );
                    newCandidate->SetType( CNATFWCandidate::ERelay );
                    newCandidate->SetBase( relayAddr );
                    newCandidate->SetTransportAddrL( relayAddr );
                    newCandidate->SetTransportProtocol(
                        iStreamArray[streamInd].iTransportProtocol );
                    
                    CNATFWCandidate* copyCandidate = CNATFWCandidate::NewL(
                        *newCandidate );
                    connection->iLocalCandidate = copyCandidate;
                    
                    iAsyncCallback->MakeCallbackL(
                        TTurnPluginCallbackInfo::ELocalCandidateFound,
                            streamId, KErrNone, newCandidate );
                    
                    iAsyncCallback->MakeCallbackL(
                        TTurnPluginCallbackInfo::EFetchingEnd, streamId,
                            KErrNone, NULL );
                    
                    CleanupStack::Pop( newCandidate );
                    }
                }
                break;
            
            case ECredentialsRejected:
                {
                __TURNPLUGIN( "CNATFWTurnConnectionHandler::\
                    STUNBindingEventOccurredL  Credentials Rejected" )
                
                // TURN server rejected the credentials provided that were
                // set with CSTUNClient::SetCredentialsL. Application
                // should obtain valid credentials and then use
                // CSTUNClient::SetCredentialsL.
                iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                    EFetchingEnd, streamId, ( KClientSpecificError +
                        KErrPermissionDenied ), NULL );

                // Remove failed entry from array
                DeleteStream( streamInd, ETrue );
                }
                break;
            
            case ETCPConnectOk:
                {
                __TURNPLUGIN( "CNATFWTurnConnectionHandler::\
                    STUNBindingEventOccurredL  TCP Connect Ok" )
                
                // After TCP connect is OK we can set Active Destination.
                connection->iTurnBinding->SetActiveDestinationRequestL(
                    connection->iPeerAddr, connection->iTimerValue );
                }
                break;
            
            // Success
            case EActiveDestinationSet:
                {
                __TURNPLUGIN( "CNATFWTurnConnectionHandler::\
                    STUNBindingEventOccurredL  Active Destination Set" )

                if ( TConnectionData::EActDestNoneSet == connection->
                    iActDestState && !connection->iPeerAddr.IsUnspecified() )
                    {
                    if ( connection->iActDestAlreadySetOnServer )
                        {
                        // Active Destination was reset on server. Media
                        // flows in indications while trying to set the
                        // Active Destination again after 5 seconds.
                        iAsyncCallback->MakeCallbackL(
                            TTurnPluginCallbackInfo::EActiveSending,
                                iStreamArray[streamInd].iStreamId, KErrNone,
                                    NULL );

                        iActiveDestinationTimer->StartTimer(
                            KWaitTimeAfterReset, iStreamArray[streamInd].
                                iStreamId, connection->iConnectionId );
                        }
                    else
                        {
                        iAsyncCallback->MakeCallbackL(
                            TTurnPluginCallbackInfo::EActiveSending,
                                iStreamArray[streamInd].iStreamId, KErrNone,
                                    NULL );
                        
                        iActiveDestinationTimer->StartTimer(
                            KDefaultSrvTransitioningTime,
                                iStreamArray[streamInd].iStreamId,
                                    connection->iConnectionId );
                        
                        // If a successful response was received, but
                        // there was a REMOTE-ADDRESS in the request, the
                        // state machine transitions to the "Set" state
                        // after three seconds, and the client sets the
                        // active destination to the value of the REMOTE-
                        // ADDRESS attribute that was in the request.
                        }
                    }
                
                // else If, while in the "None Set" state, the client
                // sent a Set Active Destination request without a
                // REMOTE-ADDRESS, and got a successful response,
                // there is no change in state.
                
                else if ( TConnectionData::EActDestSet == connection->
                    iActDestState )
                    {
                    if ( connection->iPeerAddr.IsUnspecified() ||
                        !MatchAddresses( connection->iPeerAddr, connection->
                        iCurrentActDest ) )
                        {
                        connection->iActDestState = TConnectionData::
                            EActDestTransitioning;
                        
                        // the client enters the "Transitioning" state.
                        // While in this state, the client MUST NOT send
                        // a new Set Active Destination request. The value
                        // of the active destination remains unchanged. In
                        // addition, the client sets a timer. This timer
                        // MUST have a value equal to the value of the
                        // TIMER-VAL attribute from the Set Active
                        // Destination response.  This is necessary for
                        // coordinating the state machines between client
                        // and server.
                        
                        iActiveDestinationTimer->StartTimer(
                            connection->iTimerValue, iStreamArray[streamInd].
                                iStreamId, connection->iConnectionId );
                        }
                    
                    // In addition, if, while in the "Set" state, the
                    // client sends a Set Active Destination request whose
                    // REMOTE-ADDRESS attribute equals the current active
                    // destination, and that request generates a success
                    // response, the client remains in the "Set" state.
                    }
                else
                    {
                    // Only Active Destination reset request can be done when
                    // in "Transitioning" state.
                    if ( connection->iPeerAddr.IsUnspecified() )
                        {
                        iActiveDestinationTimer->StartTimer( connection->
                            iTimerValue, iStreamArray[streamInd].iStreamId,
                                connection->iConnectionId );
                        }
                    }
                }
                break;
            }
        }

    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::STUNBindingEventOccurredL end" )
    }


// ---------------------------------------------------------------------------
// From base class MStunClientObserver
// CNATFWTurnConnectionHandler::STUNBindingErrorOccurred
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::STUNBindingErrorOccurred(
    const CBinding& aBinding,
    TInt aError )
    {
    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::STUNBindingErrorOccurred start" )

    const CSTUNRelayBinding& turnBinding =
        static_cast<const CSTUNRelayBinding&>( aBinding );
    TUint streamId( turnBinding.StreamId() );
    TUint connId( turnBinding.ConnectionId() );
    
    TInt streamInd( IndexByStreamId( streamId ) );
    TConnectionData* connection( NULL );
    
    if ( KErrNotFound != streamInd )
        {
        connection = ConnectionById( streamInd, connId );
        }
    
    if ( connection )
        {
        if ( KNoBinding == aError && connection->iLocalCandidate )
            {
            // A 437 response implies that the allocation has been
            // removed, and thus the state machine destroyed. A client
            // MUST NOT send a new Set Active Destination request prior
            // to the receipt of a response to the previous. The state
            // machine will further limit the transmission of subsequent
            // Set Active Destination requests.
            TRAP_IGNORE( iAsyncCallback->MakeCallbackL(
                TTurnPluginCallbackInfo::EActiveSending, streamId,
                    KErrNotFound, NULL ) )

            // Remove entry from array as binding does not exist anymore
            DeleteStream( streamInd, ETrue );
            }
        else if ( KServerTransitioning == aError && connection->
            iLocalCandidate )
            {
            if ( TConnectionData::EActDestSet == connection->iActDestState )
                {
                // When this error is received, the client remains in the
                // "Set" state. The client SHOULD retry its Set Active
                // Destination request, but no sooner than 500ms after
                // receipt of the 439 response.
                iActiveDestinationTimer->StartTimer(
                    KRetryTimeAfter439, iStreamArray[streamInd].iStreamId,
                        connection->iConnectionId );
                }
            else if ( TConnectionData::EActDestNoneSet == connection->
                iActDestState )
                {
                // Set Active Destination request was received by the
                // server over UDP. However, the active destination is
                // already set to another value. The client should reset
                // the active destination, wait for 5 seconds and set the
                // active destination to the new value.
                connection->iActDestAlreadySetOnServer = ETrue;
                
                TRAP_IGNORE( connection->iTurnBinding->
                    SetActiveDestinationRequestL( KAFUnspec, connection->
                        iTimerValue ) )
                }
            else
                {
                // Not possible that this error occurs when in
                // "transitioning" state
                }
            }
        else
            {
            if ( !connection->iLocalCandidate )
                {
                if( KTryAlternate == aError )
                    {
                    __TURNPLUGIN( "CNATFWTurnConnectionHandler::\
                    STUNBindingErrorOccurred  KTryAlternate == aError" )
                    
                    connection->iServerAddr.SetAddress( turnBinding.
                        AlternateServerAddr().Address() );
                    connection->iServerAddr.SetPort( turnBinding.
                        AlternateServerAddr().Port() );
                    
                    delete connection->iTurnBinding;
                    connection->iTurnBinding = NULL;
                    
                    TRAP_IGNORE( iConnMux->SetSendingStateL( streamId, connId,
                        connection->iServerAddr, EStreamingStateActive ) )
                    // wait MNcmConnectionObserver::ConnectionNotify
                    }
                else
                    {
                    // If error occurs when trying to fetch candidate
                    TInt errcode( KClientSpecificError + ( aError ) );
                    TRAP_IGNORE( iAsyncCallback->MakeCallbackL(
                        TTurnPluginCallbackInfo::EFetchingEnd, streamId,
                            errcode, NULL ) )
                    
                    // Remove failed entry from array
                    DeleteStream( streamInd, ETrue );
                    }
                }
            else
                {
                // Do nothing if error occurs when doing binding refresh
                }
            }
        }

    __TURNPLUGIN(
        "CNATFWTurnConnectionHandler::STUNBindingErrorOccurred end" )
    }


// ---------------------------------------------------------------------------
// from base class MNcmIncomingConnectionObserver
// CNATFWTurnConnectionHandler::IncomingMessageL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::IncomingMessageL(
    TUint aStreamId,
    const TDesC8& aMessage,
    const TInetAddr& /*aLocalAddr*/,
#ifdef _DEBUG
    const TInetAddr& aFromAddr,
    const TInetAddr& aPeerAddr,
#else
    const TInetAddr& /*aFromAddr*/,
    const TInetAddr& /*aPeerAddr*/,
#endif
    TBool& aConsumed )
    {
    __TURNPLUGIN_ADDRLOG(
        "CNATFWTurnConnectionHandler::IncomingMessageL FROMADDR", aFromAddr )
    __TURNPLUGIN_ADDRLOG(
        "CNATFWTurnConnectionHandler::IncomingMessageL PEERADDR", aPeerAddr )
    
    TInetAddr peerAddrFromIndication( KAFUnspec );
    HBufC8* indicationData( NULL );
    TInt consumingBindingInd( KErrNotFound );
    
    TInt streamInd( IndexByStreamId( aStreamId ) );
    TInt connInd( KErrNotFound );
    
    if ( KErrNotFound != streamInd )
        {
        connInd = iStreamArray[streamInd].iConnArray.Count() - 1;
        }
    
    // Offer message for every binding in the stream until consumed
    while ( KErrNotFound != streamInd && KErrNotFound == consumingBindingInd
        && ( 0 <= connInd ) )
        {
        __TURNPLUGIN( "CNATFWTurnConnectionHandler::IncomingMessageL\
        HandleDataL method to be called" )
        
        TBool consumed( EFalse );
        TConnectionData* connection = ConnectionByIndex( streamInd, connInd );
        
        if ( connection->iTurnBinding )
            {
            if ( indicationData )
                {
                connection->iTurnBinding->HandleDataL( *indicationData,
                    consumed, peerAddrFromIndication );
                }
            else
                {
                indicationData = connection->iTurnBinding->HandleDataL(
                    aMessage, consumed, peerAddrFromIndication );
                }
            }
        
        if ( consumed )
            {
            consumingBindingInd = connInd;
            aConsumed = ETrue;
            }
        
        connInd--;
        }
    
    delete indicationData;
    }


// ---------------------------------------------------------------------------
// from base class MNcmOutgoingConnectionObserver
// CNATFWTurnConnectionHandler::OutgoingMessageL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::OutgoingMessageL(
    TUint aStreamId,
    TUint aConnectionId,
    const TInetAddr& aPeerAddr,
    const TDesC8& aMessage,
    TBool& aConsumed )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::OutgoingMessageL" )
    
    aConsumed = EFalse;
    
    TBool controlMsgDetected( EFalse );
    TRAPD( err, controlMsgDetected = IsRequestOrIndicationL( aMessage ) )
    
    if ( !controlMsgDetected || err ) // Prevent loopback in msg handling
        {        
        TInt streamInd( IndexByStreamId( aStreamId ) );
        TConnectionData* connection( NULL );
        
        if ( KErrNotFound != streamInd )
            {
            connection = ConnectionById( streamInd, aConnectionId );
            }
        
        if ( connection )
            {
            aConsumed = ETrue;
            
            if ( aPeerAddr.IsUnspecified() )
                {
                if ( TConnectionData::EActDestNoneSet == connection->
                    iActDestState || TConnectionData::EActDestTransitioning ==
                    connection->iActDestState )
                    {
                    __TURNPLUGIN( "CNATFWTurnConnectionHandler::\
                    OutgoingMessageL  Media will be sent in indication" )
                    
                    connection->iTurnBinding->SendIndicationL( connection->
                        iPeerAddr, aMessage, EFalse );
                    }
                else
                    {
                    __TURNPLUGIN( "CNATFWTurnConnectionHandler::\
                    OutgoingMessageL  Media will be sent directly" )
                    
                    if ( KProtocolInetTcp == iStreamArray[streamInd].
                        iTransportProtocol )
                        {
                        CNATFWUNSAFTcpRelayPacket* packet =
                            CNATFWUNSAFTcpRelayPacket::NewLC( aMessage,
                                CNATFWUNSAFTcpRelayPacket::EFrameTypeData);
                        
                        CBufBase* message = packet->EncodeL();
                        CleanupStack::PopAndDestroy( packet );
                        
                        CleanupStack::PushL( message );
                        iConnMux->SendL( aStreamId, connection->iConnectionId,
                            message->Ptr(0), ETrue );
                        CleanupStack::PopAndDestroy( message );
                        }
                    else
                        {
                        iConnMux->SendL( aStreamId, connection->iConnectionId,
                            aMessage, ETrue );
                        }
                    }
                }
            else
                {
                // Must be protocol message, send in indication(TURNbis-03, 4)
                __TURNPLUGIN( "CNATFWTurnConnectionHandler::\
                    OutgoingMessageL  PROTOCOL MSG will be sent" )
                __TURNPLUGIN_ADDRLOG( 
                    "CNATFWTurnConnectionHandler::OutgoingMessageL PEERADDR",
                    aPeerAddr )
                connection->iTurnBinding->SendIndicationL( aPeerAddr,
                    aMessage, EFalse );
                }
            }
        }
    }


// ---------------------------------------------------------------------------
// from base class MNcmMessageObserver
// CNATFWTurnConnectionHandler::IncomingMessageNotify
// ---------------------------------------------------------------------------
//
HBufC8* CNATFWTurnConnectionHandler::IncomingMessageNotify(
    TUint aStreamId,
    const TDesC8& aMessage,
    const TInetAddr& aLocalAddr,
    const TInetAddr& aFromAddr,
    TInetAddr& aPeerAddr )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::IncomingMessageNotify" )
    
    HBufC8* modifiedMessage( NULL );
    TRAPD( error, modifiedMessage = HandleIncomingMessageL( 
        aStreamId, aMessage, aLocalAddr, aFromAddr, aPeerAddr ) )
    if ( KErrNone == error )
        {
        return modifiedMessage;
        }
    else
        {
        __TURNPLUGIN_INT1(
            "CNATFWTurnConnectionHandler::IncomingMessageNotify ERR", error )
        
        return NULL;
        }
    }


// ---------------------------------------------------------------------------
// from base class MNcmMessageObserver
// CNATFWTurnConnectionHandler::OutgoingMessageNotify
// ---------------------------------------------------------------------------
//
HBufC8* CNATFWTurnConnectionHandler::OutgoingMessageNotify(
    TUint /*aStreamId*/,
    TUint /*aConnectionId*/,
    const TInetAddr& /*aDestinationAddress*/,
    const TDesC8& /*aMessage*/ )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::OutgoingMessageNotify" )
    
    return NULL;
    }


// ---------------------------------------------------------------------------
// from base class MNcmConnectionObserver
// CNATFWTurnConnectionHandler::ConnectionNotify
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::ConnectionNotify(
    TUint aStreamId,
    TUint aConnectionId,
    TConnectionNotifyType aType,
    TInt aError )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::ConnectionNotify start" )
    
    TInt streamInd( IndexByStreamId( aStreamId ) );
    TConnectionData* connection( NULL );
    
    if ( KErrNotFound != streamInd )
        {
        connection = ConnectionById( streamInd, aConnectionId );
        }
    
    if ( connection )
        {
        if ( aError || EConnectionError == aType )
            {
            if ( !connection->iLocalCandidate )
                {
                // If error occurs when trying to fetch candidate
                TInt errcode( KClientSpecificError + ( aError ) );
                TRAP_IGNORE( iAsyncCallback->MakeCallbackL(
                    TTurnPluginCallbackInfo::EFetchingEnd, aStreamId,
                        errcode, NULL ) )
                }
            else
                {
                TRAP_IGNORE( iAsyncCallback->MakeCallbackL(
                    TTurnPluginCallbackInfo::EError, aStreamId,
                        aError, NULL ) )
                }
            
            // Remove failed entry from array
            DeleteStream( streamInd, ETrue );
            }
        else
            {
            switch ( aType )
                {
                case ESendingActivated:
                    {
                    connection->iSendingActivated = ETrue;
                    
                    if ( !connection->iTurnBinding )
                        {
                        TRAP_IGNORE( CreateTURNBindingL( aStreamId,
                            aConnectionId ) )
                        }
                    }
                    break;
                
                case EReceivingActivated:
                    {
                    connection->iReceivingActivated = ETrue;
                    
                    if ( connection->iLocalCandidate )
                        {
                        TRAP_IGNORE( iAsyncCallback->MakeCallbackL(
                            TTurnPluginCallbackInfo::EActiveReceiving,
                                aStreamId, KErrNone, NULL ) )
                        }
                    }
                    break;
                
                case ESendingDeactivated:
                    {
                    connection->iSendingActivated = EFalse;
                    }
                    break;
                
                case EReceivingDeactivated:
                    {
                    connection->iReceivingActivated = EFalse;
                    
                    if ( connection->iLocalCandidate )
                        {
                        TRAP_IGNORE( iAsyncCallback->MakeCallbackL(
                            TTurnPluginCallbackInfo::EDeactiveReceiving,
                                aStreamId, KErrNone, NULL ) )
                        }
                    }
                    break;
                
                case EConnectionRemoved:
                    {
                    DeleteStream( streamInd, EFalse );
                    }
                    break;
                }
            }
        }
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::ConnectionNotify end" )
    }


// ---------------------------------------------------------------------------
// from base class MNATFWRefreshObserver
// CNATFWTurnConnectionHandler::BindingRefreshL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::BindingRefreshL()
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::BindingRefreshL start" )
    
    TInt streamCount( iStreamArray.Count() );
    
    for ( TInt streamInd = 0; streamInd < streamCount; streamInd++ )
        {
        TInt connCount( iStreamArray[streamInd].iConnArray.Count() );
        
        for ( TInt connInd = 0; connInd < connCount; connInd++ )
            {
            TConnectionData* connection = ConnectionByIndex( streamInd,
                connInd );
            connection->iTurnBinding->AllocateRequestL(
                iStreamArray[streamInd].iRtoValue );
            }
        }
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::BindingRefreshL end" )
    }


// ---------------------------------------------------------------------------
// from base class MNATFWTimerObserver
// CNATFWTurnConnectionHandler::TimerTriggeredL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::TimerTriggeredL( TUint aStreamId,
                                                   TUint aConnectionId )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::TimerTriggeredL start" )
    
    TInt streamInd( IndexByStreamId( aStreamId ) );
    TConnectionData* connection( NULL );
    
    if ( KErrNotFound != streamInd )
        {
        connection = ConnectionById( streamInd, aConnectionId );
        }
    
    if ( connection )
        {
        if ( TConnectionData::EActDestTransitioning == connection->
            iActDestState )
            {
            // Once the timer fires, if the REMOTE-ADDRESS was not absent
            // from the Set Active Destination request which caused the
            // client to start the timer, the client moves back to the
            // "Set" state, and then updates the value of the active
            // destination to the value of REMOTE-ADDRESS.
            
            if ( !connection->iPeerAddr.IsUnspecified() )
                {
                // Plug-in's client changed Active Destination on
                // server successfully.
                
                connection->iActDestState = TConnectionData::EActDestSet;
                connection->iCurrentActDest.SetAddress( connection->iPeerAddr.
                    Address() );
                connection->iCurrentActDest.SetPort( connection->iPeerAddr.
                    Port() );
                
                iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                    EActiveSending, iStreamArray[streamInd].iStreamId,
                        KErrNone, NULL );
                }
            else
            // If REMOTE-ADDRESS was absent, the client sets the Active
            // Destination to null and enders the "None Set" state.
                {
                // Plug-in's client resetted Active Destination on
                // server successfully.
                
                connection->iActDestState = TConnectionData::EActDestNoneSet;
                connection->iCurrentActDest = KAFUnspec;
                
                iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                    EDeactiveSending, iStreamArray[streamInd].iStreamId,
                        KErrNone, NULL );
                }
            }
        else if ( TConnectionData::EActDestNoneSet == connection->
            iActDestState )
            {
            if ( !connection->iActDestAlreadySetOnServer )
                {
                connection->iActDestState = TConnectionData::EActDestSet;
                connection->iCurrentActDest.SetAddress( connection->iPeerAddr.
                    Address() );
                connection->iCurrentActDest.SetPort( connection->iPeerAddr.
                    Port() );
                }
            else
                {
                connection->iActDestAlreadySetOnServer = EFalse;
                
                // Active Destination was reset on server so now
                // try to set Active Destination to remote address.
                connection->iTurnBinding->SetActiveDestinationRequestL(
                    connection->iPeerAddr, connection->iTimerValue );
                }
            }
        else
            {
            if ( !connection->iActDestReqResent )
                {
                // As server was on "transitioning" state for the first
                // Set Active Destination Request, a new request will
                // be sent only once.
                connection->iActDestReqResent = ETrue;
                
                connection->iTurnBinding->SetActiveDestinationRequestL(
                    connection->iPeerAddr, connection->iTimerValue );
                }
            else
                {
                iAsyncCallback->MakeCallbackL( TTurnPluginCallbackInfo::
                    EActiveSending, aStreamId, KErrNotReady, NULL );
                }
            }
        }
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::TimerTriggeredL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::DeleteStream
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::DeleteStream( TUint aStreamInd, 
    TBool aRemoveMuxConn )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::DeleteStream, start" )
    ASSERT( aStreamInd < TUint( iStreamArray.Count() ) );
    
    TRAP_IGNORE(
        iConnMux->UnregisterConnectionObserverL(
            iStreamArray[aStreamInd].iStreamId, *this ) )
    
    TRAP_IGNORE(
        iConnMux->UnregisterIncomingConnectionObserverL(
            iStreamArray[aStreamInd].iStreamId, *this ) )
    
    TRAP_IGNORE(
        iConnMux->UnregisterOutgoingConnectionObserverL(
            iStreamArray[aStreamInd].iStreamId, *this ) )
    
    TRAP_IGNORE(
        iConnMux->UnregisterMessageObserverL(
            iStreamArray[aStreamInd].iStreamId, *this ) )
    
    // Remove the stream's connections
    TInt connIndex( iStreamArray[aStreamInd].iConnArray.Count() - 1 );
    
    for ( TInt i( connIndex ); i >= 0; i-- )
        {
        TConnectionData* connection = ConnectionByIndex( aStreamInd, i );
        
        if( connection->iTurnBinding )
            {
            connection->iTurnBinding->CancelRequest();
            
            delete connection->iTurnBinding;
            connection->iTurnBinding = NULL;
            delete connection->iLocalCandidate;
            connection->iLocalCandidate = NULL;
            }
        
        if ( aRemoveMuxConn )
            {
            TRAP_IGNORE( iConnMux->RemoveConnectionL(
                iStreamArray[aStreamInd].iStreamId, connection->
                    iConnectionId ) )
            }
        
        iStreamArray[aStreamInd].iConnArray.Remove( i );
        }
    
    iStreamArray[aStreamInd].iConnArray.Close();
    iStreamArray.Remove( aStreamInd );
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::DeleteStream, end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::FindConnection
// Function expects that system does not have multiple TURN-connections bound
// to the same local address and stream.
// ---------------------------------------------------------------------------
//
TConnectionData* CNATFWTurnConnectionHandler::FindConnection(
    TUint aStreamId,
    const TInetAddr& aLocalAddr )
    {
    // find stream index
    TInt foundStreamInd( KErrNotFound );
    TInt streamInd( iStreamArray.Count() - 1 );
    while ( KErrNotFound == foundStreamInd && 0 <= streamInd )
        {
        if ( iStreamArray[streamInd].iStreamId == aStreamId )
            {
            foundStreamInd = streamInd;
            }
        
        streamInd--;
        }
    
    TInt foundConnectionInd( KErrNotFound );
    if ( KErrNotFound != foundStreamInd )
        {
        // find connection inside stream
        const RArray<TConnectionData>& connArray( 
            iStreamArray[foundStreamInd].iConnArray );
        TInt connectionInd( connArray.Count() - 1 );
        
        while ( KErrNotFound == foundConnectionInd && 0 <= connectionInd )
            {
            const TInetAddr& localAddr( connArray[connectionInd].iLocalAddr );
            
            if ( MatchAddresses( localAddr, aLocalAddr ) )
                {
                foundConnectionInd = connectionInd;
                }
            
            connectionInd--;
            }
        }
    
    if ( ( KErrNotFound != foundStreamInd ) && 
         ( KErrNotFound != foundConnectionInd ) )
        {
        return &(iStreamArray[foundStreamInd].iConnArray[foundConnectionInd]);
        }
    else
        {
        return NULL;
        }
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::HandleIncomingMessageL
// ---------------------------------------------------------------------------
//
HBufC8* CNATFWTurnConnectionHandler::HandleIncomingMessageL(
    TUint aStreamId,
    const TDesC8& aMessage,
    const TInetAddr& aLocalAddr,
    const TInetAddr& aFromAddr,
    TInetAddr& aPeerAddr )
    {
    CNATFWUNSAFMessageFactory* decoder = CNATFWUNSAFMessageFactory::NewLC();
    CNATFWUNSAFMessage* msg( NULL );
    TRAPD( error, msg = decoder->DecodeL( aMessage ) )
    CleanupStack::PopAndDestroy( decoder );
    CleanupStack::PushL( msg );
    
    if ( error || NULL == msg )
        {
        // Not STUN message, must be media
        TConnectionData* activeConnection( NULL );
        TInt streamInd( iStreamArray.Count() - 1 );
        
        while ( !activeConnection && 0 <= streamInd )
            {
            TInt connInd = iStreamArray[streamInd].iConnArray.Count() - 1;
            
            while ( !activeConnection && 0 <= connInd )
                {
                activeConnection = ConnectionByIndex( streamInd, connInd );
                
                if ( TConnectionData::EActDestSet != activeConnection->
                    iActDestState )
                    {
                    activeConnection = NULL;
                    }
                
                connInd--;
                }
            
            streamInd--;
            }
        
        if ( activeConnection )
            {
            aPeerAddr = activeConnection->iCurrentActDest;
            }
        else
            {
            aPeerAddr = aFromAddr;
            }
        
        CleanupStack::PopAndDestroy( msg );
        return NULL;
        }
    
    if ( msg->Type() == CNATFWUNSAFMessage::EDataIndication )
        {
        // Must be data from peer, either protocol message or media
        // from nonactivated peer address
        CNATFWUNSAFRemoteAddressAttribute* remoteAddress =
            static_cast<CNATFWUNSAFRemoteAddressAttribute*>
            ( msg->Attribute( CNATFWUNSAFAttribute::ERemoteAddress ) );
        
        if ( remoteAddress )
            {
            aPeerAddr = remoteAddress->Address();
            __TURNPLUGIN_ADDRLOG( 
               "CNATFWTurnConnectionHandler::HandleIncomingMessageL DATAIND",
               aPeerAddr )
            }
        else
            {
            __TURNPLUGIN( 
                "CNATFWTurnConnectionHandler::HandleIncomingMessageL, \
                NO REMOTE ADDR FIELD" )
            }
        
        const TDesC8& data = static_cast<CNATFWUNSAFDataAttribute*>(
            msg->Attribute( CNATFWUNSAFAttribute::EData ) )->Value();
        
        HBufC8* modifiedMsg = data.AllocL();
        CleanupStack::PopAndDestroy( msg );
        return modifiedMsg;
        }
    else
        {
        // This is either response from physical TURN-server or STUN-data from
        // peer address which is set as active destination.
        TConnectionData* activeConnection 
            = FindConnection( aStreamId, aLocalAddr );
        
        if ( activeConnection && 
                !activeConnection->iCurrentActDest.IsUnspecified() )
            {
            aPeerAddr = activeConnection->iCurrentActDest;
            }
        else
            {
            aPeerAddr = aFromAddr;
            }
        
        __TURNPLUGIN_ADDRLOG( 
            "CNATFWTurnConnectionHandler::HandleIncomingMessageL NOT DATAIND",
            aPeerAddr )
        
        CleanupStack::PopAndDestroy( msg );
        return NULL;
        }
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::IndexByStreamId
// ---------------------------------------------------------------------------
//
TInt CNATFWTurnConnectionHandler::IndexByStreamId( TUint aStreamId )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::IndexByStreamId" )
    
    TInt index( iStreamArray.Count() - 1 );
    
    while ( 0 <= index )
        {
        if ( aStreamId == iStreamArray[index].iStreamId )
            {
            return index;
            }
        
        index--;
        }

    return KErrNotFound;
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::ConnectionById
// ---------------------------------------------------------------------------
//
TConnectionData* CNATFWTurnConnectionHandler::ConnectionById(
    TUint aStreamInd,
    TUint aConnectionId )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::ConnectionById" )
    
    TInt index( iStreamArray[aStreamInd].iConnArray.Count() - 1 );
    
    while ( 0 <= index )
        {
        if ( aConnectionId == iStreamArray[aStreamInd].iConnArray[index].
            iConnectionId )
            {
            return &iStreamArray[aStreamInd].iConnArray[index];
            }
        
        index--;
        }
    
    return NULL;
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::ConnectionByIndex
// ---------------------------------------------------------------------------
//
TConnectionData* CNATFWTurnConnectionHandler::ConnectionByIndex(
    TUint aStreamInd,
    TUint aConnectionInd )
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::ConnectionByIndex" )
    
    return &iStreamArray[aStreamInd].iConnArray[aConnectionInd];
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::GenerateServerListL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::GenerateServerListL()
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::GenerateServerListL start" )
    
    RPointerArray<MNATFWServerSettings> serverArray;
    iTurnSettings->GetTurnServerArrayL( serverArray );
    CleanupClosePushL( serverArray );
    TInt count( serverArray.Count() );
    
    HBufC8* serverAddress =
        iTurnSettings->LatestConnectedServerAddr().AllocLC();
    TUint latestPort = iTurnSettings->LatestConnectedServerPort();
    
    // Add Latest connected server
    if ( serverAddress->Compare( KNullDesC8 ) )
        {
        CTurnServerSettings* settings = CTurnServerSettings::NewLC();
        settings->SetAddressL( *serverAddress );
        settings->SetPort( latestPort );
        
        for ( TInt index( 0 ); index < count; index++ )
            {
            if ( *serverAddress == serverArray[index]->Address() &&
                serverArray[index]->Username().Length() &&
                serverArray[index]->Password().Length() )
                {
                settings->SetUsernameL( serverArray[index]->Username() );
                settings->SetPasswordL( serverArray[index]->Password() );
                break;
                }
            }
            
        if ( settings->Address().Compare( KNullDesC8 ) )
            {
            __TURNPLUGIN( "CNATFWStunConnectionHandler::GenerateServerListL\
            - provisioned server added" )
            }
            
        iServerList.AppendL( settings );
        CleanupStack::Pop( settings );
        }
    
    CleanupStack::PopAndDestroy( serverAddress );
    
    // Add provisioned servers
    for ( TInt index( 0 ); index < count; index++ )
        {
        CTurnServerSettings* settings = CTurnServerSettings::NewLC();
        
        settings->SetAddressL( serverArray[index]->Address() );
        settings->SetPort( serverArray[index]->Port() );
        
        if ( serverArray[index]->Username().Length() &&
            serverArray[index]->Password().Length() )
            {
            settings->SetUsernameL( serverArray[index]->Username() );
            settings->SetPasswordL( serverArray[index]->Password() );
            }
        
        iServerList.AppendL( settings );
        CleanupStack::Pop( settings );
        }
    
    // Domain
    CTurnServerSettings* settings = CTurnServerSettings::NewLC();
    settings->SetAddressL( *iDomain );
    settings->SetPort( ( latestPort ) ? latestPort : KDefaultStunSrvPort );
    
    for ( TInt index( 0 ); index < count; index++ )
        {
        if ( *iDomain == serverArray[index]->Address() &&
            serverArray[index]->Username().Length() &&
            serverArray[index]->Password().Length() )
            {
            settings->SetUsernameL( serverArray[index]->Username() );
            settings->SetPasswordL( serverArray[index]->Password() );
            break;
            }
        }
    
    iServerList.AppendL( settings );
    CleanupStack::Pop( settings );
    CleanupStack::PopAndDestroy( &serverArray );
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::GenerateServerListL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWStunConnectionHandler::TryNextServerL
// ---------------------------------------------------------------------------
//
void CNATFWTurnConnectionHandler::TryNextServerL()
    {
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::TryNextServerL start" )
    
    delete iStunClient;
    iStunClient = NULL;
    
    if ( iServerIndex < iServerList.Count() )
        {
        TInt index( iServerIndex );
        iServerIndex++;
        
        TInt refresh( iNatSettings->RefreshIntervalUdp() );
        iTurnRefreshInterval = ( refresh ) ? ( refresh * KMicrosecFactor ) :
            KDefaultRefreshInterval;
        
        TInt retransmitInterval( iTurnSettings->RetransmissionTimeout() );
        
        TUint port = iServerList[index]->Port() ? iServerList[index]->Port() :
            KDefaultStunSrvPort;
        
        iStunClient = CSTUNClient::NewL( retransmitInterval,
                                         iServerList[index]->Address(),
                                         port,
                                         KStunRelay,
                                         iSocketServ,
                                         iConnection,
                                         *iTimerServ,
                                         *this,
                                         ETrue,
                                         EFalse,
                                         EFalse,
                                         iConnMux );
        
        if ( iServerList[index]->Username() && iServerList[index]->
            Password() )
            {
            TRAP_IGNORE( iStunClient->SetCredentialsL( *iServerList[index]->
                Username(), *iServerList[index]->Password() ) )
            }
        }
    else
        {
        User::Leave( KErrNotFound );
        }
    
    __TURNPLUGIN( "CNATFWTurnConnectionHandler::TryNextServerL end" )
    }


// ---------------------------------------------------------------------------
// CNATFWTurnConnectionHandler::MatchAddresses
// ---------------------------------------------------------------------------
//
TBool CNATFWTurnConnectionHandler::MatchAddresses( const TInetAddr& aAddr1,
    const TInetAddr& aAddr2 )
    {
    // CmpAddr does not interpret IPv4 and IPv4 mapped/compatible addresses
    // as same even if they represent same address. Thus extra testing is
    // needed to handle that case.
    TBool isMatch = ( aAddr1.CmpAddr( aAddr2 ) || ( aAddr1.Address() ==
        aAddr2.Address() && aAddr1.Port() == aAddr2.Port() ) );
    
    return isMatch;
    }

// End of file
