// Copyright (c) 2003-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:
//

#include <e32base.h>
#include <f32file.h>
#include "smsprot.h"
#include "WAPDGRM.H"
#include "wappstor.h"
#include "ws_main.h"
#include "es_wsms.h"
#include "ws_obsvr.h"
#include "SmsuBackup.h"

//
// Sets the values used for the local wap ports
// Limits the number of simultaneously open SAPs to 254
//
const TInt KMaxWapPortNumber = 255;
const TInt KMinWapPortNumber = 1;

const TInt KMax8BitPortNumber = 255;
const TInt KMax16BitPortNumber = 65535;


/**
 *  Constructor
 */
CWapSmsProtocol::CWapSmsProtocol()
    {
	iSAPList.SetOffset(CWapSmsProvider::LinkOffset());
    } // CWapSmsProtocol::CWapSmsProtocol


//
// Factory
//
CWapSmsProtocol* CWapSmsProtocol::NewL()
	{
	LOGWAPPROT1("CWapSmsProtocol::NewL()");

	CWapSmsProtocol* p=new(ELeave) CWapSmsProtocol;
	CleanupStack::PushL(p);
	User::LeaveIfError(p->iFs.Connect());
	p->iObserver[0]=CWapProtocolObserver::NewL(p);
	p->iObserver[1]=CWapProtocolObserver::NewL(p);
	p->iObserver[2]=CWapProtocolObserver::NewL(p);
	p->iWapStore=CWapReassemblyStore::NewL(p->iFs);

	p->iBackupRestoreSession=CBackupAndRestore::NewL(*p);
	CleanupStack::Pop();
	return p;
	} // CWapSmsProtocol::NewL


void CWapSmsProtocol::HandleBackupOrRestoreStartingL()
	{
	LOGWAPPROT1("CWapSmsProtocol::HandleBackupOrRestoreStartingL");
	iWapStore->Close();
	} // CWapSmsProtocol::HandleBackupOrRestoreStartingL


void CWapSmsProtocol::HandleBackupOrRestoreCompleteL()
	{
	LOGWAPPROT1("CWapSmsProtocol::HandleBackupOrRestoreCompleteL ");
	iWapStore->DoOpenL();
	} // CWapSmsProtocol::HandleBackupOrRestoreCompleteL


/**
 *  Destructor
 */
CWapSmsProtocol::~CWapSmsProtocol()
    {
    delete iBackupRestoreSession;

    RemoveObserversFromSmsProtocol();
    delete iObserver[0];
    delete iObserver[1];
    delete iObserver[2];
    delete iWapStore;

    if (iSmsProtocol)
        {
        iSmsProtocol->Close();
        }

    iFs.Close();
    } // CWapSmsProtocol::~CWapSmsProtocol


//
// Socket server asking for a new sap
//
CServProviderBase *CWapSmsProtocol::NewSAPL(TUint aSocketType)
	{
        LOGWAPPROT1("*CWapSmsProtocol::NewSAPL");

	if (aSocketType!=KSockDatagram)
		User::Leave(KErrNotSupported);
	CWapSmsProvider *pSAP = CWapSmsProvider::NewL(this);
	AddSAP(pSAP, aSocketType);
	return pSAP;
	} // CWapSmsProtocol::NewSAPL


//
// InitL call from socket server.
//
void CWapSmsProtocol::InitL(TDesC& /*aTag*/)
	{
	}


//
// Called by another protocol that runs on top of WAP
//
void CWapSmsProtocol::BindL(CProtocolBase* /*aProtocol*/, TUint /*aId*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF
    LOGWAPPROT1("CWapSmsProtocol::BindL");
    Panic(EWapSmsCantBind);
    BULLSEYE_RESTORE
    }

//
// StartL call from socket server.
// Open lower layers
//
void CWapSmsProtocol::StartL()
	{
	}


//
// Identify request from SOCKET server
//
void CWapSmsProtocol::Identify(TServerProtocolDesc *aDes) const
	{
    LOGWAPPROT1("CWapSmsProtocol::Identify");

	aDes->iName=KWAPSMSProtocolId;
	aDes->iAddrFamily=KWAPSMSAddrFamily;
	aDes->iSockType=KSockDatagram;
	aDes->iProtocol=KWAPSMSDatagramProtocol;

	aDes->iVersion=TVersion(KWapSmsMajorVersionNumber,KWapSmsMinorVersionNumber,KWapSmsBuildVersionNumber);
	aDes->iByteOrder=ELittleEndian;
	aDes->iServiceInfo=KWAPSMSDatagramServiceInfo;
	aDes->iNamingServices=0;
	aDes->iSecurity=KSocketNoSecurity;
	aDes->iMessageSize=KWAPSMSMaxDatagramSize;
	aDes->iServiceTypeInfo=0;
	aDes->iNumSockets=KWAPSMSNumberSockets;
	} // CWapSmsProtocol::Identify


//
// Called by socket server to initiate bind to SMS protocol
//
void CWapSmsProtocol::BindToL(CProtocolBase* aProtocol)
	{
    LOGWAPPROT1("CWapSmsProtocol::BindToL");

	TServerProtocolDesc info;
	aProtocol->Identify(&info);
	TUint theirId = info.iProtocol;
	Identify(&info);
	TUint ourId = info.iProtocol;

	if (theirId==ourId)
		User::Leave(KErrGeneral); // Nutter!

	if (theirId!=KSMSDatagramProtocol)
		User::Leave(KErrGeneral);

	iSmsProtocol = (CSmsProtocol*)aProtocol;
	iSmsProtocol->Open();
	iSmsProtocol->StartL();
	BindObserversToSmsL();
	} // CWapSmsProtocol::BindToL


//
// Register the observers with sms.prt
//
void CWapSmsProtocol::BindObserversToSmsL()
	{
    LOGWAPPROT1("CWapSmsProtocol::BindObserversToSmsL");

	iNextSapPort=KMinWapPortNumber;
	TSmsAddr addr0;
	TSmsAddr addr1;
	TSmsAddr addr2;

	addr0.SetSmsAddrFamily(ESmsAddrMatchIEI);
	addr0.SetIdentifierMatch(CSmsInformationElement::ESmsIEIApplicationPortAddressing8Bit);
	addr1.SetSmsAddrFamily(ESmsAddrMatchIEI);
	addr1.SetIdentifierMatch(CSmsInformationElement::ESmsIEIApplicationPortAddressing16Bit);
	addr2.SetSmsAddrFamily(ESmsAddrMatchText);
	addr2.SetTextMatch(_L8("//SCK"));

	iSmsProtocol->AddSmsMessageObserverL(*iObserver[0]);
	TInt ret=iSmsProtocol->BindSmsMessageObserver(*iObserver[0],addr0);
    User::LeaveIfError(ret);

	iSmsProtocol->AddSmsMessageObserverL(*iObserver[1]);
	ret=iSmsProtocol->BindSmsMessageObserver(*iObserver[1],addr1);
    User::LeaveIfError(ret);

	iSmsProtocol->AddSmsMessageObserverL(*iObserver[2]);
	ret=iSmsProtocol->BindSmsMessageObserver(*iObserver[2],addr2);
    User::LeaveIfError(ret);
	} // CWapSmsProtocol::BindObserversToSmsL


//
// Deregister the observers
//
void CWapSmsProtocol::RemoveObserversFromSmsProtocol()
	{
        LOGWAPPROT1("CWapSmsProtocol::RemoveObserversFromSmsProtocol");

	if (iSmsProtocol==NULL)
		return;
	iSmsProtocol->RemoveSmsMessageObserver(*iObserver[0]);
	iSmsProtocol->RemoveSmsMessageObserver(*iObserver[1]);
	iSmsProtocol->RemoveSmsMessageObserver(*iObserver[2]);
	} // CWapSmsProtocol::RemoveObserversFromSmsProtocol


//
// Send a datagram originating from a higher protocol
// Can never be called as we don't implement BindL
//
//
TInt CWapSmsProtocol::Send(TDes8 &, TSockAddr* /*to*/,TSockAddr* /*from*/,CProtocolBase* /*aSourceProtocol*/)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF
    LOGWAPPROT1("CWapSmsProtocol::Send");
    Panic(EWapSmsSendCallCantBind);
    return KErrNone;
    BULLSEYE_RESTORE
    }

//
// Receive an SMS
//
void CWapSmsProtocol::ProcessSmsL(const CSmsMessage& aSmsMessage)
	{
    LOGWAPPROT1("CWapSmsProtocol::ProcessSmsL");
	TInt index=0;
	TBool storeDatagramComplete = EFalse;
	TBool isNewStyleClient = EFalse;

	__ASSERT_DEBUG(aSmsMessage.IsComplete(),Panic(EWapSmsIncompleteSms));

	CWapDatagram* wapDatagram = CWapDatagram::NewL(aSmsMessage);

	CleanupStack::PushL(wapDatagram);
	TBool isCompleteDatagram = wapDatagram->IsComplete();
	if (!isCompleteDatagram)
		{

		storeDatagramComplete = iWapStore->AddMessageL(index,*wapDatagram);
		if (!storeDatagramComplete)
			{
			CleanupStack::PopAndDestroy(wapDatagram);
			return;
			}
		iWapStore->GetDatagramL(index,*wapDatagram);
		}

	CWapSmsProvider* wapsmsProvider = LookupSAP(wapDatagram);

	if (wapsmsProvider)
		{
		isNewStyleClient= wapsmsProvider->IsNewStyleClient();
		if(isCompleteDatagram && isNewStyleClient)//8 bit datagram or complete messages, Need to store it for new clients
			{
			storeDatagramComplete = iWapStore->AddMessageL(index,*wapDatagram);
			if (!storeDatagramComplete)
				{
				CleanupStack::PopAndDestroy(wapDatagram);
				return;
				}
			}
		if(!isNewStyleClient && !isCompleteDatagram)
			{
			iWapStore->BeginTransactionLC();
			iWapStore->DeleteEntryL(index);
			iWapStore->CommitTransactionL();
			}
		CleanupStack::Pop(wapDatagram);
		wapsmsProvider->AddToQueue(wapDatagram);
		return;
		}
	else if(!isCompleteDatagram)
		{
		iWapStore->BeginTransactionLC();
		iWapStore->DeleteEntryL(index);
		iWapStore->CommitTransactionL();
		}

	CleanupStack::PopAndDestroy(wapDatagram);
	User::Leave(KErrNotFound);
	} // CWapSmsProtocol::ProcessSmsL


//
// Get Wap Protocol options
// If none match the level/name pass the query on to SMS
//
TInt CWapSmsProtocol::GetOption(TUint aLevel, TUint aName, TDes8& aOption, CProtocolBase* /*aSourceProtocol*/)
	{
    LOGWAPPROT1("CWapSmsProtocol::GetOption");

	TInt ret = iSmsProtocol->GetOption(aLevel, aName, aOption,this);
	return ret;
	} // CWapSmsProtocol::GetOption


//
// Set Wap Protocol options
// If none match the level/name pass the query on to SMS
//
TInt CWapSmsProtocol::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption, CProtocolBase* /*aSourceProtocol*/)
    {
    LOGWAPPROT1("CWapSmsProtocol::SetOption");
    
    TInt ret= iSmsProtocol->SetOption(aLevel,aName,aOption,this);
    return ret;
    } // CWapSmsProtocol::SetOption


//
// Inform all SAPs of error.
//
void CWapSmsProtocol::Error(TInt aError, CProtocolBase* /*aSourceProtocol*/)
    {
    LOGWAPPROT1("CWapSmsProtocol::Error");
    
    TDblQueIter<CWapSmsProvider> iter(iSAPList);
    CWapSmsProvider* sap;
    while (sap = iter++, sap!=NULL)
        sap->Error(aError,MSocketNotify::EErrorAllOperations);
    }

//
// Socket server asking for a host resolver
//
CHostResolvProvdBase *CWapSmsProtocol::NewHostResolverL()
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF
    LOGWAPPROT1("*CWapSmsProtocol::NewHostResolverL");
    Panic(EWapSmsCantCreateHostResolver);
    return NULL;
    BULLSEYE_RESTORE
    }

//
// Socket server asking for a service resolver
//
CServiceResolvProvdBase *CWapSmsProtocol::NewServiceResolverL()
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF
    LOGWAPPROT1("*CWapSmsProtocol::NewServiceResolverL");
    Panic(EWapSmsCantCreateServiceResolver);
    return NULL;
    BULLSEYE_RESTORE
    }

//
// Socket server asking for a net data base
//
CNetDBProvdBase* CWapSmsProtocol::NewNetDatabaseL()
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF
    LOGWAPPROT1("CWapSmsProtocol::NewNetDatabaseL");
    Panic(EWapSmsCantCreateNetDatabase);
    return NULL;
    BULLSEYE_RESTORE
    }

//
// Add a SAP to the SAP list and checks the SAR store for this SAP's entries
//
void CWapSmsProtocol::AddSAP(CWapSmsProvider* aSAP, TUint /*aSockType*/)
	{
    LOGWAPPROT1("CWapSmsProtocol::AddSAP");

	iSAPList.AddLast(*aSAP);
	} // CWapSmsProtocol::AddSAP


//
// Set the sap port number
//
TBool CWapSmsProtocol::AllocateLocalAddress(TWapAddr& aAddr)
	{
    LOGWAPPROT1("CWapSmsProtocol::AllocateLocalAddressL");
	//
	TBool found=EFalse;
	TUint count=0,attempts=0;
	count =KMaxWapPortNumber-KMinWapPortNumber+1;

	TSmsAddr addr8;
	addr8.SetSmsAddrFamily(ESmsAddrApplication8BitPort);

	for(;!found && attempts < count;attempts++)
		{
			addr8.SetPort(iNextSapPort++);
			if(iNextSapPort > KMaxWapPortNumber)iNextSapPort=KMinWapPortNumber;
			if(!iSmsProtocol->SmsAddrIsAlreadyUsed(NULL,addr8))found=ETrue;
		}
	if(found)
		aAddr.SetWapPort(static_cast<TWapPortNumber>(addr8.Port()));

	return found;
	} // CWapSmsProtocol::AllocateLocalAddress


//
// Find the provider who wants the message
//
CWapSmsProvider* CWapSmsProtocol::LookupSAP(CWapDatagram* aMsg)
	{
	LOGWAPPROT1("CWapSmsProtocol::LookupSAP");

	TBuf8<KMaxSockAddrSize> addrBuf;
	addrBuf.Copy(aMsg->FromAddress());
	TInt toPort=0;
	TInt fromPort=0;
	TInt Is16BitPorts = ETrue;
	aMsg->Ports(fromPort,toPort,&Is16BitPorts);

	// Modification to relax port checking to allow 16 bit port number < KMax8BitPortNumber
	if(toPort < 0 || (!Is16BitPorts && toPort > KMax8BitPortNumber) || (Is16BitPorts && toPort > KMax16BitPortNumber) )
		{
		return NULL;
		}

	TWapAddr addr;
	addr.SetWapAddress(addrBuf);
	addr.SetWapPort(static_cast<TWapPortNumber>(toPort));

	TDblQueIter<CWapSmsProvider> iter(iSAPList);
	CWapSmsProvider* sap;
	while (sap = iter++, sap!=NULL)
		{
		if (sap->MatchesLocalAddress(addr))
			return sap;
		}

	return NULL;
	} // CWapSmsProtocol::LookupSAP


//
// Check for duplicate address
//
TInt CWapSmsProtocol::AddrAlreadyUsedByWAP(const TWapAddr &aAddr, const CWapSmsProvider* aSap)
	{
    LOGWAPPROT1("CWapSmsProtocol::AddrAlreadyUsedByWAP");

	TDblQueIter<CWapSmsProvider> iter(iSAPList);
	CWapSmsProvider* sap;

	while ((sap = iter++)!=NULL)
		{
		if (sap->MatchesLocalAddress(aAddr))
			{
			if(sap==aSap)
				return KErrAlreadyExists;
			else
				return KErrInUse;
			}
		}
	LOGWAPPROT1("CWapSmsProtocol::AddrAlreadyUsedByWAP not used by WAP");
	return KErrNone;
	} // CWapSmsProtocol::AddrAlreadyUsedByWAP


//
// Return a pointer to the sms protocol
//
CSmsProtocol* CWapSmsProtocol::SmsProtocol()
    {
    LOGWAPPROT1("CWapSmsProtocol::SmsProtocol()");

    return iSmsProtocol;
    } // CWapSmsProtocol::SmsProtocol


//
//	Search the store for particular datagram, if found the entry is deleted
//
TBool CWapSmsProtocol::FindAndDeleteMsg(CWapDatagram& aDatagram)
	{
	LOGWAPPROT1("CWapSmsProtocol::FindAndDeleteMsg()");

	TInt err;
	TInt ret = EFalse;
	TRAP(err,ret=iWapStore->FindAndDeleteDatagramL(aDatagram));
	__ASSERT_DEBUG(!err,Panic(EWapSmsNotFoundInStore));
	if(!err && ret)
		return ETrue;
	else
		return EFalse;
	} // CWapSmsProtocol::FindAndDeleteMsg


//
//	Search for SAR for this SAP entries. If found any, adds it to message queue
//	Note:	This retrieves stored but not acked messages for this SAP
//			Is called when client's socket binds to address
//
TInt CWapSmsProtocol::CheckSarL(const TWapAddr& aAddr,CWapSmsProvider* aSap)
	{
	LOGWAPPROT1("CWapSmsProtocol::CheckSarL()");

	TInt count=0;
	TInt err = KErrNone;
	count = iWapStore->Entries().Count();
	TWapReassemblyEntry entry;


	TWapPortNumber portNumber = aAddr.WapPort() ;
	for(TInt index=0;index< count; index++)
		{
		entry = (TWapReassemblyEntry&)iWapStore->Entries()[index];
		if(entry.ToPort()==portNumber)
			{
			CWapDatagram* wapDatagram = NULL;
			wapDatagram = CWapDatagram::NewL(KNullDesC8);
			CleanupStack::PushL(wapDatagram);
			TRAP(err,iWapStore->GetDatagramL( index,*wapDatagram));
			__ASSERT_DEBUG(!err,Panic(EWapSmsBadGetDataCall));
			CleanupStack::Pop(wapDatagram);
			aSap->AddToQueue(wapDatagram);
			}
		}
	return err;
	} // CWapSmsProtocol::CheckSarL
