/*
* Copyright (c) 2005-2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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:  CEapVpnInterfaceImplementation
*
*/


// INCLUDE FILES
#include <w32std.h>
#include <pathinfo.h>

#include "eap_vpn_if_implementation.h"
#include "eap_am_memory.h"
#include "eap_tools.h"
#include "eap_type_all.h"
#include "eap_am_tools_symbian.h"
#include "eap_crypto_api.h"
#include "eap_type_gsmsim.h"
#include "eap_type_aka.h"
#include "eap_header_string.h"
#include "EapolDbDefaults.h"
#include "EapolDbParameterNames.h"
#include "eap_am_file_input_symbian.h"
#include "abs_eap_vpn_if.h"
#include "eap_array_algorithms.h"

#include "EapSimDbDefaults.h"

// LOCAL CONSTANTS
const TUint KMaxConfigStringLength = 256;
const u32_t KMTU = 1500u;
const u32_t KTrailerLength = 0;
const u32_t KHeaderOffset = 0;

#ifndef USE_EAP_EXPANDED_TYPES

static const TUint KMaxEapCueLength = 3;

#endif //#ifndef USE_EAP_EXPANDED_TYPES

#define KEapIdentityOffset 5
const eap_const_string EAPPLUGIN_TRACE_FILE = EAPL("logs\\eapol\\eap_vpn_if.txt");

enum eapol_am_core_timer_id_e
{
	EAPOL_AM_CORE_TIMER_RESTART_AUTHENTICATION_ID,
	EAPOL_AM_CORE_TIMER_DELETE_STACK_ID,
	EAPOL_AM_CORE_TIMER_FAILED_COMPLETELY_ID,
};

// ================= MEMBER FUNCTIONS =======================
// Create instance of concrete ECOM interface implementation
CEapVpnInterfaceImplementation* CEapVpnInterfaceImplementation::NewL(TAbsEapVpnInterfaceParams* aParams)
{
	CEapVpnInterfaceImplementation* self = new (ELeave) CEapVpnInterfaceImplementation;
	CleanupStack::PushL(self);
	self->ConstructL(aParams->iCaller, aParams->iClient);

	if (self->get_is_valid() != true)
	{
		User::Leave(KErrGeneral);
	}

	CleanupStack::Pop();
	return self;
}

	
//--------------------------------------------------
// Constructor
//--------------------------------------------------

/// VPN does not have any IAP setting at all but we use index 1 in the EAP databases
/// for all VPN related settings.
const TInt EAP_VPN_DEFAULT_SERVICE_TABLE_INDEX = 1; 

CEapVpnInterfaceImplementation::CEapVpnInterfaceImplementation()
: m_trace_log_file_name(0)
, m_is_client(true)
#ifdef USE_EAP_EXPANDED_TYPES
, m_eap_type_array(0)
#endif //#ifdef USE_EAP_EXPANDED_TYPES
, m_index_type(EVpn)
, m_index(EAP_VPN_DEFAULT_SERVICE_TABLE_INDEX)
{
}
	
//--------------------------------------------------
// Second phase construction.
//--------------------------------------------------
void CEapVpnInterfaceImplementation::ConstructL(MAbsEapVpnInterface* aCaller, TBool aClient)
{
    EAP_TRACE_DEBUG(
        m_am_tools,
        TRACE_FLAGS_DEFAULT,
        (EAPL("CEapVpnInterfaceImplementation::ConstructL()\n")));

	iCaller = aCaller;
	if(aClient)
	{
		m_is_client = true;
	}
	else
	{
		m_is_client = false;
	}

	if (iCaller == 0)
	{
		User::Leave(KErrGeneral);
	}

	// Create tools class
	m_am_tools = new(ELeave) eap_am_tools_symbian_c(0);

	if (m_am_tools->get_is_valid() != true)
	{
		// The real reason most likely is KErrNoMemory but since that is not sure we'll use KErrGeneral
		User::Leave(KErrGeneral);
	}

	if (m_am_tools->configure() != eap_status_ok)
	{
		User::Leave(KErrGeneral);
	}


	{
		TFileName drivePath( PathInfo::MemoryCardRootPath() );

		const TInt MAXPATHNAME=256ul;

		HBufC* buf = HBufC::NewLC(MAXPATHNAME);
		TPtr pathbuffer = buf->Des();

		pathbuffer.Append( TParsePtrC( drivePath ).DriveAndPath() );

		{
			eap_variable_data_c unicode_drivepath(m_am_tools);
			if (unicode_drivepath.get_is_valid() == false)
			{
				(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
				User::Leave(KErrGeneral);
			}

			eap_status_e status = unicode_drivepath.set_buffer(pathbuffer.Ptr(), pathbuffer.Length(), false, false);
			if (status != eap_status_ok)
			{
				(void) EAP_STATUS_RETURN(m_am_tools, status);
				User::Leave(KErrGeneral);
			}

			eap_variable_data_c utf8_drivepath(m_am_tools);
			if (utf8_drivepath.get_is_valid() == false)
			{
				(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
				User::Leave(KErrGeneral);
			}

			status = m_am_tools->convert_unicode_to_utf8(
				utf8_drivepath,
				unicode_drivepath);
			if (status != eap_status_ok)
			{
				(void) EAP_STATUS_RETURN(m_am_tools, status);
				User::Leave(KErrGeneral);
			}

			delete m_trace_log_file_name;
			m_trace_log_file_name = new eap_variable_data_c(m_am_tools);

			if (m_trace_log_file_name == 0
				|| m_trace_log_file_name->get_is_valid() == false)
			{
				(void) EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
				User::Leave(KErrGeneral);
			}

			status = m_trace_log_file_name->set_copy_of_buffer(&utf8_drivepath);
			if (status != eap_status_ok)
			{
				(void) EAP_STATUS_RETURN(m_am_tools, status);
				User::Leave(KErrGeneral);
			}

			status = m_trace_log_file_name->add_data(EAPPLUGIN_TRACE_FILE, sizeof(EAPPLUGIN_TRACE_FILE)-1);
			if (status != eap_status_ok)
			{
				(void) EAP_STATUS_RETURN(m_am_tools, status);
				User::Leave(KErrGeneral);
			}

			status = m_trace_log_file_name->add_end_null();
			if (status != eap_status_ok)
			{
				(void) EAP_STATUS_RETURN(m_am_tools, status);
				User::Leave(KErrGeneral);
			}
		}

		CleanupStack::PopAndDestroy(buf); // Delete pathbuffer.
	}
    

#ifdef USE_EAP_EXPANDED_TYPES

	m_eap_type_array = new eap_array_c<eap_type_value_e>(m_am_tools);
	if (m_eap_type_array == 0)
	{
		User::Leave(KErrGeneral);
	}

#endif //#ifdef USE_EAP_EXPANDED_TYPES


    //------ Create network id
	{
		EAP_TRACE_DEBUG(
    		m_am_tools,
    		TRACE_FLAGS_DEFAULT,
    		(EAPL("CEapVpnInterfaceImplementation: Create network ID\n")));

		u32_t NWID_SRC = 0;
		u32_t NWID_DEST = 1;
		const u16_t EAP_DUMMY_PACKET_TYPE = 1;

		m_receive_network_id = new eap_am_network_id_c(
			m_am_tools);

		if (m_receive_network_id == NULL
			|| m_receive_network_id->get_is_valid() != true)
		{
			delete m_receive_network_id;
			m_receive_network_id = 0;
			User::Leave(KErrGeneral);
		}

		eap_status_e status = m_receive_network_id->set_copy_of_am_network_id(
			&NWID_SRC,
			sizeof(NWID_SRC),
			&NWID_DEST,
			sizeof(NWID_DEST),
			EAP_DUMMY_PACKET_TYPE);
		if (status != eap_status_ok)
		{
			delete m_receive_network_id;
			m_receive_network_id = 0;
			User::Leave(KErrGeneral);
		}
	}


    m_am_tools->set_trace_mask(
		eap_am_tools_c::eap_trace_mask_debug
		| eap_am_tools_c::eap_trace_mask_always
		| eap_am_tools_c::eap_trace_mask_functions
		| eap_am_tools_c::eap_trace_mask_error
		| eap_am_tools_c::eap_trace_mask_message_data);

    
#if !defined(USE_EAP_RDEBUG_TRACE)
	{
		eap_variable_data_c trace_output_file(m_am_tools);

		eap_status_e status = read_configure(
			cf_str_EAP_TRACE_output_file_name.get_field(),
			&trace_output_file);

		if (status == eap_status_ok
			&& trace_output_file.get_is_valid_data() == true)
		{
			(void) m_am_tools->set_trace_file_name(&trace_output_file);
		}
	}
#endif //#if defined(USE_EAP_RDEBUG_TRACE)

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation: configure\n")));

	if (configure() != eap_status_ok)
	{
		User::Leave(KErrGeneral);
	}

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("Configured EAPPlugin...\n")));

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("==================\n")));

	set_is_valid();
}


//--------------------------------------------------
// Destructor    
//--------------------------------------------------

CEapVpnInterfaceImplementation::~CEapVpnInterfaceImplementation()
{
	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::~CEapVpnInterfaceImplementation() - destructor\n")));

	delete m_trace_log_file_name;
	m_trace_log_file_name = 0;

	shutdown();
}


    
//--------------------------------------------------
// Shutdown()
//--------------------------------------------------
eap_status_e CEapVpnInterfaceImplementation::shutdown()
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation: Shutdown called\n")));

	// Cancel timer	
	cancel_all_timers();

	// Delete upper stack if it still exists
	if (iEapCore != 0)
	{
		iEapCore->shutdown();
		delete iEapCore;
		iEapCore = NULL;
	}

	// Print some statistics
	if (m_is_client)
	{
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_ALWAYS|TRACE_TEST_VECTORS,
			(EAPL("client authentication SUCCESS %d, FAILED %d\n"),
			m_successful_authentications,
			m_failed_authentications));	
	}
	else
	{
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_ALWAYS|TRACE_TEST_VECTORS,
			(EAPL("server authentication SUCCESS %d, FAILED %d\n"),
			m_successful_authentications,
			m_failed_authentications));
	}	

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("EAPPlugin EXITING.\n")));

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	

	if(m_receive_network_id != 0)
	{
		delete m_receive_network_id;
	}

	// Unload all loaded plugins
	for(int i = 0; i < m_plugin_if_array.Count(); i++)
	{
		delete m_plugin_if_array[i];
	}

	m_plugin_if_array.Close();

#ifdef USE_EAP_EXPANDED_TYPES

	m_enabled_expanded_eap_array.ResetAndDestroy();

	m_disabled_expanded_eap_array.ResetAndDestroy();
	
	m_eap_type_array->reset();

	delete m_eap_type_array;
	m_eap_type_array = 0;

#else // For normal EAP type.

	// Delete the IAP EAP type info array
	m_iap_eap_array.ResetAndDestroy();

	m_eap_type_array.Close();

#endif //#ifdef USE_EAP_EXPANDED_TYPES

	delete iManualUsername;
	iManualUsername = NULL;

	delete iManualRealm;
	iManualRealm = NULL;

	delete iRealmPrefix;
	iRealmPrefix = NULL;

	// Finally delete tools. No logging is allowed after this.
	if (m_am_tools != 0)
	{
		m_am_tools->shutdown();
		delete m_am_tools;
	}

	return eap_status_ok;
}
    


//--------------------------------------------------

//
void CEapVpnInterfaceImplementation::RunL()
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation: RunL(): iStatus.Int() = %d\n"),
		iStatus.Int()));

	if (iStatus.Int() != KErrNone)
	{
		return;
	}

	// Authentication cancelled.
	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_ALWAYS|TRACE_FLAGS_DEFAULT,
		(EAPL("Authentication cancelled.\n")));

	// Set block on.
	m_block_packet_sends_and_notifications = true;

	// Reset flags
	m_stack_marked_to_be_deleted = true;
	set_timer(this, EAPOL_AM_CORE_TIMER_DELETE_STACK_ID, 0, 0);

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_ALWAYS|TRACE_FLAGS_DEFAULT,
		(EAPL("Indication sent: EFailedCompletely.\n")));

	iCaller->EapIndication(EFailedCompletely);

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
}

//--------------------------------------------------

//
void CEapVpnInterfaceImplementation::DoCancel()
{	
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("eap_vpn_if::DoCancel()\n")));

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
}

    
//--------------------------------------------------
// Configure()        
//--------------------------------------------------

eap_status_e CEapVpnInterfaceImplementation::configure()
{	
	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::configure()\n")));

    //----------------------------------------------------------

	{		
		eap_variable_data_c EAP_TRACE_activate_trace_on_error(m_am_tools);

		eap_status_e status = read_configure(
			cf_str_EAP_TRACE_activate_trace_on_error.get_field(),
			&EAP_TRACE_activate_trace_on_error);

		if (status == eap_status_ok
			&& EAP_TRACE_activate_trace_on_error.get_is_valid_data() == true)
		{
			u32_t *activate_trace_on_error = (u32_t *)EAP_TRACE_activate_trace_on_error.get_data(sizeof(u32_t));

			if (activate_trace_on_error != 0
				&& *activate_trace_on_error != 0)
			{
				m_am_tools->set_activate_trace_on_error();
			}
		}
	}

	//----------------------------------------------------------

	// All of the configuration options are optional.
	// So we return OK.
	return eap_status_ok;
}

    
//----------------------------------------------------------        
// Implementations of virtual functions from CEapVpnInterface
//----------------------------------------------------------

/**
 * Function:    EapConfigure
 *
 * Description: Configures the eap plugin
 *
 * @param       aManualUsername The username, if not zero
 * @param       aManualRealm The realm, if not zero
 * @param       aManualRealmPrefix The realm prefix, if not zero
 * @param       aHideInitialIdentity Scramble username, if true
 *
 */
TInt CEapVpnInterfaceImplementation::EapConfigure(
	TDesC8& aManualUsername,
	TDesC8& aManualRealm,
	TDesC8& aRealmPrefix,
	TBool aHideInitialIdentity)
{
	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::EapConfigure()\n")));

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::EapConfigure(): aManualUsername\n"),
		aManualUsername.Ptr(),
		aManualUsername.Size()));

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::EapConfigure(): aManualRealm\n"),
		aManualRealm.Ptr(),
		aManualRealm.Size()));

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::EapConfigure(): aRealmPrefix\n"),
		aRealmPrefix.Ptr(),
		aRealmPrefix.Size()));

	iHideInitialIdentity = aHideInitialIdentity;

	delete iManualUsername;
	iManualUsername = NULL;

	delete iManualRealm;
	iManualRealm = NULL;

	delete iRealmPrefix;
	iRealmPrefix = NULL;

	TRAPD(error,
		iManualUsername = aManualUsername.AllocL();
		iManualRealm = aManualRealm.AllocL();
		iRealmPrefix = aRealmPrefix.AllocL();
		);

	if (error != KErrNone)
	{
		EAP_TRACE_ERROR(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("CEapVpnInterfaceImplementation::EapConfigure(): error = %d\n"),
			error));
		return error;
	}

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::EapConfigure(): iManualUsername\n"),
		iManualUsername->Ptr(),
		iManualUsername->Size()));

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::EapConfigure(): iManualRealm\n"),
		iManualRealm->Ptr(),
		iManualRealm->Size()));

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::EapConfigure(): iRealmPrefix\n"),
		iRealmPrefix->Ptr(),
		iRealmPrefix->Size()));

	return KErrNone;
}

    
/**
 * Function:    StartL
 *
 * Description: Initializes the eap plugin
 *
 */
TInt CEapVpnInterfaceImplementation::StartL(const TUint8 aEapType)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::StartL()\n")));

	eap_status_e status(eap_status_ok);
	iQueryIdentity = EFalse;

	iRequestedEapType = static_cast<eap_type_ietf_values_e>(aEapType);

	if (iEapCore != 0)
	{
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("Deleting previously used stack.\n")));

		// It is an error to call start without calling disassociated
		if (m_stack_marked_to_be_deleted == false)
		{	
			EAP_TRACE_ERROR(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("eap_vpn_if::Start called twice!\n")));
			return KErrAlreadyExists;
		}

		// The previously used stack is perhaps still waiting for deletion.
		cancel_timer(this, EAPOL_AM_CORE_TIMER_DELETE_STACK_ID);

		// Delete stack
		iEapCore->shutdown();
		delete iEapCore;
		iEapCore = 0;				

		m_stack_marked_to_be_deleted = false;
	}

	// Clear packet send and notification blocking.
	m_block_packet_sends_and_notifications = false;

#ifdef USE_EAP_EXPANDED_TYPES

	if (m_enabled_expanded_eap_array.Count() == 0)
	{
		// The EAP field was empty. Allow all types.

		EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("Empty EAP field -> enable all types.\n")));

		RImplInfoPtrArray eapArray;
		CleanupStack::PushL( TCleanupItem( CleanupImplArray, &eapArray ) );

		REComSession::ListImplementationsL(KEapTypeInterfaceUid, eapArray);

		for (TInt i = 0; i < eapArray.Count(); i++)
		{
			SEapExpandedType * expandedEAPType = new (ELeave) SEapExpandedType;
			expandedEAPType->EapExpandedType = eapArray[i]->DataType();
					
			eap_expanded_type_c tmpExpEAPType;
			
			// This is to make the tmpExpEAPType in 8 byte string with correct vendor type and vendor id details.
			status = eap_expanded_type_c::read_type(m_am_tools,
													0, // index should be zero here.
													expandedEAPType->EapExpandedType.Ptr(),
													KExpandedEapTypeSize,
													&tmpExpEAPType);
			if (status != eap_status_ok)
			{
				delete expandedEAPType;
				expandedEAPType = 0;

				EAP_TRACE_DEBUG(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("CEapVpnInterfaceImplementation::StartL: eap_expanded_type_c::write_type failed \n")));
			
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
			
			// Now add the EAP types to the array
			if(tmpExpEAPType == iRequestedEapType)
			{
				// This is the requested EAP type.
				
				EAP_TRACE_DATA_DEBUG(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("CEapVpnInterfaceImplementation::StartL:Enabled (Requested) EAP type:"),
					expandedEAPType->EapExpandedType.Ptr(),
					expandedEAPType->EapExpandedType.Size()));
			
				m_enabled_expanded_eap_array.Insert(expandedEAPType, 0); // This goes to the beginning.
			}
			else
			{
				EAP_TRACE_DATA_DEBUG(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("CEapVpnInterfaceImplementation::StartL:Disabled EAP type:"),
					expandedEAPType->EapExpandedType.Ptr(),
					expandedEAPType->EapExpandedType.Size()));

				delete expandedEAPType;
				expandedEAPType = 0;

			}
		} // for()

		CleanupStack::PopAndDestroy(&eapArray);
	}

#else // For normal EAP type.

	if (m_iap_eap_array.Count() == 0)
	{
		// The EAP field was empty. Allow all types.

		EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("Empty EAP field -> enable all types.\n")));

		RImplInfoPtrArray eapArray;
		CleanupStack::PushL( TCleanupItem( CleanupImplArray, &eapArray ) );

		REComSession::ListImplementationsL(KEapTypeInterfaceUid, eapArray);

		for (TInt i = 0; i < eapArray.Count(); i++)
		{
			TEap *eap = new(ELeave) TEap;
			eap->UID.Copy(eapArray[i]->DataType());
			eap->Enabled = EFalse;

			// Convert the string to integer
			TLex8 tmp(eap->UID);
			TInt val(0);
			tmp.Val(val);

			if(val == iRequestedEapType)
			{
				eap->Enabled = ETrue;

				EAP_TRACE_ALWAYS(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("EAP-Type %d enabled\n"), val));

				m_iap_eap_array.Insert(eap, 0);
			}
			else
			{
				m_iap_eap_array.Append(eap);

				EAP_TRACE_ALWAYS(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("EAP-Type %d disabled\n"), val));
			}
		} // for()

		CleanupStack::PopAndDestroy(&eapArray);
	}

#endif //#ifdef USE_EAP_EXPANDED_TYPES

	CompleteAssociation(status);

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
	return m_am_tools->convert_eapol_error_to_am_error(EAP_STATUS_RETURN(m_am_tools, status));
}

//--------------------------------------------------

//
TInt CEapVpnInterfaceImplementation::CompleteAssociation(
		const TInt aResult
		)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::CompleteAssociation(): aResult=%d\n"),
		aResult));

	eap_status_e status(eap_status_ok);

	// ASSOCIATION UNSUCCESSFUL
	if (aResult != KErrNone)
	{
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("CompleteAssociation: Unsuccessful.\n")));
			iCaller->EapIndication(EFailedCompletely);
		return KErrNone;
	}

	// ASSOCIATION SUCCESSFUL
	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_ALWAYS|TRACE_FLAGS_DEFAULT,
		(EAPL("CompleteAssociation: Successful.\n")));

	// Create stack if it does not already exist. 
	status = create_upper_stack();

	if (status != eap_status_ok
		&& status != eap_status_already_exists)
	{
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_ALWAYS|TRACE_FLAGS_DEFAULT,
			(EAPL("Indication sent to VPN: EFailedCompletely.\n")));

		iCaller->EapIndication(EFailedCompletely);
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
		return KErrNone; 
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
	return m_am_tools->convert_eapol_error_to_am_error(EAP_STATUS_RETURN(m_am_tools, status));
}

/**
 * Function:    QueryIdentity
 *
 * Description: Ask the identity
 *
 * @param       aType name of requested eap type implementation.
 */
TInt CEapVpnInterfaceImplementation::QueryIdentity()
{
	// Build Eap Identity reques message
	// and send it to eap_core.
	// Eap core determines the identity and
	// responses with packet_send() callback
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::QueryIdentity()\n")));

	// Build identity request only if GSM/SIM or AKA
	if((iRequestedEapType != eap_type_gsmsim) && (iRequestedEapType != eap_type_aka))
	{
		TRAPD(error, (iCaller->EapIdentityResponseL(NULL)));
		if(error != KErrNone)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, m_am_tools->convert_am_error_to_eapol_error(error));				
		}
		return m_am_tools->convert_eapol_error_to_am_error(EAP_STATUS_RETURN(m_am_tools, eap_status_ok));
	}

	iQueryIdentity = ETrue;
	eap_status_e status(eap_status_ok);

	status = send_eap_identity_request();

	if(status == eap_status_pending_request)
	{
		// Request pending, it's OK
		status = eap_status_ok;
	}

	if(status != eap_status_ok)
	{
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("eap_vpn_if::QueryIdentity() error, status = %d\n"), status));

		iQueryIdentity = EFalse;
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return m_am_tools->convert_eapol_error_to_am_error(EAP_STATUS_RETURN(m_am_tools, status));
}

//--------------------------------------------------

//
eap_status_e CEapVpnInterfaceImplementation::send_eap_identity_request()
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);

	eap_status_e status = eap_status_process_general_error;

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::send_eap_identity_request()\n")));

	// Creates a identity request message.
	eap_buf_chain_wr_c request_packet(
		eap_write_buffer, 
		m_am_tools, 
		EAP_CORE_PACKET_BUFFER_LENGTH);

	if (request_packet.get_is_valid() == false)
	{
		EAP_TRACE_ERROR(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("send_eap_identity_request(): %s, %s, packet buffer corrupted.\n"),
			(m_is_client == true) ? "client": "server"
			));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	u32_t buffer_size = EAP_CORE_PACKET_BUFFER_LENGTH;

	eap_header_wr_c eap_request(
		m_am_tools,
		request_packet.get_data_offset(0, buffer_size),
		buffer_size);

	if (eap_request.get_is_valid() == false)
	{
		EAP_TRACE_ERROR(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("send_eap_identity_request():packet buffer corrupted.\n")
			));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_header_corrupted);
	}

	eap_request.set_length((u16_t)(EAP_CORE_PACKET_BUFFER_LENGTH), true);
	eap_request.set_code(eap_code_request);
	eap_request.set_identifier(0);
	eap_request.set_type_data_length(0u, false);
	eap_request.set_type(eap_type_identity, false);

	EAP_ASSERT(eap_request.get_length() >= 4);

	status = iEapCore->packet_process(
		m_receive_network_id,
		&eap_request,
		eap_request.get_length());

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::send_identity_request(): iEapCore->packet_process() = %d\n"), status));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);

	return EAP_STATUS_RETURN(m_am_tools, status);
}


/**
 * Function:    EapInbound
 *
 * Description: Handle incoming eap message.
 *
 * @param       aMessage incoming eap message.
 *              
 */
TInt CEapVpnInterfaceImplementation::EapInbound(const TDesC8& aMessage)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);	

	// from eapol_am_core_symbian.cpp ReceivePacket
	iQueryIdentity = EFalse;
	eap_status_e status(eap_status_ok);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::EapInbound()\n")));

	TInt length = aMessage.Length();
	if (length < 4)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return m_am_tools->convert_eapol_error_to_am_error(
			EAP_STATUS_RETURN(m_am_tools, eap_status_too_short_message));
	}

	// Creates a identity request message.
	eap_buf_chain_wr_c request_packet(
		eap_write_buffer, 
		m_am_tools, 
		aMessage.Length());

	request_packet.add_data(aMessage.Ptr(), aMessage.Length());

	eap_header_wr_c eap_request(
		m_am_tools,
		request_packet.get_data_offset(0, aMessage.Length()),
		aMessage.Length());

	// Forward the packet to the Eap layer of the EAPOL stack. Ignore return value. Failure is signalled using state_notification.
	status = iEapCore->packet_process(
		m_receive_network_id,
		&eap_request,
		length);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::iEapCore->packet_process() = %d\n"), status));

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);

	return m_am_tools->convert_eapol_error_to_am_error(EAP_STATUS_RETURN(m_am_tools, status));
}

//--------------------------------------------------
// create_upper_stack
//--------------------------------------------------
eap_status_e CEapVpnInterfaceImplementation::create_upper_stack()
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::create_upper_stack()\n")));

	eap_status_e status(eap_status_ok);

	if (iEapCore == 0)
	{        
		iEapCore = new eap_core_c(m_am_tools, this, m_is_client, m_receive_network_id, EFalse);

		if (iEapCore == 0
			|| iEapCore->get_is_valid() != true)
		{
			if (iEapCore != 0)
			{
				iEapCore->shutdown();
				delete iEapCore;
				iEapCore = 0;							
			}			
			EAP_TRACE_ERROR(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("Stack creation failed.\n")));			
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
			return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);	
		}

		// Initialise upper stack
		status = iEapCore->configure();

		if (status != eap_status_ok)
		{
			iEapCore->shutdown();
			delete iEapCore;
			iEapCore = 0;							

			EAP_TRACE_ERROR(m_am_tools, TRACE_FLAGS_DEFAULT, (EAPL("Stack creation failed.\n")));			
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
			return EAP_STATUS_RETURN(m_am_tools, eap_status_process_general_error);	
		}
	}
	else
	{			
		status = eap_status_already_exists;
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return status;	
}


// Virtual callback function implementations, called by the eap_core

/**
 * The derived class could send packets to partner class with this function.
 * @see abs_eap_base_type_c::packet_send().
 */
eap_status_e CEapVpnInterfaceImplementation::packet_send(
	const eap_am_network_id_c * const /*send_network_id*/,
	eap_buf_chain_wr_c * const sent_packet,
	const u32_t header_offset,
	const u32_t data_length,
	const u32_t /*buffer_length*/)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::packet_send(data_length=%d)\n"),
		data_length));

	if (header_offset != 0u)
	{
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("packet_send: packet buffer corrupted.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return eap_status_process_general_error;
	}
	else if (header_offset+data_length != sent_packet->get_data_length())
	{
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("ERROR: packet_send: packet buffer corrupted (data_length != sent_packet->get_data_length()).\n")));
			EAP_ASSERT(data_length == sent_packet->get_buffer_length());
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return eap_status_process_general_error;
	}	

	if (m_block_packet_sends_and_notifications == true)
	{
		// Packet sending block is active. This happens when disassociated has been called.  
		// start_authentication clears the block.
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("packet_send: packet ignored because Disassociated() was called.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return eap_status_ok;
	}

	eap_header_wr_c eap(
		m_am_tools,
		sent_packet->get_data_offset(
			header_offset, data_length),
			data_length);

	TInt status(KErrNone);
	if (status == KErrNone)
	{
		u8_t * const packet_data = sent_packet->get_data_offset(header_offset, data_length);
		if (packet_data == 0)
		{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return eap_status_buffer_too_short;
		}

		// Here we send the original packet.
		HBufC8* eapData = NULL;
		TInt error = KErrNone;

		if(iQueryIdentity)
		{
			TPtr8 eapPtr(packet_data + KEapIdentityOffset, data_length - KEapIdentityOffset, data_length - KEapIdentityOffset);
			TRAP(error, eapData = eapPtr.Alloc());
		}
		else
		{
			TPtr8 eapPtr(packet_data, data_length, data_length);
			TRAP(error, eapData = eapPtr.Alloc());
		}

		if(error != KErrNone)
		{
			EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("Allocation error\n")));
			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
		}

		if(iQueryIdentity)
		{
			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("CEapVpnInterfaceImplementation::EapIdentityResponseL()\n")));

			TRAPD(error, (iCaller->EapIdentityResponseL(eapData)));
			if(error != KErrNone)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, m_am_tools->convert_am_error_to_eapol_error(error));				
			}

			iQueryIdentity = EFalse;
		}
		else
		{
			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("CEapVpnInterfaceImplementation::EapOutboundL()\n")));

			TRAPD(error, (iCaller->EapOutboundL(eapData)));
			if(error != KErrNone)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, m_am_tools->convert_am_error_to_eapol_error(error));				
			}
		}
		++m_packet_index;
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);

	return EAP_STATUS_RETURN(m_am_tools, m_am_tools->convert_am_error_to_eapol_error(status));
}


/**
 * The get_header_offset() function obtains the header offset of EAP-packet.
 * @see abs_eap_base_type_c::get_header_offset().
 */
u32_t CEapVpnInterfaceImplementation::get_header_offset(
	u32_t * const MTU,
	u32_t * const trailer_length)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	*MTU = KMTU;
	*trailer_length = KTrailerLength;

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);

	return KHeaderOffset;
}

/**
 * The load_module() function function indicates the lower level to
 * load new module of EAP-type.
 * @param type is the requested EAP-type.
 * @param partner is pointer to the caller object.
 * The partner of the new created EAP-type object is the caller object.
 * @param eap_type is a pointer to a pointer of EAP-type object.
 * Adaptation module sets eap_type pointer to created EAP-type object.
 * @param is_client_when_true parameter indicates whether the network entity should
 * act as a client (true) or server (false), in terms of EAP-protocol whether
 * this network entity is EAP-supplicant (true) or EAP-authenticator (false).
 * @param receive_network_id includes the addresses (network identity) and packet type.
 */
eap_status_e CEapVpnInterfaceImplementation::load_module(
	const eap_type_value_e type,
	const eap_type_value_e tunneling_type,
	abs_eap_base_type_c * const partner,
	eap_base_type_c ** const eap_type_if,
	const bool is_client_when_true,
	const eap_am_network_id_c * const receive_network_id)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::load_module()\n")));

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("eap_vpn_if::load_module(type %d=%s, tunneling_type %d=%s)\n"),
		convert_eap_type_to_u32_t(type),
		eap_header_string_c::get_eap_type_string(type),
		convert_eap_type_to_u32_t(tunneling_type),
		eap_header_string_c::get_eap_type_string(tunneling_type)));

	EAP_UNREFERENCED_PARAMETER(type);
	EAP_UNREFERENCED_PARAMETER(tunneling_type);

	eap_status_e status = eap_status_process_general_error;

	if (type != iRequestedEapType)
	{
		EAP_TRACE_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("load_module: eap_expanded_type_c::write_type(): not supported EAP-method\n")));
	
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_type);
	}

	EAP_ASSERT_TOOLS(m_am_tools, (type == iRequestedEapType));

#ifdef USE_EAP_EXPANDED_TYPES

		TBuf8<KExpandedEapTypeSize> cue;
		
		// Some indirect way of forming the 8 byte string of an EAP type for the cue is needed here.		
		TUint8 tmpExpCue[KExpandedEapTypeSize];

		// This is to make the tmpExpCue in 8 byte string with correct vendor type and vendor id details.
		status = eap_expanded_type_c::write_type(m_am_tools,
												0, // index should be zero here.
												tmpExpCue,
												KExpandedEapTypeSize,
												true,
												iRequestedEapType);
		if (status != eap_status_ok)
		{
			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("load_module: eap_expanded_type_c::write_type failed \n")));
		
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		
		// Now copy the 8 byte string to the real expanded cue.
		cue.Copy(tmpExpCue, KExpandedEapTypeSize);

		EAP_TRACE_DATA_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("EAPOL:CEapVpnInterfaceImplementation::load_module: Expanded CUE:"),
			cue.Ptr(),
			cue.Size()));

#else

	TBuf8<KMaxEapCueLength> cue;
	cue.Num(static_cast<TInt>(iRequestedEapType));

#endif //#ifdef USE_EAP_EXPANDED_TYPES

	CEapType* eapType = 0;
	TInt error(KErrNone);

#ifdef USE_EAP_EXPANDED_TYPES

	// Check if this EAP type has already been loaded
	TInt eapArrayIndex = find<eap_type_value_e>(
		m_eap_type_array,
		&iRequestedEapType,
		m_am_tools);

	if (eapArrayIndex >= 0)

#else // For normal EAP type.

	// Check if this EAP type has already been loaded
	TInt eapArrayIndex = m_eap_type_array.Find(iRequestedEapType);

	if (eapArrayIndex != KErrNotFound)

#endif

	{
		// Yep. It was loaded already.
		EAP_TRACE_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("CEapVpnInterfaceImplementation: EAP Type Already loaded\n")));

		eapType = m_plugin_if_array[eapArrayIndex];		
	}
	else 
	{
		// We must have a trap here since the EAP core knows nothing about Symbian.
		EAP_TRACE_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("CEapVpnInterfaceImplementation: EAP Type new\n")));

		TRAP(error, (eapType = CEapType::NewL(cue, m_index_type, m_index)));	

		if (error != KErrNone
			|| eapType == 0)
		{
			// Interface not found or implementation creation function failed
			
			delete eapType;
			eapType = 0;
			
			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("CEapVpnInterfaceImplementation: EAP Type new, unable to load\n")));
			EAP_TRACE_ALWAYS(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("ECom could not find/initiate implementation.\n")));
			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
		}
	}

	//--------------------------------------------------------
	//--------------------------------------------------------
	// Set the values for realm and user name if there is any.
	// If there is no values the default settings will be used( automatic realm and username).


	{
		EAP_TRACE_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("CEapVpnInterfaceImplementation::load_module - About to configure manual/auto username and manual/auto realm \n")));
	
	    EAPSettings* setSettings = new EAPSettings;
		if( setSettings == NULL )     
		{
			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("CEapVpnInterfaceImplementation::load_module - EAPSettings allocation error \n")));
	    	
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);				
		}
	    
		if(iRequestedEapType == eap_type_aka)
		{
			setSettings->iEAPType = EAPSettings::EEapAka;
			
		}
		else if(iRequestedEapType == eap_type_gsmsim)
		{
			setSettings->iEAPType = EAPSettings::EEapSim;
		}
		else
		{
			delete setSettings;
			setSettings = 0;

			// Only EAP-SIM and AKA are possible now.
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_eap_type);
		}
	    
	    if(iManualUsername && iManualUsername->Length() != 0)
	    {
    		setSettings->iUsernamePresent = EGSMSIMUseManualUsernameYes; // Same value for both SIM and AKA.
	    	setSettings->iUsername.Copy(iManualUsername->Des());
	    }
	    else
	    {
	    	// No user name. we have to set automatic now.
	    	setSettings->iUsernamePresent = EGSMSIMUseManualUsernameNo; // Same value for both SIM and AKA.
	    }
	    
	    if(iManualRealm && iManualRealm->Length() != 0)
	    {
	    	setSettings->iRealmPresent = EGSMSIMUseManualRealmYes; // Same value for both SIM and AKA.
	    	setSettings->iRealm.Copy(iManualRealm->Des());
	    }
	    else
	    {
	    	// No realm. we have to set automatic now.
	    	setSettings->iRealmPresent = EGSMSIMUseManualRealmNo; // Same value for both SIM and AKA.
	    }    
	    
		TRAP(error, eapType->SetConfigurationL(*setSettings) );
	    if ( error != KErrNone )
	    {
			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("CEapVpnInterfaceImplementation::load_module - Setting Manual Username and Realm failed.error=%d, Continuing \n")));
	    }
	    
	    delete setSettings;
	    setSettings = 0;    
	}
    
	//--------------------------------------------------------
	//--------------------------------------------------------
	
	// Create the EAP protocol interface implementation.
	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation: EAP Type new, GetStackInterfaceL()\n")));

#ifdef USE_EAP_SIMPLE_CONFIG

	TRAP(error, (*eap_type_if = eapType->GetStackInterfaceL(
		m_am_tools, 
		partner, 
		is_client_when_true, 
		receive_network_id,
		0))); // Check this up.

#else

	TRAP(error, (*eap_type_if = eapType->GetStackInterfaceL(
		m_am_tools, 
		partner, 
		is_client_when_true, 
		receive_network_id)));

#endif // #ifdef USE_EAP_SIMPLE_CONFIG

	if (error != KErrNone 
		|| *eap_type_if == 0 
		|| (*eap_type_if)->get_is_valid() == false)
	{
		EAP_TRACE_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("CEapVpnInterfaceImplementation: EAP Type new, GetStackInterfaceL(), failed = %d\n"), error));

		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("Could not create EAP type interface instance. Error: %d\n"), error));

		status = eap_status_allocation_error;

		// Unload DLL (two ways, depending whether this type was already loaded...)
		if  (eapArrayIndex == KErrNotFound)
		{
			// No need to call shutdown here because GetStackInterfaceL has done it.
			delete eapType;
		}
		else
		{
			unload_module((eap_type_value_e)iRequestedEapType);
		}
		// Note: even in error cases eap_core_c deletes eap_type_if
	}
	else
	{
		EAP_TRACE_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("CEapVpnInterfaceImplementation: EAP Type new, GetStackInterfaceL(), success\n")));

		status = eap_status_ok;

		if (eapArrayIndex  == KErrNotFound)
		{
			// Add plugin information to the member arrays. There is no need to store eap_type pointer because
			// the stack takes care of its deletion.
			if (m_plugin_if_array.Append(eapType) != KErrNone)
			{
				delete eapType;
				status = eap_status_allocation_error;
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);				
			}

#ifdef USE_EAP_EXPANDED_TYPES

			eap_type_value_e * tmpEAPType = new eap_type_value_e();
			if(tmpEAPType == NULL)
			{
				EAP_TRACE_DEBUG(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("eapol_am_wlan_authentication_symbian_c::load_module() eap_type_value_e creation failed\n")));
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);				
			}
			
			*tmpEAPType = type;
			
			status = m_eap_type_array->add_object(tmpEAPType, true);
			
			if (status != eap_status_ok)			

#else // For normal EAP type.			

			if (m_eap_type_array.Append(iRequestedEapType) != KErrNone)

#endif
			{
				// Remove the eap type added just previously
				m_plugin_if_array.Remove(m_plugin_if_array.Count() - 1);
				delete eapType;
				status = eap_status_allocation_error;

				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);				
			}
		} 
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

/**
 * The unload_module() function unloads the module of a EAP-type. 
 * @param eap_type is the requested EAP-type.
 */
eap_status_e CEapVpnInterfaceImplementation::unload_module(const eap_type_value_e type)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
	eap_status_e status(eap_status_type_does_not_exists_error);

#ifdef USE_EAP_EXPANDED_TYPES

	// Check if this EAP type has already been loaded
	TInt index = find<eap_type_value_e>(
		m_eap_type_array,
		&type,
		m_am_tools);
		
	if (index >= 0)
	{
		// EAP was loaded before.
		
		delete m_plugin_if_array[index];
		m_plugin_if_array.Remove(index);
		
		status = m_eap_type_array->remove_object(index);
	}

#else // For normal EAP types.

	TInt index = m_eap_type_array.Find(type);

	if (index != KErrNotFound)
	{
		delete m_plugin_if_array[index];
		m_plugin_if_array.Remove(index);
		m_eap_type_array.Remove(index);
		status = eap_status_ok;			
	}

#endif

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}
//--------------------------------------------------

void CEapVpnInterfaceImplementation::set_is_valid()
{
	m_is_valid = true;
}

bool CEapVpnInterfaceImplementation::get_is_valid()
{
	return m_is_valid;
}

void CEapVpnInterfaceImplementation::increment_authentication_counter()
{
	++m_authentication_counter;
}

u32_t CEapVpnInterfaceImplementation::get_authentication_counter()
{
	return m_authentication_counter;
}

bool CEapVpnInterfaceImplementation::get_is_client()
{
	return m_is_client;
}

/**
 * Note this function is just an example. Parameters will change later.
 * The packet_data_crypto_keys() function gives the generated keys to lower level.
 * After EAP-authentication has generated the keys it calls this function
 * to offer the keys to lower level.
 * @see abs_eap_base_type_c::packet_data_crypto_keys().
 */
eap_status_e CEapVpnInterfaceImplementation::packet_data_crypto_keys(
    const eap_am_network_id_c * const /*send_network_id*/,
    const eap_master_session_key_c * const master_session_key
    )
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	HBufC8* sharedKey = NULL;
	TPtr8 keyPtr(
		master_session_key->get_data(
			master_session_key->get_data_length()/2),
		master_session_key->get_data_length()/2,
		master_session_key->get_data_length()/2);

	TRAPD(error, (sharedKey = keyPtr.AllocL()));
	if(error != KErrNone)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);				
	}

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation: EapSharedKeyL()\n")));

	TRAP(error, (iCaller->EapSharedKeyL(sharedKey)));
	if(error != KErrNone)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, m_am_tools->convert_am_error_to_eapol_error(error));				
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return eap_status_ok;
}

/**
 * The packet_data_session_key() function passes one traffic encryption key to 
 * the lower layers. Ultimately the key can end up to the WLAN hardware.
 * @param send_network_id carries the addresses (network identity) and type of the packet.
 * @param key is the encryption key
 * @param key_length is the length of the key
 * @param key_type describes the type of the key (WEP or something else...)
 * @param key_index is the index of the encryption key (there can be four broadcast keys in WEP for example)
 */
eap_status_e CEapVpnInterfaceImplementation::packet_data_session_key(
    const eap_am_network_id_c * const /*send_network_id*/,
    const eapol_session_key_c * const /*key*/
    )
{
	// Not used, but might be called?
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
	TInt status(KErrNone);

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, m_am_tools->convert_am_error_to_eapol_error(status));
}

/**
 * The read_configure() function reads the configuration data identified
 * by the field string of field_length bytes length. Adaptation module must direct
 * the query to some persistent store.
 * @see abs_eap_base_type_c::read_configure().
 */
eap_status_e CEapVpnInterfaceImplementation::read_configure(
    const eap_configuration_field_c * const field,
    eap_variable_data_c * const data)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
	EAP_ASSERT_ALWAYS(data != NULL);
	EAP_ASSERT_ALWAYS(field->get_field_length() <= KMaxConfigStringLength);

	EAP_UNREFERENCED_PARAMETER(KMaxConfigStringLength);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::read_configure()\n")));

	eap_status_e status(eap_status_ok);

	m_am_tools->trace_configuration(
		status,
		field,
		data);


	// Check if the wanted parameter is default type

	{
		eap_variable_data_c wanted_field(m_am_tools);

		status = wanted_field.set_buffer(
			field->get_field(),
			field->get_field_length(),
			false,
			false);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return status;
		}


		if (wanted_field.compare(
		cf_str_EAP_default_type_u32_t.get_field()->get_field(),
		cf_str_EAP_default_type_u32_t.get_field()->get_field_length()) == 0
		|| wanted_field.compare(
		cf_str_EAP_server_default_type_u32_t.get_field()->get_field(),
		cf_str_EAP_server_default_type_u32_t.get_field()->get_field_length()) == 0)
		{
			// We need to return here the next EAP type we should try
			
#ifdef USE_EAP_EXPANDED_TYPES

			if(m_enabled_expanded_eap_array.Count() < 1)
			{
				// No enabled EAP types.
				
				// Send WLM notification because there is no way that the authentication
				// can be successful if we don't have any EAP types to use...
				if (m_is_client)
				{
					EAP_TRACE_ERROR(
						m_am_tools,
						TRACE_FLAGS_DEFAULT,
						(EAPL("ERROR: No configured EAP types or all tried unsuccessfully.\n")));
				}

				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_configure_field);				
			}

			// Now there are enabled EAP type, we need to take the first EAP type from the arrary.
			SEapExpandedType * expandedEAPType = m_enabled_expanded_eap_array[0]; //First item.
			
			status = data->set_copy_of_buffer(expandedEAPType->EapExpandedType.Ptr(), KExpandedEapTypeSize);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);			
			}
			
			// This is to check if this is the requested EAP type.
			
			eap_expanded_type_c tmpExpEAPType;
			
			// This is to make the tmpExpEAPType in 8 byte string with correct vendor type and vendor id details.
			status = eap_expanded_type_c::read_type(m_am_tools,
													0, // index should be zero here.
													expandedEAPType->EapExpandedType.Ptr(),
													KExpandedEapTypeSize,
													&tmpExpEAPType);
			if (status != eap_status_ok)
			{
				EAP_TRACE_DEBUG(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("CEapVpnInterfaceImplementation::read_configure: eap_expanded_type_c::write_type failed \n")));
			
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
			
			if(tmpExpEAPType == iRequestedEapType)
			{			
				EAP_TRACE_DATA_DEBUG(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("CEapVpnInterfaceImplementation::StartL:Trying EAP type:"),
					expandedEAPType->EapExpandedType.Ptr(),
					expandedEAPType->EapExpandedType.Size()));
			}							

#else // For normal EAP types
		
			TInt i; 	

			for (i = 0; i < m_iap_eap_array.Count(); i++)
			{
				// Find the first enabled EAP type (highest priority)
				TEap *eapType = m_iap_eap_array[i];			
				if (eapType->Enabled == 1)
				{
					// Convert the string to integer
					TLex8 tmp(eapType->UID);
					TInt val(0);
					tmp.Val(val);

					status = data->set_copy_of_buffer((u8_t*) &val, sizeof(TUint));
					if (status != eap_status_ok)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);			
					}

					if(val == iRequestedEapType)
					{
						EAP_TRACE_ALWAYS(
							m_am_tools,
							TRACE_FLAGS_DEFAULT,
							(EAPL("EAPOL: Trying EAP type: %d.\n"), val));
						break;
					}
					else
					{
						continue;
					}
				}
			} // for()

			if (i >= m_iap_eap_array.Count())
			{
				// Not found
				// Send WLM notification because there is no way that the authentication
				// can be successful if we don't have any EAP types to use...
				if (m_is_client)
				{
					EAP_TRACE_ERROR(
						m_am_tools,
						TRACE_FLAGS_DEFAULT,
						(EAPL("ERROR: No configured EAP types or all tried unsuccessfully.\n")));
				}

				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_configure_field);
			}

#endif //#ifdef USE_EAP_EXPANDED_TYPES

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_GSMSIM_UMA_profile.get_field()->get_field(),
				cf_str_EAP_GSMSIM_UMA_profile.get_field()->get_field_length()) == 0
			|| wanted_field.compare(
					cf_str_EAP_AKA_UMA_profile.get_field()->get_field(),
					cf_str_EAP_AKA_UMA_profile.get_field()->get_field_length()) == 0)
		{
			// Set uma profile of EAP-SIM and EAP-AKA.
			TInt val(1);
			status = data->set_copy_of_buffer((u8_t*) &val, sizeof(TUint));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_GSMSIM_wait_eap_success_packet.get_field()->get_field(),
				cf_str_EAP_GSMSIM_wait_eap_success_packet.get_field()->get_field_length()) == 0
			|| wanted_field.compare(
					cf_str_EAP_AKA_wait_eap_success_packet.get_field()->get_field(),
					cf_str_EAP_AKA_wait_eap_success_packet.get_field()->get_field_length()) == 0)
		{
			// Set wait eap success of EAP-SIM and EAP-AKA.
			TInt val(1);
			status = data->set_copy_of_buffer((u8_t*) &val, sizeof(TUint));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_GSMSIM_UMA_realm_prefix.get_field()->get_field(),
				cf_str_EAP_GSMSIM_UMA_realm_prefix.get_field()->get_field_length()) == 0
			|| wanted_field.compare(
					cf_str_EAP_AKA_UMA_realm_prefix.get_field()->get_field(),
					cf_str_EAP_AKA_UMA_realm_prefix.get_field()->get_field_length()) == 0)
		{
			// Set uma realm prefix of EAP-SIM and EAP-AKA.
			status = data->set_copy_of_buffer(iRealmPrefix->Ptr(), iRealmPrefix->Length());
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_GSMSIM_use_manual_realm.get_field()->get_field(),
				cf_str_EAP_GSMSIM_use_manual_realm.get_field()->get_field_length()) == 0
			|| wanted_field.compare(
					cf_str_EAP_AKA_use_manual_realm.get_field()->get_field(),
					cf_str_EAP_AKA_use_manual_realm.get_field()->get_field_length()) == 0)
		{
			// Use ManualRealm of EAP-SIM and EAP-AKA.
			TInt val(1);
			if(iManualRealm == NULL || (iManualRealm && (iManualRealm->Length() == 0)))
			{
				val = 0;
			}
			status = data->set_copy_of_buffer((u8_t*) &val, sizeof(TUint));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_GSMSIM_manual_realm.get_field()->get_field(),
				cf_str_EAP_GSMSIM_manual_realm.get_field()->get_field_length()) == 0
			|| wanted_field.compare(
					cf_str_EAP_AKA_manual_realm.get_field()->get_field(),
					cf_str_EAP_AKA_manual_realm.get_field()->get_field_length()) == 0)
		{
			// ManualRealm of EAP-SIM and EAP-AKA.
			status = data->set_copy_of_buffer(iManualRealm->Ptr(), iManualRealm->Length());
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_GSMSIM_use_manual_username.get_field()->get_field(),
				cf_str_EAP_GSMSIM_use_manual_username.get_field()->get_field_length()) == 0
			|| wanted_field.compare(
				cf_str_EAP_AKA_use_manual_username.get_field()->get_field(),
				cf_str_EAP_AKA_use_manual_username.get_field()->get_field_length()) == 0)
		{
			// Use ManualUsername of EAP-SIM and EAP-AKA.
			TInt val(1);
			if(iManualUsername == NULL || (iManualUsername && (iManualUsername->Length() == 0)))
			{
				val = 0;
			}
			status = data->set_copy_of_buffer((u8_t*) &val, sizeof(TUint));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_GSMSIM_manual_username.get_field()->get_field(),
				cf_str_EAP_GSMSIM_manual_username.get_field()->get_field_length()) == 0
			|| wanted_field.compare(
				cf_str_EAP_AKA_manual_username.get_field()->get_field(),
				cf_str_EAP_AKA_manual_username.get_field()->get_field_length()) == 0)
		{
			// ManualUsername of EAP-SIM and EAP-AKA.
			status = data->set_copy_of_buffer(iManualUsername->Ptr(), iManualUsername->Length());
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_GSMSIM_2_digit_mnc_map_of_mcc_of_imsi_array.get_field()->get_field(),
				cf_str_EAP_GSMSIM_2_digit_mnc_map_of_mcc_of_imsi_array.get_field()->get_field_length()) == 0
			|| wanted_field.compare(
					cf_str_EAP_AKA_2_digit_mnc_map_of_mcc_of_imsi_array.get_field()->get_field(),
					cf_str_EAP_AKA_2_digit_mnc_map_of_mcc_of_imsi_array.get_field()->get_field_length()) == 0)
		{
			// 2-digit MNC configuration of EAP-SIM and EAP-AKA.
			const TUint32 K2DigitMncMccList[]
				= {
					202,204,206,208,213,214,216,218,
					219,220,222,226,228,230,231,232,
					234,238,240,242,244,246,247,248,
					250,255,257,259,260,262,266,268,
					270,272,274,276,278,280,282,284,
					286,288,290,293,294,295,308,340,
					362,363,400,401,402,404,410,413,
					414,415,416,417,419,420,421,422,
					424,425,426,427,428,429,432,434,
					436,437,438,440,441,452,454,455,
					456,457,460,470,472,502,505,510,
					515,520,525,528,530,537,539,541,
					546,547,549,602,603,604,605,607,
					608,609,610,611,612,613,614,615,
					616,617,619,620,621,622,623,624,
					625,626,628,629,630,631,633,634,
					635,636,638,639,640,641,642,643,
					645,646,647,648,649,650,651,652,
					653,654,655,702,704,706,710,712,
					714,716,724,730,734,744,746,901
				};

			status = data->set_copy_of_buffer((u32_t*) K2DigitMncMccList, sizeof(K2DigitMncMccList));
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
				cf_str_EAP_TRACE_output_file_name.get_field()->get_field(),
				cf_str_EAP_TRACE_output_file_name.get_field()->get_field_length()) == 0)
		{
			// Trace output file name.
			status = data->set_copy_of_buffer(
				m_trace_log_file_name);
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
					cf_str_EAP_GSMSIM_max_session_validity_time.get_field()->get_field(),
					cf_str_EAP_GSMSIM_max_session_validity_time.get_field()->get_field_length()) == 0
				|| wanted_field.compare(
					cf_str_EAP_AKA_max_session_validity_time.get_field()->get_field(),
					cf_str_EAP_AKA_max_session_validity_time.get_field()->get_field_length()) == 0)
		{
			u32_t session_validity_time_in_seconds(43200ul);

			status = data->set_copy_of_buffer(reinterpret_cast<u8_t*>(&session_validity_time_in_seconds), sizeof(session_validity_time_in_seconds));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else if (wanted_field.compare(
					cf_str_EAP_CORE_wait_eap_request_type_timeout.get_field()->get_field(),
					cf_str_EAP_CORE_wait_eap_request_type_timeout.get_field()->get_field_length()) == 0)
		{
			u32_t wait_eap_request_type_timeout_in_milli_seconds(30000ul);

			status = data->set_copy_of_buffer(reinterpret_cast<u8_t*>(&wait_eap_request_type_timeout_in_milli_seconds), sizeof(wait_eap_request_type_timeout_in_milli_seconds));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);		
		}
		else
		{
			EAP_TRACE_DATA_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("WARNING: CEapVpnInterfaceImplementation: read_configure(): Unknown configuration\n"),
				field->get_field(),
				field->get_field_length()));
		}
	}

	// Otherways just use the default value
	m_am_tools->trace_configuration(
		status,
		field,
		data);

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

/**
 * The write_configure() function writes the configuration data identified
 * by the field string of field_length bytes length. Adaptation module must direct
 * the action to some persistent store.
 * @see abs_eap_base_type_c::write_configure().
 */
eap_status_e CEapVpnInterfaceImplementation::write_configure(
	const eap_configuration_field_c * const /*field*/,
	eap_variable_data_c * const /*data*/)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return eap_status_not_supported;
}

/**
 * This is notification of internal state transition.
 * This is used for notifications, debugging and protocol testing.
 * The primal notifications are eap_state_variable_e::eap_state_authentication_finished_successfully
 * and eap_state_variable_e::eap_state_authentication_terminated_unsuccessfully.
 * These two notifications are sent from EAP-protocol layer (eap_protocol_layer_e::eap_protocol_layer_eap).
 * These two notifications tells the end state of authentication session. These are the only
 * reliable indications of the final status of authentication session.
 * You MUST NOT make decision based on the return value of abs_eap_stack_interface_c::packet_process().
 */
void CEapVpnInterfaceImplementation::state_notification(
                                const abs_eap_state_notification_c * const state)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::state_notification() = %d\n"), state->get_current_state()));

	if (m_block_packet_sends_and_notifications == true)
	{
		// Notification block is active.		
		EAP_TRACE_ALWAYS(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("state_notification: notification ignored because Disassociated() was called.\n")));
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return;
	}

	// Check if this is EAP layer notification
	if(state->get_protocol_layer() == eap_protocol_layer_eap)
	{
		switch (state->get_current_state())
		{
		case eap_state_none:
			break;
		case eap_state_identity_request_sent:
			// This is for server only so no need to notify WLM.
			break;
		case eap_state_identity_request_received:
			break;
		case eap_state_identity_response_received:
			// This is for server only so no need to notify WLM.
			break;
		case eap_state_authentication_finished_successfully:
		{
			increment_authentication_counter();
			m_successful_authentications++;	
	
			EAP_TRACE_ALWAYS(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("Indication sent: ESuccess.\n")));

			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("CEapVpnInterfaceImplementation: Indication sent: ESuccess\n")));
			iCaller->EapIndication(ESuccess);
			break;
		}
		case eap_state_authentication_terminated_unsuccessfully:
		{
			increment_authentication_counter();
			m_failed_authentications++;

			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("CEapVpnInterfaceImplementation: Indication sent: EFailure\n")));

			EAP_TRACE_ALWAYS(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("Indication sent: EFailure.\n")));
			iCaller->EapIndication(EFailure);
			break;
		}
		default:
			break;
		}
	}

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
}

/**
 * The set_timer() function initializes timer to be elapsed after time_ms milliseconds.
 * @param initializer is pointer to object which timer_expired() function will
 * be called after timer elapses.
 * @param id is identifier which will be returned in timer_expired() function.
 * The user selects and interprets the id for this timer.
 * @param data is pointer to any user selected data which will be returned in timer_expired() function.
 * @param time_ms is the time of timer in milli seconds.
 *
 * Adaptation module internally implements the timer.
 */
eap_status_e CEapVpnInterfaceImplementation::set_timer(
	abs_eap_base_timer_c * const p_initializer, 
	const u32_t p_id, 
	void * const p_data,
	const u32_t p_time_ms)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	const eap_status_e status = m_am_tools->am_set_timer(
		p_initializer, 
		p_id, 
		p_data,
		p_time_ms);

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return status;
}

/**
 * The cancel_timer() function cancels the timer id initiated by initializer.
 * @param initializer is pointer to object which set the cancelled timer.
 * @param id is identifier which will be returned in timer_expired() function.
 * The user selects and interprets the id for this timer.
 *
 * Adaptation module internally implements the timer.
 */
eap_status_e CEapVpnInterfaceImplementation::cancel_timer(
	abs_eap_base_timer_c * const p_initializer, 
	const u32_t p_id)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	const eap_status_e status = m_am_tools->am_cancel_timer(
		p_initializer, 
		p_id);

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return status;
}

/**
 * The cancel_all_timers() function cancels all timers.
 * User should use this in termination of the stack before
 * the adaptation module of tools is deleted.
 * Preferred mode is to cancel each timer directly
 * using cancel_timer() function.
 *
 * Adaptation module internally implements the timer.
 */
eap_status_e CEapVpnInterfaceImplementation::cancel_all_timers()
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	const eap_status_e status = m_am_tools->am_cancel_all_timers();

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return status;
}

/**
 * This function queries the validity of EAP-type.
 * Lower layer should return eap_status_ok if this EAP-type is supported.
 * @param eap_type is the requested EAP-type.
 */
eap_status_e CEapVpnInterfaceImplementation::check_is_valid_eap_type(const eap_type_value_e eap_type)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status(eap_status_illegal_eap_type);

#ifdef USE_EAP_EXPANDED_TYPES

	for (int i = 0; i < m_enabled_expanded_eap_array.Count(); i++)
	{
		TBuf8<KExpandedEapTypeSize> tmpExpEAP(m_enabled_expanded_eap_array[i]->EapExpandedType);

		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("CEapVpnInterfaceImplementation::check_is_valid_eap_type:Enabled expanded EAP type at index=%d\n"),
			 i));

		EAP_TRACE_DATA_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("Enabled expanded EAP type:"),
			tmpExpEAP.Ptr(),
			tmpExpEAP.Size()));

		// This is for one expanded EAP type (for the above one).
		eap_expanded_type_c expandedEAPType;
				
		// Read the expanded EAP type details for this item in m_enabled_expanded_eap_array.
		eap_status_e status = eap_expanded_type_c::read_type(m_am_tools,
												0,
												tmpExpEAP.Ptr(),
												tmpExpEAP.Size(),
												&expandedEAPType);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		if (eap_type == expandedEAPType)
		{
			// This is Allowed and Valid.
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
		}
	} // End of for()
		 
#else // For normal unexpanded EAP type

	TEap *eapType = 0; 

	for (int i = 0; i < m_iap_eap_array.Count(); i++)
	{
		// Try next EAP type
		eapType = m_iap_eap_array[i];
		if (eapType->Enabled == 1)
		{	
			// Convert the string to integer
			TLex8 tmp(eapType->UID);
			TInt val(0);
			tmp.Val(val);

			if (val == eap_type)
			{
				// Allowed
				status = eap_status_ok;
				break;
			}	
		}
	} // for()

#endif // #ifdef USE_EAP_EXPANDED_TYPES

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);

	return EAP_STATUS_RETURN(m_am_tools, status);
}

/**
 * This function queries the list of supported EAP-types.
 * Lower layer should return eap_status_ok if this call succeeds.
 * @param eap_type_list will include the list of supported EAP-types. Each value in list
 * is type of u32_t and represent one supported EAP-type. List consists of subsequent u32_t type values.
 */
eap_status_e CEapVpnInterfaceImplementation::get_eap_type_list(eap_array_c<eap_type_value_e> * const eap_type_list)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status(eap_status_illegal_eap_type);

	status = eap_type_list->reset();
	if (status != eap_status_ok)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, status);
	}

	eap_header_string_c eap_string;
	EAP_UNREFERENCED_PARAMETER(eap_string);

#ifdef USE_EAP_EXPANDED_TYPES

	// This function is same as get_selected_eap_types in behavior.

	// We need to return only the EAP types available as enabled types.
	// It means only the ones available in m_enabled_expanded_eap_array.
	
	for (TInt i = 0; i < m_enabled_expanded_eap_array.Count(); i++)
	{	
		TBuf8<KExpandedEapTypeSize> tmpExpEAP(m_enabled_expanded_eap_array[i]->EapExpandedType);

		EAP_TRACE_DEBUG(
			m_am_tools, 
			TRACE_FLAGS_DEFAULT, 
			(EAPL("CEapVpnInterfaceImplementation::get_eap_type_list:Enabled expanded EAP type at index=%d\n"),
			 i));

		EAP_TRACE_DATA_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("Enabled expanded EAP type:"),
			tmpExpEAP.Ptr(),
			tmpExpEAP.Size()));

		// This is for one expanded EAP type (for the above one).
		eap_expanded_type_c * expandedEAPType = new eap_type_value_e();
				
		// Read the expanded EAP type details from an item in m_enabled_expanded_eap_array.
		status = eap_expanded_type_c::read_type(m_am_tools,
												0,
												tmpExpEAP.Ptr(),
												tmpExpEAP.Size(),
												expandedEAPType);
		if (status != eap_status_ok)
		{
			delete expandedEAPType;
			expandedEAPType = 0;

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}

		// Add EAP-type to list.		
		status = eap_type_list->add_object(expandedEAPType, true);
		if (status != eap_status_ok)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}		
			
		EAP_TRACE_DEBUG(
			m_am_tools,
			TRACE_FLAGS_DEFAULT,
			(EAPL("get_eap_type_list():added EAP-type=0x%08x=%s\n"),
			expandedEAPType->get_vendor_type(),
			eap_string.get_eap_type_string(*expandedEAPType)));			
	}

#else // for normal EAP types.

	TEap *eapType = 0; 

	for (TInt i = 0; i < m_iap_eap_array.Count(); i++)
	{
		// Check if type is enabled
		eapType = m_iap_eap_array[i];
		if (eapType->Enabled == 1)
		{
			TLex8 tmp(eapType->UID);
			TInt val(0);
			tmp.Val(val);

			eap_type_value_e * const eap_type = new eap_type_value_e(
			static_cast<eap_type_ietf_values_e>(val));
			if (eap_type == 0)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
			}

			status = eap_type_list->add_object(eap_type, true);
			if (status != eap_status_ok)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
	} // for()

#endif //#ifdef USE_EAP_EXPANDED_TYPES

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return eap_status_ok;
}
//--------------------------------------------------


eap_status_e CEapVpnInterfaceImplementation::add_rogue_ap(eap_array_c<eap_rogue_ap_entry_c> & /*rogue_ap_list*/)
{
	// Not used, but might be called
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
	TInt err = KErrNone;

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("eap_vpn_if::add_rogue_ap()\n")));

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);	
	return EAP_STATUS_RETURN(m_am_tools, m_am_tools->convert_am_error_to_eapol_error(err));
}


eap_status_e CEapVpnInterfaceImplementation::restart_authentication(
	const eap_am_network_id_c * const /*receive_network_id*/,
	const bool /*is_client_when_true*/,
	const bool /*force_clean_restart*/,
	const bool /*from_timer*/)
{
	// Not used, but might be called
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("eap_vpn_if::restart_authentication()\n")));

	eap_status_e status = eap_status_ok;

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}


//--------------------------------------------------

//
eap_status_e CEapVpnInterfaceImplementation::asynchronous_init_remove_eap_session(
	const eap_am_network_id_c * const /* send_network_id */)
{
	// eapol_core_c object does not support asynchronous_init_remove_eap_session().
	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
}

//--------------------------------------------------

//
eap_status_e CEapVpnInterfaceImplementation::set_session_timeout(
	const u32_t /* session_timeout_ms */)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_ASSERT(m_am_tools->get_global_mutex()->get_is_reserved() == true);

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, eap_status_not_supported);
}


//--------------------------------------------------

//
eap_status_e CEapVpnInterfaceImplementation::timer_expired(
	const u32_t id,
	void * /*data*/)
{
	// Not used, but might be called
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("CEapVpnInterfaceImplementation::timer_expired()\n")));

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("TIMER: [0x%08x]->eap_vpn_if::timer_expired(id 0x%02x).\n"),
		this,
		id));

	switch (id)
	{
	case EAPOL_AM_CORE_TIMER_DELETE_STACK_ID:
		{
			EAP_TRACE_ALWAYS(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("EAPOL_AM_CORE_TIMER_DELETE_STACK_ID elapsed: Delete stack.\n")));

			cancel_all_timers();

			// Delete stack
			if (iEapCore != 0)
			{
				iEapCore->shutdown();
				delete iEapCore;
				iEapCore = 0;				
			}
			m_stack_marked_to_be_deleted = false;

			// Re-activates timer queue.
			eap_status_e status = m_am_tools->re_activate_timer_queue();
			if (status != eap_status_ok)
			{
				EAP_TRACE_ALWAYS(
					m_am_tools,
					TRACE_FLAGS_DEFAULT,
					(EAPL("ERROR: re_activate_timer_queue() failed, status = %d\n")));
			}

			break;
		}

	default:
		break;
	} // switch()

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
}

//--------------------------------------------------

//
eap_status_e CEapVpnInterfaceImplementation::timer_delete_data(
    const u32_t /*id*/, void * /*data*/)
{
	// Not used, but might be called
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_ALWAYS(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("TIMER: [0x%08x]->eap_vpn_if::timer_delete_data().\n"),
		this));

	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, eap_status_ok);
}

// -----------------------------------------------------------------------------
// CleanupImplArray
// -----------------------------------------------------------------------------
//
void CEapVpnInterfaceImplementation::CleanupImplArray( TAny* aAny )
{
	RImplInfoPtrArray* implArray = 
		reinterpret_cast<RImplInfoPtrArray*>( aAny );

	implArray->ResetAndDestroy();
	implArray->Close();
}

//--------------------------------------------------

// End
