// 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:
// Defines the CSimSmartCardEap class componenet
// 
//

/**
 @file
*/

#include <testconfigfileparser.h>
#include "csimsmartcardeap.h"
#include "Simlog.h"
#include "etelext.h"

// CSimSmartCardEapManager implementation; related to CSimSmartCardEap //

/**
Default constructor to initialise Ptrs.
*/
TEapChngResp::TEapChngResp()
: iChallenge(NULL), iResp(NULL),
  iAuthStatus(RMobileSmartCardEap::ENoAuthStarted)
	{
	}

/**
Default constructor to initialise Ptrs.
*/
TEapProcedureData::TEapProcedureData()
: iEapKey(NULL), iEapExtKey(NULL), iEapId(NULL), iEapPsId(NULL)
	{
	}

/**
Factory constructor.
*/
CSimSmartCardEapManager* CSimSmartCardEapManager::NewL(CSimPhone *aPhone)
	{
	CSimSmartCardEapManager* phone = new(ELeave) CSimSmartCardEapManager(aPhone);
	CleanupStack::PushL(phone);
	phone->ConstructL();
	CleanupStack::Pop();
	return phone;
	}

/**
Default constructor, initialises a pointer to the owner phone object.
*/
CSimSmartCardEapManager::CSimSmartCardEapManager(CSimPhone *aPhone)
: iPhone(aPhone)
	{
	}

/**
Second-phase constructor.
*/
void CSimSmartCardEapManager::ConstructL()
	{
	LOGPHONE1("CSimSmartCardEapManager second phase construction created");

	ParseEapInfoL();

	LOGPHONE1("CSimSmartCardEapManager second phase construction completed");
	}

/**
If this is destroyed then so should all EAP sub-session objects.
*/
CSimSmartCardEapManager::~CSimSmartCardEapManager()
	{
	LOGPHONE1("CSimSmartCardEapManager destructing");

	for (TInt jj = iSubSessionObjs.Count()-1; jj >= 0; jj--)
		{
		delete iSubSessionObjs[jj];
		}

	iSubSessionObjs.Close();

	ClearParsedData();

	LOGPHONE1("CSimSmartCardEapManager destructed");
	}

void CSimSmartCardEapManager::ClearParsedData()
	{
	// Cleanup remaining unused parsed EAP procedure data
	for (TInt jjj = 0; jjj < iEapProcData.Count(); jjj++)
		{
		for (TInt aa = 0; aa < iEapProcData[jjj].iChResp.Count(); aa++)
			{
			TEapChngResp& temp = iEapProcData[jjj].iChResp[aa];
			delete (temp.iChallenge);
			delete (temp.iResp);
			temp.iChallenge = NULL;
			temp.iResp = NULL;
			}
		iEapProcData[jjj].iChResp.Close();

		delete iEapProcData[jjj].iEapKey;
		delete iEapProcData[jjj].iEapExtKey;
		delete iEapProcData[jjj].iEapId;
		delete iEapProcData[jjj].iEapPsId;
		iEapProcData[jjj].iEapKey = NULL;
		iEapProcData[jjj].iEapExtKey = NULL;
		iEapProcData[jjj].iEapId = NULL;
		iEapProcData[jjj].iEapPsId = NULL;
		}

	iEapProcData.Close();
	iDiscardedProcedure.Close();
	}

/**
Returns a pointer to the selected config.txt file section.
*/
const CTestConfigSection* CSimSmartCardEapManager::CfgFile()
	{
	return iPhone->CfgFile();
	}

/**
Parses the UICC App EAP specific settings from the config.txt.
*/
void CSimSmartCardEapManager::ParseEapInfoL()
	{
	LOGPHONE1("CSimSmartCardEapManager::ParseEapInfoL called");

	CTestConfigItem* item = NULL;

	LOGPHONE1("Starting to Parse Smart Card EAP Info");
	TInt count = CfgFile()->ItemCount(KScEapProcedures);

	// Used in parsing to keep track of the nested items
	TInt nestedKeyTag = 0;
	TInt nestedExtKeyTag = 0;
	TInt nestedIdTag = 0;
	TInt nestedPsIdTag = 0;
	TInt nestedChlTag = 0;

	// Counts of the total number of tags (considered as nested tags)
	TInt countKey = CfgFile()->ItemCount(KScEapKeyMSK);
	TInt countExtKey = CfgFile()->ItemCount(KScEapKeyEMSK);
	TInt countId = CfgFile()->ItemCount(KScEapIdentity);
	TInt countPsId = CfgFile()->ItemCount(KScEapPsIdentity);
	TInt countChl = CfgFile()->ItemCount(KScEapChallenge);


	for (TInt index = 0; index < count; index++)
		{
		item = const_cast<CTestConfigItem*>(CfgFile()->Item(KScEapProcedures, index));
		if(item == NULL)
			{
			LOGPHONE2("WARNING CONFIGURATION FILE PARSING: SC EAP PROC INFO tag not read [%d]", index);
			continue;
			}

		TInt dataFrmt = 0;
		TInt numChallenges = 0;
		TInt ret = KErrNone;
		TPtrC8 appId, eapType;

		// To be populated and appended to iEapProcData later
		TEapProcedureData procInfo;

		// Get AID; convert to bin if required
		ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 0, appId);
		if(ret != KErrNone)
			{
			LOGPARSERR("appId", ret,0,&KScEapProcedures);
			continue;
			}
		else
			{
			procInfo.iAID = appId;

			// AID is always in binary format (because of the RMobileSmartCardEap::Open construction)
			ParseMixedBinaryAsciiDataL(procInfo.iAID);
			}

		// Get eap type
		ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 1, eapType);
		if(ret != KErrNone)
			{
			LOGPARSERR("eapType", ret,1,&KScEapProcedures);
			continue;
			}
		else
			{
			// EAP type is always in ASCII
			procInfo.iEapType = eapType;
			}

		// Find number of challenges
		ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 2, numChallenges);
		if(ret != KErrNone)
			{
			LOGPARSERR("numChallenges", ret,2,&KScEapProcedures);
			continue;
			}
		else if (numChallenges > (countChl - nestedChlTag))
			{
			LOGPHONE2("ERROR CONFIGURATION FILE PARSING: error SC EAP PROC INFO specifies more challenges than available [%d]", index);
			continue;
			}
		else if (numChallenges < 0)
			{
			LOGPHONE2("ERROR CONFIGURATION FILE PARSING: error SC EAP PROC INFO specifies -ve challenge number [%d]", index);
			continue;
			}

		// Get optional data format; this format is used for all data of this parsed procedure
		ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 3, dataFrmt);
		if(ret != KErrNone)
			{
			LOGPHONE2("CONFIGURATION FILE PARSING: SC EAP PROC INFO tag with no data format [%d]", index);
			}
		else if (dataFrmt >= EMaxConfigDataFormat)
			{
			LOGPHONE2("WARNING IN CONFIGURATION FILE PARSING - error wrong data format value SC EAP PROC INFO tag [%d] (ASCII format will be used)", index);
			dataFrmt = EConfigDataFormatAscii;
			}

		TPtrC8 ptr;
		TPtr8 tempPtr(NULL,0);
		HBufC8* startData = NULL;

		// Get MSK
		if (nestedKeyTag < countKey)
			{
			item = const_cast<CTestConfigItem*>(CfgFile()->Item(KScEapKeyMSK, nestedKeyTag++));

			// parse delay and key
			if (item == NULL)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP Key tag [%d]", nestedKeyTag-1);
				}
			else
				{
				ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 0, ptr);
				if (ret != KErrNone)
					{
					LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP Key tag's data [%d]", nestedKeyTag-1);
					}
				else
					{
					TRAPD(kAllocErr, startData = ptr.AllocL());
					if (kAllocErr != KErrNone)
						{
						LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP Key data [%d]", nestedKeyTag-1);
						}
					else
						{
						tempPtr.Set(startData->Des());
						switch (dataFrmt)
							{
						case EConfigDataFormatMixedBinaryAndAscii:
							ParseMixedBinaryAsciiDataL(tempPtr);
							break;
						//case EConfigDataFormatAscii: // Do nothing
						//default:
							}
						// need to re-copy because converting to binary changes size
						TRAP(kAllocErr, procInfo.iEapKey = tempPtr.AllocL());
						if (kAllocErr != KErrNone)
							{
							LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP Key data copy [%d]", nestedKeyTag-1);
							}
						delete startData;
						startData = NULL;
						}
					}
				}
			}
		else
			{
			LOGPHONE1("ERROR CONFIGURATION FILE PARSING: NO SC EAP KEY INFO TAG");
			}

		// Get EMSK
		if (nestedExtKeyTag < countExtKey)
			{
			item = const_cast<CTestConfigItem*>(CfgFile()->Item(KScEapKeyEMSK, nestedExtKeyTag++));

			// parse key
			if (item == NULL)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP Ext Key tag [%d]", nestedExtKeyTag-1);
				}
			else
				{
				ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 0, ptr);
				if (ret != KErrNone)
					{
					LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP Ext Key tag's data [%d]", nestedExtKeyTag-1);
					}
				else
					{
					TRAPD(kAllocErr, startData = ptr.AllocL());
					if (kAllocErr != KErrNone)
						{
						LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP Ext Key data [%d]", nestedExtKeyTag-1);
						}
					else
						{
						tempPtr.Set(startData->Des());
						switch (dataFrmt)
							{
						case EConfigDataFormatMixedBinaryAndAscii:
							ParseMixedBinaryAsciiDataL(tempPtr);
							break;
						//case EConfigDataFormatAscii: // Do nothing
						//default:
							}
						// need to re-copy because converting to binary changes size
						TRAP(kAllocErr, procInfo.iEapExtKey = tempPtr.AllocL());
						if (kAllocErr != KErrNone)
							{
							LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP Ext Key data copy [%d]", nestedExtKeyTag-1);
							}
						delete startData;
						startData = NULL;
						}
					}
				}
			}
		else
			{
			LOGPHONE1("ERROR CONFIGURATION FILE PARSING: NO SC EAP EXT KEY INFO TAG");
			}

		// Get Permanent Identity
		if (nestedIdTag < countId)
			{
			item = const_cast<CTestConfigItem*>(CfgFile()->Item(KScEapIdentity, nestedIdTag++));

			// parse id
			if (item == NULL)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP id tag [%d]", nestedIdTag-1);
				}
			else
				{
				ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 0, ptr);
				if (ret != KErrNone)
					{
					LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP Id tag's data [%d]", nestedIdTag-1);
					}
				else
					{
					TRAPD(idAllocErr, startData = ptr.AllocL());
					if (idAllocErr != KErrNone)
						{
						LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP Id data [%d]", nestedIdTag-1);
						}
					else
						{
						TPtr8 tempPtr(NULL,0);
						tempPtr.Set(startData->Des());
						switch (dataFrmt)
							{
						case EConfigDataFormatMixedBinaryAndAscii:
							ParseMixedBinaryAsciiDataL(tempPtr);
							break;
						//case EConfigDataFormatAscii: // Do nothing
						//default:
							}
						// need to re-copy because converting to binary changes size
						TRAP(idAllocErr, procInfo.iEapId = tempPtr.AllocL());
						if (idAllocErr != KErrNone)
							{
							LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP Id data copy [%d]", nestedIdTag-1);
							}
						delete startData;
						startData = NULL;
						}
					}
				}
			}
		else
			{
			LOGPHONE1("WARNING CONFIGURATION FILE PARSING: NO SC EAP ID INFO TAG");
			}

		// Get Pseudonym Identity
		if (nestedPsIdTag < countPsId)
			{
			item = const_cast<CTestConfigItem*>(CfgFile()->Item(KScEapPsIdentity, nestedPsIdTag++));

			// parse id
			if (item == NULL)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP Pseudonym id tag [%d]", nestedPsIdTag-1);
				}
			else
				{
				ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 0, ptr);
				if (ret != KErrNone)
					{
					LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP Pseudonym Id tag's data [%d]", nestedPsIdTag-1);
					}
				else
					{
					TRAPD(idAllocErr, startData = ptr.AllocL());
					if (idAllocErr != KErrNone)
						{
						LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP Pseudonym Id data [%d]", nestedPsIdTag-1);
						}
					else
						{
						TPtr8 tempPtr(NULL,0);
						tempPtr.Set(startData->Des());
						switch (dataFrmt)
							{
						case EConfigDataFormatMixedBinaryAndAscii:
							ParseMixedBinaryAsciiDataL(tempPtr);
							break;
						//case EConfigDataFormatAscii: // Do nothing
						//default:
							}
						// need to re-copy because converting to binary changes size
						TRAP(idAllocErr, procInfo.iEapPsId = tempPtr.AllocL());
						if (idAllocErr != KErrNone)
							{
							LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP Pseudonym Id data copy [%d]", nestedPsIdTag-1);
							}
						delete startData;
						startData = NULL;
						}
					}
				}
			}
		else
			{
			LOGPHONE1("WARNING CONFIGURATION FILE PARSING: NO SC EAP PS ID INFO TAG");
			}

		// Get challenges
		// numChallenges is what is parsed from config.txt and will be
		//    decremented in loop till zero is reached.
		// nestedChlTag keeps track of the current KScEapChallenge tag
		//    being read of the total; i.e. not just nested.
		// countChl is the total number of KScEapChallenge found.
		while (numChallenges != 0)
			{
			if (nestedChlTag >= countChl)
				{
				LOGPHONE1("WARNING CONFIGURATION FILE PARSING: NO MORE SC EAP Challenge INFO TAG");
				break;
				}

			item = const_cast<CTestConfigItem*>(CfgFile()->Item(KScEapChallenge, nestedChlTag++));
			numChallenges--;

			// parse delay and challenge/response and auth status
			if (item == NULL)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP challenge tag [%d]", nestedChlTag-1);
				continue;
				}

			// Parse challenge
			ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 0, ptr);
			if (ret != KErrNone)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP challenge data [%d]", nestedChlTag-1);
				continue;
				}

			TRAPD(leaveErr, startData = ptr.AllocL());
			if (leaveErr != KErrNone)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP challenge data [%d]", nestedChlTag-1);
				continue;
				}
			tempPtr.Set(startData->Des());
			switch (dataFrmt)
				{
			case EConfigDataFormatMixedBinaryAndAscii:
				ParseMixedBinaryAsciiDataL(tempPtr);
				break;
			//case EConfigDataFormatAscii: // Do nothing
			//default:
				}
			TEapChngResp newChRespData;
			TRAP(leaveErr, newChRespData.iChallenge = tempPtr.AllocL());
			if (leaveErr != KErrNone)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP challenge data copy [%d]", nestedKeyTag-1);
				}
			delete startData;
			startData = NULL;

			// Parse response
			ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 1, ptr);
			if (ret != KErrNone)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP response data [%d]", nestedChlTag-1);
				continue;
				}

			TRAP(leaveErr, startData = ptr.AllocL());
			if (leaveErr != KErrNone)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP response data [%d]", nestedChlTag-1);
				continue;
				}
			tempPtr.Set(startData->Des());
			switch (dataFrmt)
				{
			case EConfigDataFormatMixedBinaryAndAscii:
				ParseMixedBinaryAsciiDataL(tempPtr);
				break;
			//case EConfigDataFormatAscii: // Do nothing
			//default:
				}
			TRAP(leaveErr, newChRespData.iResp = tempPtr.AllocL());
			if (leaveErr != KErrNone)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP response data copy [%d]", nestedKeyTag-1);
				}
			delete startData;
			startData = NULL;

			// Parse status
			TInt stat;
			ret = CTestConfig::GetElement(item->Value(), KStdDelimiter, 2, stat);
			if (ret != KErrNone)
				{
				LOGPHONE2("WARNING CONFIGURATION FILE PARSING: could not read EAP auth status [%d]", nestedChlTag-1);
				continue;
				}

			newChRespData.iAuthStatus = static_cast<RMobileSmartCardEap::TEapAuthStatus>(stat);

			leaveErr = procInfo.iChResp.Append(newChRespData);
			if (leaveErr != KErrNone)
				{
				LOGPHONE3("WARNING CONFIGURATION FILE PARSING: could not allocate mem for EAP challenge/resp data [%d] [err=%d]", nestedChlTag-1, leaveErr);
				}
			} // end while

		TInt errAppend = iEapProcData.Append(procInfo);
		if (errAppend != KErrNone)
			{
			LOGPHONE2("ERROR CONFIGURATION FILE PARSING: Could not store parsed EAP procedure data [err=%d]", errAppend);
			}
		else
			{
			iDiscardedProcedure.Append(EFalse);
			}
		} // end for; parsing EAP procedures from config.txt

	LOGPHONE1("CSimSmartCardEapManager::ParseEapInfoL completed");
	}

/**
Function leaves if <aAID,aEapType> is not in config file.

@leave KErrNotFound if <aAID,aEapType> is not found.
*/
void CSimSmartCardEapManager::AID_EapType_ExistsInConfigL(const RMobilePhone::TAID& aAID, const RMobileSmartCardEap::TEapType& aEapType)
	{
	for (TInt ii = 0; ii < iEapProcData.Count(); ii++)
		{
		TEapProcedureData& temp = iEapProcData[ii];
		if (temp.iAID == aAID)
			{
			// two ifs rather than && to help debug
			if (temp.iEapType == aEapType)
				{
				if (!iDiscardedProcedure[ii])
					{
					return;
					}
				}
			}
		}
	User::Leave(KErrNotFound);
	}

CTelObject* CSimSmartCardEapManager::CreateScEapSubSessionL(RMobilePhone::TAID& aAID, RMobileSmartCardEap::TEapType& aEapType)
	{
	LOGPHONE1("CSimSmartCardEapManager::CreateScEapSubSessionL called");
	// If no config exists for this eapAID,eapType pair, then this will leave
	AID_EapType_ExistsInConfigL(aAID, aEapType);

	// If exists then phoneScEap guaranteed to get data in
	// InitialiseEapMethod, since only one object can be created
	// using the unique <iAID,iEapType> pair.

	CSimSmartCardEap* phoneScEap = NULL;
	TRAPD(err, phoneScEap = CSimSmartCardEap::NewL(iPhone, this, aAID, aEapType));

	if (err != KErrNone)
		{
		LOGPHONE2("ERROR could not create CSimSmartCardEap object [err=%d]", err);
		User::Leave(err);
		}

	return phoneScEap;
	}

/**
Returns the first procedure data section, as parsed from the
config.txt, with aAID and aEapType.

@param aAID
@param aEapType
@return The first procedure data with aAID and aEapType from the
        config.txt.  NULL if no such procedure data is found.
*/
TEapProcedureData* CSimSmartCardEapManager::ProcData(const RMobilePhone::TAID& aAID, const RMobileSmartCardEap::TEapType& aEapType)
	{
	for (TInt ii = 0; ii < iEapProcData.Count(); ii++)
		{
		if (iEapProcData[ii].iAID == aAID)
			{
			// two ifs rather than && to help debug
			if (iEapProcData[ii].iEapType == aEapType)
				{
				if (!iDiscardedProcedure[ii])
					{
					return &iEapProcData[ii];
					}
				}
			}
		}
	return NULL;
	}

void CSimSmartCardEapManager::ProcDataUseCompleted(const TEapProcedureData* aProcData)
	{
	if (aProcData == NULL)
		{
		return;
		}

	TInt pos = KErrNotFound;

	for (TInt ii = 0; ii < iEapProcData.Count(); ii++)
		{
		if ((&iEapProcData[ii]) == aProcData)
			{
			pos = ii;
			break;
			}
		}

	if (pos != KErrNotFound)
		{
		iDiscardedProcedure[pos] = ETrue;
		}
	}

/**
Register a sub-session object.  At the present time, list is maintained
for clean-up only; i.e. this object stores pointers to all EAP sub-
session objects.
*/
void CSimSmartCardEapManager::RegisterSubSessionL(CSimSmartCardEap* aToRegister)
	{
	iSubSessionObjs.AppendL(aToRegister);
	}

/**
Remove a sub-session object from list of active sub-sessions.
*/
TInt CSimSmartCardEapManager::DeRegisterSubSession(const CSimSmartCardEap* aToDeRegister)
	{
	TInt index = iSubSessionObjs.Find(aToDeRegister);

	if (index < 0)
		{
		return index;
		}

	iSubSessionObjs.Remove(index);
	iSubSessionObjs.Compress();

	return KErrNone;
	}

// CSimSmartCardEap implementation //

CSimSmartCardEap* CSimSmartCardEap::NewL(CSimPhone *aPhone, CSimSmartCardEapManager* aEapMan, RMobilePhone::TAID& aAID, RMobileSmartCardEap::TEapType& aEapType)
	{
	CSimSmartCardEap* phone = new(ELeave) CSimSmartCardEap(aPhone, aAID, aEapType);
	CleanupStack::PushL(phone);
	phone->ConstructL(aEapMan);
	CleanupStack::Pop();
	return phone;
	}

CSimSmartCardEap::CSimSmartCardEap(CSimPhone *aPhone, RMobilePhone::TAID& aAID, RMobileSmartCardEap::TEapType& aEapType)
: iPhone(aPhone), iProcedureData(NULL), iSSInitialised(EFalse),
  iAccessStatus(RMobileSmartCardEap::EEapMethodAvailable), iAuthStatus(RMobileSmartCardEap::ENoAuthStarted),
  iCliTerminationListener(NULL), iCurrentChallenge(0)
	{
	iAID = aAID;
	iEapType = aEapType;
	}

void CSimSmartCardEap::ConstructL(CSimSmartCardEapManager* aEapMan)
	{
	LOGPHONE1("CSimSmartCardEap: starting second phase construction");

	iSemaphr.CreateGlobal(KNullDesC, EOwnerThread);
	aEapMan->RegisterSubSessionL(this);
	iEapMan = aEapMan;

	LOGPHONE1("CSimSmartCardEap created");
	}

CSimSmartCardEap::~CSimSmartCardEap()
	{
	if (iCliTerminationListener != NULL)
		{
		delete iCliTerminationListener;
		iCliTerminationListener = NULL;
		}
	iSemaphr.Close();

	// remove config entry from manager
	if (iProcedureData != NULL)
		{
		iEapMan->ProcDataUseCompleted(iProcedureData);
		iProcedureData = NULL;
		}

	TInt err = iEapMan->DeRegisterSubSession(this);
	LOGPHONE2("CSimSmartCardEap destroyed, deregistering returned %d", err);

	iEapMan = NULL;
	}


void CSimSmartCardEap::Init()
	{
	// server calls this function once it has created the sub-session
	// it gives the TSY chance to do any initialisation it may need to do for
	// this sub-session
	}

CTelObject* CSimSmartCardEap::OpenNewObjectByNameL(const TDesC& /*aName*/)
	{
	// Server calls this function when a client is opening an object from the phone
	// for the first time.
	// Multiple clients opening handles to the same sub-session object will be dealt with
	// by the server - i.e. by reference counting
	User::Leave(KErrNotSupported);
	return NULL;
	}

CTelObject* CSimSmartCardEap::OpenNewObjectL(TDes& /*aNewName*/)
	{
	// all objects opened from the phone are opened by name, hence this method
	// is not supported
	User::Leave(KErrNotSupported);
	return NULL;
	}

CTelObject::TReqMode CSimSmartCardEap::ReqModeL(const TInt aIpc)
	{
	// ReqModeL is called from the server's CTelObject::ReqAnalyserL
	// in order to check the type of request it has

	// The following are example request types for this dummy TSY
	// All TSYs do not have to have these request types but they have been given
	// "sensible" values in this test code

	CTelObject::TReqMode ret = 0;

	switch (aIpc)
		{
	// Non flow-controlled requests
	case EMobileSmartCardEapInitialiseEapMethod:
	case EMobileSmartCardEapGetUserIdentity:
	case EMobileSmartCardEapGetAuthenticationStatus:
	case EMobileSmartCardEapGetEapKey:
	case EMobileSmartCardEapAuthenticationPhase1:
	case EMobileSmartCardEapAuthenticationPhase2:
	case EMobileSmartCardEapReleaseEapMethod:
	case EMobileSmartCardEapGetEapMethodAccessStatus:
		break;

	case EMobileSmartCardEapNotifyEapMethodAccessStatusChange:
		ret = KReqModeMultipleCompletionEnabled;
		break;

	default:
		User::Leave(KErrNotSupported);
		break;
		}

	return ret;
	}

TInt CSimSmartCardEap::RegisterNotification(const TInt /*aIpc*/)
	{
	// RegisterNotification is called when the server recognises that this notification
	// is being posted for the first time on this sub-session object.

	// It enables the TSY to "turn on" any regular notification messages that it may 
	// receive from the phone

	return KErrNone;
	}

TInt CSimSmartCardEap::DeregisterNotification(const TInt /*aIpc*/)
	{
	// DeregisterNotification is called when the server recognises that this notification
	// will not be posted again because the last client to have a handle on this sub-session
	// object has just closed the handle.

	// It enables the TSY to "turn off" any regular notification messages that it may
	// receive from the phone

	return KErrNone;
	}

TInt CSimSmartCardEap::NumberOfSlotsL(const TInt aIpc)
	{
	// NumberOfSlotsL is called by the server when it is registering a new notification
	// It enables the TSY to tell the server how many buffer slots to allocate for
	// "repost immediately" notifications that may trigger before clients collect them

	TInt numberOfSlots = 1;

	switch (aIpc)
		{
	case EMobileSmartCardEapNotifyEapMethodAccessStatusChange:
		numberOfSlots = 3;
		break;

	default:
		// Unknown or invalid Phone IPC
		User::Leave(KErrNotSupported);
		break;
		}

	return numberOfSlots;
	}

TInt CSimSmartCardEap::ExtFunc(const TTsyReqHandle aTsyReqHandle,const TInt aIpc,
                               const TDataPackage& aPackage)
	{
	// ExtFunc is called by the server when it has a "extended", i.e. non-core ETel request 
	// for the TSY to process
	// A request handle, request type and request data are passed to the TSY

	TAny* dataPtr = aPackage.Ptr1();
	TAny* dataPtr2 = aPackage.Ptr2();

	// The request data has to extracted from TDataPackage and the TAny* pointers have to
	// be "cast" to the expected request data type

	switch(aIpc)
		{
	// Non-Flow controlled requests

	case EMobileSmartCardEapInitialiseEapMethod:
		return SimInitialiseEapMethod(aTsyReqHandle,
		         reinterpret_cast<TThreadId*>(dataPtr));

	case EMobileSmartCardEapGetUserIdentity:
		return SimGetUserIdentity(aTsyReqHandle,
		         reinterpret_cast<RMobileSmartCardEap::TEapUserIdType*>(dataPtr),
		         aPackage.Des2n());

	case EMobileSmartCardEapGetAuthenticationStatus:
		return SimGetAuthenticationStatus(aTsyReqHandle, 
		         reinterpret_cast<RMobileSmartCardEap::TEapAuthStatus*>(dataPtr));

	case EMobileSmartCardEapGetEapKey:
		return SimGetEapKey(aTsyReqHandle,
		         reinterpret_cast<RMobileSmartCardEap::TEapKeyTag*>(dataPtr),
		         aPackage.Des2n());

	case EMobileSmartCardEapAuthenticationPhase1:
		return SimSetAuthenticateDataForPhase1(aTsyReqHandle,
		         aPackage.Des1n(), reinterpret_cast<TInt*>(dataPtr2));

	case EMobileSmartCardEapAuthenticationPhase2:
		return SimGetAuthenticateDataForPhase2(aTsyReqHandle,
		         aPackage.Des1n(), aPackage.Des2n());

	case EMobileSmartCardEapReleaseEapMethod:
		return SimReleaseEapMethod(aTsyReqHandle);

	case EMobileSmartCardEapGetEapMethodAccessStatus:
		return SimGetEapMethodAccessStatus(aTsyReqHandle,
		         reinterpret_cast<RMobileSmartCardEap::TEapMethodAccessStatus*>(dataPtr));

	case EMobileSmartCardEapNotifyEapMethodAccessStatusChange:
		return SimNotifyEapMethodAccessStatusChange(aTsyReqHandle,
		         reinterpret_cast<RMobileSmartCardEap::TEapMethodAccessStatus*>(dataPtr));

	default:
		return KErrNotSupported;
		}
	}

TInt CSimSmartCardEap::CancelService(const TInt aIpc,const TTsyReqHandle aTsyReqHandle)
	{
	// CancelService is called by the server when it is "cleaning-up" any still outstanding
	// asynchronous requests before closing a client's sub-session.
	// This will happen if a client closes its R-class handle without cancelling outstanding
	// asynchronous requests.

	switch (aIpc)
		{
	case EMobileSmartCardEapGetUserIdentity:
		return SimGetUserIdentityCancel(aTsyReqHandle);
	case EMobileSmartCardEapGetAuthenticationStatus:
		return SimGetAuthenticationStatusCancel(aTsyReqHandle);
	case EMobileSmartCardEapGetEapKey:
		return SimGetEapKeyCancel(aTsyReqHandle);
	case EMobileSmartCardEapInitialiseEapMethod:
		return SimInitialiseEapMethodCancel(aTsyReqHandle);
	case EMobileSmartCardEapAuthenticationPhase1:
	case EMobileSmartCardEapAuthenticationPhase2:
		return SimSmartCardEapAuthenticationCancel(aTsyReqHandle);
	case EMobileSmartCardEapNotifyEapMethodAccessStatusChange:
		return SimNotifyEapMethodAccessStatusChangeCancel(aTsyReqHandle);
	default:
		return KErrNotSupported;
		}
	}

RHandleBase* CSimSmartCardEap::GlobalKernelObjectHandle()
	{
	return &iSemaphr;
	}

TInt CSimSmartCardEap::SimInitialiseEapMethod(const TTsyReqHandle aTsyReqHandle, TThreadId* aThreadId)
	{
	LOGPHONE1("CSimSmartCardEap::SimInitialiseEapMethod called");
	// This can only be called through RMobileSmartCardEap for one instance

	if (iSSInitialised)
		{
		// re-initialise request sent, so will be treated as a mistake
		// and nothing will happen!
		ReqCompleted(aTsyReqHandle, KErrNone);
		}
	else
		{
		delete iCliTerminationListener;
		TRAPD(err, iCliTerminationListener = CThreadTerminationListener::NewL(this, *aThreadId));
		if (err != KErrNone)
			{
			LOGPHONE2("ERROR could not create a client termination listener [err=%d] (not initialised)", err);
			ReqCompleted(aTsyReqHandle, err);
			}
		else
			{
			iProcedureData = iEapMan->ProcData(iAID, iEapType);
			if (iProcedureData == NULL)
				{
				LOGPHONE1("ERROR could not find sub-session's procedure");
				ReqCompleted(aTsyReqHandle, KErrNotFound);
				return KErrNone;
				}

			iSSInitialised = ETrue;
			iCliTerminationListener->Start();
			iAccessStatus = RMobileSmartCardEap::EEapMethodInUseApplicationActive;
			SimCompleteNotifyEapMethodAccessStatusChange();
			ReqCompleted(aTsyReqHandle, KErrNone);
			}
		}

	return KErrNone;
	}

TInt CSimSmartCardEap::SimInitialiseEapMethodCancel(const TTsyReqHandle aTsyReqHandle)
	{
	LOGPHONE1("CSimSmartCardEap::SimInitialiseEapMethodCancel called");
	iProcedureData = NULL;
	iSSInitialised = EFalse;
	iAccessStatus = RMobileSmartCardEap::EEapMethodAvailable;
	SimCompleteNotifyEapMethodAccessStatusChange();
	ReqCompleted(aTsyReqHandle, KErrCancel);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimGetUserIdentity(const TTsyReqHandle aTsyReqHandle, RMobileSmartCardEap::TEapUserIdType* aEapIdType, TDes8* aUserId)
	{
	LOGPHONE1("CSimSmartCardEap::SimGetUserIdentity called");

	RMobileSmartCardEap::TEapUserIdentityV6Pckg *userIdPckg = reinterpret_cast<RMobileSmartCardEap::TEapUserIdentityV6Pckg*>(aUserId);
	RMobileSmartCardEap::TEapUserIdentityV6 &userId = (*userIdPckg)();
	
	// Check that the data structure is supported by the simulated TSY version
	TInt err = iPhone->CheckSimTsyVersion(userId);
		if(err != KErrNone)
			{
			iPhone->ReqCompleted(aTsyReqHandle, err);
			return KErrNone;
			}

	if (*aEapIdType == RMobileSmartCardEap::EPermanentIdentity)
		{
		if (iProcedureData->iEapId == NULL)
			{
			ReqCompleted(aTsyReqHandle, KErrNotFound);
			LOGPHONE1("ERROR EAP sub-session does not contain EPermanentIdentity");
			return KErrNone;
			}

		userId.iEapId = iProcedureData->iEapId->Des();
		}
	else if (*aEapIdType == RMobileSmartCardEap::EPseudonymIdentity)
		{
		if (iProcedureData->iEapPsId == NULL)
			{
			ReqCompleted(aTsyReqHandle, KErrNotFound);
			LOGPHONE1("ERROR EAP sub-session does not contain EPseudonymIdentity");
			return KErrNone;
			}

		userId.iEapId = iProcedureData->iEapPsId->Des();
		}
	else
		{
		ReqCompleted(aTsyReqHandle, KErrArgument);
		LOGPHONE2("ERROR invalid EAP id type requested [tag=%d]", *aEapIdType);
		return KErrNone;
		}

	ReqCompleted(aTsyReqHandle, KErrNone);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimGetUserIdentityCancel(const TTsyReqHandle aTsyReqHandle)
	{
	LOGPHONE1("CSimSmartCardEap::SimGetUserIdentityCancel called");
	ReqCompleted(aTsyReqHandle, KErrCancel);
	return KErrNone;
	}
	
TInt CSimSmartCardEap::SimGetAuthenticationStatus(const TTsyReqHandle aTsyReqHandle, RMobileSmartCardEap::TEapAuthStatus* aAuthStatus)
	{
	LOGPHONE1("CSimSmartCardEap::SimGetAuthenticationStatus called");

	(*aAuthStatus) = iAuthStatus;

	ReqCompleted(aTsyReqHandle, KErrNone);
	return KErrNone;
	}
	
TInt CSimSmartCardEap::SimGetAuthenticationStatusCancel(const TTsyReqHandle aTsyReqHandle)
	{
	LOGPHONE1("CSimSmartCardEap::SimGetAuthenticationStatusCancel called");
	ReqCompleted(aTsyReqHandle, KErrCancel);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimGetEapKey(const TTsyReqHandle aTsyReqHandle, RMobileSmartCardEap::TEapKeyTag* aEapKeyTag, TDes8* aKey)
	{
	LOGPHONE1("CSimSmartCardEap::SimGetEapKey called");

	RMobileSmartCardEap::TEapKeyV6Pckg *keyPckg = reinterpret_cast<RMobileSmartCardEap::TEapKeyV6Pckg*>(aKey);
	RMobileSmartCardEap::TEapKeyV6 &key = (*keyPckg)();

	// Check that the data structure is supported by the simulated TSY version
	TInt err = iPhone->CheckSimTsyVersion(key);
		if(err != KErrNone)
			{
			iPhone->ReqCompleted(aTsyReqHandle, err);
			return KErrNone;
			}

	if (*aEapKeyTag == RMobileSmartCardEap::EEapKeyMSK)
		{
		if (iProcedureData->iEapKey == NULL)
			{
			ReqCompleted(aTsyReqHandle, KErrNotFound);
			LOGPHONE1("ERROR EAP sub-session does not contain EEapKeyMSK");
			return KErrNone;
			}

		key.iEapKey = iProcedureData->iEapKey->Des();
		}
	else if (*aEapKeyTag == RMobileSmartCardEap::EEapKeyEMSK)
		{
		if (iProcedureData->iEapExtKey == NULL)
			{
			ReqCompleted(aTsyReqHandle, KErrNotFound);
			LOGPHONE1("ERROR EAP sub-session does not contain EEapKeyEMSK");
			return KErrNone;
			}

		key.iEapKey = iProcedureData->iEapExtKey->Des();
		}
	else
		{
		ReqCompleted(aTsyReqHandle, KErrArgument);
		LOGPHONE2("ERROR invalid EAP key tag requested [tag=%d]", *aEapKeyTag);
		return KErrNone;
		}

	ReqCompleted(aTsyReqHandle, KErrNone);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimGetEapKeyCancel(const TTsyReqHandle aTsyReqHandle)
	{
	LOGPHONE1("CSimSmartCardEap::SimGetEapKeyCancel called");
	ReqCompleted(aTsyReqHandle, KErrCancel);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimSetAuthenticateDataForPhase1(const TTsyReqHandle aTsyReqHandle, TDes8* aEapAuthData, TInt* aPhase1Size)
	{
	LOGPHONE1("CSimSmartCardEap::SimSetAuthenticateDataForPhase1 called");

	if (iCurrentChallenge >= iProcedureData->iChResp.Count())
		{
		ReqCompleted(aTsyReqHandle, KErrAccessDenied);
		return KErrNone;
		}

	RMobileSmartCardEap::CEapAuthenticateRequestDataV6* authReq = NULL;
	TRAPD(err, authReq = RMobileSmartCardEap::CEapAuthenticateRequestDataV6::NewL());
	if (err != KErrNone)
		{
		LOGPHONE2("ERR Could not allocate memory for challenge request object [err=%d]", err);
		ReqCompleted(aTsyReqHandle, err);
		return KErrNone;
		}

	TRAP(err, authReq->InternalizeL(*aEapAuthData));
	if (err != KErrNone)
		{
		LOGPHONE2("ERR Could not allocate memory for challenge request [err=%d]", err);
		ReqCompleted(aTsyReqHandle, err);
		return KErrNone;
		}

	TPtr8 reqPacket = authReq->GetEapReqPacket();

	TPtr8 tempPtr(NULL, 0);
	tempPtr.Set(iProcedureData->iChResp[iCurrentChallenge].iChallenge->Des());

	if (reqPacket != tempPtr)
		{
		LOGPHONE2("ERR challenge request does not match config [currentChallenge=%d]", iCurrentChallenge);
		ReqCompleted(aTsyReqHandle, KErrCorrupt);
		return KErrNone;
		}

	(*aPhase1Size) = iProcedureData->iChResp[iCurrentChallenge].iResp->Length();

	ReqCompleted(aTsyReqHandle, KErrNone);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimGetAuthenticateDataForPhase2(const TTsyReqHandle aTsyReqHandle, TDes8* /*aEapAuthData*/, TDes8* aPhase2Resp)
	{
	LOGPHONE1("CSimSmartCardEap::SimSetAuthenticateDataForPhase2 called");

	if (iCurrentChallenge >= iProcedureData->iChResp.Count())
		{
		ReqCompleted(aTsyReqHandle, KErrAccessDenied);
		return KErrNone;
		}

	TPtr8 tempPtr(NULL, 0);
	tempPtr.Set(iProcedureData->iChResp[iCurrentChallenge].iResp->Des());

	aPhase2Resp->Copy(tempPtr);
 	iAuthStatus = iProcedureData->iChResp[iCurrentChallenge].iAuthStatus;
 	iCurrentChallenge++;

	ReqCompleted(aTsyReqHandle, KErrNone);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimSmartCardEapAuthenticationCancel(const TTsyReqHandle aTsyReqHandle)
	{
	LOGPHONE1("CSimSmartCardEap::SimSmartCardEapAuthenticationCancel called");
	ReqCompleted(aTsyReqHandle, KErrCancel);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimReleaseEapMethod(const TTsyReqHandle aTsyReqHandle)
	{
	LOGPHONE1("CSimSmartCardEap::SimReleaseEapMethod called");
	iSSInitialised = EFalse;
	iAccessStatus = RMobileSmartCardEap::EEapMethodAvailable;
	ReqCompleted(aTsyReqHandle, KErrNone);

	SimCompleteNotifyEapMethodAccessStatusChange();

	// remove config entry from manager
	iEapMan->ProcDataUseCompleted(iProcedureData);
	iProcedureData = NULL;

	return KErrNone;
	}

TInt CSimSmartCardEap::SimGetEapMethodAccessStatus(const TTsyReqHandle aTsyReqHandle, RMobileSmartCardEap::TEapMethodAccessStatus* aEapState)
	{
	LOGPHONE1("CSimSmartCardEap::SimGetEapMethodAccessStatus called");
	*aEapState = iAccessStatus;
	ReqCompleted(aTsyReqHandle, KErrNone);
	return KErrNone;
	}

TInt CSimSmartCardEap::SimNotifyEapMethodAccessStatusChange(const TTsyReqHandle aTsyReqHandle, RMobileSmartCardEap::TEapMethodAccessStatus* aEapState)
	{
	LOGPHONE1("CSimSmartCardEap::SimNotifyEapMethodAccessStatusChange called");
	__ASSERT_ALWAYS(!iEapAccessNotifyData.iNotifyPending, PanicClient(EEtelPanicRequestAsyncTwice));

	iEapAccessNotifyData.iNotifyPending = ETrue;
	iEapAccessNotifyData.iNotifyHandle = aTsyReqHandle;
	iEapAccessNotifyData.iNotifyData = aEapState;

	return KErrNone;
	}

TInt CSimSmartCardEap::SimNotifyEapMethodAccessStatusChangeCancel(const TTsyReqHandle aTsyReqHandle)
	{
	LOGPHONE1("CSimSmartCardEap::SimNotifyEapMethodAccessStatusChangeCancel called");
	if(iEapAccessNotifyData.iNotifyPending)
		{
		iEapAccessNotifyData.iNotifyPending = EFalse;
		ReqCompleted(aTsyReqHandle, KErrCancel);
		return KErrNone;
		}	

//	iPhone->ReqCompleted(aTsyReqHandle,KErrNone);
	return KErrNone;
	}

void CSimSmartCardEap::SimCompleteNotifyEapMethodAccessStatusChange()
	{
	LOGPHONE1("CSimSmartCardEap::SimCompleteNotifyEapMethodAccessStatusChange called");

	if(iEapAccessNotifyData.iNotifyPending)
		{
		iEapAccessNotifyData.iNotifyPending = EFalse;
		*(reinterpret_cast<RMobileSmartCardEap::TEapMethodAccessStatus*>(iEapAccessNotifyData.iNotifyData)) = iAccessStatus;
		ReqCompleted(iEapAccessNotifyData.iNotifyHandle, KErrNone);
		}
	}

void CSimSmartCardEap::ClientHasTerminated(TInt /*aExitReason*/)
	{
	// Can TSY do anything with the thread's exit reason?
	// Exit code can be a zero (e.g. for KERN-EXEC 0) a positive value
	// (e.g. for KERN-EXEC 3) or a negative error.

	switch (iAccessStatus)
		{
	case RMobileSmartCardEap::EEapMethodInUseApplicationActive:
		iSSInitialised = EFalse;
		iAccessStatus = RMobileSmartCardEap::EEapMethodAvailable;
		SimCompleteNotifyEapMethodAccessStatusChange();
		iSemaphr.Signal();

		// remove config entry from manager
		iEapMan->ProcDataUseCompleted(iProcedureData);
		iProcedureData = NULL;

		break;

	case RMobileSmartCardEap::EEapMethodInUseApplicationInactive:
		iAccessStatus = RMobileSmartCardEap::EEapMethodAvailable;
		SimCompleteNotifyEapMethodAccessStatusChange();
		iSemaphr.Signal();
		break;

	default:
		;
		}
	}

//
// Class definition for monitoring thread termination
//

CSimSmartCardEap::CThreadTerminationListener* CSimSmartCardEap::CThreadTerminationListener::NewL(CSimSmartCardEap* aSubSess, const TThreadId& aId)
	{
	CThreadTerminationListener* self = new(ELeave) CThreadTerminationListener(aSubSess);
	CleanupStack::PushL(self);
	self->ConstructL(aId);
	CleanupStack::Pop(self);
	return self;
	}

CSimSmartCardEap::CThreadTerminationListener::CThreadTerminationListener(CSimSmartCardEap* aSubSess)
: CActive(EPriorityStandard), iSubSess(aSubSess)
	{
	}

void CSimSmartCardEap::CThreadTerminationListener::ConstructL(const TThreadId& aId)
	{
	TInt openTh = iCliThread.Open(aId);
	User::LeaveIfError(openTh);
    CActiveScheduler::Add(this);
	}

void CSimSmartCardEap::CThreadTerminationListener::Start()
	{
	iCliThread.Logon(iStatus);
	SetActive();
	}

void CSimSmartCardEap::CThreadTerminationListener::RunL()
	{
	iSubSess->ClientHasTerminated(iStatus.Int());
	}

void CSimSmartCardEap::CThreadTerminationListener::DoCancel()
	{
	iCliThread.LogonCancel(iStatus);
	}

CSimSmartCardEap::CThreadTerminationListener::~CThreadTerminationListener()
	{
	Cancel();
	iCliThread.Close();
	}
