/*
* Copyright (c) 2001-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:  EAP and WLAN authentication protocols.
*
*/

/*
* %version: %
*/

// This is enumeration of EAPOL source code.
#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)
	#undef EAP_FILE_NUMBER_ENUM
	#define EAP_FILE_NUMBER_ENUM 114 
	#undef EAP_FILE_NUMBER_DATE 
	#define EAP_FILE_NUMBER_DATE 1127594498 
#endif //#if defined(USE_EAP_MINIMUM_RELEASE_TRACES)


// INCLUDE FILES

#include "eap_am_memory.h"
#include "eap_state_notification.h"
#include "eap_type_securid.h"
#include "eap_type_securid_types.h"
#include "eap_buffer.h"

const u8_t EAP_SECURID_PIN_STRING[] = "pin";
const u8_t EAP_SECURID_PASSCODE_STRING[] = "passcode";


EAP_FUNC_EXPORT eap_status_e eap_type_securid_c::query_eap_identity(
	const bool /*must_be_synchronous*/,
	eap_variable_data_c * const identity,
	const eap_am_network_id_c * const receive_network_id,
	const u8_t eap_identifier)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	// Here we swap the addresses.
	eap_am_network_id_c send_network_id(m_am_tools,
		receive_network_id->get_destination_id(),
		receive_network_id->get_source_id(),
		receive_network_id->get_type());

	m_identifier = eap_identifier;

	eap_status_e status;

	if (m_identity.get_is_valid_data() == false
        || m_identity.get_data_length() == 0)
	{
		EAP_TRACE_DEBUG(m_am_tools, 
			TRACE_FLAGS_DEFAULT, (
			EAPL("eap_type_securid_c::query_eap_identity(): Identity (username) is empty or invalid\n")));
	
		// Ask identity
		if (m_is_pending == false)
		{
			m_is_pending = true;
			status = m_am_type_securid->show_identity_query_dialog(m_eap_type, &m_identity);

			EAP_TRACE_DEBUG(m_am_tools, 
				TRACE_FLAGS_DEFAULT, (
				EAPL("eap_type_securid_c::query_eap_identity(): show_identity_query_dialog returns %d\n"),
				status));

			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, status);
		}
		else
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_pending_request);
		}
	}
	else
	{
		EAP_TRACE_DEBUG(m_am_tools, 
			TRACE_FLAGS_DEFAULT, (
			EAPL("eap_type_securid_c::query_eap_identity(): m_identity (identity or username) is valid.\n")));

		status = identity->set_copy_of_buffer(&m_identity);

		m_state.set_state(eap_type_securid_state_identity_query);
	}

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

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

EAP_FUNC_EXPORT eap_status_e eap_type_securid_c::complete_eap_identity_query(
	const eap_variable_data_c * const identity_utf8)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	EAP_TRACE_DATA_DEBUG(
		m_am_tools,
		TRACE_FLAGS_DEFAULT,
		(EAPL("EAP_type_GTC: function: complete_eap_identity_query, identity:"),
		 m_identity.get_data(m_identity.get_data_length()),
		 m_identity.get_data_length()));

	eap_status_e status = get_type_partner()->complete_eap_identity_query(
		&m_send_network_id,
		identity_utf8,
		m_identifier);

	if (status == eap_status_ok)
	{
		m_state.set_state(eap_type_securid_state_identity_query);
	}

	m_identity_asked = true;
	m_is_pending = false;

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

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

eap_status_e eap_type_securid_c::client_packet_process(
	eap_header_wr_c * const received_eap,
	const u32_t eap_packet_length)
{	
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);
	eap_status_e status = eap_status_drop_packet_quietly;

	m_identifier = received_eap->get_identifier();

	if (received_eap->get_type() == eap_type_identity)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
	}

	if (received_eap->get_code() == eap_code_failure)
	{
		if (m_state.is_valid_state(eap_type_securid_state_failure) == false)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
		}

		status = finish_unsuccessful_authentication(false);

		m_state.set_state(eap_type_securid_state_failure);
	}
	else if (received_eap->get_code() == eap_code_success)
	{
		if (m_state.is_valid_state(eap_type_securid_state_success) == false)
		{
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
		}

		status = finish_successful_authentication();

		m_state.set_state(eap_type_securid_state_success);
	}
	else if (received_eap->get_type() != m_eap_type)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
	}
	else if (received_eap->get_code() == eap_code_request) // Request
	{
		// Securid
		if (m_eap_type == eap_type_securid)
		{
			status = client_securid_packet_process(
				received_eap,
				eap_packet_length);			    
		}
		// GTC
		else if (m_eap_type == eap_type_generic_token_card)
		{ 
			status = client_gtc_packet_process(
				received_eap,
				eap_packet_length);			    
		}
	}

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

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

eap_status_e eap_type_securid_c::client_securid_packet_process(
	eap_header_wr_c * const eap,
	const u32_t eap_packet_length)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status = eap_status_drop_packet_quietly;

	if (eap_packet_length < eap->get_length())
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
	}

	u32_t type_data_length = eap->get_data_length();

	if (type_data_length == 0)
	{
		// Passcode query
		if (m_is_pending == false)
		{
			m_is_pending = true;
			m_am_type_securid->show_passcode_query_dialog(&m_passcode, true);
			if (status != eap_status_pending_request)
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, status);
			}
		}
	}
	else
	{
		u8_t * data = eap->get_type_data(type_data_length);
		if (data == 0)
		{
			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
		}
		
		if (type_data_length < 4)
		{
			return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
		}
		else if (type_data_length >= 4)
		{
			if (m_am_tools->memcmp(data, EAP_SECURID_PIN_STRING, 3) == 0
				&& data[3] == 0x00)
			{
				// Pincode query
				if (m_is_pending == false)
				{
					m_is_pending = true;
					m_am_type_securid->show_pincode_query_dialog(&m_passcode, &m_pincode, true);
					if (status != eap_status_pending_request)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}
				}
			}
			else if (type_data_length == 9
				&& m_am_tools->memcmp(data, EAP_SECURID_PASSCODE_STRING, 8) == 0
				&& data[8] == 0x00)
			{
				// Additional passcode query
				if (m_is_pending == false)
				{
					m_is_pending = true;
					m_am_type_securid->show_passcode_query_dialog(&m_passcode, false);
					if (status != eap_status_pending_request)
					{
						EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
						return EAP_STATUS_RETURN(m_am_tools, status);
					}
				}
			}
			else
			{
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
			}
		}
	}

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

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

eap_status_e eap_type_securid_c::client_gtc_packet_process(
	eap_header_wr_c * const eap,
	const u32_t eap_packet_length)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	eap_status_e status = eap_status_drop_packet_quietly;

	if (eap_packet_length < eap->get_length())
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_drop_packet_quietly);
	}

	u32_t type_data_length = eap->get_type_data_length();
	if (type_data_length > 1020)
	{
		type_data_length = 1020; // Magic
	}

	// Passcode query
	if (m_is_pending == false)
	{
		m_is_pending = true;

#if defined(USE_EAP_CONFIGURATION_TO_SKIP_USER_INTERACTIONS)
		if (m_skip_user_interactions == true)
		{
			EAP_TRACE_DEBUG(
				m_am_tools,
				TRACE_FLAGS_DEFAULT,
				(EAPL("WARNING: EAP-GTC: eap_type_securid_c::client_gtc_packet_process(): skips user interactions\n")));

			status = client_gtc_complete_user_input_query(&m_passcode);
		}
		else
#endif //#if defined(USE_EAP_CONFIGURATION_TO_SKIP_USER_INTERACTIONS)
		{
			eap_variable_data_c message(m_am_tools);
			status = message.set_copy_of_buffer(eap->get_type_data(type_data_length), type_data_length);
			if (status != eap_status_ok)
			{
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = m_am_type_securid->show_gtc_query_dialog(
				&m_passcode,
				message.get_data(),
				message.get_data_length(),
				true);
		}
	}
			
	EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
	return EAP_STATUS_RETURN(m_am_tools, status);
}

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

EAP_FUNC_EXPORT eap_status_e eap_type_securid_c::client_securid_complete_passcode_query(
	const eap_variable_data_c * const passcode_utf8)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	// Send passcode
	u32_t packet_length = eap_header_base_c::get_type_data_start_offset(m_use_eap_expanded_type)
		+ passcode_utf8->get_data_length() + 2 + 1;

	eap_buf_chain_wr_c * packet = create_send_packet(packet_length);
	if (!packet)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	eap_header_base_c eap_header(
		m_am_tools,
		packet->get_data_offset(m_offset, packet_length),
		packet_length);
	if (eap_header.get_is_valid() == false)
	{
		delete packet;
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
	}
	eap_header.set_code(eap_code_response);
	eap_header.set_identifier(m_identifier);
	eap_header.set_length(
		static_cast<u16_t>(packet_length),
		m_use_eap_expanded_type);
	eap_header.set_type(
		m_eap_type,
		m_use_eap_expanded_type);

	u8_t * type_data = const_cast<u8_t *> (eap_header.get_type_data_offset(0, eap_header.get_type_data_length()));
	if (type_data == 0)
	{
		delete packet;
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	{
		m_am_tools->memset(type_data, 0x00, 2);
		type_data += 2;
		m_am_tools->memmove(
			type_data,
			passcode_utf8->get_data(passcode_utf8->get_data_length()),
			passcode_utf8->get_data_length());
		type_data += passcode_utf8->get_data_length();
		type_data[0] = 0x00;
	}

	eap_status_e status = packet_send(packet, packet_length);
	delete packet;

	m_is_pending = false;

	m_state.set_state(eap_type_securid_state_passcode_query);

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

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

EAP_FUNC_EXPORT eap_status_e eap_type_securid_c::client_securid_complete_pincode_query(
	const eap_variable_data_c * const pincode,
	const eap_variable_data_c * const passcode)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	m_is_first_pincode_query = false;

	// Send pincode
	u32_t packet_length = eap_header_base_c::get_type_data_start_offset(m_use_eap_expanded_type)
		+ passcode->get_data_length() + 2 + 1 + pincode->get_data_length() + 1;
	eap_buf_chain_wr_c * packet = create_send_packet(packet_length);
	if (!packet)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	eap_header_base_c eap_header(
		m_am_tools,
		packet->get_data_offset(m_offset, packet_length),
		packet_length);
	if (eap_header.get_is_valid() == false)
	{
		delete packet;
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
	}
	eap_header.set_code(eap_code_response);
	eap_header.set_identifier(m_identifier);
	eap_header.set_length(
		static_cast<u16_t>(packet_length),
		m_use_eap_expanded_type);
	eap_header.set_type(
		m_eap_type,
		m_use_eap_expanded_type);

	u8_t * type_data = const_cast<u8_t *> (eap_header.get_type_data_offset(0, eap_header.get_type_data_length()));
	if (type_data == 0)
	{
		delete packet;
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	m_am_tools->memset(type_data, 0x00, 2);
	type_data += 2;
	// identity
	m_am_tools->memmove(
		type_data,
		passcode->get_data(passcode->get_data_length()),
		passcode->get_data_length());
	type_data += passcode->get_data_length();
	type_data[0] = 0x00;
	type_data++;
	m_am_tools->memmove(
		type_data,
		pincode->get_data(pincode->get_data_length()),
		pincode->get_data_length());
	type_data += pincode->get_data_length();
	type_data[0] = 0x00;

	eap_status_e status = packet_send(packet, packet_length);
	delete packet;

	m_is_pending = false;

	m_state.set_state(eap_type_securid_state_pincode_query);

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

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

EAP_FUNC_EXPORT eap_status_e eap_type_securid_c::client_gtc_complete_user_input_query(
	const eap_variable_data_c * const response_utf8)
{
	EAP_TRACE_BEGIN(m_am_tools, TRACE_FLAGS_DEFAULT);

	// Send response
	u32_t packet_length = eap_header_base_c::get_type_data_start_offset(m_use_eap_expanded_type)
		+ response_utf8->get_data_length();

#if defined(USE_FAST_EAP_TYPE)
	if (m_use_EAP_FAST_response == true)
	{
		packet_length += EAP_FAST_EAP_GTC_RESPONSE_PREFIX_LENGTH + m_identity.get_data_length() + EAP_FAST_EAP_GTC_RESPONSE_SEPARATOR_LENGTH;
	}
#endif //#if defined(USE_FAST_EAP_TYPE)

	eap_buf_chain_wr_c * packet = create_send_packet(packet_length);
	if (!packet)
	{
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
	}

	eap_header_base_c eap_header(
		m_am_tools,
		packet->get_data_offset(m_offset, packet_length),
		packet_length);
	if (eap_header.get_is_valid() == false)
	{
		delete packet;
		EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
		return EAP_STATUS_RETURN(m_am_tools, eap_status_illegal_parameter);
	}
	eap_header.set_code(eap_code_response);
	eap_header.set_identifier(m_identifier);
	eap_header.set_length(
		static_cast<u16_t>(packet_length),
		m_use_eap_expanded_type);
	eap_header.set_type(
		m_eap_type,
		m_use_eap_expanded_type);

	if (response_utf8->get_data_length() > 0UL)
	{
		u8_t * type_data = const_cast<u8_t *> (eap_header.get_type_data_offset(0, eap_header.get_type_data_length()));
		if (type_data == 0)
		{
			delete packet;
			EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
			return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
		}

#if defined(USE_FAST_EAP_TYPE)
		if (m_use_EAP_FAST_response == true)
		{
			eap_variable_data_c eap_fast_response(m_am_tools);
			if (eap_fast_response.get_is_valid() == false)
			{
				delete packet;
				EAP_TRACE_END(m_am_tools, TRACE_FLAGS_DEFAULT);
				return EAP_STATUS_RETURN(m_am_tools, eap_status_allocation_error);
			}

			eap_status_e status = eap_fast_response.set_copy_of_buffer(EAP_FAST_EAP_GTC_RESPONSE_PREFIX, EAP_FAST_EAP_GTC_RESPONSE_PREFIX_LENGTH);
			if (status != eap_status_ok)
			{
				delete packet;
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = eap_fast_response.add_data(&m_identity);
			if (status != eap_status_ok)
			{
				delete packet;
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = eap_fast_response.add_data(EAP_FAST_EAP_GTC_RESPONSE_SEPARATOR, EAP_FAST_EAP_GTC_RESPONSE_SEPARATOR_LENGTH);
			if (status != eap_status_ok)
			{
				delete packet;
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			status = eap_fast_response.add_data(response_utf8);
			if (status != eap_status_ok)
			{
				delete packet;
				return EAP_STATUS_RETURN(m_am_tools, status);
			}

			m_am_tools->memmove(
				type_data,
				eap_fast_response.get_data(),
				eap_fast_response.get_data_length());
		}
		else
#endif //#if defined(USE_FAST_EAP_TYPE)
		{
			m_am_tools->memmove(
				type_data,
				response_utf8->get_data(response_utf8->get_data_length()),
				response_utf8->get_data_length());
		}
	}

	eap_status_e status = packet_send(packet, packet_length);
	delete packet;

	m_is_pending = false;

	m_state.set_state(eap_type_securid_state_gtc_user_input_query);

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

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


// End.
