// Copyright (c) 2006-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:
// This is part of an ECOM plug-in
// 
//

#define SYMBIAN_NETWORKING_UPS

#include "netmcpr.h"
#include "netmcprstates.h"
#include "netmcpractivities.h"
#include "policyrequest.h"



#include <comms-infras/ss_log.h>
#include <comms-infras/ss_msgintercept.h>
//#include <comms-infras/ss_roles.h>
#include <ss_glob.h>
#include <cs_subconparams.h>
#include <networking/qos3gpp_subconparams.h>

#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
#include <tcpdfltrecvwin.h>
#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
#include <commsdattypeinfov1_1_internal.h>

#if defined __CFLOG_ACTIVE || defined SYMBIAN_TRACE_ENABLE
#define KNetMCprTag KESockMetaConnectionTag
_LIT8(KNetMCprSubTag, "netmcpr");
#endif

using namespace Messages;
using namespace MeshMachine;
using namespace ESock;
using namespace NetStateMachine;
using namespace MCprActivities;
using namespace CommsDat;

//
// CNetworkMetaConnectionProvider

#ifndef SYMBIAN_NETWORKING_UPS

CNetworkMetaConnectionProvider* CNetworkMetaConnectionProvider::NewL(CMetaConnectionProviderFactoryBase& aFactory, const TProviderInfo& aProviderInfo)
	{
	__CFLOG_VAR((KNetMCprTag, KNetMCprSubTag, _L8("CNetworkMetaConnectionProvider:\tNewL()")));

	CNetworkMetaConnectionProvider* self = new (ELeave) CNetworkMetaConnectionProvider(aFactory,aProviderInfo,NetMCprActivities::netMCprActivities::Self());
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

#endif

CNetworkMetaConnectionProvider::CNetworkMetaConnectionProvider(CMetaConnectionProviderFactoryBase& aFactory, const TProviderInfo& aProviderInfo, const MeshMachine::TNodeActivityMap& aActivityMap)
:	CMobilityMetaConnectionProvider(aFactory,aProviderInfo,aActivityMap)
	{
	LOG_NODE_CREATE(KNetMCprTag, CNetworkMetaConnectionProvider);
	}

void CNetworkMetaConnectionProvider::ConstructL()
	{
	CCoreMetaConnectionProvider::ConstructL();
	
#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
	RMetaExtensionContainer mec;
	mec.Open(iAccessPointConfig);
	CleanupClosePushL(mec);
	
	//Append pointer to lookup table which holds the various TCP receive window sizes for different bearer types.
	CTCPReceiveWindowSize* receiveWindow;
	receiveWindow = new (ELeave)CDfltTCPReceiveWindowSize();
	CleanupStack::PushL(receiveWindow);
	mec.AppendExtensionL(receiveWindow);
	CleanupStack::Pop(receiveWindow);
	
	//Append the pointer of CSAPSetOpt which provides generic SetOpt( ) implementation
	CSAPSetOpt* protoOption = new (ELeave)CSAPSetOpt();
	CleanupStack::PushL(protoOption);
	mec.AppendExtensionL(protoOption);
	CleanupStack::Pop(protoOption);

	iAccessPointConfig.Close();
	iAccessPointConfig.Open(mec);
	CleanupStack::PopAndDestroy(&mec);
#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
	}


CNetworkMetaConnectionProvider::~CNetworkMetaConnectionProvider()
	{
	delete iPolicySelectorRecSet;
	delete iDbSession;
	LOG_NODE_DESTROY(KNetMCprTag, CNetworkMetaConnectionProvider);
	}


void CNetworkMetaConnectionProvider::ReceivedL(const TRuntimeCtxId& aSender, const TNodeId& aRecipient, TSignatureBase& aMessage)
    {
	__CFLOG_VAR((KNetMCprTag, KNetMCprSubTag, _L8("CNetworkMetaConnectionProvider %08x:\tReceivedL() aMessage=%d"),
	   this, aMessage.MessageId().MessageId()));

	ESOCK_DEBUG_MESSAGE_INTERCEPT(aSender, aMessage, aRecipient);

	TNodeContext<CNetworkMetaConnectionProvider> ctx(*this, aMessage, aSender, aRecipient);
    CMobilityMetaConnectionProvider::Received(ctx);
    User::LeaveIfError(ctx.iReturn);
	}

void CNetworkMetaConnectionProvider::ProcessPolicyParamsL(const TRuntimeCtxId& aSender, TCFIPMessage::TPolicyParams& aPolicyParam)
	{
	// this is the best effort call. If db is not there then ignore it
	TRAPD(err, InitDbL());
	if(err == KErrNone)
		{
		RCFParameterFamilyBundleC paramBundle = CreateParameterBundleL(FindMatchingPolicyL(aPolicyParam));

		// create worker node

#ifndef __GCCXML__
		RClientInterface::OpenPostMessageClose(Id(),
			SockManGlobals::Get()->GetPlaneFC(TCFPlayerRole(TCFPlayerRole::ESubConnPlane)),
			TCFConnPolicyRequest(paramBundle, aPolicyParam.iFlowId, aPolicyParam.iSrcNodeId, address_cast<TNodeId>(aSender)));
#endif
		}
	}

void CNetworkMetaConnectionProvider::InitDbL()
	{
	if(iDbSession == NULL)
		{
		iDbSession = CMDBSession::NewL(KCDVersion1_2);
		}

	if(iPolicySelectorRecSet == NULL)
		{
	    iPolicySelectorRecSet = new (ELeave) CMDBRecordSet<CCDPolicySelectorRecord>(KCDTIdPolicySelectorRecord);
		iPolicySelectorRecSet->LoadL(*iDbSession);

		if(iPolicySelectorRecSet->iRecords.Count() == 0)
			{
			User::Leave(KErrNotFound);
			}
		}
	else
		{
		iPolicySelectorRecSet->RefreshL(*iDbSession);

		if(iPolicySelectorRecSet->iRecords.Count() == 0)
			{
			User::Leave(KErrNotFound);
			}
		}
	}

TInt CNetworkMetaConnectionProvider::FindMatchingPolicyL(TCFIPMessage::TPolicyParams& aPolicyParam)
	{
	TBool found(EFalse);
	TUint count(iPolicySelectorRecSet->iRecords.Count());

	CCDPolicySelectorRecord* currentRec(NULL);

	for(TInt i=0;i<count && !found;++i)
		{
		currentRec = static_cast<CCDPolicySelectorRecord*>(iPolicySelectorRecSet->iRecords[i]);

		found = (CheckProtocol(aPolicyParam.iAddrUpdate.iProtocolId, currentRec)
		    && CheckSrcPort(aPolicyParam.iAddrUpdate.iSrcSockAddr.Port(), currentRec)
		    && CheckDstPort(aPolicyParam.iAddrUpdate.iDestSockAddr.Port(), currentRec)
		    && CheckIap(aPolicyParam.iAddrUpdate.iIapId, currentRec)
		    && CheckSrcAddressMatch(aPolicyParam.iAddrUpdate.iSrcSockAddr, currentRec)
		    && CheckDstAddressMatch(aPolicyParam.iAddrUpdate.iDestSockAddr, currentRec)
			&& CheckAppUid(aPolicyParam.iAppSid, currentRec));
		}

	if (!found)
		{
		__CFLOG_VAR((KNetMCprTag, KNetMCprSubTag, _L8("CNetworkMetaConnectionProvider::FindMatchingPolicyL not found")));
		User::Leave(KErrNotFound);
		}
	else
		{
		__CFLOG_VAR((KNetMCprTag, KNetMCprSubTag, _L8("CNetworkMetaConnectionProvider::FindMatchingPolicyL found")));
		return currentRec->iPolicyId;
		}

	// never executed
	return 0;
	}

ESock::RCFParameterFamilyBundleC CNetworkMetaConnectionProvider::CreateParameterBundleL(TUint aPolicyId)
	{
	CMDBRecordSet<CCDPolicySelector2ParamsRecord>* iRecSet = new(ELeave) CMDBRecordSet<CCDPolicySelector2ParamsRecord>(KCDTIdPolicySelector2ParamsRecord);
	CleanupStack::PushL(iRecSet);

	// create new record
	CCDPolicySelector2ParamsRecord* iRec = static_cast<CCDPolicySelector2ParamsRecord*>(CCDRecordBase::RecordFactoryL(KCDTIdPolicySelector2ParamsRecord));

	// Prime record
	iRec->iPolicySelectorId = aPolicyId;
	// Append primed record to recordset
	TInt err = iRecSet->iRecords.Append(iRec);
	if(err != KErrNone)
		{
		delete iRec;
		User::Leave(err);
		}

	TBool searchResult = iRecSet->FindL(*iDbSession);
	if(!searchResult)
		{
		// Params not found
		User::Leave(KErrNotFound);
		}

	TUint count = iRecSet->iRecords.Count();
	err = KErrNotFound;

	RCFParameterFamilyBundle tempBundle;
	RCFParameterFamilyBundleC paramBundle;
	if(count>0)
		{
		paramBundle.CreateL();
		CleanupClosePushL(paramBundle);
		tempBundle.CreateL();
		CleanupClosePushL(tempBundle);


		// create family
		paramBundle.Open(tempBundle);
		RParameterFamily family = tempBundle.CreateFamilyL(KSubConQoSFamily);
		CleanupStack::Pop(/*tempBundle*/);
		
		//[401TODO]: Replace with fency code with uid's */
		for (TUint i=0; i<count ; ++i)
			{
			// check if at least one param is filled in
			if(FillInParamsL(static_cast<CCDPolicySelector2ParamsRecord*>(iRecSet->iRecords[i])->iParamsId, family) == KErrNone)
				{
				// found at least one param, it is not an error any more
				err = KErrNone;
				}
			}
		}

	if(err != KErrNone)
		{
		CleanupStack::PopAndDestroy(/*paramBundle*/);
		}
	else
		{
		CleanupStack::Pop(/*paramBundle*/);
		}

	CleanupStack::PopAndDestroy(iRecSet);

	return paramBundle;
	}

TInt CNetworkMetaConnectionProvider::FillInParamsL(TUint aParamId, RParameterFamily& aFamily)
	{
	TInt err(KErrGeneral);

	switch(aParamId & KCDMaskShowRecordType)
		{
		case KCDTIdGenericQosRecord:
			err = FillInGenericQosParamsL(aParamId, aFamily);
				break;
		case KCDTIdUmtsR99QoSAndOnTableRecord:
			err = FillInUMTSParamsL(aParamId, aFamily);
				break;
		default: ;
		}

	return err;
	}

TInt CNetworkMetaConnectionProvider::FillInGenericQosParamsL(TUint aParamId, RParameterFamily& aFamily)
	{
	CCDGenericQosRecord *rec = static_cast<CCDGenericQosRecord*>(CCDRecordBase::RecordFactoryL(KCDTIdGenericQosRecord));
	CleanupStack::PushL(rec);

	rec->SetRecordId(aParamId & ~KCDMaskShowRecordType);
	TRAPD(err, rec->LoadL(*iDbSession);)
	if(err == KErrNone)
		{
	    CSubConQosGenericParamSet* param = CSubConQosGenericParamSet::NewL(aFamily,RParameterFamily::ERequested);

		param->SetHeaderMode(rec->iHeaderMode);
		param->SetDownlinkBandwidth(rec->iDownlinkBandwidth);
		param->SetUplinkBandwidth(rec->iUplinkBandwidth);
		param->SetDownLinkMaximumBurstSize(rec->iDownLinkMaximumBurstSize);
		param->SetUpLinkMaximumBurstSize(rec->iUpLinkMaximumBurstSize);
		param->SetDownLinkAveragePacketSize(rec->iDownLinkAveragePacketSize);
		param->SetUpLinkAveragePacketSize(rec->iUpLinkAveragePacketSize);
		param->SetDownLinkMaximumPacketSize(rec->iDownLinkMaximumPacketSize);
		param->SetUpLinkMaximumPacketSize(rec->iUpLinkMaximumPacketSize);
		param->SetDownLinkDelay(rec->iDownLinkDelay);
		param->SetUpLinkDelay(rec->iUpLinkDelay);
		param->SetDownLinkDelayVariation(rec->iDownLinkDelayVariation);
		param->SetUpLinkDelayVariation(rec->iUpLinkDelayVariation);
		param->SetDownLinkPriority(rec->iDownLinkPriority);
		param->SetUpLinkPriority(rec->iUpLinkPriority);
		}

	CleanupStack::PopAndDestroy(rec);
	return err;
	}

TInt CNetworkMetaConnectionProvider::FillInUMTSParamsL(TUint aParamId, RParameterFamily& aFamily)
	{
	CCDUmtsR99QoSAndOnTableRecord *rec = static_cast<CCDUmtsR99QoSAndOnTableRecord*>(CCDRecordBase::RecordFactoryL(KCDTIdUmtsR99QoSAndOnTableRecord));
	CleanupStack::PushL(rec);

	rec->SetRecordId(aParamId & ~KCDMaskShowRecordType);
	TRAPD(err, rec->LoadL(*iDbSession);)
	if(err == KErrNone)
		{
	    //Fill up the structures
	    CSubConQosR5ParamSet* param = CSubConQosR5ParamSet::NewL(aFamily,RParameterFamily::ERequested);

	    param->SetTrafficClass(rec->iGPRSReqTrafficClass);
		param->SetDeliveryOrder(rec->iGPRSReqDeliveryOrder);
		param->SetErroneousSDUDelivery(rec->iGPRSReqDeliverErroneousSDU);
		param->SetResidualBitErrorRatio(rec->iGPRSReqBER);
		param->SetSDUErrorRatio(rec->iGPRSReqSDUErrorRatio);
		param->SetTrafficHandlingPriority(rec->iGPRSReqTrafficHandlingPriority);
		param->SetTransferDelay(rec->iGPRSReqTransferDelay);
		param->SetMaxSduSize(rec->iGPRSReqMaxSDUSize);
		param->SetMaxBitrateUplink(rec->iGPRSReqMaxUplinkRate);
		param->SetMaxBitrateDownlink(rec->iGPRSReqMaxDownlinkRate);
		param->SetGuaBitrateUplink(rec->iGPRSReqGuaranteedUplinkRate);
		param->SetGuaBitrateDownlink(rec->iGPRSReqGuaranteedDownlinkRate);
		param->SetSignallingIndicator(rec->iGPRSSignallingIndication);
		param->SetSourceStatisticsDescriptor(rec->iGPRSSourceStatisticsDescriptor);
		}

	CleanupStack::PopAndDestroy(rec);
	return err;
	}

TBool CNetworkMetaConnectionProvider::CheckSrcAddressMatch(const TSockAddr& aFirst, CCDPolicySelectorRecord* aRecord)
	{
	if(aRecord && (!(aRecord->iSrcAddress.IsNull() || aRecord->iSrcMask.IsNull())))
		{
		TInetAddr first(aFirst);

		TInetAddr second;
		second.Input(aRecord->iSrcAddress);

		TInetAddr mask;
		mask.Input(aRecord->iSrcMask);

		return first.Match(second, mask);
		}

	return ETrue;
	}

TBool CNetworkMetaConnectionProvider::CheckDstAddressMatch(const TSockAddr& aFirst, CCDPolicySelectorRecord* aRecord)
	{
	if(aRecord && (!(aRecord->iDstAddress.IsNull() || aRecord->iDstMask.IsNull())))
		{
		TInetAddr first(aFirst);

		TInetAddr second;
		second.Input(aRecord->iDstAddress);

		TInetAddr mask;
		mask.Input(aRecord->iDstMask);

		return first.Match(second, mask);
		}

	return ETrue;
	}

TBool CNetworkMetaConnectionProvider::CheckProtocol(TUint aProtocolId, CCDPolicySelectorRecord* aRecord)
	{
	// compare protocolId
	if(aRecord && !aRecord->iProtocolId.IsNull())
		{
		return aProtocolId == aRecord->iProtocolId;
		}

	return ETrue;
	}


TBool CNetworkMetaConnectionProvider::CheckSrcPort(TUint aPort, CCDPolicySelectorRecord* aRecord)
	{
	// match src port
	if(aRecord && (!(aRecord->iSrcPort.IsNull() || aRecord->iSrcPortMax.IsNull())))
		{
		return (aPort >= aRecord->iSrcPort)  && (aPort <= aRecord->iSrcPortMax);
		}

	return ETrue;
	}

TBool CNetworkMetaConnectionProvider::CheckDstPort(TUint aPort, CCDPolicySelectorRecord* aRecord)
	{
	// match src port
	if(aRecord && (!(aRecord->iDstPort.IsNull() || aRecord->iDstPortMax.IsNull())))
		{
		return (aPort >= aRecord->iDstPort)  && (aPort <= aRecord->iDstPortMax);
		}

	return ETrue;
	}

TBool CNetworkMetaConnectionProvider::CheckIap(TUint aIapId, CCDPolicySelectorRecord* aRecord)
	{
	// check IAP
	if(aRecord && !aRecord->iIapId.IsNull())
		{
		return aIapId == aRecord->iIapId;
		}

	return ETrue;
	}

TBool CNetworkMetaConnectionProvider::CheckAppUid(TUid aAppUid, CCDPolicySelectorRecord* aRecord)
	{
	// check AppUid
	if(aRecord && !aRecord->iAppUid.IsNull())
		{
		return aAppUid == aRecord->iAppUid;
		}

	return ETrue;
	}

#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW

CDfltTCPReceiveWindowSize::CDfltTCPReceiveWindowSize()
                          :CTCPReceiveWindowSize() 	
                      
/**
 *  Populate TCP window lookup table.
 */
	{
	Init();
	}

void CDfltTCPReceiveWindowSize::Init()
/**
 * Initialize TCP receive window lookup table
 */
	{  		
	//TCP receive window size for GPRS
	iBearerInfoMap.Insert(KGprsBearer,KBearerGprsWinSize);

	//TCP receive window size for EGPRS
	iBearerInfoMap.Insert(KEGprsBearer,KBearerEdgeWinSize);

	//TCP receive window size for UMTS
	iBearerInfoMap.Insert(KUmtsBearer,KBearerUmtsWinSize);

	//TCP receive window size for HSDPA
	iBearerInfoMap.Insert(KHsdpaBearer,KBearerHsdpaWinSize);
	
   	//TCP receive window size for ethernet
   	iBearerInfoMap.Insert(KEthernetBearer,KBearerEthernetWinSize);

   	//TCP receive window size for other bearer
   	iBearerInfoMap.Insert(KDefaultBearer,KBearerWlanWinSize);
	}

void CDfltTCPReceiveWindowSize::SetTcpWin(TUint aBearerType)
/**
 * Set TCP receive window 
 */
	{
	//Set the TCP Receive Window.
	iWinSize = *static_cast<TUint*>(iBearerInfoMap.Find(aBearerType));
	  
	//Set the Max TCP receive Window.
	SetMaxWinSize(aBearerType);
	}
	

void CDfltTCPReceiveWindowSize::SetMaxWinSize(TUint aBearerType)
/**
 * Set Max TCP receive Window.
 * @Param Bearer type
 */
	{
	switch(aBearerType)
		{
		case KGprsBearer:
		case KEGprsBearer:
		case KUmtsBearer:
		case KHsdpaBearer:
		//
		// TCP receive window size will be maximum for HSDPA bearers.
		//
			iMaxWinSize = KBearerHsdpaWinSize; 
			break; 
		case KEthernetBearer:
			iMaxWinSize = KEthernetMaxWinSize;
			break;
   
		default:
			iMaxWinSize = KEthernetMaxWinSize;
			break;
		}
	}


CDfltTCPReceiveWindowSize::~CDfltTCPReceiveWindowSize()
/**
 * Close TCP receive window lookup table
 */
	{
	iBearerInfoMap.Close();
	}
	
/**
 * Register the variables of TCP receive window class,
 * since it derives from SMetaData class
 */
START_ATTRIBUTE_TABLE(CTCPReceiveWindowSize,CTCPReceiveWindowSize::ERealmId, CTCPReceiveWindowSize::iId)
  REGISTER_ATTRIBUTE(CTCPReceiveWindowSize, iMaxWinSize, TMetaNumber)
  REGISTER_ATTRIBUTE(CTCPReceiveWindowSize, iWinSize, TMetaNumber)
END_ATTRIBUTE_TABLE()

#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
