/*
 * libZRTP SDK library, implements the ZRTP secure VoIP protocol.
 * Copyright (c) 2006-2009 Philip R. Zimmermann.  All rights reserved.
 * Contact: http://philzimmermann.com
 * For licensing and other legal details, see the file zrtp_legal.c.
 * 
 * Viktor Krykun <v.krikun at zfoneproject.com> 
 */

#include "zrtp.h"

#define _ZTU_ "zrtp engine"

/*!
 * Data type for state-handlers: every state has a state handler
 * function which is called by zrtp_process_srtp().
 */
typedef zrtp_status_t state_handler_t( zrtp_stream_t* stream, zrtp_rtp_info_t* packet );
extern state_handler_t* state_handler[ZRTP_STATE_COUNT];

extern zrtp_status_t _zrtp_machine_process_sasrelay(zrtp_stream_t *stream, zrtp_rtp_info_t *packet);

static void _zrtp_machine_switch_to_error(zrtp_stream_t* stream);
static zrtp_status_t _zrtp_machine_enter_initiatingclear(zrtp_stream_t* stream);
static zrtp_status_t _zrtp_machine_enter_clear(zrtp_stream_t* stream);
static zrtp_status_t _zrtp_machine_enter_pendingerror(zrtp_stream_t *stream, zrtp_protocol_error_t code);

zrtp_status_t _zrtp_machine_process_hello(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
zrtp_status_t _zrtp_machine_process_goclear(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);

static void _send_helloack(zrtp_stream_t* stream);
static void _send_goclearack(zrtp_stream_t* stream);

zrtp_status_t _zrtp_machine_start_send_and_resend_hello(zrtp_stream_t* stream);
static zrtp_status_t _zrtp_machine_start_send_and_resend_goclear(zrtp_stream_t* stream);
static zrtp_status_t _zrtp_machine_start_send_and_resend_errorack(zrtp_stream_t* stream);
static zrtp_status_t _zrtp_machine_start_send_and_resend_error(zrtp_stream_t* stream);

void _clear_stream_crypto(zrtp_stream_t* stream);


/*===========================================================================*/
// MARK: ===> Main ZRTP interfaces
/*===========================================================================*/

/*----------------------------------------------------------------------------*/
zrtp_status_t zrtp_process_rtcp(zrtp_stream_t *stream, char* packet, unsigned int* length)
{

    /*
	 * In transition states, drop outgoing packets. In SECURE state, encrypt
       outgoing packets.  In all other states leave them unchanged.
	 */

    if (stream) {
		switch (stream->state)
		{
		case ZRTP_STATE_START_INITIATINGSECURE:
		case ZRTP_STATE_INITIATINGSECURE:
		case ZRTP_STATE_WAIT_CONFIRM1:
		case ZRTP_STATE_WAIT_CONFIRMACK:
		case ZRTP_STATE_PENDINGSECURE:
		case ZRTP_STATE_WAIT_CONFIRM2:
		case ZRTP_STATE_PENDINGCLEAR:
			return zrtp_status_drop;

		case ZRTP_STATE_SASRELAYING:
		case ZRTP_STATE_SECURE:
		{
			zrtp_rtp_info_t info;

			if (*length < RTCP_HDR_SIZE) {
				return zrtp_status_fail;
			}

			zrtp_memset(&info, 0, sizeof(info));
			info.packet = packet;
			info.length = length;
			info.seq = 0; /*sequence number will be generated in zrtp_srtp_protect_rtcp()*/
			info.ssrc = (uint32_t) *(packet+sizeof(uint32_t));

			return _zrtp_protocol_encrypt(stream->protocol, &info, 0);
		}

		default:
		return zrtp_status_ok;
		}
    }

    return zrtp_status_ok;
}

/*----------------------------------------------------------------------------*/
zrtp_status_t zrtp_process_srtcp(zrtp_stream_t *stream, char* packet, unsigned int* length)
{

    /*
	 * In transition states, drop incoming packets. In SECURE state, decrypt
	 * incoming packets. In all other states leave them unchanged.
	 */

    if (stream) {
		switch (stream->state)
		{
		case ZRTP_STATE_INITIATINGCLEAR:
			case ZRTP_STATE_PENDINGCLEAR:
			case ZRTP_STATE_INITIATINGSECURE:
			case ZRTP_STATE_PENDINGSECURE:
				return zrtp_status_drop;

			case ZRTP_STATE_SECURE:
			case ZRTP_STATE_SASRELAYING:
			{
				zrtp_rtp_info_t info;

				if (*length < RTCP_HDR_SIZE) {
					return zrtp_status_fail;
				}

				zrtp_memset(&info, 0, sizeof(info));
				info.packet = packet;
				info.length = length;
				info.seq = 0; /*sequence number will be determined from packet in zrtp_srtp_unprotect_rtcp()*/
				info.ssrc = (uint32_t) *(packet+sizeof(uint32_t));

				return _zrtp_protocol_decrypt(stream->protocol, &info, 0);
			}

			default:
				return zrtp_status_ok;
		}
    }

    return zrtp_status_ok;
}

/*----------------------------------------------------------------------------*/
zrtp_status_t zrtp_process_rtp(zrtp_stream_t *stream, char* packet, unsigned int* length)
{
	zrtp_rtp_info_t info;

	if (!stream || !packet || !length) {
		return zrtp_status_bad_param;
	}

	/* Skip packet processing within uninitiated stream */
	if ((stream->state < ZRTP_STATE_START) || (stream->state > ZRTP_STATE_NO_ZRTP)) {
		return zrtp_status_ok;
	}

	/* Prepare RTP packet: detect type and other options */
	if (zrtp_status_ok != _zrtp_packet_preparse(stream, packet, length, &info, 0)) {
		return zrtp_status_fail;
	}
	
	/* Drop packets in transition states and encrypt in SECURE state */
	switch (stream->state)
	{
	case ZRTP_STATE_START_INITIATINGSECURE:
	case ZRTP_STATE_INITIATINGSECURE:
	case ZRTP_STATE_WAIT_CONFIRM1:
	case ZRTP_STATE_WAIT_CONFIRMACK:
	case ZRTP_STATE_PENDINGSECURE:
	case ZRTP_STATE_WAIT_CONFIRM2:
	case ZRTP_STATE_PENDINGCLEAR:
		if (ZRTP_NONE == info.type) {	
			/* Add dropped media to the entropy hash */
			ZRTP_LOG(1,(_ZTU_,"Add %d bytes of entropy to the RNG pool.\n", *length));
			zrtp_entropy_add(stream->zrtp, (unsigned char*)packet, *length);
			
			return zrtp_status_drop;
		}
		break;

	case ZRTP_STATE_SASRELAYING:
	case ZRTP_STATE_SECURE:
		if (ZRTP_NONE == info.type) {
			return _zrtp_protocol_encrypt(stream->protocol, &info, 1);
		}
		break;

	default:
		break;
	}

	return zrtp_status_ok;
}

/*----------------------------------------------------------------------------*/
extern int _send_message(zrtp_stream_t* stream, zrtp_msg_type_t type, const void* message, uint32_t ssrc);
zrtp_status_t zrtp_process_srtp(zrtp_stream_t *stream, char* packet, unsigned int* length)
{
    zrtp_rtp_info_t info;
	zrtp_status_t s = zrtp_status_ok;

    if (!stream || !packet || !length) {
		return zrtp_status_bad_param;
	}
	
	if (*length <= RTP_HDR_SIZE) {
		return zrtp_status_bad_param;
	}
	
	/* Preparse RTP packet: detect type and other options */
	s = _zrtp_packet_preparse(stream, packet, length, &info, 1);
	if (zrtp_status_ok != s) {
		return s;
	}
	
	/*************************************************************************/
	/* For Zfone3 Compatibility */
	if (ZRTP_ZFONEPING == info.type) {
		zrtp_packet_zfoneping_t* ping = (zrtp_packet_zfoneping_t*) info.message;
		zrtp_packet_zfonepingack_t pingack;
		
		zrtp_memcpy(pingack.version, ZRTP_ZFONE_PROTOCOL_VERSION, 4);
		zrtp_memcpy(pingack.endpointhash, stream->session->zid.buffer, sizeof(pingack.endpointhash));
		zrtp_memcpy(pingack.peerendpointhash, ping->endpointhash, sizeof(pingack.endpointhash));
		pingack.peerssrc = info.ssrc;
		
		_zrtp_packet_fill_msg_hdr( stream,
								   ZRTP_ZFONEPINGACK,
								   sizeof(zrtp_packet_zfonepingack_t) - sizeof(zrtp_msg_hdr_t),
								   &pingack.hdr);
		
		_zrtp_packet_send_message(stream, ZRTP_ZFONEPINGACK, &pingack);
		return zrtp_status_drop;
	}
	/*************************************************************************/
	
	/* Skip packet processing within non-started stream */
	if ((stream->state < ZRTP_STATE_START) || (stream->state > ZRTP_STATE_NO_ZRTP)) {		
		return (ZRTP_NONE == info.type) ? zrtp_status_ok : zrtp_status_drop;
	}

	/*
	 * This mutex should protect stream data against asynchr. calls e.g.:
	 * zrtp_stream_secure(), zrtp_stream_clear() etc. Media packet handlers
	 * don't change any internal data, so this applies only to ZRTP messages.
	 */
	if (info.type != ZRTP_NONE) {
		zrtp_mutex_lock(stream->stream_protector);
	}

	/* Extra protection. We need protocol to handle ZRTP messages in following states. */
	switch (stream->state)
	{
	case ZRTP_STATE_INITIATINGSECURE:
	case ZRTP_STATE_WAIT_CONFIRM1:
	case ZRTP_STATE_WAIT_CONFIRMACK:
	case ZRTP_STATE_PENDINGSECURE:
	case ZRTP_STATE_WAIT_CONFIRM2:
	case ZRTP_STATE_SECURE:
	case ZRTP_STATE_SASRELAYING:
		if (!stream->protocol) {
			if (info.type != ZRTP_NONE) {
				zrtp_mutex_unlock(stream->stream_protector);
			}
			return zrtp_status_fail;
		}
	default:
		break;
	}

	/* Handle Error packet from any state */
	if (ZRTP_ERROR == info.type && stream->state > ZRTP_STATE_START)
	{
		switch (stream->state)
		{
		case ZRTP_STATE_NONE:
		case ZRTP_STATE_ACTIVE:
		case ZRTP_STATE_SECURE:
		case ZRTP_STATE_PENDINGERROR:
		case ZRTP_STATE_INITIATINGERROR:
		case ZRTP_STATE_NO_ZRTP:
		    break;
		default:
			{
				zrtp_packet_Error_t* error = (zrtp_packet_Error_t*) info.message;
				_zrtp_machine_enter_pendingerror(stream, zrtp_ntoh32(error->code));
			} break;
		}
	}

	/* Process packet by state-machine according to packet type and current protocol state */
	if (state_handler[stream->state]) {
		s = state_handler[stream->state](stream, &info);
	}

	/* Unlock stream mutex for a ZRTP message packet. See comments above. */
	if (info.type != ZRTP_NONE) {
		s = zrtp_status_drop;
		zrtp_mutex_unlock(stream->stream_protector);
	}

	return s;
}

/*----------------------------------------------------------------------------*/
zrtp_status_t zrtp_stream_start(zrtp_stream_t* stream, uint32_t ssrc)
{
	zrtp_status_t s = zrtp_status_ok;
	 /*
	  * (ZRTP stream starts from START state and HELLO packets resending.
	  * Stream can be started from START, ERROR or NOZRTP states only.)
	  */
	ZRTP_LOG(3,(_ZTU_,"START STREAM ID=%u mode=%s state=%s.\n",
				stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));

	if ( (ZRTP_STATE_ACTIVE != stream->state) &&
		 (ZRTP_STATE_ERROR != stream->state) &&
		 (ZRTP_STATE_NO_ZRTP != stream->state)) {
		ZRTP_LOG(1,(_ZTU_,"ERROR! Can't start Stream ID=%u from %s state.\n",
					stream->id, zrtp_log_state2str(stream->state)));
		s = zrtp_status_wrong_state;
	} else {
		stream->media_ctx.ssrc = zrtp_hton32(ssrc);
		
		_zrtp_change_state(stream, ZRTP_STATE_START);
		_zrtp_machine_start_send_and_resend_hello(stream);
	}
	
	return s;
}

/*----------------------------------------------------------------------------*/
zrtp_status_t zrtp_stream_stop(zrtp_stream_t* stream)
{
	zrtp_status_t s = zrtp_status_ok;
	/*
	 * Stop all packet replays, deinitialize crypto data and prepare the stream
	 * for the next use. The stream can be terminated from any protocol state.
	 */
	 ZRTP_LOG(3,(_ZTU_,"STOP STREAM ID=%u mode=%s state=%s.\n",
				stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));

    if (stream->state != ZRTP_STATE_NONE) {
		/*
		 * This function can be called in parallel to the main processing loop
		 * - protect internal stream data.
		 */
		zrtp_mutex_lock(stream->stream_protector);
		
		_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
		if (stream->zrtp->cb.sched_cb.on_wait_call_later) {
			stream->zrtp->cb.sched_cb.on_wait_call_later(stream);
		}
		
		_clear_stream_crypto(stream);

		zrtp_mutex_unlock(stream->stream_protector);
		zrtp_mutex_destroy(stream->stream_protector);

		zrtp_memset(stream, 0, sizeof(zrtp_stream_t));
		stream->mode = ZRTP_STREAM_MODE_UNKN;
		_zrtp_change_state(stream, ZRTP_STATE_NONE);
    } else {
		s = zrtp_status_wrong_state;
	}
	
	return s;
}

/*----------------------------------------------------------------------------*/
zrtp_status_t zrtp_stream_clear(zrtp_stream_t *stream)
{
	/*
	 * This function can be called for two reasons: either our user is
	 * initiating the go-clear ritual or we accepting that ritual as
	 * initiated by the other end of the line. If our user initiates the
	 * go-clear process libzrtp switches to INITIATING_CLEAR and runs
	 * GoClear replays. The go-clear ritual can be started from SECURE state
	 * only. If the other end of the line is initiating and this function is
	 * being called to accept the go-clear procedure - protocol transites to
	 * CLEAR state imediately. One can accept go-clear from PENDING CLEAR
	 * state only. See state-macine diagram for more information.
	 */
	zrtp_status_t s = zrtp_status_fail;

	/* This function can be called in parallel to the main processing loop - protect stream data. */
	zrtp_mutex_lock(stream->stream_protector);

	ZRTP_LOG(3,(_ZTU_,"CLEAR STREAM ID=%u mode=%s state=%s.\n",
				stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));

    switch (stream->state)
    {
	case ZRTP_STATE_SECURE:
		/* Clearing ritual can't be started if "allow clear" is disabled */
		if (stream->session->profile.allowclear) {
			s = _zrtp_machine_enter_initiatingclear(stream);
		}
		break;
	case ZRTP_STATE_PENDINGCLEAR:
		s = _zrtp_machine_enter_clear(stream);
		break;
	default:
		break;
    }

	zrtp_mutex_unlock(stream->stream_protector);

	return s;
}

/*----------------------------------------------------------------------------*/
void _initiating_secure(zrtp_stream_t *stream, zrtp_retry_task_t* task)
{
	/*
	 * In accordance with the ZRTP standard, there can be multiple simultaneous
	 * DH streams, as well as preshared streams.
	 *
	 * Before entering the INITIATING_SECURE state, we check several conditions.
	 * For details see \doc\img\odg\zrtp_streams.odg and zrtp_statemach.odg)
	 */

	/* The first call to this function is already protected by a mutex in zrtp_process_srtp() */
	uint8_t use_mutex = (task->_retrys > 0);

	if (!task->_is_enabled) {
		return;
	}

	if (use_mutex) {
		zrtp_mutex_lock(stream->stream_protector);
	}
	
	ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure iteration... ID=%u.\n", stream->id));

	/* Skip the last replay after switching to another state to avoid unwanted replays */
	if (stream->state <= ZRTP_STATE_START_INITIATINGSECURE)
	{
		stream->mode = _zrtp_define_stream_mode(stream);
		ZRTP_LOG(3,(_ZTU_,"\tGot mode=%s. Check approval of starting.\n", zrtp_log_mode2str(stream->mode)));
		if (!_zrtp_can_start_stream(stream, &stream->concurrent, stream->mode))
		{
			if (task->_retrys > ZRTP_PROCESS_T1_MAX_COUNT) {
				ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure. Max retransmissions count reached"
							 "for stream ID=%u.\n", stream->id));
				
				_zrtp_machine_enter_initiatingerror(stream, zrtp_error_timeout, 0);
			} else {
				ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure. stream ID=%u is DH but one more DH"
							" stream is in progress - waiting...\n", stream->id));

				task->_retrys++;
				if (stream->zrtp->cb.sched_cb.on_call_later) {
					stream->zrtp->cb.sched_cb.on_call_later(stream, task);
				}
			}
		}
		else
		{
			ZRTP_LOG(3,(_ZTU_,"\tMode=%s Cccepted. Starting ZRTP Initiator Protocol.\n", zrtp_log_mode2str(stream->mode)));
			_zrtp_cancel_send_packet_later(stream, ZRTP_PROCESS);
			_zrtp_machine_enter_initiatingsecure(stream);
		}
	}

	if (use_mutex) {
		zrtp_mutex_unlock(stream->stream_protector);
	}
}

zrtp_status_t _zrtp_machine_start_initiating_secure(zrtp_stream_t *stream)
{
	/*
	 * This function creates a task to do retries of the first packet in the
	 * "Going secure" procedure, and then _initiating_secure() will start
	 * protocol.
	 */
	zrtp_retry_task_t* task = &stream->messages.dh_task;
	task->_is_enabled = 1;
	task->_retrys = 0;
	task->callback = _initiating_secure;
	task->timeout = ZRTP_PROCESS_T1;

	/*
	 * Prevent race conditions on starting multiple streams.
	 */
	zrtp_mutex_lock(stream->session->init_protector);

	_zrtp_change_state(stream, ZRTP_STATE_START_INITIATINGSECURE);
	_initiating_secure(stream, task);

	zrtp_mutex_unlock(stream->session->init_protector);

	return zrtp_status_ok;
}


zrtp_status_t zrtp_stream_secure(zrtp_stream_t *stream)
{
	/*
	 * Wrapper function for going into secure mode.  It can be initiated in
	 * parallel to the main processing loop.  The internal stream data has to
	 * be protected by mutex.
	 */

	zrtp_status_t s = zrtp_status_fail;

	ZRTP_LOG(3,(_ZTU_,"SECURE STREAM ID=%u mode=%s state=%s.\n",
				stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));

	zrtp_mutex_lock(stream->stream_protector);

    /* Limit ZRTP Session initiation procedure according to the license */
	if ( (stream->state == ZRTP_STATE_CLEAR) && ZRTP_PASSIVE1_TEST(stream)) {
		s = _zrtp_machine_start_initiating_secure(stream);
	} else {
		ZRTP_LOG(1,(_ZTU_,"\tWARNING! Can't Start Stream from %s state and with %d license mode. ID=%u\n",
					zrtp_log_state2str(stream->state), stream->zrtp->lic_mode, stream->id));
		
		if (!ZRTP_PASSIVE1_TEST(stream)) {
			if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event ) {
				stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
			}
		}
	}

	zrtp_mutex_unlock(stream->stream_protector);

    return s;
}


/*===========================================================================*/
/*		State handlers														 */
/*===========================================================================*/

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_start( zrtp_stream_t* stream,
													zrtp_rtp_info_t* packet)
{
	zrtp_status_t s = zrtp_status_ok;

	switch (packet->type)
	{
	case ZRTP_HELLO:
		s = _zrtp_machine_process_hello(stream, packet);
		if (zrtp_status_ok != s) {
			ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello() failed with status=%d. ID=%u\n", s, stream->id));
			break; /* Just stay in START state. */
		}

		/* Now we have ZIDs for both sides and can upload secrets from the cache */
		s = _zrtp_prepare_secrets(stream->session);
		if (zrtp_status_ok != s) {
			ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_prepare_secrets() failed with status=%d. ID=%u\n", s, stream->id));
			break; /* Just stay in START state. */
		}

		_send_helloack(stream);
		_zrtp_change_state(stream, ZRTP_STATE_WAIT_HELLOACK);
		break;

	case ZRTP_HELLOACK:
		_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
		_zrtp_change_state(stream, ZRTP_STATE_WAIT_HELLO);
		break;

	default:
		break;
	}

	return s;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_wait4hello( zrtp_stream_t* stream,
														 zrtp_rtp_info_t* packet)
{
	zrtp_status_t s = zrtp_status_ok;

	switch (packet->type)
	{
	case ZRTP_HELLO:
		s = _zrtp_machine_process_hello(stream, packet);
		if (zrtp_status_ok != s) {
			ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello()2 failed with status=%d. ID=%u\n", s, stream->id));
			break; /* Just stay in the current state. */
		}

		/* Now we have ZIDs for both sides and can upload secrets from the cache */
		s = _zrtp_prepare_secrets(stream->session);
		if (zrtp_status_ok != s) {
			ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_prepare_secrets()2 failed with status=%d. ID=%u\n", s, stream->id));
			break; /* Just stay in the current state. */
		}

		/* Start initiating the secure state if "autosecure" is enabled */
		if ((stream->session->profile.autosecure) && ZRTP_PASSIVE1_TEST(stream)) {			
			if (!stream->session->profile.discovery_optimization) {
				_send_helloack(stream); /* Response with HelloAck before start computing DH value */
			}
			s = _zrtp_machine_start_initiating_secure(stream);
		} else {			
			_send_helloack(stream);
			
			if (!ZRTP_PASSIVE1_TEST(stream)) {
				if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
					stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
				}
			}
			
			s = _zrtp_machine_enter_clear(stream);
		}

		break;

	default:
		break;
	}

	return s;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_wait4helloack( zrtp_stream_t* stream,
														    zrtp_rtp_info_t* packet)
{
	zrtp_status_t status = zrtp_status_ok;

	switch (packet->type)
	{
	case ZRTP_HELLO:
		_send_helloack(stream);
		break;

	case ZRTP_COMMIT:
	{
		/* Passive Initiator can't talk to anyone */
		if (ZRTP_PASSIVE2_TEST(stream))
		{
			zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
			if (ZRTP_STATEMACHINE_RESPONDER == role) {
				_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
				status = _zrtp_machine_enter_pendingsecure(stream, packet);
			} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
				_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
				status = _zrtp_machine_start_initiating_secure(stream);
			} else {
				status = zrtp_status_fail;
			}
		} else {
			ZRTP_LOG(2,(_ZTU_,"\tERROR: The endpoint is in passive mode and Signaling Initiator -"
						" can't handle connections from anyone. ID=%u\n", stream->id));
			if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
				stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
			}
			_zrtp_machine_enter_initiatingerror(stream, zrtp_error_service_unavail, 1);												
		}
	} break;

	case ZRTP_HELLOACK:
		_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);

		/* Start initiating the secure state if "autosecure" is enabled */
		if ((stream->session->profile.autosecure) && ZRTP_PASSIVE1_TEST(stream)) {
			status = _zrtp_machine_start_initiating_secure(stream);
		} else {
			if (!ZRTP_PASSIVE1_TEST(stream)) {
				if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
					stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
				}
			}
			status = _zrtp_machine_enter_clear(stream);
		}

		break;

	default:
		break;
	}

	return status;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_clear( zrtp_stream_t* stream,
												    zrtp_rtp_info_t* packet)
{
	zrtp_status_t s = zrtp_status_ok;

	switch (packet->type)
	{
	case ZRTP_GOCLEAR:
		_send_goclearack(stream);
		break;

	case ZRTP_HELLO:
		_send_helloack(stream);
		break;

	case ZRTP_COMMIT:
	{
		zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
		if (ZRTP_STATEMACHINE_RESPONDER == role) {
			s = _zrtp_machine_enter_pendingsecure(stream, packet);
		} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
			s = _zrtp_machine_start_initiating_secure(stream);
		} else {
			s = zrtp_status_fail;
		}
	} break;

	default:
		break;
	}

	return s;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_initiatingclear( zrtp_stream_t* stream,
															  zrtp_rtp_info_t* packet)
{
	zrtp_status_t s = zrtp_status_ok;

	switch (packet->type)
	{
	case ZRTP_GOCLEARACK:
	case ZRTP_COMMIT:
		s = _zrtp_machine_enter_clear(stream);
		break;

	case ZRTP_NONE:
		s = zrtp_status_drop;
		break;

	default:
		break;
	}

	return s;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_pendingclear( zrtp_stream_t* stream,
														   zrtp_rtp_info_t* packet)
{
	zrtp_status_t s = zrtp_status_ok;

	switch (packet->type)
	{
	case ZRTP_GOCLEAR:
		_send_goclearack(stream);
		break;

	case ZRTP_COMMIT:
	{
		zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
		if (ZRTP_STATEMACHINE_RESPONDER == role) {
			s = _zrtp_machine_enter_pendingsecure(stream, packet);
		} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
			s = _zrtp_machine_start_initiating_secure(stream);
		} else {
			s = zrtp_status_fail;
		}
	} break;

	default:
		break;
	}

	return s;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_start_initiatingsecure( zrtp_stream_t* stream,
																	 zrtp_rtp_info_t* packet)
{
	zrtp_status_t s = zrtp_status_ok;

	switch (packet->type)
	{
	case ZRTP_HELLO:
		_send_helloack(stream);
		break;
			
	case ZRTP_COMMIT:
	{
		zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
		if (ZRTP_STATEMACHINE_RESPONDER == role) {
			_zrtp_cancel_send_packet_later(stream, ZRTP_PROCESS);
			s = _zrtp_machine_enter_pendingsecure(stream, packet);
		} else {
			s = zrtp_status_fail;
		}
	} break;

	default:
		break;
	}

	return s;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_secure( zrtp_stream_t* stream,
													 zrtp_rtp_info_t* packet)
{
	zrtp_status_t s = zrtp_status_ok;

	switch (packet->type)
	{
	case ZRTP_CONFIRM2:
		_zrtp_packet_send_message(stream, ZRTP_CONFIRM2ACK, NULL);
		break;

	case ZRTP_SASRELAY:
		/*
		 * _zrtp_machine_process_sasrelay() updates SAS, sends events and does
		 * other things if SAS transferring is allowed
		 */
		s = _zrtp_machine_process_sasrelay(stream, packet);
		if (zrtp_status_ok == s) {
			_zrtp_packet_send_message(stream, ZRTP_RELAYACK, NULL);
		}
		break;

	case ZRTP_GOCLEAR:
		s = _zrtp_machine_process_goclear(stream, packet);
		if (zrtp_status_ok == s) {			
			s = _zrtp_machine_enter_pendingclear(stream);
			_send_goclearack(stream);
		}		
		break;

	case ZRTP_NONE:
		s = _zrtp_protocol_decrypt(stream->protocol, packet, 1);
		break;

	default:
		break;
	}

	return s;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_initiatingerror( zrtp_stream_t* stream,
															  zrtp_rtp_info_t* packet)
{
	switch (packet->type)
	{
	case ZRTP_ERROR:
		_zrtp_machine_enter_pendingerror(stream, ((zrtp_packet_Error_t*) packet->message)->code );
		break;
			 
	case ZRTP_ERRORACK:
		_zrtp_machine_switch_to_error(stream);
		break;
			 
	default:
		break;
	}

	return zrtp_status_ok;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_while_in_nozrtp( zrtp_stream_t* stream,
													 zrtp_rtp_info_t* packet)
{
	zrtp_status_t s = zrtp_status_ok;

	switch (packet->type)
	{
		case ZRTP_HELLO:
			s = _zrtp_machine_process_hello(stream, packet);
			if (zrtp_status_ok != s) {
				ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello()3 failed with status=%d ID=%u.\n", s, stream->id));
				break;
			}
				
			_zrtp_change_state(stream, ZRTP_STATE_START);
			_zrtp_machine_start_send_and_resend_hello(stream);		
			break;
		
		case ZRTP_COMMIT: /* this logic should be similar to Commit handler in ZRTP_STATE_WAIT_HELLOACK state */
		{						
			/* Passive Initiator can't talk to anyone */
			if (ZRTP_PASSIVE2_TEST(stream))
			{
				zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
				if (ZRTP_STATEMACHINE_RESPONDER == role) {					
					s = _zrtp_machine_enter_pendingsecure(stream, packet);
				} else if (ZRTP_STATEMACHINE_INITIATOR == role) {					
					s = _zrtp_machine_start_initiating_secure(stream);
				} else {
					s = zrtp_status_fail;
				}
			} else {
				ZRTP_LOG(2,(_ZTU_,"\tERROR: The endpoint is in passive mode and Signaling Initiator -"
							" can't handle connections from anyone. ID=%u\n", stream->id));				
				if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event ) {
					stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
				}				
				_zrtp_machine_enter_initiatingerror(stream, zrtp_error_service_unavail, 1);
			}
		} break;
			
		default:
			break;
	}

	return s;
}


/* Initiator logic */
extern zrtp_status_t _zrtp_machine_process_while_in_initiatingsecure(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirmack(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirm1(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);

/* Responder logic */
extern zrtp_status_t _zrtp_machine_process_while_in_pendingsecure(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirm2(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);

/* PBX transferring logic */
extern zrtp_status_t _zrtp_machine_process_while_in_sasrelaying(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);

#if (defined(ZRTP_BUILD_FOR_CSD) && (ZRTP_BUILD_FOR_CSD == 1))
/* Driven Discovery state-machine */
extern zrtp_status_t _zrtp_machine_process_while_in_driven_initiator(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
extern zrtp_status_t _zrtp_machine_process_while_in_driven_responder(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
extern zrtp_status_t _zrtp_machine_process_while_in_driven_pending(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
#endif

state_handler_t* state_handler[ZRTP_STATE_COUNT] =
{
	NULL,
	NULL,
	_zrtp_machine_process_while_in_start,
	_zrtp_machine_process_while_in_wait4helloack,
	_zrtp_machine_process_while_in_wait4hello,
	_zrtp_machine_process_while_in_clear,
	_zrtp_machine_process_while_in_start_initiatingsecure,
	_zrtp_machine_process_while_in_initiatingsecure,
	_zrtp_machine_process_while_in_waitconfirm1,
	_zrtp_machine_process_while_in_waitconfirmack,
	_zrtp_machine_process_while_in_pendingsecure,
	_zrtp_machine_process_while_in_waitconfirm2,
	_zrtp_machine_process_while_in_secure,
	_zrtp_machine_process_while_in_sasrelaying,
	_zrtp_machine_process_while_in_initiatingclear,
	_zrtp_machine_process_while_in_pendingclear,
	_zrtp_machine_process_while_in_initiatingerror,
	NULL,
	NULL,
#if (defined(ZRTP_BUILD_FOR_CSD) && (ZRTP_BUILD_FOR_CSD == 1))
	_zrtp_machine_process_while_in_driven_initiator,
	_zrtp_machine_process_while_in_driven_responder,
	_zrtp_machine_process_while_in_driven_pending,
#endif
	_zrtp_machine_process_while_in_nozrtp
};
			 
			 
/*===========================================================================*/
/*		State switchers													     */
/*===========================================================================*/

static void _zrtp_machine_switch_to_error(zrtp_stream_t* stream)
{
	_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
	_clear_stream_crypto(stream);
	
	_zrtp_change_state(stream, ZRTP_STATE_ERROR);
	
	if (stream->zrtp->cb.event_cb.on_zrtp_security_event) {		
		stream->zrtp->cb.event_cb.on_zrtp_security_event(stream, ZRTP_EVENT_PROTOCOL_ERROR);
	}
	if (stream->zrtp->cb.event_cb.on_zrtp_not_secure) {		
		stream->zrtp->cb.event_cb.on_zrtp_not_secure(stream);
	}
    stream->last_error = 0;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_enter_pendingclear(zrtp_stream_t* stream)
{
	_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
	_zrtp_change_state(stream, ZRTP_STATE_PENDINGCLEAR);

	/*
	 * We have to destroy the ZRTP Session Key because user may not press "clear
	 * button", and the remote endpoint may subsequently initiate a new secure
	 * session.  Other secret values will be destroyed in Clear state or
	 * rewritten with new.
	 */
	{
		zrtp_string64_t new_zrtpsess = ZSTR_INIT_EMPTY(new_zrtpsess);
		// TODO: hash
		stream->session->hash->hash( stream->session->hash,
									 ZSTR_GV(stream->session->zrtpsess),
									 ZSTR_GV(new_zrtpsess));
		zrtp_zstrcpy(ZSTR_GV(stream->session->zrtpsess), ZSTR_GV(new_zrtpsess));
	}

	if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
		stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PENDINGCLEAR);
	}

	return zrtp_status_ok;
}

/*---------------------------------------------------------------------------*/
static zrtp_status_t _zrtp_machine_enter_initiatingclear(zrtp_stream_t* stream)
{	
	
	_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
	_zrtp_change_state(stream, ZRTP_STATE_INITIATINGCLEAR);
	
	{
	zrtp_string64_t new_zrtpsess = ZSTR_INIT_EMPTY(new_zrtpsess);
	// TODO: hash
	stream->session->hash->hash( stream->session->hash,
								 ZSTR_GV(stream->session->zrtpsess),
								 ZSTR_GV(new_zrtpsess));
	zrtp_zstrcpy(ZSTR_GV(stream->session->zrtpsess), ZSTR_GV(new_zrtpsess));
	}

	return _zrtp_machine_start_send_and_resend_goclear(stream);
}

/*---------------------------------------------------------------------------*/
static zrtp_status_t _zrtp_machine_enter_clear(zrtp_stream_t* stream)
{
	_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
	_clear_stream_crypto(stream);
	_zrtp_change_state(stream, ZRTP_STATE_CLEAR);

	if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
		stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_CLEAR);
	}

	return zrtp_status_ok;
}


/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_enter_initiatingerror( zrtp_stream_t *stream,
												   zrtp_protocol_error_t code,
												   uint8_t notif)
{
	if ( (ZRTP_STATE_ERROR != stream->state) &&
		 (ZRTP_STATE_INITIATINGERROR != stream->state) &&
		 (ZRTP_STATE_PENDINGERROR != stream->state) )
	{
		stream->last_error = code;
		
		ZRTP_LOG(3,(_ZTU_,"\tEnter InitiatingError State with ERROR:<%s>, notification %s. ID=%u\n",
				zrtp_log_error2str(stream->last_error), (notif?"Enabled":"Disabled"), stream->id));

		/* If we can't deliver a ZRTP message, just switch to the ERROR state. */
		if (notif) {
			_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
			_zrtp_change_state(stream, ZRTP_STATE_INITIATINGERROR);
			_zrtp_machine_start_send_and_resend_error(stream);
		} else {
			_zrtp_machine_switch_to_error(stream);
		}
	}
	
	return zrtp_status_ok;
}

zrtp_status_t _zrtp_machine_enter_pendingerror(zrtp_stream_t *stream, zrtp_protocol_error_t code)
{
	ZRTP_LOG(3,(_ZTU_,"\tEnter PendingError State with ERROR:<%s>. ID=%u\n",
				zrtp_log_error2str(stream->last_error), stream->id));
				
	_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
	_zrtp_change_state(stream, ZRTP_STATE_PENDINGERROR);

	stream->last_error = code;
	_zrtp_machine_start_send_and_resend_errorack(stream);
	return zrtp_status_ok;
}


/*===========================================================================*/
/*		Packet handlers														 */
/*===========================================================================*/

zrtp_status_t _zrtp_machine_process_goclear(zrtp_stream_t* stream, zrtp_rtp_info_t* packet)
{
	zrtp_packet_GoClear_t *goclear	= (zrtp_packet_GoClear_t*) packet->message;
	zrtp_string128_t clear_hmac = ZSTR_INIT_EMPTY(clear_hmac);
	static const zrtp_string16_t clear_hmac_str	= ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_CLEAR_HMAC_STR);

	if (!stream->allowclear) {
		ZRTP_LOG(2, (_ZTU_,"\tWARNING! Allowclear is disabled but GoClear was received. ID=%u.\n", stream->id));		
		_zrtp_machine_enter_initiatingerror(stream, zrtp_error_goclear_unsp, 1);
		return zrtp_status_fail;
	}

	stream->session->hash->hmac( stream->session->hash,
								 ZSTR_GV(stream->cc.peer_hmackey),
								 ZSTR_GV(clear_hmac_str),
								 ZSTR_GV(clear_hmac));
	clear_hmac.length = ZRTP_HMAC_SIZE;

	if (0 != zrtp_memcmp(clear_hmac.buffer, goclear->clear_hmac, ZRTP_HMAC_SIZE)) {
		ZRTP_LOG(2, (_ZTU_,"\tWARNING! Wrong GoClear hmac. ID=%u.\n", stream->id));
		return zrtp_status_fail; /* EH: Just ignore malformed packets */
	}

	return zrtp_status_ok;
}

/*---------------------------------------------------------------------------*/
zrtp_status_t _zrtp_machine_process_hello(zrtp_stream_t* stream, zrtp_rtp_info_t* packet)
{
	zrtp_session_t* session = stream->session;
    zrtp_packet_Hello_t* peer_hello = NULL;
	uint32_t comp_block_len = 0;
	uint8_t id = 0;

	/* Size of HELLO packet must be bigger then <RTP+static HELLO part>. */
	if (*(packet->length) < (ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE)) {
		ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO static size=%d must be=%d. ID=%u\n", *packet->length,
					ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE, stream->id));

		_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
		return zrtp_status_fail;
	}

	peer_hello = (zrtp_packet_Hello_t*) packet->message;

	/* Now we can verify packet size according to size of its parts */
	comp_block_len = ( peer_hello->hc + peer_hello->cc +
					   peer_hello->ac + peer_hello->kc +
					   peer_hello->sc) * ZRTP_COMP_TYPE_SIZE;

	if (*packet->length < (ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + comp_block_len + ZRTP_HMAC_SIZE))
	{
		ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO dynamic size=%d must be=%d. ID=%u\n", *packet->length,
					comp_block_len+ ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE, stream->id));

		_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
		return zrtp_status_fail;
	}

	/* Every component quantity must be less than or equal to 7 */
	if ( (peer_hello->hc > ZRTP_MAX_COMP_COUNT) || (peer_hello->cc > ZRTP_MAX_COMP_COUNT) ||
		 (peer_hello->ac > ZRTP_MAX_COMP_COUNT) || (peer_hello->kc > ZRTP_MAX_COMP_COUNT) ||
		 (peer_hello->sc > ZRTP_MAX_COMP_COUNT) )
	{
		ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO packet data. Components count can't be greater"
					" then 7. ID=%u\n", stream->id));

		_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
		return zrtp_status_fail;
	}
	
	/* Print out ZRTP Hello message for debug purposes */	
	{
	char print_buffer[ZRTP_MAX_COMP_COUNT*20];	
	zrtp_memcpy(print_buffer, peer_hello->comp, comp_block_len);
	print_buffer[comp_block_len] = 0;
	ZRTP_LOG(3,(_ZTU_,"\tProcessing HELLO from %.16s V=%.4s, P=%d, M=%d.\n",
				peer_hello->cliend_id, peer_hello->version, peer_hello->pasive, peer_hello->mitmflag));
	ZRTP_LOG(3,(_ZTU_,"\t\tac=%d cc=%d sc=%d kc=%d\n",
				peer_hello->ac, peer_hello->cc, peer_hello->sc, peer_hello->kc));
	ZRTP_LOG(3,(_ZTU_,"\t\t%s\n", print_buffer));
	}
	
	/*
	 * Check protocol version. Try to resolve versions missmatch according to ZRTP Draft sec. 5.1
	 */
	{
		uint32_t peer_version = 0;
		peer_version = (char)((*peer_hello->version) - '0') *10; /* only 3 first octets are significant */
		peer_version += (char)(*(peer_hello->version+2) - '0');
				
		if ((ZRTP_PROTOCOL_VERSION_VALUE/10) == peer_version) {
			ZRTP_LOG(3,(_ZTU_,"\tReceived HELLO had the same protocol V.\n"));
		}
		else if ((ZRTP_PROTOCOL_VERSION_VALUE/10) < peer_version) {
			ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received HELLO greater ZRTP V=%d - wait for other party"
						" to resolve this issue. ID=%u.\n", peer_version, stream->id));
		} else {
			ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received a ZRTP_HELLO smaller ZRTP V=%d and we don't"
						" support it - terminate session. ID=%u\n", peer_version, stream->id));
			
			_zrtp_machine_enter_initiatingerror(stream, zrtp_error_version, 1);
			return zrtp_status_fail;
		}
	}
	
	/* Close session if ZID duplication */
	if (!zrtp_memcmp(stream->messages.hello.zid, peer_hello->zid, sizeof(zrtp_zid_t))) {
		ZRTP_LOG(2,(_ZTU_,ZRTP_EQUAL_ZID_WARNING_STR));
		_zrtp_machine_enter_initiatingerror(stream, zrtp_error_equal_zid, 1);
		return zrtp_status_fail;
	}	

	/* All streams within a single session MUST have the same ZID */
	if (session->peer_zid.length > 0) {
		if (0 != zrtp_memcmp(session->peer_zid.buffer, peer_hello->zid, sizeof(zrtp_zid_t))) {
			ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received HELLO which had a different ZID from that of the"
						" previous stream within the same session. sID=%u ID=%u\n", session->id, stream->id));

			_zrtp_machine_enter_initiatingerror(stream, zrtp_error_wrong_zid, 1);
			return zrtp_status_fail;
		}
	} else {
		zrtp_zstrncpyc(ZSTR_GV(session->peer_zid), (const char*) peer_hello->zid, sizeof(zrtp_zid_t));
	}

	/* Remember remote Passive flag */
	stream->peer_passive = peer_hello->pasive;	
	stream->peer_mitm_flag = peer_hello->mitmflag;
	if (stream->peer_mitm_flag) {
		stream->mitm_mode = ZRTP_MITM_MODE_CLIENT;
	}
	
	/* Current version doesn't support Digital Signatures. Ignore peer Hello with S flag enabled. */
	if (peer_hello->sigflag) {
		ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received a ZRTP_HELLO with S flag enabled. We don't support Digital Signatures - ignore message.\n"));
		return zrtp_status_fail;
	}
	
	/* Copy packet for future hashing */
	zrtp_memcpy(&stream->messages.peer_hello, peer_hello, zrtp_ntoh16(peer_hello->hdr.length)*4);
	stream->is_hello_received = 1;

	/*
	 * Choose PK exchange scheme and PK mode.
	 * We do this right after receiving Hello to speedup DH calculations.
	 */
	stream->pubkeyscheme = zrtp_comp_find(ZRTP_CC_PKT, ZRTP_PKTYPE_DH3072, session->zrtp);
	id = _zrtp_choose_best_comp(&session->profile, peer_hello, ZRTP_CC_PKT);
	if (id != ZRTP_COMP_UNKN) {
		stream->pubkeyscheme = zrtp_comp_find(ZRTP_CC_PKT, id, session->zrtp);
	}
	
	ZRTP_LOG(3,(_ZTU_,"\tReceived HELLO Accepted\n"));
	
    return zrtp_status_ok;
}


/*===========================================================================*/
/*		Packet senders														 */
/*===========================================================================*/

/*---------------------------------------------------------------------------*/
static void _send_and_resend_hello(zrtp_stream_t* stream, zrtp_retry_task_t* task)
{	
	if ((task->_retrys == ZRTP_NO_ZRTP_FAST_COUNT) && !stream->is_hello_received) {
		ZRTP_LOG(2,(_ZTU_,"WARNING! HELLO have been resent %d times without a response."
					" Raising ZRTP_EVENT_NO_ZRTP_QUICK event. ID=%u\n", task->_retrys, stream->id));

		if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
			stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_NO_ZRTP_QUICK);
		}
	}
	
	if (task->_retrys >= (uint32_t)((ZRTP_STATE_WAIT_HELLOACK==stream->state)?ZRTP_T1_MAX_COUNT_EXT:ZRTP_T1_MAX_COUNT)) {				
		ZRTP_LOG(2,(_ZTU_,"WARNING! HELLO Max retransmissions count reached (%d retries). ID=%u\n", task->_retrys, stream->id));

		_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
		_clear_stream_crypto(stream);
		_zrtp_change_state(stream, ZRTP_STATE_NO_ZRTP);
		
		if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
			stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_NO_ZRTP);
		}
	} else if (task->_is_enabled) {		
		zrtp_status_t s = _zrtp_packet_send_message(stream, ZRTP_HELLO, &stream->messages.hello);
		task->timeout = _zrtp_get_timeout((uint32_t)task->timeout, ZRTP_HELLO);
		if (zrtp_status_ok == s) {
			task->_retrys++;
		}
		
		
		if (stream->zrtp->cb.sched_cb.on_call_later) {
			stream->zrtp->cb.sched_cb.on_call_later(stream, task);
		}
	}
}

zrtp_status_t _zrtp_machine_start_send_and_resend_hello(zrtp_stream_t* stream)
{
	zrtp_retry_task_t* task = &stream->messages.hello_task;
	
	task->_is_enabled = 1;
	task->callback = _send_and_resend_hello;
	task->_retrys = 0;
	
	_send_and_resend_hello(stream, task);
	
	return zrtp_status_ok;
}

static void _send_helloack(zrtp_stream_t* stream)
{
	_zrtp_packet_send_message(stream, ZRTP_HELLOACK, NULL);
}


/*---------------------------------------------------------------------------*/
static void _send_and_resend_goclear(zrtp_stream_t* stream, zrtp_retry_task_t* task)
{
	if (task->_is_enabled) {
		if (task->_retrys > ZRTP_T2_MAX_COUNT) {
			ZRTP_LOG(2,(_ZTU_,"\tWARNING!: GOCLEAR Nax retransmissions count reached. ID=%u\n", stream->id));
			_zrtp_machine_enter_clear(stream);
		} else {
			zrtp_packet_GoClear_t* goclear = (zrtp_packet_GoClear_t*) &stream->messages.goclear;

			_zrtp_packet_send_message(stream, ZRTP_GOCLEAR, goclear);
			task->_retrys++;
			if (stream->zrtp->cb.sched_cb.on_call_later) {
				stream->zrtp->cb.sched_cb.on_call_later(stream, task);
			}
		}
	}
}

static zrtp_status_t  _zrtp_machine_start_send_and_resend_goclear(zrtp_stream_t* stream)
{
	zrtp_retry_task_t* task = &stream->messages.goclear_task;
	zrtp_string128_t clear_hmac = ZSTR_INIT_EMPTY(clear_hmac);
	static const zrtp_string16_t clear_hmac_str	= ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_CLEAR_HMAC_STR);
	
	zrtp_memset(&stream->messages.goclear, 0, sizeof(zrtp_packet_GoClear_t));
	
	/* Compute Clear HMAC as: HMAC(hmackey, "Clear hmac") */
	stream->session->hash->hmac( stream->session->hash,
								 ZSTR_GV(stream->cc.hmackey),
								 ZSTR_GV(clear_hmac_str),
								 ZSTR_GV(clear_hmac));
	clear_hmac.length = ZRTP_HMAC_SIZE;
	
	zrtp_memcpy(stream->messages.goclear.clear_hmac, clear_hmac.buffer, clear_hmac.length);	
	_zrtp_packet_fill_msg_hdr( stream,
							   ZRTP_GOCLEAR,
							   sizeof(zrtp_packet_GoClear_t) - sizeof(zrtp_msg_hdr_t),
							   &stream->messages.goclear.hdr);
	
	task->_is_enabled	= 1;
	task->callback		= _send_and_resend_goclear;
	task->timeout		= ZRTP_T2;
	task->_retrys		= 0;
	
	_send_and_resend_goclear(stream, task);
	
	return zrtp_status_ok;
}


static void _send_goclearack(zrtp_stream_t* stream)
{
	_zrtp_packet_send_message(stream, ZRTP_GOCLEARACK, NULL);
}

/*---------------------------------------------------------------------------*/
static void _send_and_resend_error(zrtp_stream_t* stream, zrtp_retry_task_t* task)
{
	if (task->_retrys >= ZRTP_ETI_MAX_COUNT) {
		ZRTP_LOG(2,(_ZTU_,"\tWARNING! ERROR Max retransmissions count reached. ID=%u\n", stream->id));
		_zrtp_machine_switch_to_error(stream);
	} else if (task->_is_enabled) {
		if (zrtp_status_ok == _zrtp_packet_send_message(stream, ZRTP_ERROR, &stream->messages.error)) {
			task->_retrys++;
		}
		if (stream->zrtp->cb.sched_cb.on_call_later) {
			stream->zrtp->cb.sched_cb.on_call_later(stream, task);
		}
	}
}

static zrtp_status_t  _zrtp_machine_start_send_and_resend_error(zrtp_stream_t* stream)
{
	zrtp_retry_task_t* task = &stream->messages.error_task;
	
	zrtp_memset(&stream->messages.error, 0, sizeof(zrtp_packet_Error_t));
	stream->messages.error.code = zrtp_hton32(stream->last_error);
	
	_zrtp_packet_fill_msg_hdr( stream,
							   ZRTP_ERROR,
							   sizeof(zrtp_packet_Error_t) - sizeof(zrtp_msg_hdr_t),
							   &stream->messages.error.hdr);
	
	task->_is_enabled	= 1;
	task->callback		= _send_and_resend_error;
	task->timeout		= ZRTP_ET;
	task->_retrys		= 0;
	
	_send_and_resend_error(stream, task);
	
	return zrtp_status_ok;
}

/*---------------------------------------------------------------------------*/
static void _send_and_resend_errorack(zrtp_stream_t* stream, zrtp_retry_task_t* task)
{
	if (task->_retrys >= ZRTP_ETR_MAX_COUNT) {
		ZRTP_LOG(2,(_ZTU_,"\tWARNING! ERRORACK Max retransmissions count reached. ID=%u\n", stream->id));
		_zrtp_machine_switch_to_error(stream);
	} else if (task->_is_enabled) {
		if (zrtp_status_ok == _zrtp_packet_send_message(stream, ZRTP_ERRORACK, NULL)) {
			task->_retrys++;
		}
		if (stream->zrtp->cb.sched_cb.on_call_later) {
			stream->zrtp->cb.sched_cb.on_call_later(stream, task);
		}
	}
}

static zrtp_status_t  _zrtp_machine_start_send_and_resend_errorack(zrtp_stream_t* stream)
{
	zrtp_retry_task_t* task = &stream->messages.errorack_task;
	
	task->_is_enabled	= 1;
	task->callback		= _send_and_resend_errorack;
	task->timeout		= ZRTP_ET;
	task->_retrys		= 0;
	
	_send_and_resend_errorack(stream, task);
	
	return zrtp_status_ok;
}


void _clear_stream_crypto(zrtp_stream_t* stream)
{
	if (stream->protocol) {
		_zrtp_protocol_destroy(stream->protocol);
		stream->protocol = 0;
	}

	zrtp_wipe_zstring(ZSTR_GV(stream->cc.hmackey));
	zrtp_wipe_zstring(ZSTR_GV(stream->cc.peer_hmackey));
	zrtp_wipe_zstring(ZSTR_GV(&stream->cc.zrtp_key));
	zrtp_wipe_zstring(ZSTR_GV(stream->cc.peer_zrtp_key));
}