// Copyright (c) 2005-2009 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:
// IP Shim Flow Base class.
// This class is derived from by the separate IP4 and IP6 shim flow classes.
// 
//

/**
 @file flow.cpp
*/

#include <e32std.h>
#include <e32test.h>
#include <ecom/ecom.h>
#include <ecom/implementationproxy.h>
#include <comms-infras/ss_metaconnprov.h>
#include <comms-infras/ss_subconnflow.h>
#include "flow.h"
#include "nif.h"
#include "panic.h"
#include "ItfInfoConfigExt.h"
#include "IPProtoMessages.h"	// TCFIPProtoMessage
#include "IPProtoCPR.h"
#include "idletimer.h"
#include <networking/ipaddrinfoparams.h>
#include "IPProtoDeMux.h"
#include <comms-infras/ss_log.h>
#include <es_prot_internal.h>

using namespace Messages;
using namespace MeshMachine;
using namespace ESock;
using namespace IpProtoCpr;
//
// CIPShimSubConnectionFlow
//

CIPShimSubConnectionFlow::CIPShimSubConnectionFlow(CSubConnectionFlowFactoryBase& aFactory, const TNodeId& aSubConnId, CProtocolIntfBase* aProtocolIntf)
: CSubConnectionFlowBase(aFactory, aSubConnId, aProtocolIntf), iCleanupError(KErrUnknown),
  iAsyncBinderClose(*this)
	{
	LOG_NODE_CREATE(KIPProtoTag1, CIPShimSubConnectionFlow);
	}

CIPShimSubConnectionFlow::~CIPShimSubConnectionFlow()
	{
	ASSERT(iBinderList.Count() == 0);
    iConnectionInfo.Close();
	// iIntf doesn't need to be deleted explicitly, as it will be deleted
	// when the last Close() is issued on it (i.e. either by us or by the TCP/IP
	// stack).  At the time of writing, our call to Close() is the last one and
	// will delete it.
	LOG_NODE_DESTROY(KIPProtoTag1, CIPShimSubConnectionFlow);
	}

MFlowBinderControl* CIPShimSubConnectionFlow::DoGetBinderControlL()
	{
	return this;
	}


void CIPShimSubConnectionFlow::InitialiseDataMonitoringL(CIPShimIfBase* intf)
	{
	const TPacketActivity* packetActivity =
		static_cast<const TPacketActivity*>(AccessPointConfig().FindExtension(TPacketActivity::TypeId()));
	if (packetActivity)
		{
		intf->ShimNotify()->SetPacketActivityFlag(packetActivity->iPacketActivity);
		}
	else
		{
		__CFLOG_VAR((KIPProtoTag1, KIPProtoTag2, _L8("Packet activity provisioning information missing")));
    	User::Leave(KErrNotReady);
		}

	const TDataMonitoringConnProvisioningInfo* connProvisioningInfo =
		static_cast<const TDataMonitoringConnProvisioningInfo*>(AccessPointConfig().FindExtension(STypeId::CreateSTypeId(TDataMonitoringConnProvisioningInfo::iUid, TDataMonitoringConnProvisioningInfo::iId)));

	const TDataMonitoringSubConnProvisioningInfo* subConnProvisioningInfo =
		static_cast<const TDataMonitoringSubConnProvisioningInfo*>(AccessPointConfig().FindExtension(STypeId::CreateSTypeId(TDataMonitoringSubConnProvisioningInfo::iUid, TDataMonitoringSubConnProvisioningInfo::iId)));

    if(connProvisioningInfo && subConnProvisioningInfo)
    	{
		intf->ShimNotify()->SetDataVolumePtrs(connProvisioningInfo->iDataVolumesPtr, subConnProvisioningInfo->iDataVolumesPtr);
		intf->ShimNotify()->SetNotificationThresholdPtrs(connProvisioningInfo->iThresholdsPtr, subConnProvisioningInfo->iThresholdsPtr);
    	}
    else
    	{
		__CFLOG_VAR((KIPProtoTag1, KIPProtoTag2, _L8("Data monitoring provisioning information missing")));
    	User::Leave(KErrNotReady);
    	}
	}


// ============================================================================
//
// from Messages::ANode

void CIPShimSubConnectionFlow::ReceivedL(const TRuntimeCtxId& aSender, const TNodeId& aRecipient, TSignatureBase& aMessage)
/**
Receive function for incoming messages from SCPR.

@param aCFMessage message from SCPR
*/
    {
	CSubConnectionFlowBase::ReceivedL(aSender, aRecipient, aMessage);
	if(aMessage.IsMessage<TEBase::TCancel>())
		{
		if (iStarting )
			{
			StopFlow(KErrCancel);
			}
		}
	else if(aMessage.IsMessage<TEChild::TDestroy>())
		{
		Destroy();
		}
	else if(aMessage.IsMessage<TCFDataClient::TBindTo>())
		{
		TCFDataClient::TBindTo& msg = message_cast<TCFDataClient::TBindTo>(aMessage);
		BindToL(msg.iNodeId);
		ASSERT(iSubConnectionProvider == aSender);
		iSubConnectionProvider.PostMessage(Id(), TCFDataClient::TBindToComplete().CRef());
		}
	else if(aMessage.IsMessage<TCFDataClient::TStart>())
		{
		StartFlowL();
		}
	else if(aMessage.IsMessage<TCFDataClient::TStop>())
		{
		StopFlow(static_cast<TCFDataClient::TStop&>(aMessage).iValue);
		}
	else if(aMessage.IsMessage<TCFDataClient::TProvisionConfig>())
		{
		iAccessPointConfig.Close();		
		iAccessPointConfig.Open(static_cast<TCFDataClient::TProvisionConfig&>(aMessage).iConfig);
		ProcessProvisionConfigL();
		}
#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
	else if(aMessage.IsMessage<TCFScpr::TSetParamsRequest>())
		{
		UpdateIpAddressInfoL(static_cast<TCFScpr::TSetParamsRequest&>(aMessage));
		}
#else
	else if(aMessage.IsMessage<TCFScpr::TParamsRequest>())
		{
		UpdateIpAddressInfoL(static_cast<TCFScpr::TParamsRequest&>(aMessage));
		}
#endif // SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
	else
		{
		Panic(EUnexpectedSubConnectionMsg);
		}
    }


//
// Dispatch functions for messages from SCPR
//

__CFLOG_STMT
(
void LOG_ADDRESS(TSockAddr addr)
	{
	TBuf<KMaxSockAddrSize> buff;
	TInetAddr iaddr(addr);
	iaddr.Output(buff);

	__CFLOG_VAR((KIPProtoTag1, KIPProtoTag2, _L("CIPShimSubConnectionFlow::UpdateIpAddressInfoL %S %d"), &buff, addr.Port()));
	}
)

//helper function
CIPProtoBinder * FindBinderForProtocol(const TDesC8& proto, RPointerArray<CIPProtoBinder> &array)
	{
	TInt count= array.Count();
	for (TInt i = 0; i < count; i++)
		{
		if (array[i]->ProtocolName().Compare(proto) == 0)
			{
			return array[i];
			}
		}
	return 0;
	}

#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
void CIPShimSubConnectionFlow::UpdateIpAddressInfoL(TCFScpr::TSetParamsRequest& aParamReq)
#else
void CIPShimSubConnectionFlow::UpdateIpAddressInfoL(TCFScpr::TParamsRequest& aParamReq)
#endif // SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
	{

	//[401TODO] DL: deal with the granted also.
	const RCFParameterFamilyBundleC& newParamBundle = aParamReq.iFamilyBundle;

	CSubConIPAddressInfoParamSet* IPAddressInfoSet;
	RParameterFamily family = newParamBundle.FindFamily(KSubConIPAddressInfoFamily);
	if ( family.IsNull() )
		{
		 IPAddressInfoSet = NULL;
		}
    else
    	{
    	 IPAddressInfoSet = static_cast<CSubConIPAddressInfoParamSet*>
		 	(family.FindParameterSet(
		 			STypeId::CreateSTypeId(CSubConIPAddressInfoParamSet::EUid,
				 		CSubConIPAddressInfoParamSet::ETypeId)
						 ,RParameterFamily::ERequested));
		}

	if (IPAddressInfoSet)
        {
		TUint aParamSetNum = IPAddressInfoSet->GetParamNum();
		/** TODO: It should be only one in the queue, so remove the loop */
		for (TUint i=0; i<aParamSetNum; ++i)
			{

			CSubConIPAddressInfoParamSet::TSubConIPAddressInfo paramInfo(IPAddressInfoSet->GetParamInfoL(i));

			//The only protocol we are going to deal with is TCP/IP
			//as the flow association gets lost in when it goes through the current stack.
			if (paramInfo.iCliDstAddr.Family() == KAfInet6 ||
				paramInfo.iCliDstAddr.Family() == KAfInet)
				{
				__CFLOG_STMT(LOG_ADDRESS(paramInfo.iCliDstAddr);)
				__CFLOG_STMT(LOG_ADDRESS(paramInfo.iCliSrcAddr);)

				TInetAddr addr(paramInfo.iCliDstAddr);
				TUint32 ipv4Addr = addr.Address();

				if (iBinderList.Count())
					{
					CIPShimIfBase* nif = iBinderList[0]->iNif;
					ASSERT(nif);
					CIPProtoBinder *binder = 0;
					if(ipv4Addr)
						{
						//find ip4 binder
						binder = FindBinderForProtocol(KProtocolIp, iBinderList);
						}
					else
						{
						//find ip6 binder
						binder = FindBinderForProtocol(KProtocolIp6, iBinderList);
						}

					if (IPAddressInfoSet->GetOperationCode() == CSubConIPAddressInfoParamSet::EDelete)
						{
						nif->RemoveIpAddrInfo(binder, paramInfo);
						}
					else
						{
						nif->AddIpAddrInfoL(binder, paramInfo);
						}
					}
				}
			}
        }
	}


class TCleanupNifPair {
public:
	TCleanupNifPair(CIPShimIfBase* aNif) : iNif(aNif), iBinder(NULL) {}

	void Cleanup();
	static void CleanupReleaseNif(TAny* aThis);

	CIPShimIfBase* iNif;
	CIPProtoBinder* iBinder;
	};

void TCleanupNifPair::Cleanup()
	{
	ASSERT(iNif);

	if (iBinder)
		{
		iNif->UnbindFrom(iBinder);
		iBinder->UnbindFromLowerFlow();
		}
	iNif->Release(KErrAbort);
	}

void TCleanupNifPair::CleanupReleaseNif(TAny* aThis)
	{
	TCleanupNifPair* pair = static_cast<TCleanupNifPair*>(aThis);
	pair->Cleanup();
	}

void CIPShimSubConnectionFlow::BindToL(const Messages::TNodeId& aCommsBinder)
	{
	// Assumption that TCommsBinder object contains pointer to Flow
    if (aCommsBinder.IsNull())
     	{
     	User::Leave(KErrNotSupported);
     	}

	ANode& mcfNode = aCommsBinder.Node();
#if !defined(__GCCXML__)
	CSubConnectionFlowBase& flow = mcfnode_cast<CSubConnectionFlowBase>(mcfNode);
#else
	CSubConnectionFlowBase& flow = reinterpret_cast<CSubConnectionFlowBase&>(mcfNode);
#endif

	__CFLOG_VAR((KIPProtoTag1, KIPProtoTag2, _L8("CIPShimSubConnectionFlow %08x:\tBindToL(): flow 0x%08x, protocol list '%S'"),
 				this, &flow, &iProtocolList));

	MFlowBinderControl* binderControl = flow.GetBinderControlL();
	ASSERT(binderControl);

	// Loop through all the protocols in the list and create binders for them
	TPtrC8 protoList(iProtocolList);
	TPtrC8 proto;
	TInt pos = KErrNotFound;

	do
		{
		pos = protoList.LocateF(',');
		if (pos == KErrNotFound)
			{
			proto.Set(protoList);
			}
		else
			{
			proto.Set(protoList.Left(pos));
			protoList.Set(protoList.Mid(pos + 1));
			}

		if (proto.Length() > 0)
			{
			CIPProtoBinder* protoBinder = CIPProtoBinder::NewL(*this, proto);
			CleanupStack::PushL(protoBinder);

			CIPShimIfBase* nif = ProtocolIntf()->FindOrCreateNifL(proto,
				reinterpret_cast<const TConnectionInfo&>(*iConnectionInfo.Ptr()));
			TCleanupNifPair cleanuppair(nif);
			CleanupStack::PushL(TCleanupItem(TCleanupNifPair::CleanupReleaseNif, &cleanuppair));

			__CFLOG_VAR((KIPProtoTag1, KIPProtoTag2, _L8("CIPShimSubConnectionFlow %08x:\tBindToL(): nif 0x%08x, binder 0x%08x"), this, nif, protoBinder));
			nif->BindToL(protoBinder);
			cleanuppair.iBinder = protoBinder;

			protoBinder->BindToLowerFlowL(*binderControl);

			iBinderList.AppendL(protoBinder);

			CleanupStack::Pop(2);

			InitialiseDataMonitoringL(nif);
			}
		}
	while (pos != KErrNotFound);

	NM_LOG((KIPProtoTag1, _L8("CIPShimSubConnectionFlow %08x:\tSynchronous call: From=%08x To=%08x Func=BindToL"), this, static_cast<Messages::ANode*>(this), &mcfNode));

	TInt binderCount = iBinderList.Count();
	for (TInt i = 0; i < binderCount; i++)
		{
		iBinderList[i]->StartL();
		}
	}



void CIPShimSubConnectionFlow::StartFlowL()
/**
Message from SCPR to start.
*/
	{
	ASSERT(!iStarting);

	iStarting = ETrue;

	PostDataClientStartedIfReady();
	}

void CIPShimSubConnectionFlow::StopFlow(TInt aError)
/**
Message from SCPR to stop.

The aError reason code is stored to be passed to the TCP/IP stack via IfUserInterfaceDown().

@param aError Reason code.
*/
	{
	ASSERT(aError != KErrNone);
	iStopCode = aError;

	for(TInt i =0; i < iBinderList.Count(); i++)
		{
		MarkBinderForClosure(iBinderList[i]);
		}
	CloseMarkedBinders();

	PostDataClientStoppedIfReady();
	}



void CIPShimSubConnectionFlow::ProcessProvisionConfigL()
/**
Message from SCPR providing configuration information.
*/
	{
    const TItfInfoConfigExt* ext = static_cast<const TItfInfoConfigExt*>(AccessPointConfig().FindExtension(
    		STypeId::CreateSTypeId(KIpProtoConfigExtUid, EItfInfoConfigExt)));
    if (ext)
        {
        TInt err = SetConnectionInfo(ext->iConnectionInfo);
		if (err == KErrNone)
			{
			err = SetProtocolList(ext->iProtocolList);
			}
        if (err != KErrNone)
            {
    	    User::Leave(err);
            }
	    }
	}



TInt CIPShimSubConnectionFlow::SetProtocolList(const TDesC8& aProtocolList)
/**
Save a pointer to the protocol list string in the provisioning information
*/
	{
	if (aProtocolList.Length() > 0)
		{
		iProtocolList.Set(aProtocolList);
		return KErrNone;
		}
	else
		{
		return KErrArgument;
		}
	}

void CIPShimSubConnectionFlow::Destroy()
/**
Message from SCPR to self destruct.
*/
	{
	// No-one should be bound to us from above if we are about to disappear.
	ASSERT(iBindCallCount == 0);

	MarkAllBindersForClosure();

	if (iAsyncBinderClose.IsActive())
		{
		// Can't destroy ourselves whilst an asynchronous binder close is pending.
		// Arrange for it to close all binders and initiate the destroy.
		//
		// Note that the async binder close may have been originally invoked with
		// just a single binder marked for closure, and we are now marking them all
		// for closure and requesting a destroy.  This is the main reason for using
		// the "marked for closure" flag - to deal with events that require further
		// binders to be closed whilst the async binder close is still pending.
		iDestroyPending = ETrue;
		}
	else
		{
		// No async binder close pending - close binders and destroy ourselves here.
		CloseMarkedBinders();
		DeleteThisFlow();
		}
	}

//
// Utility functions for sending messages to SCPR
//

void CIPShimSubConnectionFlow::PostDataClientStartedIfReady()
	{
	if (iBinderReady && iStarting)
    	{
    	iSubConnectionProvider.PostMessage(Id(), TCFDataClient::TStarted().CRef());
    	iStarting = EFalse;
    	iStarted = ETrue;
    	}
	}

void CIPShimSubConnectionFlow::PostDataClientStoppedIfReady()
	{
	if (iStopCode != KErrNone)
    	{
    	iSubConnectionProvider.PostMessage(Id(), TCFDataClient::TStopped(iStopCode).CRef());
    	iStopCode = KErrNone;
    	iStarted = EFalse;

		MarkAllBindersForClosure();

		if (!iAsyncBinderClose.IsActive())
			{
			// No async binder close pending - close binders
			CloseMarkedBinders();
			}
       	}
	}

void CIPShimSubConnectionFlow::Error(TInt aError)
	{
    ASSERT(iStarted || iStarting);
    ASSERT(iSubConnectionProvider.IsOpen());
    if (iStarting)
        {
        iSubConnectionProvider.PostMessage(Id(), TEBase::TError(TCFDataClient::TStart::Id(), aError).CRef());
        }
    else
        {
        iSubConnectionProvider.PostMessage(Id(), TCFControlProvider::TDataClientGoneDown(aError, MNifIfNotify::EDisconnect).CRef());
        }

    iStopCode = aError;
    iStarting = EFalse;
    }

MLowerControl* CIPShimSubConnectionFlow::GetControlL(const TDesC8& aProtocol)
    {
    if (aProtocol.Length())
        {
        User::Leave(KErrNotSupported);
        }
    return this;
    }

MLowerDataSender* CIPShimSubConnectionFlow::BindL(const TDesC8& /*aProtocol*/, MUpperDataReceiver* /*aReceiver*/, MUpperControl* /*aControl*/)
    {
    iBindCallCount++;
    if (iBindCallCount == 1)
    	{
	    iSubConnectionProvider.RNodeInterface::PostMessage(Id(), TCFControlProvider::TActive().CRef());
    	}
    return NULL;
    }

void CIPShimSubConnectionFlow::Unbind( MUpperDataReceiver* /*aReceiver*/, MUpperControl* /*aControl*/)
    {
    iBindCallCount--;
    if (iBindCallCount <= 0)
    	{
    	ASSERT(iBindCallCount == 0);
	    PostClientIdleIfIdle();
    	}
    }

CSubConnectionFlowBase* CIPShimSubConnectionFlow::Flow()
    {
    return this;
    }

void CIPShimSubConnectionFlow::OpenRoute()
	{
	// Post directly to cpr bypassing the scpr purely for efficiency
	__CFLOG_1(KIPProtoTag1, KIPProtoTag2, _L("CIPShimSubConnectionFlow %08x:\tOpenRoute()"), this);

	RClientInterface::OpenPostMessageClose(Id(), TNodeCtxId(ECFActivityOpenCloseRoute, iProtocolIntf->ControlProviderId()),
		TCFIPProtoMessage::TOpenCloseRoute(ETrue).CRef());
	}


void CIPShimSubConnectionFlow::CloseRoute()
	{
	// Post directly to cpr bypassing the scpr purely for efficiency
	__CFLOG_1(KIPProtoTag1, KIPProtoTag2, _L("CIPShimSubConnectionFlow %08x:\tCloseRoute()"), this);

	RClientInterface::OpenPostMessageClose(Id(), TNodeCtxId(ECFActivityOpenCloseRoute, iProtocolIntf->ControlProviderId()),
		TCFIPProtoMessage::TOpenCloseRoute(EFalse).CRef());
	}

void CIPShimSubConnectionFlow::PostClientIdleIfIdle()
    {
    if (iBindCallCount == 0)
        {
        iSubConnectionProvider.PostMessage(Id(), TCFControlProvider::TIdle().CRef());
        }
    }


TInt CIPShimSubConnectionFlow::Control(TUint aLevel, TUint aName, TDes8& aOption)
    {
    TInt err = KErrNotSupported;
    
    //It returns interface info alias TConnectionInfo in a form of an old fashioned descriptor
    //for SetOption
    if (aLevel == (TUint)KSOLProvider && aName == (TUint)KSoConnectionInfo)
        {
	    // Get the connection info from the config if it has not been stored yet in case the
	    // CTransportFlowShim locks the connection info for a host resolver and calls StartSending()
	    // on the host resolver before CIPShimSubConnectionFlow::ProcessProvisionConfigL() is called.
	    // Otherwise, if ProcessProvisionConfigL() is not called in time the CTransportFlowShim will
	    // lock to empty connection info.  ProcessProvisionConfigL() is called when the
	    // CIPShimSubConnectionFlow receives the request to bind to the bearer SCPR which is not
	    // synchronised in any way with the request to bind the CIPShimSubConnectionFlow to the
	    // CTransportFlowShim which triggers the call to StartSending().  We still return
	    // KErrNotReady when we are not bound so that a socket does not try to flow on too early
	    // (this was causing failures in te_spudnetworkside).  A host resolver does not check the
	    // error code when calling KSoConnectionInfo and simply uses the info if it's there anyway.
	    //
	    // See DEF128574.
	   	if(iConnectionInfo.Length() == 0)
	   		{
			const TItfInfoConfigExt* ext = static_cast<const TItfInfoConfigExt*>(AccessPointConfig().FindExtension(STypeId::CreateSTypeId(KIpProtoConfigExtUid, EItfInfoConfigExt)));
			if (ext)
			    {
				aOption.Copy((const TUint8*)&ext->iConnectionInfo, sizeof(TConnectionInfo));
		   		err = KErrNotReady;
			    }
	   		}
	   	else
	   		{
	        if(iConnectionInfo.Length() == 0)
	        	{
	        	err = KErrNotReady;
	        	}
	        else
	        	{
		        aOption.Copy(iConnectionInfo);
		   		err = KErrNone;
	        	}
	   		}
        }
    
    return err;
    }

TInt CIPShimSubConnectionFlow::SetConnectionInfo(const TConnectionInfo& aInfo)
	{
	iConnectionInfo.Close();
	TInt ret = iConnectionInfo.Create(sizeof(TConnectionInfo));
	if (ret == KErrNone)
	    {
    	iConnectionInfo.Copy((TUint8*)&aInfo, sizeof(TConnectionInfo));
	    }
	return ret;
	}

void CIPShimSubConnectionFlow::BinderReady()
/**
Called from binder to indicate that it is ready.
*/
	{
	iBinderReady = ETrue;
	PostDataClientStartedIfReady();
	}

/**
Called from lower binder to indicate an error that renders the binder unusable

Ensure that the TCP/IP stack is notified and the binder closed down.

@param aError error code
@param aIf CIPShimIfBase object associated with the binder
*/
#ifdef _DEBUG
void CIPShimSubConnectionFlow::BinderError(TInt aError, CIPProtoBinder* aBinder)
#else
void CIPShimSubConnectionFlow::BinderError(TInt /*aError*/, CIPProtoBinder* aBinder)
#endif
	{
	__CFLOG_VAR((KIPProtoTag1, KIPProtoTag2, _L8("CIPShimSubConnectionFlow %08x:\tBinderError(%d)"), this, aError));

	// NOTE:
	// - We are currently in the stack frame of the lower binder as it makes the Error() upcall.
	// - Calling CloseBinder() directly here will result in a recursive Unbind() and destructor
	//   downcall back into the binder.  Consequently, schedule asynchronous closure of the binder
	//   to ensure that the binder does not have the burden of dealing with this recursion.
	// - Note that CloseBinder() will also Close() the CIPShimIfBase, which may cause it to be destructed
	//   (if all other references on it are gone, in particular the TCP/IP stack)
	MarkBinderForClosure(aBinder);
	if (!iAsyncBinderClose.IsActive())
		{
		iAsyncBinderClose.Call();
		}
	}


void CIPShimSubConnectionFlow::SendMessageToSubConnProvider(const Messages::TSignatureBase& aCFMessage)
    {
    iSubConnectionProvider.PostMessage(Id(), aCFMessage);
    }

const Messages::TNodeId& CIPShimSubConnectionFlow::GetCommsId()
    {
    return NodeId();
    }


// MIpDataMonitoringNotifications

void CIPShimSubConnectionFlow::PostConnDataReceivedThresholdReached(TUint aVolume)
	{
	RClientInterface::OpenPostMessageClose(Id(), iProtocolIntf->ControlProviderId(),
		TCFDataMonitoringNotification::TDataMonitoringNotification(EReceived, aVolume).CRef());
	}

void CIPShimSubConnectionFlow::PostConnDataSentThresholdReached(TUint aVolume)
	{
	RClientInterface::OpenPostMessageClose(Id(), iProtocolIntf->ControlProviderId(),
		TCFDataMonitoringNotification::TDataMonitoringNotification(ESent, aVolume).CRef());
	}

void CIPShimSubConnectionFlow::PostSubConnDataReceivedThresholdReached(TUint aVolume)
	{
	iSubConnectionProvider.PostMessage(Id(),
		TCFDataMonitoringNotification::TDataMonitoringNotification(EReceived, aVolume).CRef());
	}

void CIPShimSubConnectionFlow::PostSubConnDataSentThresholdReached(TUint aVolume)
	{
	iSubConnectionProvider.PostMessage(Id(),
		TCFDataMonitoringNotification::TDataMonitoringNotification(ESent, aVolume).CRef());
	}

//
// Asynchronous Binder Close routines
//
// Class used to asynchronously unbind/destroy a binder from outside of the binder's call stack.
//

CAsyncBinderClose::CAsyncBinderClose(CIPShimSubConnectionFlow& aFlow)
  : CAsyncOneShot(EPriorityStandard), iFlow(aFlow)
  	{
  	}

void CAsyncBinderClose::RunL()
	{
	iFlow.AsyncBinderClose();
	}

void CIPShimSubConnectionFlow::AsyncBinderClose()
/**
Main method of asynchronous binder close callback.
*/
	{
	CloseMarkedBinders();
	if (iDestroyPending)
		{
		// Deal with a pending Destroy() that happened whilst the asynchronous binder close was pending.
		iDestroyPending = EFalse;
		DeleteThisFlow();
		}
	}

void CIPShimSubConnectionFlow::MarkAllBindersForClosure()
/**
Mark all binders for closure in the asynchronous binder close callback
*/
	{
	for (TInt index = 0 ; index < iBinderList.Count() ; ++index)
		{
		MarkBinderForClosure(iBinderList[index]);
		}
	}

void CIPShimSubConnectionFlow::MarkBinderForClosure(CIPProtoBinder* aBinder)
	{
	__CFLOG_VAR((KIPProtoTag1, KIPProtoTag2, _L8("CIPShimSubConnectionFlow %08x:\tMarkBinderForClosure(%08x)"), this, aBinder));
	aBinder->iMarkedForClosure = ETrue;
	}

void CIPShimSubConnectionFlow::CloseMarkedBinders()
	{
	TInt count = iBinderList.Count();
	TBool reset = count > 0 ? ETrue : EFalse;
	for (TInt index = count -1  ; index >= 0; --index)
		{
		CIPProtoBinder* binder = iBinderList[index];
		if (binder->iMarkedForClosure)
			{
			CIPShimIfBase *nif = binder->iNif;
			if (nif) // nif may have unbound itself
				{
				nif->RemoveIpAddrInfo(binder);
				// The removal of interface name has to be extracted from Release() and
				// performed before UnbindFrom() as otherwise Release() would have no CIPProtoBinders
				// to navigate up to the Flow, which is required to remove the interface name.
				nif->RemoveInterfaceName(binder);
				nif->UnbindFrom(binder);
				nif->Release(iStopCode);
				}
			binder->UnbindFromLowerFlow();

			iBinderList.Remove(index);
			delete binder;
			}
		else
			{
			reset = EFalse;
			}
		}

	if (reset)
		{
		// If we've closed all binders in the list, and the list is non-empty, then reset it.
		iBinderList.Reset();
		}
	}

// Utility functions

GLDEF_C void Panic(TIPShimPanic aReason)
	{
	_LIT(KPanicCategory, "IPShim");

	User::Panic(KPanicCategory(), aReason);
	}

// =================================================================================
//
// Flow Factory methods
//

CIPShimFlowFactory* CIPShimFlowFactory::NewL(TAny* aConstructionParameters)
/**
Constructs a Default SubConnection Flow Factory

@param aConstructionParameters construction data passed by ECOM

@returns pointer to a constructed factory
*/
	{
	CIPShimFlowFactory* ptr = new (ELeave) CIPShimFlowFactory(TUid::Uid(KIPShimFlowImplUid), *(reinterpret_cast<CSubConnectionFlowFactoryContainer*>(aConstructionParameters)));
	return ptr;
	}

CIPShimFlowFactory::~CIPShimFlowFactory()
	{
	}


CIPShimFlowFactory::CIPShimFlowFactory(TUid aFactoryId, CSubConnectionFlowFactoryContainer& aParentContainer)
	: CSubConnectionFlowFactoryBase(aFactoryId, aParentContainer)
/**
Default SubConnection Flow Factory Constructor

@param aFactoryId ECOM Implementation Id
@param aParentContainer Object Owner
*/
	{
	}


CSubConnectionFlowBase* CIPShimFlowFactory::DoCreateFlowL(ESock::CProtocolIntfBase* aProtocolIntf, TFactoryQueryBase& aQuery)
	{
	const TDefaultFlowFactoryQuery& query = static_cast<const TDefaultFlowFactoryQuery&>(aQuery);
	return new (ELeave) CIPShimSubConnectionFlow(*this, query.iSCprId, aProtocolIntf);
	}

CProtocolIntfFactoryBase* CIPShimFlowFactory::CreateProtocolIntfFactoryL(CProtocolIntfFactoryContainer& aParentContainer)
	{
	CProtocolIntfFactoryBase* factory = CIPShimProtocolIntfFactory::NewL(TUid::Uid(KIPShimFlowImplUid),
																		 aParentContainer);
    return factory;
	}

CIPShimProtocolIntf::CIPShimProtocolIntf(CProtocolIntfFactoryBase& aFactory,const Messages::TNodeId& aCprId)
	:CProtocolIntfBase(aFactory,aCprId)
	{
	}

CIPShimProtocolIntf::~CIPShimProtocolIntf()
	{
	// if there is still a nif open, then one of the ipshimflows
	// is not being cleaned up correctly
	ASSERT(iNifs.Count() == 0);
	iNifs.Close();
	}

void CIPShimProtocolIntf::DoFlowCreated(ESock::CSubConnectionFlowBase& /*aFlow*/)
	{
	// do nothing
	}

void CIPShimProtocolIntf::DoFlowBeingDeleted(ESock::CSubConnectionFlowBase& /*aFlow*/)
	{
	// do nothing
	}

CIPShimIfBase* CIPShimProtocolIntf::FindOrCreateNifL(const TDesC8& aProtocol, const TConnectionInfo& aConnectionInfo)
	{
	CIPShimIfBase* shim = NULL;

	for (TInt i = 0; i < iNifs.Count(); i++)
		{
		if (iNifs[i]->ProtocolName().Compare(aProtocol) == 0)
			{
			shim = iNifs[i];
			break;
			}
		}
	if (shim == NULL)
		{
		shim = CIPShimIfBase::NewL(aProtocol, this);
		
		CleanupStack::PushL(shim);
		iNifs.AppendL(shim);
		CleanupStack::Pop(shim);
		
		shim->SetConnectionInfo(aConnectionInfo);
		}

	return shim;
	}

void CIPShimProtocolIntf::NifDisappearing(CIPShimIfBase* aNif)
	{
	TInt index = iNifs.Find(aNif);
	// index can be KErrNotFound if there was an OOM whilst trying to append a CIPShimIfBase to iNifs.
	ASSERT(index >= KErrNotFound);
	if (index >= 0)
		{
		iNifs.Remove(index);
		}
	}

//TNodeCtxId(ECFActivityOpenCloseRoute, ControlProviderId())
void CIPShimProtocolIntf::OpenRoute()
	{
		// Post directly to cpr bypassing the scpr purely for efficiency
	__CFLOG_1(KIPProtoTag1, KIPProtoTag2, _L("CIPShimProtocolIntf %08x:\tOpenRoute()"), this);

	RClientInterface::OpenPostMessageClose(ControlProviderId(), ControlProviderId(),
		TCFIPProtoMessage::TOpenCloseRoute(ETrue).CRef());
	}

//TNodeCtxId(ECFActivityOpenCloseRoute, ControlProviderId())
void CIPShimProtocolIntf::CloseRoute()
	{
	// Post directly to cpr bypassing the scpr purely for efficiency
	__CFLOG_1(KIPProtoTag1, KIPProtoTag2, _L("CIPShimProtocolIntf %08x:\tCloseRoute()"), this);

	RClientInterface::OpenPostMessageClose(ControlProviderId(), ControlProviderId(),
		TCFIPProtoMessage::TOpenCloseRoute(EFalse).CRef());
	}


CIPShimProtocolIntfFactory* CIPShimProtocolIntfFactory::NewL(TUid aFactoryId,
															 ESock::CProtocolIntfFactoryContainer& aParentContainer)
	{
	CIPShimProtocolIntfFactory *fact = new(ELeave) CIPShimProtocolIntfFactory(aFactoryId, aParentContainer);
	CleanupStack::PushL(fact);
	fact->ConstructL();
	CleanupStack::Pop(fact);
	return fact;
	}

CProtocolIntfBase* CIPShimProtocolIntfFactory::DoCreateProtocolIntfL(TFactoryQueryBase& aQuery)
	{
	const TDefaultProtocolIntfFactoryQuery& query = static_cast<const TDefaultProtocolIntfFactoryQuery&>(aQuery);
	return new(ELeave) CIPShimProtocolIntf(*this, query.iCprId);
	}

CIPShimProtocolIntfFactory::CIPShimProtocolIntfFactory(TUid aFactoryId,
													   ESock::CProtocolIntfFactoryContainer& aParentContainer)
	:CProtocolIntfFactoryBase(aFactoryId, aParentContainer)
	{
	}
